Commit | Line | Data |
---|---|---|
984263bc | 1 | /*- |
558a398b | 2 | * Copyright (c) 2000-2004 Taku YAMAMOTO <taku@tackymt.homeip.net> |
984263bc MD |
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 | * | |
558a398b | 26 | * maestro.c,v 1.23.2.1 2003/10/03 18:21:38 taku Exp |
984263bc MD |
27 | */ |
28 | ||
29 | /* | |
30 | * Credits: | |
31 | * | |
32 | * Part of this code (especially in many magic numbers) was heavily inspired | |
33 | * by the Linux driver originally written by | |
34 | * Alan Cox <alan.cox@linux.org>, modified heavily by | |
35 | * Zach Brown <zab@zabbo.net>. | |
36 | * | |
37 | * busdma()-ize and buffer size reduction were suggested by | |
558a398b | 38 | * Cameron Grant <cg@freebsd.org>. |
984263bc MD |
39 | * Also he showed me the way to use busdma() suite. |
40 | * | |
41 | * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 | |
42 | * were looked at by | |
43 | * Munehiro Matsuda <haro@tk.kubota.co.jp>, | |
44 | * who brought patches based on the Linux driver with some simplification. | |
558a398b SS |
45 | * |
46 | * Hardware volume controller was implemented by | |
47 | * John Baldwin <jhb@freebsd.org>. | |
984263bc MD |
48 | */ |
49 | ||
2a1ad637 FT |
50 | #ifdef HAVE_KERNEL_OPTION_HEADERS |
51 | #include "opt_snd.h" | |
52 | #endif | |
53 | ||
984263bc MD |
54 | #include <dev/sound/pcm/sound.h> |
55 | #include <dev/sound/pcm/ac97.h> | |
67931cc4 FT |
56 | #include <bus/pci/pcireg.h> |
57 | #include <bus/pci/pcivar.h> | |
58 | #include <sys/thread2.h> | |
984263bc MD |
59 | |
60 | #include <dev/sound/pci/maestro_reg.h> | |
61 | ||
2a1ad637 | 62 | SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/maestro.c 274035 2014-11-03 11:11:45Z bapt $"); |
984263bc MD |
63 | |
64 | /* | |
65 | * PCI IDs of supported chips: | |
66 | * | |
67 | * MAESTRO-1 0x01001285 | |
68 | * MAESTRO-2 0x1968125d | |
69 | * MAESTRO-2E 0x1978125d | |
70 | */ | |
71 | ||
72 | #define MAESTRO_1_PCI_ID 0x01001285 | |
73 | #define MAESTRO_2_PCI_ID 0x1968125d | |
74 | #define MAESTRO_2E_PCI_ID 0x1978125d | |
75 | ||
76 | #define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ | |
77 | #define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ | |
78 | ||
558a398b SS |
79 | #ifdef AGG_MAXPLAYCH |
80 | # if AGG_MAXPLAYCH > 4 | |
81 | # undef AGG_MAXPLAYCH | |
82 | # define AGG_MAXPLAYCH 4 | |
83 | # endif | |
84 | #else | |
984263bc MD |
85 | # define AGG_MAXPLAYCH 4 |
86 | #endif | |
87 | ||
88 | #define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ | |
89 | ||
90 | ||
67931cc4 FT |
91 | /* compatibility */ |
92 | # define critical_enter() crit_enter() | |
93 | # define critical_exit() crit_exit() | |
94 | ||
558a398b SS |
95 | #ifndef PCIR_BAR |
96 | #define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) | |
97 | #endif | |
98 | ||
99 | ||
984263bc MD |
100 | /* ----------------------------- |
101 | * Data structures. | |
102 | */ | |
103 | struct agg_chinfo { | |
558a398b SS |
104 | /* parent softc */ |
105 | struct agg_info *parent; | |
106 | ||
107 | /* FreeBSD newpcm related */ | |
108 | struct pcm_channel *channel; | |
109 | struct snd_dbuf *buffer; | |
110 | ||
111 | /* OS independent */ | |
2a1ad637 | 112 | bus_dmamap_t map; |
558a398b SS |
113 | bus_addr_t phys; /* channel buffer physical address */ |
114 | bus_addr_t base; /* channel buffer segment base */ | |
115 | u_int32_t blklen; /* DMA block length in WORDs */ | |
116 | u_int32_t buflen; /* channel buffer length in WORDs */ | |
117 | u_int32_t speed; | |
118 | unsigned num : 3; | |
119 | unsigned stereo : 1; | |
120 | unsigned qs16 : 1; /* quantum size is 16bit */ | |
121 | unsigned us : 1; /* in unsigned format */ | |
122 | }; | |
123 | ||
124 | struct agg_rchinfo { | |
125 | /* parent softc */ | |
984263bc | 126 | struct agg_info *parent; |
558a398b SS |
127 | |
128 | /* FreeBSD newpcm related */ | |
984263bc MD |
129 | struct pcm_channel *channel; |
130 | struct snd_dbuf *buffer; | |
558a398b SS |
131 | |
132 | /* OS independent */ | |
2a1ad637 | 133 | bus_dmamap_t map; |
558a398b SS |
134 | bus_addr_t phys; /* channel buffer physical address */ |
135 | bus_addr_t base; /* channel buffer segment base */ | |
136 | u_int32_t blklen; /* DMA block length in WORDs */ | |
137 | u_int32_t buflen; /* channel buffer length in WORDs */ | |
984263bc | 138 | u_int32_t speed; |
558a398b SS |
139 | unsigned : 3; |
140 | unsigned stereo : 1; | |
141 | bus_addr_t srcphys; | |
142 | int16_t *src; /* stereo peer buffer */ | |
143 | int16_t *sink; /* channel buffer pointer */ | |
144 | volatile u_int32_t hwptr; /* ready point in 16bit sample */ | |
984263bc MD |
145 | }; |
146 | ||
147 | struct agg_info { | |
558a398b | 148 | /* FreeBSD newbus related */ |
984263bc | 149 | device_t dev; |
558a398b SS |
150 | |
151 | /* I wonder whether bus_space_* are in common in *BSD... */ | |
984263bc MD |
152 | struct resource *reg; |
153 | int regid; | |
984263bc MD |
154 | bus_space_tag_t st; |
155 | bus_space_handle_t sh; | |
984263bc MD |
156 | |
157 | struct resource *irq; | |
158 | int irqid; | |
159 | void *ih; | |
160 | ||
558a398b SS |
161 | bus_dma_tag_t buf_dmat; |
162 | bus_dma_tag_t stat_dmat; | |
984263bc | 163 | |
558a398b | 164 | /* FreeBSD SMPng related */ |
67931cc4 | 165 | struct lock lock; /* mutual exclusion */ |
558a398b | 166 | /* FreeBSD newpcm related */ |
984263bc | 167 | struct ac97_info *codec; |
984263bc | 168 | |
558a398b | 169 | /* OS independent */ |
2a1ad637 | 170 | bus_dmamap_t stat_map; |
558a398b SS |
171 | u_int8_t *stat; /* status buffer pointer */ |
172 | bus_addr_t phys; /* status buffer physical address */ | |
173 | unsigned int bufsz; /* channel buffer size in bytes */ | |
174 | u_int playchns; | |
175 | volatile u_int active; | |
984263bc | 176 | struct agg_chinfo pch[AGG_MAXPLAYCH]; |
558a398b SS |
177 | struct agg_rchinfo rch; |
178 | volatile u_int8_t curpwr; /* current power status: D[0-3] */ | |
984263bc MD |
179 | }; |
180 | ||
558a398b SS |
181 | |
182 | /* ----------------------------- | |
183 | * Sysctls for debug. | |
184 | */ | |
185 | static unsigned int powerstate_active = PCI_POWERSTATE_D1; | |
186 | #ifdef MAESTRO_AGGRESSIVE_POWERSAVE | |
187 | static unsigned int powerstate_idle = PCI_POWERSTATE_D2; | |
188 | #else | |
189 | static unsigned int powerstate_idle = PCI_POWERSTATE_D1; | |
190 | #endif | |
191 | static unsigned int powerstate_init = PCI_POWERSTATE_D2; | |
192 | ||
2a1ad637 FT |
193 | /* XXX: this should move to a device specific sysctl dev.pcm.X.debug.Y via |
194 | device_get_sysctl_*() as discussed on multimedia@ in msg-id | |
195 | <861wujij2q.fsf@xps.des.no> */ | |
196 | static SYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, ""); | |
558a398b SS |
197 | SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW, |
198 | &powerstate_active, 0, "The Dx power state when active (0-1)"); | |
199 | SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW, | |
200 | &powerstate_idle, 0, "The Dx power state when idle (0-2)"); | |
201 | SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW, | |
2a1ad637 FT |
202 | &powerstate_init, 0, |
203 | "The Dx power state prior to the first use (0-2)"); | |
558a398b SS |
204 | |
205 | ||
206 | /* ----------------------------- | |
207 | * Prototypes | |
208 | */ | |
209 | ||
2a1ad637 | 210 | static void agg_sleep(struct agg_info*, const char *wmesg, int msec); |
558a398b | 211 | |
2a1ad637 FT |
212 | #if 0 |
213 | static __inline u_int32_t agg_rd(struct agg_info*, int, int size); | |
214 | static __inline void agg_wr(struct agg_info*, int, u_int32_t data, | |
215 | int size); | |
216 | #endif | |
217 | static int agg_rdcodec(struct agg_info*, int); | |
218 | static int agg_wrcodec(struct agg_info*, int, u_int32_t); | |
558a398b | 219 | |
2a1ad637 | 220 | static void ringbus_setdest(struct agg_info*, int, int); |
984263bc | 221 | |
2a1ad637 FT |
222 | static u_int16_t wp_rdreg(struct agg_info*, u_int16_t); |
223 | static void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); | |
224 | static u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); | |
225 | static void wp_wrapu(struct agg_info*, unsigned, u_int16_t, u_int16_t); | |
226 | static void wp_settimer(struct agg_info*, u_int); | |
227 | static void wp_starttimer(struct agg_info*); | |
228 | static void wp_stoptimer(struct agg_info*); | |
984263bc | 229 | |
2a1ad637 FT |
230 | #if 0 |
231 | static u_int16_t wc_rdreg(struct agg_info*, u_int16_t); | |
232 | #endif | |
233 | static void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); | |
234 | #if 0 | |
235 | static u_int16_t wc_rdchctl(struct agg_info*, int); | |
236 | #endif | |
237 | static void wc_wrchctl(struct agg_info*, int, u_int16_t); | |
984263bc | 238 | |
2a1ad637 | 239 | static void agg_stopclock(struct agg_info*, int part, int st); |
984263bc | 240 | |
2a1ad637 FT |
241 | static void agg_initcodec(struct agg_info*); |
242 | static void agg_init(struct agg_info*); | |
243 | static void agg_power(struct agg_info*, int); | |
984263bc | 244 | |
2a1ad637 FT |
245 | static void aggch_start_dac(struct agg_chinfo*); |
246 | static void aggch_stop_dac(struct agg_chinfo*); | |
247 | static void aggch_start_adc(struct agg_rchinfo*); | |
248 | static void aggch_stop_adc(struct agg_rchinfo*); | |
249 | static void aggch_feed_adc_stereo(struct agg_rchinfo*); | |
250 | static void aggch_feed_adc_mono(struct agg_rchinfo*); | |
984263bc | 251 | |
2a1ad637 FT |
252 | #ifdef AGG_JITTER_CORRECTION |
253 | static void suppress_jitter(struct agg_chinfo*); | |
254 | static void suppress_rec_jitter(struct agg_rchinfo*); | |
255 | #endif | |
984263bc | 256 | |
2a1ad637 | 257 | static void set_timer(struct agg_info*); |
984263bc | 258 | |
2a1ad637 FT |
259 | static void agg_intr(void *); |
260 | static int agg_probe(device_t); | |
261 | static int agg_attach(device_t); | |
262 | static int agg_detach(device_t); | |
263 | static int agg_suspend(device_t); | |
264 | static int agg_resume(device_t); | |
265 | static int agg_shutdown(device_t); | |
984263bc | 266 | |
2a1ad637 FT |
267 | static void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*, |
268 | bus_dmamap_t *); | |
269 | static void dma_free(bus_dma_tag_t, void *, bus_dmamap_t); | |
558a398b | 270 | |
984263bc MD |
271 | |
272 | /* ----------------------------- | |
273 | * Subsystems. | |
274 | */ | |
275 | ||
558a398b | 276 | /* locking */ |
2a1ad637 FT |
277 | #define agg_lock(sc) snd_mtxlock(&((sc)->lock)) |
278 | #define agg_unlock(sc) snd_mtxunlock(&((sc)->lock)) | |
984263bc | 279 | |
2a1ad637 | 280 | static void |
558a398b SS |
281 | agg_sleep(struct agg_info *sc, const char *wmesg, int msec) |
282 | { | |
283 | int timo; | |
284 | ||
285 | timo = msec * hz / 1000; | |
286 | if (timo == 0) | |
287 | timo = 1; | |
67931cc4 | 288 | lksleep(sc, &sc->lock, 0, wmesg, timo); |
984263bc MD |
289 | } |
290 | ||
558a398b SS |
291 | |
292 | /* I/O port */ | |
293 | ||
2a1ad637 FT |
294 | #if 0 |
295 | static __inline u_int32_t | |
558a398b | 296 | agg_rd(struct agg_info *sc, int regno, int size) |
984263bc | 297 | { |
558a398b SS |
298 | switch (size) { |
299 | case 1: | |
300 | return bus_space_read_1(sc->st, sc->sh, regno); | |
301 | case 2: | |
302 | return bus_space_read_2(sc->st, sc->sh, regno); | |
303 | case 4: | |
304 | return bus_space_read_4(sc->st, sc->sh, regno); | |
305 | default: | |
306 | return ~(u_int32_t)0; | |
307 | } | |
308 | } | |
2a1ad637 | 309 | #endif |
984263bc | 310 | |
558a398b SS |
311 | #define AGG_RD(sc, regno, size) \ |
312 | bus_space_read_##size( \ | |
313 | ((struct agg_info*)(sc))->st, \ | |
314 | ((struct agg_info*)(sc))->sh, (regno)) | |
315 | ||
2a1ad637 FT |
316 | #if 0 |
317 | static __inline void | |
558a398b SS |
318 | agg_wr(struct agg_info *sc, int regno, u_int32_t data, int size) |
319 | { | |
320 | switch (size) { | |
321 | case 1: | |
322 | bus_space_write_1(sc->st, sc->sh, regno, data); | |
323 | break; | |
324 | case 2: | |
325 | bus_space_write_2(sc->st, sc->sh, regno, data); | |
326 | break; | |
327 | case 4: | |
328 | bus_space_write_4(sc->st, sc->sh, regno, data); | |
329 | break; | |
330 | } | |
331 | } | |
2a1ad637 | 332 | #endif |
558a398b SS |
333 | |
334 | #define AGG_WR(sc, regno, data, size) \ | |
335 | bus_space_write_##size( \ | |
336 | ((struct agg_info*)(sc))->st, \ | |
337 | ((struct agg_info*)(sc))->sh, (regno), (data)) | |
338 | ||
339 | /* -------------------------------------------------------------------- */ | |
340 | ||
341 | /* Codec/Ringbus */ | |
342 | ||
2a1ad637 | 343 | static int |
558a398b SS |
344 | agg_codec_wait4idle(struct agg_info *ess) |
345 | { | |
346 | unsigned t = 26; | |
347 | ||
348 | while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { | |
349 | if (--t == 0) | |
350 | return EBUSY; | |
984263bc MD |
351 | DELAY(2); /* 20.8us / 13 */ |
352 | } | |
558a398b SS |
353 | return 0; |
354 | } | |
355 | ||
356 | ||
2a1ad637 | 357 | static int |
558a398b SS |
358 | agg_rdcodec(struct agg_info *ess, int regno) |
359 | { | |
360 | int ret; | |
361 | ||
362 | /* We have to wait for a SAFE time to write addr/data */ | |
363 | if (agg_codec_wait4idle(ess)) { | |
364 | /* Timed out. No read performed. */ | |
984263bc | 365 | device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); |
558a398b SS |
366 | return -1; |
367 | } | |
984263bc | 368 | |
558a398b SS |
369 | AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); |
370 | /*DELAY(21); * AC97 cycle = 20.8usec */ | |
984263bc MD |
371 | |
372 | /* Wait for data retrieve */ | |
558a398b SS |
373 | if (!agg_codec_wait4idle(ess)) { |
374 | ret = AGG_RD(ess, PORT_CODEC_REG, 2); | |
375 | } else { | |
376 | /* Timed out. No read performed. */ | |
984263bc | 377 | device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); |
558a398b SS |
378 | ret = -1; |
379 | } | |
984263bc | 380 | |
558a398b | 381 | return ret; |
984263bc MD |
382 | } |
383 | ||
2a1ad637 | 384 | static int |
558a398b | 385 | agg_wrcodec(struct agg_info *ess, int regno, u_int32_t data) |
984263bc | 386 | { |
984263bc | 387 | /* We have to wait for a SAFE time to write addr/data */ |
558a398b | 388 | if (agg_codec_wait4idle(ess)) { |
984263bc MD |
389 | /* Timed out. Abort writing. */ |
390 | device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); | |
391 | return -1; | |
392 | } | |
393 | ||
558a398b SS |
394 | AGG_WR(ess, PORT_CODEC_REG, data, 2); |
395 | AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); | |
396 | ||
397 | /* Wait for write completion */ | |
398 | if (agg_codec_wait4idle(ess)) { | |
399 | /* Timed out. */ | |
400 | device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); | |
401 | return -1; | |
402 | } | |
984263bc MD |
403 | |
404 | return 0; | |
405 | } | |
406 | ||
2a1ad637 | 407 | static void |
984263bc MD |
408 | ringbus_setdest(struct agg_info *ess, int src, int dest) |
409 | { | |
410 | u_int32_t data; | |
411 | ||
558a398b | 412 | data = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); |
984263bc MD |
413 | data &= ~(0xfU << src); |
414 | data |= (0xfU & dest) << src; | |
558a398b | 415 | AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); |
984263bc MD |
416 | } |
417 | ||
558a398b SS |
418 | /* -------------------------------------------------------------------- */ |
419 | ||
984263bc MD |
420 | /* Wave Processor */ |
421 | ||
2a1ad637 | 422 | static u_int16_t |
984263bc MD |
423 | wp_rdreg(struct agg_info *ess, u_int16_t reg) |
424 | { | |
558a398b SS |
425 | AGG_WR(ess, PORT_DSP_INDEX, reg, 2); |
426 | return AGG_RD(ess, PORT_DSP_DATA, 2); | |
984263bc MD |
427 | } |
428 | ||
2a1ad637 | 429 | static void |
984263bc MD |
430 | wp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) |
431 | { | |
558a398b SS |
432 | AGG_WR(ess, PORT_DSP_INDEX, reg, 2); |
433 | AGG_WR(ess, PORT_DSP_DATA, data, 2); | |
984263bc MD |
434 | } |
435 | ||
2a1ad637 | 436 | static int |
558a398b | 437 | wp_wait_data(struct agg_info *ess, u_int16_t data) |
984263bc | 438 | { |
558a398b | 439 | unsigned t = 0; |
984263bc | 440 | |
558a398b SS |
441 | while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { |
442 | if (++t == 1000) { | |
443 | return EAGAIN; | |
444 | } | |
445 | AGG_WR(ess, PORT_DSP_DATA, data, 2); | |
984263bc | 446 | } |
558a398b SS |
447 | |
448 | return 0; | |
984263bc MD |
449 | } |
450 | ||
2a1ad637 | 451 | static u_int16_t |
558a398b | 452 | wp_rdapu(struct agg_info *ess, unsigned ch, u_int16_t reg) |
984263bc | 453 | { |
558a398b SS |
454 | wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); |
455 | if (wp_wait_data(ess, reg | (ch << 4)) != 0) | |
456 | device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); | |
457 | return wp_rdreg(ess, WPREG_DATA_PORT); | |
984263bc MD |
458 | } |
459 | ||
2a1ad637 | 460 | static void |
558a398b | 461 | wp_wrapu(struct agg_info *ess, unsigned ch, u_int16_t reg, u_int16_t data) |
984263bc | 462 | { |
558a398b SS |
463 | wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); |
464 | if (wp_wait_data(ess, reg | (ch << 4)) == 0) { | |
465 | wp_wrreg(ess, WPREG_DATA_PORT, data); | |
466 | if (wp_wait_data(ess, data) != 0) | |
2a1ad637 FT |
467 | device_printf(ess->dev, |
468 | "wp_wrapu() write timed out.\n"); | |
558a398b SS |
469 | } else { |
470 | device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); | |
984263bc | 471 | } |
558a398b SS |
472 | } |
473 | ||
474 | static void | |
475 | apu_setparam(struct agg_info *ess, int apuch, | |
476 | u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) | |
477 | { | |
478 | wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); | |
479 | wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); | |
480 | wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); | |
481 | wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); | |
482 | wp_wrapu(ess, apuch, APUREG_ROUTING, 0); | |
483 | wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); | |
484 | wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 | |
485 | | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) | |
486 | | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); | |
487 | wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, | |
488 | APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); | |
489 | wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); | |
984263bc MD |
490 | } |
491 | ||
2a1ad637 | 492 | static void |
558a398b | 493 | wp_settimer(struct agg_info *ess, u_int divide) |
984263bc | 494 | { |
558a398b | 495 | u_int prescale = 0; |
984263bc | 496 | |
558a398b | 497 | RANGE(divide, 2, 32 << 7); |
984263bc | 498 | |
558a398b | 499 | for (; divide > 32; divide >>= 1) { |
984263bc | 500 | prescale++; |
558a398b SS |
501 | divide++; |
502 | } | |
984263bc MD |
503 | |
504 | for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) | |
505 | prescale++; | |
506 | ||
507 | wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); | |
558a398b | 508 | wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | |
984263bc MD |
509 | (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); |
510 | wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); | |
511 | } | |
512 | ||
2a1ad637 | 513 | static void |
984263bc MD |
514 | wp_starttimer(struct agg_info *ess) |
515 | { | |
558a398b SS |
516 | AGG_WR(ess, PORT_INT_STAT, 1, 2); |
517 | AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED | |
518 | | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); | |
984263bc MD |
519 | wp_wrreg(ess, WPREG_TIMER_START, 1); |
520 | } | |
521 | ||
2a1ad637 | 522 | static void |
984263bc MD |
523 | wp_stoptimer(struct agg_info *ess) |
524 | { | |
558a398b SS |
525 | AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED |
526 | & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); | |
527 | AGG_WR(ess, PORT_INT_STAT, 1, 2); | |
984263bc | 528 | wp_wrreg(ess, WPREG_TIMER_START, 0); |
984263bc MD |
529 | } |
530 | ||
558a398b SS |
531 | /* -------------------------------------------------------------------- */ |
532 | ||
984263bc MD |
533 | /* WaveCache */ |
534 | ||
2a1ad637 FT |
535 | #if 0 |
536 | static u_int16_t | |
984263bc MD |
537 | wc_rdreg(struct agg_info *ess, u_int16_t reg) |
538 | { | |
558a398b SS |
539 | AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); |
540 | return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); | |
984263bc | 541 | } |
2a1ad637 | 542 | #endif |
984263bc | 543 | |
2a1ad637 | 544 | static void |
984263bc MD |
545 | wc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) |
546 | { | |
558a398b SS |
547 | AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); |
548 | AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); | |
984263bc MD |
549 | } |
550 | ||
2a1ad637 FT |
551 | #if 0 |
552 | static u_int16_t | |
984263bc MD |
553 | wc_rdchctl(struct agg_info *ess, int ch) |
554 | { | |
555 | return wc_rdreg(ess, ch << 3); | |
556 | } | |
2a1ad637 | 557 | #endif |
984263bc | 558 | |
2a1ad637 | 559 | static void |
984263bc MD |
560 | wc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) |
561 | { | |
562 | wc_wrreg(ess, ch << 3, data); | |
563 | } | |
564 | ||
558a398b | 565 | /* -------------------------------------------------------------------- */ |
984263bc | 566 | |
558a398b | 567 | /* Power management */ |
2a1ad637 | 568 | static void |
558a398b | 569 | agg_stopclock(struct agg_info *ess, int part, int st) |
984263bc | 570 | { |
558a398b | 571 | u_int32_t data; |
984263bc | 572 | |
558a398b SS |
573 | data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); |
574 | if (part < 16) { | |
575 | if (st == PCI_POWERSTATE_D1) | |
576 | data &= ~(1 << part); | |
577 | else | |
578 | data |= (1 << part); | |
579 | if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) | |
580 | data |= (0x10000 << part); | |
581 | else | |
582 | data &= ~(0x10000 << part); | |
583 | pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); | |
584 | } | |
984263bc MD |
585 | } |
586 | ||
587 | ||
588 | /* ----------------------------- | |
589 | * Controller. | |
590 | */ | |
591 | ||
2a1ad637 | 592 | static void |
984263bc MD |
593 | agg_initcodec(struct agg_info* ess) |
594 | { | |
595 | u_int16_t data; | |
596 | ||
558a398b SS |
597 | if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { |
598 | AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); | |
984263bc MD |
599 | DELAY(104); /* 20.8us * (4 + 1) */ |
600 | } | |
601 | /* XXX - 2nd codec should be looked at. */ | |
558a398b | 602 | AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); |
984263bc | 603 | DELAY(2); |
558a398b SS |
604 | AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); |
605 | DELAY(50); | |
606 | ||
607 | if (agg_rdcodec(ess, 0) < 0) { | |
608 | AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); | |
984263bc MD |
609 | DELAY(21); |
610 | ||
611 | /* Try cold reset. */ | |
612 | device_printf(ess->dev, "will perform cold reset.\n"); | |
558a398b | 613 | data = AGG_RD(ess, PORT_GPIO_DIR, 2); |
984263bc MD |
614 | if (pci_read_config(ess->dev, 0x58, 2) & 1) |
615 | data |= 0x10; | |
558a398b SS |
616 | data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); |
617 | AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); | |
618 | AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); | |
619 | AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); | |
984263bc | 620 | DELAY(2); |
558a398b | 621 | AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); |
984263bc | 622 | DELAY(1); |
558a398b SS |
623 | AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); |
624 | agg_sleep(ess, "agginicd", 500); | |
625 | AGG_WR(ess, PORT_GPIO_DIR, data, 2); | |
984263bc | 626 | DELAY(84); /* 20.8us * 4 */ |
558a398b SS |
627 | AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); |
628 | DELAY(50); | |
984263bc MD |
629 | } |
630 | } | |
631 | ||
632 | static void | |
633 | agg_init(struct agg_info* ess) | |
634 | { | |
635 | u_int32_t data; | |
636 | ||
637 | /* Setup PCI config registers. */ | |
638 | ||
639 | /* Disable all legacy emulations. */ | |
640 | data = pci_read_config(ess->dev, CONF_LEGACY, 2); | |
641 | data |= LEGACY_DISABLED; | |
642 | pci_write_config(ess->dev, CONF_LEGACY, data, 2); | |
643 | ||
644 | /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) | |
645 | * Enable posted write. | |
646 | * Prefer PCI timing rather than that of ISA. | |
647 | * Don't swap L/R. */ | |
648 | data = pci_read_config(ess->dev, CONF_MAESTRO, 4); | |
558a398b | 649 | data |= MAESTRO_PMC; |
984263bc MD |
650 | data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; |
651 | data &= ~MAESTRO_SWAP_LR; | |
652 | pci_write_config(ess->dev, CONF_MAESTRO, data, 4); | |
653 | ||
558a398b SS |
654 | /* Turn off unused parts if necessary. */ |
655 | /* consult CONF_MAESTRO. */ | |
656 | if (data & MAESTRO_SPDIF) | |
657 | agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); | |
658 | else | |
659 | agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); | |
660 | if (data & MAESTRO_HWVOL) | |
661 | agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); | |
662 | else | |
663 | agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); | |
664 | ||
665 | /* parts that never be used */ | |
666 | agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); | |
667 | agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); | |
668 | agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); | |
669 | agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); | |
670 | agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); | |
671 | agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); | |
672 | agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); | |
673 | ||
674 | /* parts that will be used only when play/recording */ | |
675 | agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); | |
676 | ||
677 | /* parts that should always be turned on */ | |
678 | agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); | |
679 | agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); | |
680 | agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); | |
681 | agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); | |
984263bc | 682 | |
558a398b SS |
683 | /* Reset direct sound. */ |
684 | AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); | |
685 | DELAY(100); | |
686 | AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); | |
687 | DELAY(100); | |
688 | AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); | |
689 | DELAY(100); | |
690 | AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); | |
691 | DELAY(100); | |
692 | ||
693 | /* Enable hardware volume control interruption. */ | |
694 | if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ | |
695 | AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); | |
984263bc MD |
696 | |
697 | /* Setup Wave Processor. */ | |
698 | ||
699 | /* Enable WaveCache, set DMA base address. */ | |
700 | wp_wrreg(ess, WPREG_WAVE_ROMRAM, | |
701 | WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); | |
558a398b SS |
702 | wp_wrreg(ess, WPREG_CRAM_DATA, 0); |
703 | ||
704 | AGG_WR(ess, PORT_WAVCACHE_CTRL, | |
705 | WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); | |
984263bc MD |
706 | |
707 | for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) | |
558a398b | 708 | wc_wrreg(ess, data, ess->phys >> WAVCACHE_BASEADDR_SHIFT); |
984263bc MD |
709 | |
710 | /* Setup Codec/Ringbus. */ | |
711 | agg_initcodec(ess); | |
558a398b SS |
712 | AGG_WR(ess, PORT_RINGBUS_CTRL, |
713 | RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); | |
714 | ||
715 | wp_wrreg(ess, 0x08, 0xB004); | |
716 | wp_wrreg(ess, 0x09, 0x001B); | |
717 | wp_wrreg(ess, 0x0A, 0x8000); | |
718 | wp_wrreg(ess, 0x0B, 0x3F37); | |
719 | wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ | |
720 | wp_wrreg(ess, WPREG_BASE + 1, 0x7632); | |
984263bc MD |
721 | ringbus_setdest(ess, RINGBUS_SRC_ADC, |
722 | RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); | |
723 | ringbus_setdest(ess, RINGBUS_SRC_DSOUND, | |
724 | RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); | |
725 | ||
558a398b SS |
726 | /* Enable S/PDIF if necessary. */ |
727 | if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) | |
728 | /* XXX - why not use device flags? */ | |
729 | AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | | |
730 | AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); | |
731 | ||
984263bc | 732 | /* Setup ASSP. Needed for Dell Inspiron 7500? */ |
558a398b SS |
733 | AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); |
734 | AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); | |
735 | AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); | |
984263bc MD |
736 | |
737 | /* | |
738 | * Setup GPIO. | |
739 | * There seems to be speciality with NEC systems. | |
740 | */ | |
741 | switch (pci_get_subvendor(ess->dev) | |
742 | | (pci_get_subdevice(ess->dev) << 16)) { | |
743 | case NEC_SUBID1: | |
744 | case NEC_SUBID2: | |
745 | /* Matthew Braithwaite <matt@braithwaite.net> reported that | |
746 | * NEC Versa LX doesn't need GPIO operation. */ | |
558a398b SS |
747 | AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); |
748 | AGG_WR(ess, PORT_GPIO_DIR, | |
749 | AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); | |
750 | AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); | |
751 | break; | |
752 | } | |
753 | } | |
754 | ||
755 | /* Deals power state transition. Must be called with softc->lock held. */ | |
756 | static void | |
757 | agg_power(struct agg_info *ess, int status) | |
758 | { | |
759 | u_int8_t lastpwr; | |
760 | ||
761 | lastpwr = ess->curpwr; | |
762 | if (lastpwr == status) | |
763 | return; | |
764 | ||
765 | switch (status) { | |
766 | case PCI_POWERSTATE_D0: | |
767 | case PCI_POWERSTATE_D1: | |
768 | switch (lastpwr) { | |
769 | case PCI_POWERSTATE_D2: | |
770 | pci_set_powerstate(ess->dev, status); | |
771 | /* Turn on PCM-related parts. */ | |
772 | agg_wrcodec(ess, AC97_REG_POWER, 0); | |
773 | DELAY(100); | |
774 | #if 0 | |
775 | if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) | |
2a1ad637 FT |
776 | device_printf(ess->dev, |
777 | "warning: codec not ready.\n"); | |
558a398b SS |
778 | #endif |
779 | AGG_WR(ess, PORT_RINGBUS_CTRL, | |
780 | (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) | |
781 | & ~RINGBUS_CTRL_ACLINK_ENABLED) | |
782 | | RINGBUS_CTRL_RINGBUS_ENABLED, 4); | |
783 | DELAY(50); | |
784 | AGG_WR(ess, PORT_RINGBUS_CTRL, | |
785 | AGG_RD(ess, PORT_RINGBUS_CTRL, 4) | |
786 | | RINGBUS_CTRL_ACLINK_ENABLED, 4); | |
787 | break; | |
788 | case PCI_POWERSTATE_D3: | |
789 | /* Initialize. */ | |
790 | pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); | |
791 | DELAY(100); | |
792 | agg_init(ess); | |
793 | /* FALLTHROUGH */ | |
794 | case PCI_POWERSTATE_D0: | |
795 | case PCI_POWERSTATE_D1: | |
796 | pci_set_powerstate(ess->dev, status); | |
797 | break; | |
798 | } | |
799 | break; | |
800 | case PCI_POWERSTATE_D2: | |
801 | switch (lastpwr) { | |
802 | case PCI_POWERSTATE_D3: | |
803 | /* Initialize. */ | |
804 | pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); | |
805 | DELAY(100); | |
806 | agg_init(ess); | |
807 | /* FALLTHROUGH */ | |
808 | case PCI_POWERSTATE_D0: | |
809 | case PCI_POWERSTATE_D1: | |
810 | /* Turn off PCM-related parts. */ | |
811 | AGG_WR(ess, PORT_RINGBUS_CTRL, | |
812 | AGG_RD(ess, PORT_RINGBUS_CTRL, 4) | |
813 | & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); | |
814 | DELAY(100); | |
815 | agg_wrcodec(ess, AC97_REG_POWER, 0x300); | |
816 | DELAY(100); | |
817 | break; | |
818 | } | |
819 | pci_set_powerstate(ess->dev, status); | |
820 | break; | |
821 | case PCI_POWERSTATE_D3: | |
822 | /* Entirely power down. */ | |
823 | agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); | |
824 | DELAY(100); | |
825 | AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); | |
826 | /*DELAY(1);*/ | |
827 | if (lastpwr != PCI_POWERSTATE_D2) | |
828 | wp_stoptimer(ess); | |
829 | AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); | |
830 | AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); | |
831 | pci_set_powerstate(ess->dev, status); | |
832 | break; | |
833 | default: | |
834 | /* Invalid power state; let it ignored. */ | |
835 | status = lastpwr; | |
984263bc MD |
836 | break; |
837 | } | |
558a398b SS |
838 | |
839 | ess->curpwr = status; | |
984263bc MD |
840 | } |
841 | ||
558a398b SS |
842 | /* -------------------------------------------------------------------- */ |
843 | ||
984263bc MD |
844 | /* Channel controller. */ |
845 | ||
846 | static void | |
847 | aggch_start_dac(struct agg_chinfo *ch) | |
848 | { | |
558a398b SS |
849 | bus_addr_t wpwa; |
850 | u_int32_t speed; | |
851 | u_int16_t size, apuch, wtbar, wcreg, aputype; | |
852 | u_int dv; | |
853 | int pan; | |
854 | ||
855 | speed = ch->speed; | |
856 | wpwa = (ch->phys - ch->base) >> 1; | |
857 | wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); | |
858 | wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; | |
859 | size = ch->buflen; | |
860 | apuch = (ch->num << 1) | 32; | |
861 | pan = PAN_RIGHT - PAN_FRONT; | |
862 | ||
863 | if (ch->stereo) { | |
864 | wcreg |= WAVCACHE_CHCTL_STEREO; | |
865 | if (ch->qs16) { | |
866 | aputype = APUTYPE_16BITSTEREO; | |
867 | wpwa >>= 1; | |
868 | size >>= 1; | |
869 | pan = -pan; | |
870 | } else | |
871 | aputype = APUTYPE_8BITSTEREO; | |
872 | } else { | |
873 | pan = 0; | |
874 | if (ch->qs16) | |
875 | aputype = APUTYPE_16BITLINEAR; | |
876 | else { | |
877 | aputype = APUTYPE_8BITLINEAR; | |
878 | speed >>= 1; | |
879 | } | |
984263bc | 880 | } |
558a398b SS |
881 | if (ch->us) |
882 | wcreg |= WAVCACHE_CHCTL_U8; | |
883 | ||
884 | if (wtbar > 8) | |
885 | wtbar = (wtbar >> 1) + 4; | |
984263bc MD |
886 | |
887 | dv = (((speed % 48000) << 16) + 24000) / 48000 | |
888 | + ((speed / 48000) << 16); | |
889 | ||
558a398b SS |
890 | agg_lock(ch->parent); |
891 | agg_power(ch->parent, powerstate_active); | |
892 | ||
893 | wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar, | |
894 | ch->base >> WAVCACHE_BASEADDR_SHIFT); | |
895 | wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1, | |
896 | ch->base >> WAVCACHE_BASEADDR_SHIFT); | |
897 | if (wtbar < 8) { | |
898 | wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2, | |
899 | ch->base >> WAVCACHE_BASEADDR_SHIFT); | |
900 | wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3, | |
901 | ch->base >> WAVCACHE_BASEADDR_SHIFT); | |
902 | } | |
903 | wc_wrchctl(ch->parent, apuch, wcreg); | |
904 | wc_wrchctl(ch->parent, apuch + 1, wcreg); | |
905 | ||
906 | apu_setparam(ch->parent, apuch, wpwa, size, pan, dv); | |
907 | if (ch->stereo) { | |
908 | if (ch->qs16) | |
909 | wpwa |= (WPWA_STEREO >> 1); | |
910 | apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv); | |
911 | ||
912 | critical_enter(); | |
913 | wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, | |
914 | (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); | |
984263bc | 915 | wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, |
558a398b SS |
916 | (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); |
917 | critical_exit(); | |
918 | } else { | |
919 | wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, | |
920 | (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); | |
921 | } | |
922 | ||
923 | /* to mark that this channel is ready for intr. */ | |
924 | ch->parent->active |= (1 << ch->num); | |
925 | ||
926 | set_timer(ch->parent); | |
927 | wp_starttimer(ch->parent); | |
928 | agg_unlock(ch->parent); | |
984263bc MD |
929 | } |
930 | ||
931 | static void | |
932 | aggch_stop_dac(struct agg_chinfo *ch) | |
933 | { | |
558a398b SS |
934 | agg_lock(ch->parent); |
935 | ||
936 | /* to mark that this channel no longer needs further intrs. */ | |
937 | ch->parent->active &= ~(1 << ch->num); | |
938 | ||
939 | wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE, | |
984263bc | 940 | APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); |
558a398b | 941 | wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE, |
984263bc | 942 | APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); |
558a398b SS |
943 | |
944 | if (ch->parent->active) { | |
945 | set_timer(ch->parent); | |
946 | wp_starttimer(ch->parent); | |
947 | } else { | |
948 | wp_stoptimer(ch->parent); | |
949 | agg_power(ch->parent, powerstate_idle); | |
950 | } | |
951 | agg_unlock(ch->parent); | |
952 | } | |
953 | ||
954 | static void | |
955 | aggch_start_adc(struct agg_rchinfo *ch) | |
956 | { | |
957 | bus_addr_t wpwa, wpwa2; | |
958 | u_int16_t wcreg, wcreg2; | |
959 | u_int dv; | |
960 | int pan; | |
961 | ||
962 | /* speed > 48000 not cared */ | |
963 | dv = ((ch->speed << 16) + 24000) / 48000; | |
964 | ||
965 | /* RATECONV doesn't seem to like dv == 0x10000. */ | |
966 | if (dv == 0x10000) | |
967 | dv--; | |
968 | ||
969 | if (ch->stereo) { | |
970 | wpwa = (ch->srcphys - ch->base) >> 1; | |
971 | wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1; | |
972 | wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; | |
973 | wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; | |
974 | pan = PAN_LEFT - PAN_FRONT; | |
975 | } else { | |
976 | wpwa = (ch->phys - ch->base) >> 1; | |
977 | wpwa2 = (ch->srcphys - ch->base) >> 1; | |
978 | wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; | |
979 | wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; | |
980 | pan = 0; | |
981 | } | |
982 | ||
983 | agg_lock(ch->parent); | |
984 | ||
985 | ch->hwptr = 0; | |
986 | agg_power(ch->parent, powerstate_active); | |
987 | ||
988 | /* Invalidate WaveCache. */ | |
989 | wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO); | |
990 | wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO); | |
991 | wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO); | |
992 | wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO); | |
993 | ||
994 | /* Load APU registers. */ | |
995 | /* APU #0 : Sample rate converter for left/center. */ | |
996 | apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa, | |
997 | ch->buflen >> ch->stereo, 0, dv); | |
998 | wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0); | |
999 | wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT); | |
1000 | ||
1001 | /* APU #1 : Sample rate converter for right. */ | |
1002 | apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2, | |
1003 | ch->buflen >> ch->stereo, 0, dv); | |
1004 | wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0); | |
1005 | wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT); | |
1006 | ||
1007 | /* APU #2 : Input mixer for left. */ | |
1008 | apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0, | |
1009 | ch->parent->bufsz >> 2, pan, 0x10000); | |
1010 | wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0); | |
1011 | wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0); | |
1012 | wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT); | |
1013 | ||
1014 | /* APU #3 : Input mixer for right. */ | |
1015 | apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2), | |
1016 | ch->parent->bufsz >> 2, -pan, 0x10000); | |
1017 | wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0); | |
1018 | wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0); | |
1019 | wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT); | |
1020 | ||
1021 | /* to mark this channel ready for intr. */ | |
1022 | ch->parent->active |= (1 << ch->parent->playchns); | |
1023 | ||
1024 | /* start adc */ | |
1025 | critical_enter(); | |
1026 | wp_wrapu(ch->parent, 0, APUREG_APUTYPE, | |
1027 | (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); | |
1028 | wp_wrapu(ch->parent, 1, APUREG_APUTYPE, | |
1029 | (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); | |
1030 | wp_wrapu(ch->parent, 2, APUREG_APUTYPE, | |
1031 | (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); | |
1032 | wp_wrapu(ch->parent, 3, APUREG_APUTYPE, | |
1033 | (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); | |
1034 | critical_exit(); | |
1035 | ||
1036 | set_timer(ch->parent); | |
1037 | wp_starttimer(ch->parent); | |
1038 | agg_unlock(ch->parent); | |
1039 | } | |
1040 | ||
1041 | static void | |
1042 | aggch_stop_adc(struct agg_rchinfo *ch) | |
1043 | { | |
1044 | int apuch; | |
1045 | ||
1046 | agg_lock(ch->parent); | |
1047 | ||
1048 | /* to mark that this channel no longer needs further intrs. */ | |
1049 | ch->parent->active &= ~(1 << ch->parent->playchns); | |
1050 | ||
1051 | for (apuch = 0; apuch < 4; apuch++) | |
1052 | wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, | |
1053 | APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); | |
1054 | ||
1055 | if (ch->parent->active) { | |
1056 | set_timer(ch->parent); | |
1057 | wp_starttimer(ch->parent); | |
1058 | } else { | |
1059 | wp_stoptimer(ch->parent); | |
1060 | agg_power(ch->parent, powerstate_idle); | |
1061 | } | |
1062 | agg_unlock(ch->parent); | |
1063 | } | |
1064 | ||
1065 | /* | |
1066 | * Feed from L/R channel of ADC to destination with stereo interleaving. | |
1067 | * This function expects n not overwrapping the buffer boundary. | |
1068 | * Note that n is measured in sample unit. | |
1069 | * | |
1070 | * XXX - this function works in 16bit stereo format only. | |
1071 | */ | |
2a1ad637 | 1072 | static void |
558a398b SS |
1073 | interleave(int16_t *l, int16_t *r, int16_t *p, unsigned n) |
1074 | { | |
1075 | int16_t *end; | |
1076 | ||
1077 | for (end = l + n; l < end; ) { | |
1078 | *p++ = *l++; | |
1079 | *p++ = *r++; | |
1080 | } | |
1081 | } | |
1082 | ||
1083 | static void | |
1084 | aggch_feed_adc_stereo(struct agg_rchinfo *ch) | |
1085 | { | |
1086 | unsigned cur, last; | |
1087 | int16_t *src2; | |
1088 | ||
1089 | agg_lock(ch->parent); | |
1090 | cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); | |
1091 | agg_unlock(ch->parent); | |
1092 | cur -= 0xffff & ((ch->srcphys - ch->base) >> 1); | |
1093 | last = ch->hwptr; | |
1094 | src2 = ch->src + ch->parent->bufsz/4; | |
1095 | ||
1096 | if (cur < last) { | |
1097 | interleave(ch->src + last, src2 + last, | |
1098 | ch->sink + 2*last, ch->buflen/2 - last); | |
1099 | interleave(ch->src, src2, | |
1100 | ch->sink, cur); | |
1101 | } else if (cur > last) | |
1102 | interleave(ch->src + last, src2 + last, | |
1103 | ch->sink + 2*last, cur - last); | |
1104 | ch->hwptr = cur; | |
1105 | } | |
1106 | ||
1107 | /* | |
1108 | * Feed from R channel of ADC and mixdown to destination L/center. | |
1109 | * This function expects n not overwrapping the buffer boundary. | |
1110 | * Note that n is measured in sample unit. | |
1111 | * | |
1112 | * XXX - this function works in 16bit monoral format only. | |
1113 | */ | |
2a1ad637 | 1114 | static void |
558a398b SS |
1115 | mixdown(int16_t *src, int16_t *dest, unsigned n) |
1116 | { | |
1117 | int16_t *end; | |
1118 | ||
1119 | for (end = dest + n; dest < end; dest++) | |
1120 | *dest = (int16_t)(((int)*dest - (int)*src++) / 2); | |
1121 | } | |
1122 | ||
1123 | static void | |
1124 | aggch_feed_adc_mono(struct agg_rchinfo *ch) | |
1125 | { | |
1126 | unsigned cur, last; | |
1127 | ||
1128 | agg_lock(ch->parent); | |
1129 | cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); | |
1130 | agg_unlock(ch->parent); | |
1131 | cur -= 0xffff & ((ch->phys - ch->base) >> 1); | |
1132 | last = ch->hwptr; | |
1133 | ||
1134 | if (cur < last) { | |
1135 | mixdown(ch->src + last, ch->sink + last, ch->buflen - last); | |
1136 | mixdown(ch->src, ch->sink, cur); | |
1137 | } else if (cur > last) | |
1138 | mixdown(ch->src + last, ch->sink + last, cur - last); | |
1139 | ch->hwptr = cur; | |
984263bc MD |
1140 | } |
1141 | ||
2a1ad637 | 1142 | #ifdef AGG_JITTER_CORRECTION |
984263bc MD |
1143 | /* |
1144 | * Stereo jitter suppressor. | |
1145 | * Sometimes playback pointers differ in stereo-paired channels. | |
1146 | * Calling this routine within intr fixes the problem. | |
1147 | */ | |
2a1ad637 | 1148 | static void |
984263bc MD |
1149 | suppress_jitter(struct agg_chinfo *ch) |
1150 | { | |
558a398b SS |
1151 | if (ch->stereo) { |
1152 | int cp1, cp2, diff /*, halfsize*/ ; | |
1153 | ||
1154 | /*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/ | |
1155 | cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); | |
1156 | cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR); | |
1157 | if (cp1 != cp2) { | |
1158 | diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); | |
1159 | if (diff > 1 /* && diff < halfsize*/ ) | |
1160 | AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); | |
1161 | } | |
1162 | } | |
1163 | } | |
1164 | ||
2a1ad637 | 1165 | static void |
558a398b SS |
1166 | suppress_rec_jitter(struct agg_rchinfo *ch) |
1167 | { | |
1168 | int cp1, cp2, diff /*, halfsize*/ ; | |
1169 | ||
1170 | /*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/ | |
1171 | cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1) | |
1172 | + wp_rdapu(ch->parent, 0, APUREG_CURPTR); | |
1173 | cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR); | |
1174 | if (cp1 != cp2) { | |
1175 | diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); | |
1176 | if (diff > 1 /* && diff < halfsize*/ ) | |
1177 | AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); | |
984263bc MD |
1178 | } |
1179 | } | |
2a1ad637 | 1180 | #endif |
984263bc | 1181 | |
2a1ad637 | 1182 | static u_int |
558a398b | 1183 | calc_timer_div(struct agg_chinfo *ch) |
984263bc | 1184 | { |
558a398b SS |
1185 | u_int speed; |
1186 | ||
1187 | speed = ch->speed; | |
1188 | #ifdef INVARIANTS | |
1189 | if (speed == 0) { | |
67931cc4 | 1190 | kprintf("snd_maestro: pch[%d].speed == 0, which shouldn't\n", |
558a398b SS |
1191 | ch->num); |
1192 | speed = 1; | |
1193 | } | |
1194 | #endif | |
1195 | return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo)) | |
1196 | + speed - 1) / speed; | |
1197 | } | |
984263bc | 1198 | |
2a1ad637 | 1199 | static u_int |
558a398b SS |
1200 | calc_timer_div_rch(struct agg_rchinfo *ch) |
1201 | { | |
1202 | u_int speed; | |
984263bc | 1203 | |
558a398b SS |
1204 | speed = ch->speed; |
1205 | #ifdef INVARIANTS | |
1206 | if (speed == 0) { | |
67931cc4 | 1207 | kprintf("snd_maestro: rch.speed == 0, which shouldn't\n"); |
558a398b SS |
1208 | speed = 1; |
1209 | } | |
1210 | #endif | |
1211 | return (48000 * (ch->blklen << (!ch->stereo)) | |
1212 | + speed - 1) / speed; | |
984263bc MD |
1213 | } |
1214 | ||
1215 | static void | |
1216 | set_timer(struct agg_info *ess) | |
1217 | { | |
1218 | int i; | |
558a398b | 1219 | u_int dv = 32 << 7, newdv; |
984263bc MD |
1220 | |
1221 | for (i = 0; i < ess->playchns; i++) | |
1222 | if ((ess->active & (1 << i)) && | |
558a398b SS |
1223 | (dv > (newdv = calc_timer_div(ess->pch + i)))) |
1224 | dv = newdv; | |
1225 | if ((ess->active & (1 << i)) && | |
1226 | (dv > (newdv = calc_timer_div_rch(&ess->rch)))) | |
1227 | dv = newdv; | |
984263bc | 1228 | |
558a398b | 1229 | wp_settimer(ess, dv); |
984263bc MD |
1230 | } |
1231 | ||
1232 | ||
1233 | /* ----------------------------- | |
1234 | * Newpcm glue. | |
1235 | */ | |
1236 | ||
558a398b SS |
1237 | /* AC97 mixer interface. */ |
1238 | ||
1239 | static u_int32_t | |
1240 | agg_ac97_init(kobj_t obj, void *sc) | |
1241 | { | |
1242 | struct agg_info *ess = sc; | |
1243 | ||
1244 | return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1; | |
1245 | } | |
1246 | ||
1247 | static int | |
1248 | agg_ac97_read(kobj_t obj, void *sc, int regno) | |
1249 | { | |
1250 | struct agg_info *ess = sc; | |
1251 | int ret; | |
1252 | ||
1253 | /* XXX sound locking violation: agg_lock(ess); */ | |
1254 | ret = agg_rdcodec(ess, regno); | |
1255 | /* agg_unlock(ess); */ | |
1256 | return ret; | |
1257 | } | |
1258 | ||
1259 | static int | |
1260 | agg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data) | |
1261 | { | |
1262 | struct agg_info *ess = sc; | |
1263 | int ret; | |
1264 | ||
1265 | /* XXX sound locking violation: agg_lock(ess); */ | |
1266 | ret = agg_wrcodec(ess, regno, data); | |
1267 | /* agg_unlock(ess); */ | |
1268 | return ret; | |
1269 | } | |
1270 | ||
1271 | ||
1272 | static kobj_method_t agg_ac97_methods[] = { | |
1273 | KOBJMETHOD(ac97_init, agg_ac97_init), | |
1274 | KOBJMETHOD(ac97_read, agg_ac97_read), | |
1275 | KOBJMETHOD(ac97_write, agg_ac97_write), | |
7774cda2 | 1276 | KOBJMETHOD_END |
558a398b SS |
1277 | }; |
1278 | AC97_DECLARE(agg_ac97); | |
1279 | ||
1280 | ||
1281 | /* -------------------------------------------------------------------- */ | |
1282 | ||
1283 | /* Playback channel. */ | |
1284 | ||
984263bc | 1285 | static void * |
2a1ad637 FT |
1286 | aggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, |
1287 | struct pcm_channel *c, int dir) | |
984263bc MD |
1288 | { |
1289 | struct agg_info *ess = devinfo; | |
1290 | struct agg_chinfo *ch; | |
1291 | bus_addr_t physaddr; | |
1292 | void *p; | |
1293 | ||
558a398b SS |
1294 | KASSERT((dir == PCMDIR_PLAY), |
1295 | ("aggpch_init() called for RECORDING channel!")); | |
1296 | ch = ess->pch + ess->playchns; | |
984263bc MD |
1297 | |
1298 | ch->parent = ess; | |
1299 | ch->channel = c; | |
1300 | ch->buffer = b; | |
1301 | ch->num = ess->playchns; | |
984263bc | 1302 | |
2a1ad637 | 1303 | p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr, &ch->map); |
984263bc MD |
1304 | if (p == NULL) |
1305 | return NULL; | |
558a398b SS |
1306 | ch->phys = physaddr; |
1307 | ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT); | |
1308 | ||
984263bc | 1309 | sndbuf_setup(b, p, ess->bufsz); |
558a398b SS |
1310 | ch->blklen = sndbuf_getblksz(b) / 2; |
1311 | ch->buflen = sndbuf_getsize(b) / 2; | |
1312 | ess->playchns++; | |
984263bc | 1313 | |
558a398b SS |
1314 | return ch; |
1315 | } | |
984263bc | 1316 | |
558a398b SS |
1317 | static void |
1318 | adjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size) | |
1319 | { | |
1320 | struct agg_chinfo *pchs[AGG_MAXPLAYCH]; | |
1321 | u_int i, j, k; | |
1322 | bus_addr_t base; | |
1323 | ||
1324 | /* sort pchs by phys address */ | |
1325 | for (i = 0; i < n; i++) { | |
1326 | for (j = 0; j < i; j++) | |
1327 | if (chans[i].phys < pchs[j]->phys) { | |
1328 | for (k = i; k > j; k--) | |
1329 | pchs[k] = pchs[k - 1]; | |
1330 | break; | |
1331 | } | |
1332 | pchs[j] = chans + i; | |
1333 | } | |
984263bc | 1334 | |
558a398b SS |
1335 | /* use new base register if next buffer can not be addressed |
1336 | via current base. */ | |
1337 | #define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1) | |
1338 | base = pchs[0]->base; | |
1339 | for (k = 1, i = 1; i < n; i++) { | |
1340 | if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT) | |
1341 | /* not addressable: assign new base */ | |
1342 | base = (pchs[i]->base -= k++ << BASE_SHIFT); | |
1343 | else | |
1344 | pchs[i]->base = base; | |
1345 | } | |
1346 | #undef BASE_SHIFT | |
984263bc | 1347 | |
558a398b | 1348 | if (bootverbose) { |
67931cc4 | 1349 | kprintf("Total of %d bases are assigned.\n", k); |
558a398b | 1350 | for (i = 0; i < n; i++) { |
67931cc4 | 1351 | kprintf("ch.%d: phys 0x%llx, wpwa 0x%llx\n", |
558a398b SS |
1352 | i, (long long)chans[i].phys, |
1353 | (long long)(chans[i].phys - | |
1354 | chans[i].base) >> 1); | |
1355 | } | |
1356 | } | |
984263bc MD |
1357 | } |
1358 | ||
1359 | static int | |
558a398b | 1360 | aggpch_free(kobj_t obj, void *data) |
984263bc MD |
1361 | { |
1362 | struct agg_chinfo *ch = data; | |
1363 | struct agg_info *ess = ch->parent; | |
1364 | ||
2a1ad637 FT |
1365 | /* free up buffer - called after channel stopped */ |
1366 | dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer), ch->map); | |
984263bc MD |
1367 | |
1368 | /* return 0 if ok */ | |
1369 | return 0; | |
1370 | } | |
1371 | ||
1372 | static int | |
558a398b | 1373 | aggpch_setformat(kobj_t obj, void *data, u_int32_t format) |
984263bc MD |
1374 | { |
1375 | struct agg_chinfo *ch = data; | |
984263bc | 1376 | |
558a398b SS |
1377 | if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) |
1378 | return EINVAL; | |
1379 | ch->stereo = ch->qs16 = ch->us = 0; | |
2a1ad637 | 1380 | if (AFMT_CHANNEL(format) > 1) |
558a398b | 1381 | ch->stereo = 1; |
984263bc | 1382 | |
984263bc | 1383 | if (format & AFMT_U8 || format & AFMT_S8) { |
984263bc | 1384 | if (format & AFMT_U8) |
558a398b SS |
1385 | ch->us = 1; |
1386 | } else | |
1387 | ch->qs16 = 1; | |
984263bc MD |
1388 | return 0; |
1389 | } | |
1390 | ||
2a1ad637 | 1391 | static u_int32_t |
558a398b | 1392 | aggpch_setspeed(kobj_t obj, void *data, u_int32_t speed) |
984263bc | 1393 | { |
2a1ad637 FT |
1394 | |
1395 | ((struct agg_chinfo*)data)->speed = speed; | |
1396 | ||
1397 | return (speed); | |
984263bc MD |
1398 | } |
1399 | ||
2a1ad637 | 1400 | static u_int32_t |
558a398b | 1401 | aggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) |
984263bc | 1402 | { |
558a398b SS |
1403 | struct agg_chinfo *ch = data; |
1404 | int blkcnt; | |
1405 | ||
1406 | /* try to keep at least 20msec DMA space */ | |
1407 | blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize); | |
1408 | RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); | |
1409 | ||
1410 | if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { | |
1411 | sndbuf_resize(ch->buffer, blkcnt, blocksize); | |
1412 | blkcnt = sndbuf_getblkcnt(ch->buffer); | |
1413 | blocksize = sndbuf_getblksz(ch->buffer); | |
1414 | } else { | |
1415 | sndbuf_setblkcnt(ch->buffer, blkcnt); | |
1416 | sndbuf_setblksz(ch->buffer, blocksize); | |
1417 | } | |
1418 | ||
1419 | ch->blklen = blocksize / 2; | |
1420 | ch->buflen = blkcnt * blocksize / 2; | |
1421 | return blocksize; | |
984263bc MD |
1422 | } |
1423 | ||
1424 | static int | |
558a398b | 1425 | aggpch_trigger(kobj_t obj, void *data, int go) |
984263bc MD |
1426 | { |
1427 | struct agg_chinfo *ch = data; | |
1428 | ||
1429 | switch (go) { | |
1430 | case PCMTRIG_EMLDMAWR: | |
558a398b | 1431 | break; |
984263bc | 1432 | case PCMTRIG_START: |
558a398b | 1433 | aggch_start_dac(ch); |
984263bc MD |
1434 | break; |
1435 | case PCMTRIG_ABORT: | |
1436 | case PCMTRIG_STOP: | |
558a398b | 1437 | aggch_stop_dac(ch); |
984263bc MD |
1438 | break; |
1439 | } | |
984263bc MD |
1440 | return 0; |
1441 | } | |
1442 | ||
2a1ad637 | 1443 | static u_int32_t |
558a398b | 1444 | aggpch_getptr(kobj_t obj, void *data) |
984263bc MD |
1445 | { |
1446 | struct agg_chinfo *ch = data; | |
2a1ad637 | 1447 | u_int32_t cp; |
984263bc | 1448 | |
558a398b SS |
1449 | agg_lock(ch->parent); |
1450 | cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); | |
1451 | agg_unlock(ch->parent); | |
984263bc | 1452 | |
558a398b SS |
1453 | return ch->qs16 && ch->stereo |
1454 | ? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base)) | |
1455 | : (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base)); | |
984263bc MD |
1456 | } |
1457 | ||
1458 | static struct pcmchan_caps * | |
558a398b | 1459 | aggpch_getcaps(kobj_t obj, void *data) |
984263bc MD |
1460 | { |
1461 | static u_int32_t playfmt[] = { | |
2a1ad637 FT |
1462 | SND_FORMAT(AFMT_U8, 1, 0), |
1463 | SND_FORMAT(AFMT_U8, 2, 0), | |
1464 | SND_FORMAT(AFMT_S8, 1, 0), | |
1465 | SND_FORMAT(AFMT_S8, 2, 0), | |
1466 | SND_FORMAT(AFMT_S16_LE, 1, 0), | |
1467 | SND_FORMAT(AFMT_S16_LE, 2, 0), | |
984263bc MD |
1468 | 0 |
1469 | }; | |
558a398b | 1470 | static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; |
984263bc | 1471 | |
558a398b SS |
1472 | return &playcaps; |
1473 | } | |
1474 | ||
1475 | ||
1476 | static kobj_method_t aggpch_methods[] = { | |
1477 | KOBJMETHOD(channel_init, aggpch_init), | |
1478 | KOBJMETHOD(channel_free, aggpch_free), | |
1479 | KOBJMETHOD(channel_setformat, aggpch_setformat), | |
1480 | KOBJMETHOD(channel_setspeed, aggpch_setspeed), | |
1481 | KOBJMETHOD(channel_setblocksize, aggpch_setblocksize), | |
1482 | KOBJMETHOD(channel_trigger, aggpch_trigger), | |
1483 | KOBJMETHOD(channel_getptr, aggpch_getptr), | |
1484 | KOBJMETHOD(channel_getcaps, aggpch_getcaps), | |
7774cda2 | 1485 | KOBJMETHOD_END |
558a398b SS |
1486 | }; |
1487 | CHANNEL_DECLARE(aggpch); | |
1488 | ||
1489 | ||
1490 | /* -------------------------------------------------------------------- */ | |
1491 | ||
1492 | /* Recording channel. */ | |
1493 | ||
1494 | static void * | |
2a1ad637 FT |
1495 | aggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, |
1496 | struct pcm_channel *c, int dir) | |
558a398b SS |
1497 | { |
1498 | struct agg_info *ess = devinfo; | |
1499 | struct agg_rchinfo *ch; | |
1500 | u_int8_t *p; | |
1501 | ||
1502 | KASSERT((dir == PCMDIR_REC), | |
1503 | ("aggrch_init() called for PLAYBACK channel!")); | |
1504 | ch = &ess->rch; | |
1505 | ||
1506 | ch->parent = ess; | |
1507 | ch->channel = c; | |
1508 | ch->buffer = b; | |
1509 | ||
1510 | /* Uses the bottom-half of the status buffer. */ | |
1511 | p = ess->stat + ess->bufsz; | |
1512 | ch->phys = ess->phys + ess->bufsz; | |
1513 | ch->base = ess->phys; | |
1514 | ch->src = (int16_t *)(p + ess->bufsz); | |
1515 | ch->srcphys = ch->phys + ess->bufsz; | |
1516 | ch->sink = (int16_t *)p; | |
1517 | ||
1518 | sndbuf_setup(b, p, ess->bufsz); | |
1519 | ch->blklen = sndbuf_getblksz(b) / 2; | |
1520 | ch->buflen = sndbuf_getsize(b) / 2; | |
1521 | ||
1522 | return ch; | |
1523 | } | |
1524 | ||
1525 | static int | |
1526 | aggrch_setformat(kobj_t obj, void *data, u_int32_t format) | |
1527 | { | |
1528 | struct agg_rchinfo *ch = data; | |
1529 | ||
1530 | if (!(format & AFMT_S16_LE)) | |
1531 | return EINVAL; | |
2a1ad637 | 1532 | if (AFMT_CHANNEL(format) > 1) |
558a398b SS |
1533 | ch->stereo = 1; |
1534 | else | |
1535 | ch->stereo = 0; | |
1536 | return 0; | |
1537 | } | |
1538 | ||
2a1ad637 | 1539 | static u_int32_t |
558a398b SS |
1540 | aggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) |
1541 | { | |
2a1ad637 FT |
1542 | |
1543 | ((struct agg_rchinfo*)data)->speed = speed; | |
1544 | ||
1545 | return (speed); | |
558a398b SS |
1546 | } |
1547 | ||
2a1ad637 | 1548 | static u_int32_t |
558a398b SS |
1549 | aggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) |
1550 | { | |
1551 | struct agg_rchinfo *ch = data; | |
1552 | int blkcnt; | |
1553 | ||
1554 | /* try to keep at least 20msec DMA space */ | |
1555 | blkcnt = (ch->speed << ch->stereo) / (25 * blocksize); | |
1556 | RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); | |
1557 | ||
1558 | if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { | |
1559 | sndbuf_resize(ch->buffer, blkcnt, blocksize); | |
1560 | blkcnt = sndbuf_getblkcnt(ch->buffer); | |
1561 | blocksize = sndbuf_getblksz(ch->buffer); | |
1562 | } else { | |
1563 | sndbuf_setblkcnt(ch->buffer, blkcnt); | |
1564 | sndbuf_setblksz(ch->buffer, blocksize); | |
1565 | } | |
1566 | ||
1567 | ch->blklen = blocksize / 2; | |
1568 | ch->buflen = blkcnt * blocksize / 2; | |
1569 | return blocksize; | |
1570 | } | |
1571 | ||
1572 | static int | |
1573 | aggrch_trigger(kobj_t obj, void *sc, int go) | |
1574 | { | |
1575 | struct agg_rchinfo *ch = sc; | |
1576 | ||
1577 | switch (go) { | |
1578 | case PCMTRIG_EMLDMARD: | |
1579 | if (ch->stereo) | |
1580 | aggch_feed_adc_stereo(ch); | |
1581 | else | |
1582 | aggch_feed_adc_mono(ch); | |
1583 | break; | |
1584 | case PCMTRIG_START: | |
1585 | aggch_start_adc(ch); | |
1586 | break; | |
1587 | case PCMTRIG_ABORT: | |
1588 | case PCMTRIG_STOP: | |
1589 | aggch_stop_adc(ch); | |
1590 | break; | |
1591 | } | |
1592 | return 0; | |
1593 | } | |
1594 | ||
2a1ad637 | 1595 | static u_int32_t |
558a398b SS |
1596 | aggrch_getptr(kobj_t obj, void *sc) |
1597 | { | |
1598 | struct agg_rchinfo *ch = sc; | |
1599 | ||
1600 | return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1; | |
1601 | } | |
1602 | ||
1603 | static struct pcmchan_caps * | |
1604 | aggrch_getcaps(kobj_t obj, void *sc) | |
1605 | { | |
984263bc | 1606 | static u_int32_t recfmt[] = { |
2a1ad637 FT |
1607 | SND_FORMAT(AFMT_S16_LE, 1, 0), |
1608 | SND_FORMAT(AFMT_S16_LE, 2, 0), | |
984263bc MD |
1609 | 0 |
1610 | }; | |
558a398b | 1611 | static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; |
984263bc | 1612 | |
558a398b | 1613 | return &reccaps; |
984263bc MD |
1614 | } |
1615 | ||
558a398b SS |
1616 | static kobj_method_t aggrch_methods[] = { |
1617 | KOBJMETHOD(channel_init, aggrch_init), | |
1618 | /* channel_free: no-op */ | |
1619 | KOBJMETHOD(channel_setformat, aggrch_setformat), | |
1620 | KOBJMETHOD(channel_setspeed, aggrch_setspeed), | |
1621 | KOBJMETHOD(channel_setblocksize, aggrch_setblocksize), | |
1622 | KOBJMETHOD(channel_trigger, aggrch_trigger), | |
1623 | KOBJMETHOD(channel_getptr, aggrch_getptr), | |
1624 | KOBJMETHOD(channel_getcaps, aggrch_getcaps), | |
7774cda2 | 1625 | KOBJMETHOD_END |
984263bc | 1626 | }; |
558a398b SS |
1627 | CHANNEL_DECLARE(aggrch); |
1628 | ||
984263bc MD |
1629 | |
1630 | /* ----------------------------- | |
1631 | * Bus space. | |
1632 | */ | |
1633 | ||
1634 | static void | |
1635 | agg_intr(void *sc) | |
1636 | { | |
1637 | struct agg_info* ess = sc; | |
558a398b | 1638 | register u_int8_t status; |
984263bc | 1639 | int i; |
558a398b | 1640 | u_int m; |
984263bc | 1641 | |
558a398b | 1642 | status = AGG_RD(ess, PORT_HOSTINT_STAT, 1); |
984263bc MD |
1643 | if (!status) |
1644 | return; | |
1645 | ||
558a398b SS |
1646 | /* Acknowledge intr. */ |
1647 | AGG_WR(ess, PORT_HOSTINT_STAT, status, 1); | |
1648 | ||
1649 | if (status & HOSTINT_STAT_DSOUND) { | |
1650 | #ifdef AGG_JITTER_CORRECTION | |
1651 | agg_lock(ess); | |
1652 | #endif | |
1653 | if (ess->curpwr <= PCI_POWERSTATE_D1) { | |
1654 | AGG_WR(ess, PORT_INT_STAT, 1, 2); | |
1655 | #ifdef AGG_JITTER_CORRECTION | |
1656 | for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { | |
1657 | if (ess->active & m) | |
1658 | suppress_jitter(ess->pch + i); | |
1659 | } | |
1660 | if (ess->active & m) | |
1661 | suppress_rec_jitter(&ess->rch); | |
1662 | agg_unlock(ess); | |
1663 | #endif | |
1664 | for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { | |
1665 | if (ess->active & m) { | |
1666 | if (ess->curpwr <= PCI_POWERSTATE_D1) | |
1667 | chn_intr(ess->pch[i].channel); | |
1668 | else { | |
1669 | m = 0; | |
1670 | break; | |
1671 | } | |
1672 | } | |
1673 | } | |
1674 | if ((ess->active & m) | |
1675 | && ess->curpwr <= PCI_POWERSTATE_D1) | |
1676 | chn_intr(ess->rch.channel); | |
1677 | } | |
1678 | #ifdef AGG_JITTER_CORRECTION | |
1679 | else | |
1680 | agg_unlock(ess); | |
1681 | #endif | |
1682 | } | |
984263bc MD |
1683 | |
1684 | if (status & HOSTINT_STAT_HWVOL) { | |
558a398b SS |
1685 | register u_int8_t event; |
1686 | ||
1687 | agg_lock(ess); | |
1688 | event = AGG_RD(ess, PORT_HWVOL_MASTER, 1); | |
1689 | AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1); | |
1690 | agg_unlock(ess); | |
984263bc | 1691 | |
984263bc | 1692 | switch (event) { |
984263bc MD |
1693 | case HWVOL_UP: |
1694 | mixer_hwvol_step(ess->dev, 1, 1); | |
1695 | break; | |
1696 | case HWVOL_DOWN: | |
1697 | mixer_hwvol_step(ess->dev, -1, -1); | |
1698 | break; | |
1699 | case HWVOL_NOP: | |
1700 | break; | |
1701 | default: | |
558a398b SS |
1702 | if (event & HWVOL_MUTE) { |
1703 | mixer_hwvol_mute(ess->dev); | |
1704 | break; | |
1705 | } | |
1706 | device_printf(ess->dev, | |
1707 | "%s: unknown HWVOL event 0x%x\n", | |
1708 | device_get_nameunit(ess->dev), event); | |
984263bc | 1709 | } |
984263bc | 1710 | } |
984263bc MD |
1711 | } |
1712 | ||
1713 | static void | |
1714 | setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) | |
1715 | { | |
1716 | bus_addr_t *phys = arg; | |
1717 | ||
1718 | *phys = error? 0 : segs->ds_addr; | |
1719 | ||
1720 | if (bootverbose) { | |
67931cc4 | 1721 | kprintf("setmap (%lx, %lx), nseg=%d, error=%d\n", |
984263bc MD |
1722 | (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, |
1723 | nseg, error); | |
1724 | } | |
1725 | } | |
1726 | ||
1727 | static void * | |
2a1ad637 FT |
1728 | dma_malloc(bus_dma_tag_t dmat, u_int32_t sz, bus_addr_t *phys, |
1729 | bus_dmamap_t *map) | |
984263bc MD |
1730 | { |
1731 | void *buf; | |
984263bc | 1732 | |
2a1ad637 | 1733 | if (bus_dmamem_alloc(dmat, &buf, BUS_DMA_NOWAIT, map)) |
984263bc | 1734 | return NULL; |
2a1ad637 FT |
1735 | if (bus_dmamap_load(dmat, *map, buf, sz, setmap, phys, 0) != 0 || |
1736 | *phys == 0) { | |
1737 | bus_dmamem_free(dmat, buf, *map); | |
984263bc MD |
1738 | return NULL; |
1739 | } | |
1740 | return buf; | |
1741 | } | |
1742 | ||
1743 | static void | |
2a1ad637 | 1744 | dma_free(bus_dma_tag_t dmat, void *buf, bus_dmamap_t map) |
984263bc | 1745 | { |
2a1ad637 FT |
1746 | bus_dmamap_unload(dmat, map); |
1747 | bus_dmamem_free(dmat, buf, map); | |
984263bc MD |
1748 | } |
1749 | ||
1750 | static int | |
1751 | agg_probe(device_t dev) | |
1752 | { | |
1753 | char *s = NULL; | |
1754 | ||
1755 | switch (pci_get_devid(dev)) { | |
1756 | case MAESTRO_1_PCI_ID: | |
1757 | s = "ESS Technology Maestro-1"; | |
1758 | break; | |
1759 | ||
1760 | case MAESTRO_2_PCI_ID: | |
1761 | s = "ESS Technology Maestro-2"; | |
1762 | break; | |
1763 | ||
1764 | case MAESTRO_2E_PCI_ID: | |
1765 | s = "ESS Technology Maestro-2E"; | |
1766 | break; | |
1767 | } | |
1768 | ||
1769 | if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { | |
1770 | device_set_desc(dev, s); | |
558a398b | 1771 | return BUS_PROBE_DEFAULT; |
984263bc MD |
1772 | } |
1773 | return ENXIO; | |
1774 | } | |
1775 | ||
1776 | static int | |
1777 | agg_attach(device_t dev) | |
1778 | { | |
1779 | struct agg_info *ess = NULL; | |
1780 | u_int32_t data; | |
558a398b | 1781 | int regid = PCIR_BAR(0); |
984263bc MD |
1782 | struct resource *reg = NULL; |
1783 | struct ac97_info *codec = NULL; | |
1784 | int irqid = 0; | |
1785 | struct resource *irq = NULL; | |
1786 | void *ih = NULL; | |
1787 | char status[SND_STATUSLEN]; | |
2a1ad637 | 1788 | int dacn, ret = 0; |
984263bc | 1789 | |
67931cc4 | 1790 | ess = kmalloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO); |
984263bc MD |
1791 | ess->dev = dev; |
1792 | ||
67931cc4 | 1793 | lockinit(&ess->lock, device_get_desc(dev), 0, LK_CANRECURSE); |
2a1ad637 FT |
1794 | |
1795 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
1796 | "dac", &dacn) == 0) { | |
1797 | if (dacn < 1) | |
1798 | dacn = 1; | |
1799 | else if (dacn > AGG_MAXPLAYCH) | |
1800 | dacn = AGG_MAXPLAYCH; | |
1801 | } else | |
1802 | dacn = AGG_MAXPLAYCH; | |
558a398b | 1803 | |
984263bc | 1804 | ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); |
2a1ad637 | 1805 | if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev), |
558a398b SS |
1806 | /*align */ 4, 1 << (16+1), |
1807 | /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, | |
558a398b SS |
1808 | /*size */ ess->bufsz, 1, 0x3ffff, |
1809 | /*flags */ 0, | |
558a398b SS |
1810 | &ess->buf_dmat) != 0) { |
1811 | device_printf(dev, "unable to create dma tag\n"); | |
1812 | ret = ENOMEM; | |
1813 | goto bad; | |
1814 | } | |
984263bc | 1815 | |
2a1ad637 | 1816 | if (bus_dma_tag_create(/*parent*/ bus_get_dma_tag(dev), |
558a398b SS |
1817 | /*align */ 1 << WAVCACHE_BASEADDR_SHIFT, |
1818 | 1 << (16+1), | |
1819 | /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, | |
558a398b SS |
1820 | /*size */ 3*ess->bufsz, 1, 0x3ffff, |
1821 | /*flags */ 0, | |
558a398b | 1822 | &ess->stat_dmat) != 0) { |
984263bc | 1823 | device_printf(dev, "unable to create dma tag\n"); |
558a398b | 1824 | ret = ENOMEM; |
984263bc MD |
1825 | goto bad; |
1826 | } | |
1827 | ||
558a398b | 1828 | /* Allocate the room for brain-damaging status buffer. */ |
2a1ad637 FT |
1829 | ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys, |
1830 | &ess->stat_map); | |
984263bc MD |
1831 | if (ess->stat == NULL) { |
1832 | device_printf(dev, "cannot allocate status buffer\n"); | |
558a398b | 1833 | ret = ENOMEM; |
984263bc MD |
1834 | goto bad; |
1835 | } | |
1836 | if (bootverbose) | |
558a398b SS |
1837 | device_printf(dev, "Maestro status/record buffer: %#llx\n", |
1838 | (long long)ess->phys); | |
984263bc | 1839 | |
558a398b SS |
1840 | /* State D0-uninitialized. */ |
1841 | ess->curpwr = PCI_POWERSTATE_D3; | |
1842 | pci_set_powerstate(dev, PCI_POWERSTATE_D0); | |
984263bc | 1843 | |
2a1ad637 | 1844 | pci_enable_busmaster(dev); |
984263bc | 1845 | |
558a398b | 1846 | /* Allocate resources. */ |
2a1ad637 | 1847 | reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, RF_ACTIVE); |
558a398b SS |
1848 | if (reg != NULL) { |
1849 | ess->reg = reg; | |
1850 | ess->regid = regid; | |
1851 | ess->st = rman_get_bustag(reg); | |
1852 | ess->sh = rman_get_bushandle(reg); | |
1853 | } else { | |
984263bc | 1854 | device_printf(dev, "unable to map register space\n"); |
558a398b SS |
1855 | ret = ENXIO; |
1856 | goto bad; | |
1857 | } | |
1858 | irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, | |
1859 | RF_ACTIVE | RF_SHAREABLE); | |
1860 | if (irq != NULL) { | |
1861 | ess->irq = irq; | |
1862 | ess->irqid = irqid; | |
1863 | } else { | |
1864 | device_printf(dev, "unable to map interrupt\n"); | |
1865 | ret = ENXIO; | |
984263bc MD |
1866 | goto bad; |
1867 | } | |
1868 | ||
558a398b SS |
1869 | /* Setup resources. */ |
1870 | if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) { | |
1871 | device_printf(dev, "unable to setup interrupt\n"); | |
1872 | ret = ENXIO; | |
1873 | goto bad; | |
1874 | } else | |
1875 | ess->ih = ih; | |
1876 | ||
1877 | /* Transition from D0-uninitialized to D0. */ | |
1878 | agg_lock(ess); | |
1879 | agg_power(ess, PCI_POWERSTATE_D0); | |
1880 | if (agg_rdcodec(ess, 0) == 0x80) { | |
1881 | /* XXX - TODO: PT101 */ | |
1882 | agg_unlock(ess); | |
984263bc | 1883 | device_printf(dev, "PT101 codec detected!\n"); |
558a398b | 1884 | ret = ENXIO; |
984263bc MD |
1885 | goto bad; |
1886 | } | |
558a398b | 1887 | agg_unlock(ess); |
984263bc | 1888 | codec = AC97_CREATE(dev, ess, agg_ac97); |
558a398b SS |
1889 | if (codec == NULL) { |
1890 | device_printf(dev, "failed to create AC97 codec softc!\n"); | |
1891 | ret = ENOMEM; | |
984263bc | 1892 | goto bad; |
558a398b SS |
1893 | } |
1894 | if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { | |
1895 | device_printf(dev, "mixer initialization failed!\n"); | |
1896 | ret = ENXIO; | |
984263bc MD |
1897 | goto bad; |
1898 | } | |
558a398b | 1899 | ess->codec = codec; |
984263bc | 1900 | |
2a1ad637 | 1901 | ret = pcm_register(dev, ess, dacn, 1); |
558a398b | 1902 | if (ret) |
984263bc MD |
1903 | goto bad; |
1904 | ||
1905 | mixer_hwvol_init(dev); | |
558a398b SS |
1906 | agg_lock(ess); |
1907 | agg_power(ess, powerstate_init); | |
1908 | agg_unlock(ess); | |
2a1ad637 | 1909 | for (data = 0; data < dacn; data++) |
558a398b | 1910 | pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess); |
984263bc | 1911 | pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); |
558a398b SS |
1912 | adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); |
1913 | ||
67931cc4 | 1914 | ksnprintf(status, SND_STATUSLEN, |
558a398b SS |
1915 | "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d", |
1916 | rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), | |
1917 | pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); | |
984263bc MD |
1918 | pcm_setstatus(dev, status); |
1919 | ||
1920 | return 0; | |
1921 | ||
1922 | bad: | |
1923 | if (codec != NULL) | |
1924 | ac97_destroy(codec); | |
1925 | if (ih != NULL) | |
1926 | bus_teardown_intr(dev, irq, ih); | |
1927 | if (irq != NULL) | |
1928 | bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); | |
1929 | if (reg != NULL) | |
1930 | bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); | |
1931 | if (ess != NULL) { | |
984263bc | 1932 | if (ess->stat != NULL) |
2a1ad637 | 1933 | dma_free(ess->stat_dmat, ess->stat, ess->stat_map); |
558a398b SS |
1934 | if (ess->stat_dmat != NULL) |
1935 | bus_dma_tag_destroy(ess->stat_dmat); | |
1936 | if (ess->buf_dmat != NULL) | |
1937 | bus_dma_tag_destroy(ess->buf_dmat); | |
67931cc4 FT |
1938 | lockuninit(&ess->lock); |
1939 | kfree(ess, M_DEVBUF); | |
984263bc MD |
1940 | } |
1941 | ||
558a398b | 1942 | return ret; |
984263bc MD |
1943 | } |
1944 | ||
1945 | static int | |
1946 | agg_detach(device_t dev) | |
1947 | { | |
1948 | struct agg_info *ess = pcm_getdevinfo(dev); | |
1949 | int r; | |
558a398b SS |
1950 | u_int16_t icr; |
1951 | ||
1952 | icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2); | |
1953 | AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); | |
1954 | ||
1955 | agg_lock(ess); | |
1956 | if (ess->active) { | |
1957 | AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); | |
1958 | agg_unlock(ess); | |
1959 | return EBUSY; | |
1960 | } | |
1961 | agg_unlock(ess); | |
984263bc MD |
1962 | |
1963 | r = pcm_unregister(dev); | |
558a398b SS |
1964 | if (r) { |
1965 | AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); | |
984263bc | 1966 | return r; |
558a398b | 1967 | } |
984263bc | 1968 | |
558a398b SS |
1969 | agg_lock(ess); |
1970 | agg_power(ess, PCI_POWERSTATE_D3); | |
1971 | agg_unlock(ess); | |
984263bc MD |
1972 | |
1973 | bus_teardown_intr(dev, ess->irq, ess->ih); | |
1974 | bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); | |
1975 | bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); | |
2a1ad637 | 1976 | dma_free(ess->stat_dmat, ess->stat, ess->stat_map); |
558a398b SS |
1977 | bus_dma_tag_destroy(ess->stat_dmat); |
1978 | bus_dma_tag_destroy(ess->buf_dmat); | |
67931cc4 FT |
1979 | lockuninit(&ess->lock); |
1980 | kfree(ess, M_DEVBUF); | |
984263bc MD |
1981 | return 0; |
1982 | } | |
1983 | ||
1984 | static int | |
1985 | agg_suspend(device_t dev) | |
1986 | { | |
1987 | struct agg_info *ess = pcm_getdevinfo(dev); | |
984263bc | 1988 | |
558a398b SS |
1989 | AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); |
1990 | agg_lock(ess); | |
1991 | agg_power(ess, PCI_POWERSTATE_D3); | |
1992 | agg_unlock(ess); | |
984263bc MD |
1993 | |
1994 | return 0; | |
1995 | } | |
1996 | ||
1997 | static int | |
1998 | agg_resume(device_t dev) | |
1999 | { | |
b6d92ffb | 2000 | int i; |
984263bc MD |
2001 | struct agg_info *ess = pcm_getdevinfo(dev); |
2002 | ||
984263bc MD |
2003 | for (i = 0; i < ess->playchns; i++) |
2004 | if (ess->active & (1 << i)) | |
2005 | aggch_start_dac(ess->pch + i); | |
984263bc MD |
2006 | if (ess->active & (1 << i)) |
2007 | aggch_start_adc(&ess->rch); | |
558a398b SS |
2008 | |
2009 | agg_lock(ess); | |
2010 | if (!ess->active) | |
2011 | agg_power(ess, powerstate_init); | |
2012 | agg_unlock(ess); | |
558a398b SS |
2013 | |
2014 | if (mixer_reinit(dev)) { | |
2015 | device_printf(dev, "unable to reinitialize the mixer\n"); | |
2016 | return ENXIO; | |
984263bc | 2017 | } |
558a398b | 2018 | |
984263bc MD |
2019 | return 0; |
2020 | } | |
2021 | ||
2022 | static int | |
2023 | agg_shutdown(device_t dev) | |
2024 | { | |
2025 | struct agg_info *ess = pcm_getdevinfo(dev); | |
984263bc | 2026 | |
558a398b SS |
2027 | agg_lock(ess); |
2028 | agg_power(ess, PCI_POWERSTATE_D3); | |
2029 | agg_unlock(ess); | |
984263bc | 2030 | |
984263bc MD |
2031 | return 0; |
2032 | } | |
2033 | ||
2034 | ||
2035 | static device_method_t agg_methods[] = { | |
2036 | DEVMETHOD(device_probe, agg_probe), | |
2037 | DEVMETHOD(device_attach, agg_attach), | |
2038 | DEVMETHOD(device_detach, agg_detach), | |
2039 | DEVMETHOD(device_suspend, agg_suspend), | |
2040 | DEVMETHOD(device_resume, agg_resume), | |
2041 | DEVMETHOD(device_shutdown, agg_shutdown), | |
2042 | ||
2a1ad637 | 2043 | { 0, 0 } |
984263bc MD |
2044 | }; |
2045 | ||
2046 | static driver_t agg_driver = { | |
2047 | "pcm", | |
2048 | agg_methods, | |
2049 | PCM_SOFTC_SIZE, | |
2050 | }; | |
2051 | ||
558a398b SS |
2052 | /*static devclass_t pcm_devclass;*/ |
2053 | ||
aa6ac96e | 2054 | DRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, NULL, NULL); |
558a398b | 2055 | MODULE_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); |
984263bc | 2056 | MODULE_VERSION(snd_maestro, 1); |