xref: /freebsd/sys/dev/sound/pcm/buffer.c (revision 6de306ecee3831f48debaad1d0b22418faa48e10)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <dev/sound/pcm/sound.h>
30 
31 static void
32 sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
33 {
34 	snd_dbuf *b = (snd_dbuf *)arg;
35 
36 	if (bootverbose) {
37 		printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr,
38 		       (unsigned long)segs->ds_len);
39 		printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf));
40 	}
41 }
42 
43 /*
44  * Allocate memory for DMA buffer. If the device do not perform DMA transfer,
45  * the driver can call malloc(9) by its own.
46  */
47 int
48 sndbuf_alloc(snd_dbuf *b, bus_dma_tag_t dmatag, int size)
49 {
50 	b->dmatag = dmatag;
51 	b->maxsize = size;
52 	b->bufsize = b->maxsize;
53 	if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, &b->dmamap))
54 		return ENOSPC;
55 	if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0))
56 		return ENOSPC;
57 	return sndbuf_resize(b, 2, b->maxsize / 2);
58 }
59 
60 int
61 sndbuf_setup(snd_dbuf *b, void *buf, int size)
62 {
63 	bzero(b, sizeof(*b));
64 	b->buf = buf;
65 	b->maxsize = size;
66 	b->bufsize = b->maxsize;
67 	return sndbuf_resize(b, 2, b->maxsize / 2);
68 }
69 
70 void
71 sndbuf_free(snd_dbuf *b)
72 {
73 	bus_dmamap_unload(b->dmatag, b->dmamap);
74 	bus_dmamem_free(b->dmatag, b->buf, b->dmamap);
75 }
76 
77 int
78 sndbuf_resize(snd_dbuf *b, int blkcnt, int blksz)
79 {
80 	if (blkcnt == 0)
81 		blkcnt = b->blkcnt;
82 	if (blksz == 0)
83 		blksz = b->blksz;
84 	if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize))
85 		return EINVAL;
86 	b->blkcnt = blkcnt;
87 	b->blksz = blksz;
88 	b->bufsize = blkcnt * blksz;
89 	sndbuf_reset(b);
90 	return 0;
91 }
92 
93 void
94 sndbuf_clear(snd_dbuf *b, int length)
95 {
96 	int i;
97 	u_int16_t data, *p;
98 
99 	if (length == 0)
100 		return;
101 
102 	if (b->fmt & AFMT_SIGNED)
103 		data = 0x00;
104 	else
105 		data = 0x80;
106 
107 	if (b->fmt & AFMT_16BIT)
108 		data <<= 8;
109 	else
110 		data |= data << 8;
111 
112 	if (b->fmt & AFMT_BIGENDIAN)
113 		data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
114 
115 	i = b->fp;
116 	p = (u_int16_t *)(b->buf + b->fp);
117 	while (length > 1) {
118 		*p++ = data;
119 		length -= 2;
120 		i += 2;
121 		if (i >= b->bufsize) {
122 			p = (u_int16_t *)b->buf;
123 			i = 0;
124 		}
125 	}
126 	if (length == 1)
127 		*(b->buf + i) = data & 0xff;
128 }
129 
130 void
131 sndbuf_reset(snd_dbuf *b)
132 {
133 	b->rp = b->fp = 0;
134 	b->dl = b->rl = 0;
135 	b->fl = b->bufsize;
136 	b->prev_total = b->total = 0;
137 	b->prev_int_count = b->int_count = 0;
138 	b->underflow = 0;
139 	if (b->buf && b->bufsize > 0)
140 		sndbuf_clear(b, b->bufsize);
141 }
142 
143 int
144 sndbuf_setfmt(snd_dbuf *b, u_int32_t fmt)
145 {
146 	b->fmt = fmt;
147 	b->bps = 1;
148 	b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0;
149 	b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0;
150 	b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0;
151 	return 0;
152 }
153 
154 int
155 sndbuf_getbps(snd_dbuf *b)
156 {
157 	return b->bps;
158 }
159 
160 void *
161 sndbuf_getbuf(snd_dbuf *b)
162 {
163 	return b->buf;
164 }
165 
166 int
167 sndbuf_getsize(snd_dbuf *b)
168 {
169 	return b->bufsize;
170 }
171 
172 int
173 sndbuf_runsz(snd_dbuf *b)
174 {
175 	return b->dl;
176 }
177 
178 int
179 sndbuf_isadmasetup(snd_dbuf *b, struct resource *drq)
180 {
181 	/* should do isa_dma_acquire/isa_dma_release here */
182 	if (drq == NULL) {
183 		b->flags &= ~SNDBUF_F_ISADMA;
184 		b->chan = -1;
185 	} else {
186 		b->flags &= ~SNDBUF_F_ISADMA;
187 		b->chan = rman_get_start(drq);
188 	}
189 	return 0;
190 }
191 
192 int
193 sndbuf_isadmasetdir(snd_dbuf *b, int dir)
194 {
195 	b->dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ;
196 	return 0;
197 }
198 
199 void
200 sndbuf_isadma(snd_dbuf *b, int go)
201 {
202 	KASSERT(b, ("sndbuf_isadma called with b == NULL"));
203 	KASSERT(ISA_DMA(b), ("sndbuf_isadma called on non-ISA channel"));
204 
205 	switch (go) {
206 	case PCMTRIG_START:
207 		/* isa_dmainit(b->chan, size); */
208 		isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan);
209 		break;
210 
211 	case PCMTRIG_STOP:
212 	case PCMTRIG_ABORT:
213 		isa_dmastop(b->chan);
214 		isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->chan);
215 		break;
216 	}
217 
218 	DEB(printf("buf 0x%p ISA DMA %s, channel %d\n",
219 		b,
220 		(go == PCMTRIG_START)? "started" : "stopped",
221 		b->chan));
222 }
223 
224 int
225 sndbuf_isadmaptr(snd_dbuf *b)
226 {
227 	if (ISA_DMA(b)) {
228 		int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
229 		if (i < 0)
230 			i = 0;
231 		return b->bufsize - i;
232     	} else KASSERT(1, ("sndbuf_isadmaptr called on invalid channel"));
233 	return -1;
234 }
235 
236 void
237 sndbuf_isadmabounce(snd_dbuf *b)
238 {
239 	if (ISA_DMA(b)) {
240 		/* tell isa_dma to bounce data in/out */
241     	} else
242 		KASSERT(1, ("chn_isadmabounce called on invalid channel"));
243 }
244 
245