1 /*        $NetBSD: cs428x.c,v 1.20 2019/06/08 08:02:38 isaki Exp $    */
2 
3 /*
4  * Copyright (c) 2000 Tatoku Ogaito.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Tatoku Ogaito
17  *        for the NetBSD Project.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /* Common functions for CS4280 and CS4281 */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: cs428x.c,v 1.20 2019/06/08 08:02:38 isaki Exp $");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/kmem.h>
42 #include <sys/device.h>
43 #include <sys/audioio.h>
44 #include <sys/bus.h>
45 
46 #include <dev/audio/audio_if.h>
47 #include <dev/midi_if.h>
48 
49 #include <dev/ic/ac97reg.h>
50 #include <dev/ic/ac97var.h>
51 
52 #include <dev/pci/pcidevs.h>
53 #include <dev/pci/pcivar.h>
54 #include <dev/pci/cs428xreg.h>
55 #include <dev/pci/cs428x.h>
56 
57 #if defined(CS4280_DEBUG) || defined(CS4281_DEBUG)
58 int cs428x_debug = 0;
59 #endif
60 
61 int
cs428x_round_blocksize(void * addr,int blk,int mode,const audio_params_t * param)62 cs428x_round_blocksize(void *addr, int blk,
63     int mode, const audio_params_t *param)
64 {
65           struct cs428x_softc *sc;
66           int retval;
67 
68           DPRINTFN(5,("cs428x_round_blocksize blk=%d -> ", blk));
69 
70           sc = addr;
71           if (blk < sc->hw_blocksize)
72                     retval = sc->hw_blocksize;
73           else
74                     retval = blk & -(sc->hw_blocksize);
75 
76           DPRINTFN(5,("%d\n", retval));
77 
78           return retval;
79 }
80 
81 int
cs428x_mixer_set_port(void * addr,mixer_ctrl_t * cp)82 cs428x_mixer_set_port(void *addr, mixer_ctrl_t *cp)
83 {
84           struct cs428x_softc *sc;
85           int val;
86 
87           sc = addr;
88           val = sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
89           DPRINTFN(3,("mixer_set_port: val=%d\n", val));
90           return (val);
91 }
92 
93 int
cs428x_mixer_get_port(void * addr,mixer_ctrl_t * cp)94 cs428x_mixer_get_port(void *addr, mixer_ctrl_t *cp)
95 {
96           struct cs428x_softc *sc;
97 
98           sc = addr;
99           return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
100 }
101 
102 int
cs428x_query_devinfo(void * addr,mixer_devinfo_t * dip)103 cs428x_query_devinfo(void *addr, mixer_devinfo_t *dip)
104 {
105           struct cs428x_softc *sc;
106 
107           sc = addr;
108           return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
109 }
110 
111 void *
cs428x_malloc(void * addr,int direction,size_t size)112 cs428x_malloc(void *addr, int direction, size_t size)
113 {
114           struct cs428x_softc *sc;
115           struct cs428x_dma   *p;
116           int error;
117 
118           sc = addr;
119 
120           p = kmem_alloc(sizeof(*p), KM_SLEEP);
121           error = cs428x_allocmem(sc, size, p);
122           if (error) {
123                     kmem_free(p, sizeof(*p));
124                     return 0;
125           }
126 
127           p->next = sc->sc_dmas;
128           sc->sc_dmas = p;
129           return BUFADDR(p);
130 }
131 
132 void
cs428x_free(void * addr,void * ptr,size_t size)133 cs428x_free(void *addr, void *ptr, size_t size)
134 {
135           struct cs428x_softc *sc;
136           struct cs428x_dma **pp, *p;
137 
138           sc = addr;
139           for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
140                     if (BUFADDR(p) == ptr) {
141                               bus_dmamap_unload(sc->sc_dmatag, p->map);
142                               bus_dmamap_destroy(sc->sc_dmatag, p->map);
143                               bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
144                               bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
145                               kmem_free(p->dum, p->size);
146                               *pp = p->next;
147                               kmem_free(p, sizeof(*p));
148                               return;
149                     }
150           }
151 }
152 
153 size_t
cs428x_round_buffersize(void * addr,int direction,size_t size)154 cs428x_round_buffersize(void *addr, int direction,
155     size_t size)
156 {
157           /* The real DMA buffersize are 4KB for CS4280
158            * and 64kB/MAX_CHANNELS for CS4281.
159            * But they are too small for high quality audio,
160            * let the upper layer(audio) use a larger buffer.
161            * (originally suggested by Lennart Augustsson.)
162            */
163           return size;
164 }
165 
166 int
cs428x_get_props(void * addr)167 cs428x_get_props(void *addr)
168 {
169 
170           return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
171               AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
172 }
173 
174 /* AC97 */
175 int
cs428x_attach_codec(void * addr,struct ac97_codec_if * codec_if)176 cs428x_attach_codec(void *addr, struct ac97_codec_if *codec_if)
177 {
178           struct cs428x_softc *sc;
179 
180           DPRINTF(("cs428x_attach_codec:\n"));
181           sc = addr;
182           sc->codec_if = codec_if;
183           return 0;
184 }
185 
186 int
cs428x_read_codec(void * addr,uint8_t ac97_addr,uint16_t * ac97_data)187 cs428x_read_codec(void *addr, uint8_t ac97_addr, uint16_t *ac97_data)
188 {
189           struct cs428x_softc *sc;
190           uint32_t acctl;
191           int n;
192 
193           sc = addr;
194 
195           DPRINTFN(5,("read_codec: add=0x%02x ", ac97_addr));
196           /*
197            * Make sure that there is not data sitting around from a previous
198            * uncompleted access.
199            */
200           BA0READ4(sc, CS428X_ACSDA);
201 
202           /* Set up AC97 control registers. */
203           BA0WRITE4(sc, CS428X_ACCAD, ac97_addr);
204           BA0WRITE4(sc, CS428X_ACCDA, 0);
205 
206           acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_CRW  | ACCTL_DCV;
207           if (sc->type == TYPE_CS4280)
208                     acctl |= ACCTL_RSTN;
209           BA0WRITE4(sc, CS428X_ACCTL, acctl);
210 
211           if (cs428x_src_wait(sc) < 0) {
212                     printf("%s: AC97 read prob. (DCV!=0) for add=0x%0x\n",
213                            device_xname(sc->sc_dev), ac97_addr);
214                     return 1;
215           }
216 
217           /* wait for valid status bit is active */
218           n = 0;
219           while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_VSTS) == 0) {
220                     delay(1);
221                     while (++n > 1000) {
222                               printf("%s: AC97 read fail (VSTS==0) for add=0x%0x\n",
223                                      device_xname(sc->sc_dev), ac97_addr);
224                               return 1;
225                     }
226           }
227           *ac97_data = BA0READ4(sc, CS428X_ACSDA);
228           DPRINTFN(5,("data=0x%04x\n", *ac97_data));
229           return 0;
230 }
231 
232 int
cs428x_write_codec(void * addr,uint8_t ac97_addr,uint16_t ac97_data)233 cs428x_write_codec(void *addr, uint8_t ac97_addr, uint16_t ac97_data)
234 {
235           struct cs428x_softc *sc;
236           uint32_t acctl;
237 
238           sc = addr;
239 
240           DPRINTFN(5,("write_codec: add=0x%02x  data=0x%04x\n", ac97_addr, ac97_data));
241           BA0WRITE4(sc, CS428X_ACCAD, ac97_addr);
242           BA0WRITE4(sc, CS428X_ACCDA, ac97_data);
243 
244           acctl = ACCTL_ESYN | ACCTL_VFRM | ACCTL_DCV;
245           if (sc->type == TYPE_CS4280)
246                     acctl |= ACCTL_RSTN;
247           BA0WRITE4(sc, CS428X_ACCTL, acctl);
248 
249           if (cs428x_src_wait(sc) < 0) {
250                     printf("%s: AC97 write fail (DCV!=0) for add=0x%02x data="
251                            "0x%04x\n", device_xname(sc->sc_dev), ac97_addr, ac97_data);
252                     return 1;
253           }
254           return 0;
255 }
256 
257 /* Internal functions */
258 int
cs428x_allocmem(struct cs428x_softc * sc,size_t size,struct cs428x_dma * p)259 cs428x_allocmem(struct cs428x_softc *sc, size_t size, struct cs428x_dma *p)
260 {
261           int error;
262           size_t align;
263 
264           align   = sc->dma_align;
265           p->size = sc->dma_size;
266           /* allocate memory for upper audio driver */
267           p->dum  = kmem_alloc(size, KM_SLEEP);
268 
269           error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
270                                          p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
271                                          &p->nsegs, BUS_DMA_WAITOK);
272           if (error) {
273                     aprint_error_dev(sc->sc_dev, "unable to allocate DMA. error=%d\n",
274                            error);
275                     goto allfree;
276           }
277 
278           error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
279                                      &p->addr, BUS_DMA_WAITOK|BUS_DMA_COHERENT);
280           if (error) {
281                     aprint_error_dev(sc->sc_dev, "unable to map DMA, error=%d\n",
282                            error);
283                     goto free;
284           }
285 
286           error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
287                                           0, BUS_DMA_WAITOK, &p->map);
288           if (error) {
289                     aprint_error_dev(sc->sc_dev, "unable to create DMA map, error=%d\n",
290                            error);
291                     goto unmap;
292           }
293 
294           error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
295                                         BUS_DMA_WAITOK);
296           if (error) {
297                     aprint_error_dev(sc->sc_dev, "unable to load DMA map, error=%d\n",
298                            error);
299                     goto destroy;
300           }
301           return 0;
302 
303  destroy:
304           bus_dmamap_destroy(sc->sc_dmatag, p->map);
305  unmap:
306           bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
307  free:
308           bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
309  allfree:
310           kmem_free(p->dum, size);
311 
312           return error;
313 }
314 
315 int
cs428x_src_wait(struct cs428x_softc * sc)316 cs428x_src_wait(struct cs428x_softc *sc)
317 {
318           int n;
319 
320           n = 0;
321           while ((BA0READ4(sc, CS428X_ACCTL) & ACCTL_DCV)) {
322                     delay(1000);
323                     while (++n > 1000) {
324                               printf("cs428x_src_wait: 0x%08x\n",
325                                   BA0READ4(sc, CS428X_ACCTL));
326                               return -1;
327                     }
328           }
329           return 0;
330 }
331 
332 void
cs428x_get_locks(void * addr,kmutex_t ** intr,kmutex_t ** thread)333 cs428x_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread)
334 {
335           struct cs428x_softc *sc;
336 
337           sc = addr;
338           *intr = &sc->sc_intr_lock;
339           *thread = &sc->sc_lock;
340 }
341