ad1816.c revision 78564
1203954Srdivacky/* 2203954Srdivacky * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3203954Srdivacky * Copyright Luigi Rizzo, 1997,1998 4203954Srdivacky * Copyright by Hannu Savolainen 1994, 1995 5203954Srdivacky * All rights reserved. 6203954Srdivacky * 7203954Srdivacky * Redistribution and use in source and binary forms, with or without 8203954Srdivacky * modification, are permitted provided that the following conditions 9203954Srdivacky * are met: 10203954Srdivacky * 1. Redistributions of source code must retain the above copyright 11203954Srdivacky * notice, this list of conditions and the following disclaimer. 12221345Sdim * 2. Redistributions in binary form must reproduce the above copyright 13203954Srdivacky * notice, this list of conditions and the following disclaimer in the 14221345Sdim * documentation and/or other materials provided with the distribution. 15204642Srdivacky * 16203954Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17204642Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18203954Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19203954Srdivacky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20204642Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21204642Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22204642Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23204642Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24204642Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25204642Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26204642Srdivacky * SUCH DAMAGE. 27204642Srdivacky * 28204642Srdivacky * $FreeBSD: head/sys/dev/sound/isa/ad1816.c 78564 2001-06-21 19:45:59Z greid $ 29204642Srdivacky */ 30218893Sdim 31204642Srdivacky#include <dev/sound/pcm/sound.h> 32204642Srdivacky#include <dev/sound/isa/ad1816.h> 33204642Srdivacky 34204642Srdivacky#include "mixer_if.h" 35218893Sdim 36204642Srdivackystruct ad1816_info; 37204642Srdivacky 38204642Srdivackystruct ad1816_chinfo { 39204642Srdivacky struct ad1816_info *parent; 40204642Srdivacky struct pcm_channel *channel; 41204642Srdivacky struct snd_dbuf *buffer; 42204642Srdivacky int dir, blksz; 43204642Srdivacky}; 44204642Srdivacky 45204642Srdivackystruct ad1816_info { 46204642Srdivacky struct resource *io_base; /* primary I/O address for the board */ 47204642Srdivacky int io_rid; 48204642Srdivacky struct resource *irq; 49203954Srdivacky int irq_rid; 50203954Srdivacky struct resource *drq1; /* play */ 51203954Srdivacky int drq1_rid; 52203954Srdivacky struct resource *drq2; /* rec */ 53218893Sdim int drq2_rid; 54203954Srdivacky void *ih; 55203954Srdivacky bus_dma_tag_t parent_dmat; 56203954Srdivacky void *lock; 57203954Srdivacky 58218893Sdim struct ad1816_chinfo pch, rch; 59203954Srdivacky}; 60203954Srdivacky 61203954Srdivackystatic driver_intr_t ad1816_intr; 62203954Srdivackystatic int ad1816_probe(device_t dev); 63218893Sdimstatic int ad1816_attach(device_t dev); 64204642Srdivacky 65204642Srdivacky/* IO primitives */ 66204642Srdivackystatic int ad1816_wait_init(struct ad1816_info *ad1816, int x); 67203954Srdivackystatic u_short ad1816_read(struct ad1816_info *ad1816, u_int reg); 68218893Sdimstatic void ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data); 69204642Srdivacky 70204642Srdivackystatic u_int32_t ad1816_fmt[] = { 71204642Srdivacky AFMT_U8, 72204642Srdivacky AFMT_STEREO | AFMT_U8, 73218893Sdim AFMT_S16_LE, 74218893Sdim AFMT_STEREO | AFMT_S16_LE, 75218893Sdim AFMT_MU_LAW, 76218893Sdim AFMT_STEREO | AFMT_MU_LAW, 77204792Srdivacky AFMT_A_LAW, 78204792Srdivacky AFMT_STEREO | AFMT_A_LAW, 79204792Srdivacky 0 80204792Srdivacky}; 81204792Srdivacky 82204792Srdivackystatic struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0}; 83204792Srdivacky 84218893Sdim#define AD1816_MUTE 31 /* value for mute */ 85204642Srdivacky 86204642Srdivackystatic void 87204642Srdivackyad1816_lock(struct ad1816_info *ad1816) 88204642Srdivacky{ 89218893Sdim snd_mtxlock(ad1816->lock); 90204642Srdivacky} 91204642Srdivacky 92218893Sdimstatic void 93204642Srdivackyad1816_unlock(struct ad1816_info *ad1816) 94204642Srdivacky{ 95204642Srdivacky snd_mtxunlock(ad1816->lock); 96221345Sdim} 97221345Sdim 98221345Sdimstatic int 99221345Sdimport_rd(struct resource *port, int off) 100203954Srdivacky{ 101203954Srdivacky if (port) 102218893Sdim return bus_space_read_1(rman_get_bustag(port), 103203954Srdivacky rman_get_bushandle(port), 104203954Srdivacky off); 105203954Srdivacky else 106218893Sdim return -1; 107204642Srdivacky} 108204642Srdivacky 109218893Sdimstatic void 110204642Srdivackyport_wr(struct resource *port, int off, u_int8_t data) 111203954Srdivacky{ 112204642Srdivacky if (port) 113203954Srdivacky return bus_space_write_1(rman_get_bustag(port), 114218893Sdim rman_get_bushandle(port), 115204642Srdivacky off, data); 116203954Srdivacky} 117203954Srdivacky 118203954Srdivackystatic int 119203954Srdivackyio_rd(struct ad1816_info *ad1816, int reg) 120218893Sdim{ 121204642Srdivacky return port_rd(ad1816->io_base, reg); 122204642Srdivacky} 123204642Srdivacky 124204642Srdivackystatic void 125204642Srdivackyio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data) 126204642Srdivacky{ 127204642Srdivacky return port_wr(ad1816->io_base, reg, data); 128204642Srdivacky} 129204642Srdivacky 130204642Srdivackystatic void 131204642Srdivackyad1816_intr(void *arg) 132218893Sdim{ 133204642Srdivacky struct ad1816_info *ad1816 = (struct ad1816_info *)arg; 134204642Srdivacky unsigned char c, served = 0; 135204642Srdivacky 136204642Srdivacky ad1816_lock(ad1816); 137204642Srdivacky /* get interupt status */ 138204642Srdivacky c = io_rd(ad1816, AD1816_INT); 139204642Srdivacky 140204642Srdivacky /* check for stray interupts */ 141204642Srdivacky if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { 142204642Srdivacky printf("pcm: stray int (%x)\n", c); 143204642Srdivacky c &= AD1816_INTRCI | AD1816_INTRPI; 144218893Sdim } 145203954Srdivacky /* check for capture interupt */ 146203954Srdivacky if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) { 147203954Srdivacky chn_intr(ad1816->rch.channel); 148203954Srdivacky served |= AD1816_INTRCI; /* cp served */ 149203954Srdivacky } 150204642Srdivacky /* check for playback interupt */ 151203954Srdivacky if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) { 152203954Srdivacky chn_intr(ad1816->pch.channel); 153203954Srdivacky served |= AD1816_INTRPI; /* pb served */ 154203954Srdivacky } 155203954Srdivacky if (served == 0) { 156203954Srdivacky /* this probably means this is not a (working) ad1816 chip, */ 157203954Srdivacky /* or an error in dma handling */ 158203954Srdivacky printf("pcm: int without reason (%x)\n", c); 159203954Srdivacky c = 0; 160203954Srdivacky } else c &= ~served; 161203954Srdivacky io_wr(ad1816, AD1816_INT, c); 162203954Srdivacky c = io_rd(ad1816, AD1816_INT); 163203954Srdivacky if (c != 0) printf("pcm: int clear failed (%x)\n", c); 164203954Srdivacky ad1816_unlock(ad1816); 165218893Sdim} 166203954Srdivacky 167203954Srdivackystatic int 168221345Sdimad1816_wait_init(struct ad1816_info *ad1816, int x) 169221345Sdim{ 170221345Sdim int n = 0; /* to shut up the compiler... */ 171221345Sdim 172221345Sdim for (; x--;) 173221345Sdim if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); 174203954Srdivacky else return n; 175203954Srdivacky printf("ad1816_wait_init failed 0x%02x.\n", n); 176203954Srdivacky return -1; 177203954Srdivacky} 178203954Srdivacky 179203954Srdivackystatic unsigned short 180203954Srdivackyad1816_read(struct ad1816_info *ad1816, unsigned int reg) 181203954Srdivacky{ 182203954Srdivacky u_short x = 0; 183203954Srdivacky 184218893Sdim if (ad1816_wait_init(ad1816, 100) == -1) return 0; 185203954Srdivacky io_wr(ad1816, AD1816_ALE, 0); 186203954Srdivacky io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 187203954Srdivacky if (ad1816_wait_init(ad1816, 100) == -1) return 0; 188203954Srdivacky x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW); 189203954Srdivacky return x; 190203954Srdivacky} 191203954Srdivacky 192203954Srdivackystatic void 193203954Srdivackyad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data) 194203954Srdivacky{ 195203954Srdivacky if (ad1816_wait_init(ad1816, 100) == -1) return; 196203954Srdivacky io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 197218893Sdim io_wr(ad1816, AD1816_LOW, (data & 0x000000ff)); 198204642Srdivacky io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8); 199203954Srdivacky} 200204642Srdivacky 201203954Srdivacky/* -------------------------------------------------------------------- */ 202204642Srdivacky 203203954Srdivackystatic int 204203954Srdivackyad1816mix_init(struct snd_mixer *m) 205203954Srdivacky{ 206203954Srdivacky mix_setdevs(m, AD1816_MIXER_DEVICES); 207204642Srdivacky mix_setrecdevs(m, AD1816_REC_DEVICES); 208204642Srdivacky return 0; 209204642Srdivacky} 210203954Srdivacky 211203954Srdivackystatic int 212203954Srdivackyad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 213203954Srdivacky{ 214218893Sdim struct ad1816_info *ad1816 = mix_getdevinfo(m); 215203954Srdivacky u_short reg = 0; 216204642Srdivacky 217204642Srdivacky /* Scale volumes */ 218204642Srdivacky left = AD1816_MUTE - (AD1816_MUTE * left) / 100; 219204642Srdivacky right = AD1816_MUTE - (AD1816_MUTE * right) / 100; 220204642Srdivacky 221204642Srdivacky reg = (left << 8) | right; 222204642Srdivacky 223204642Srdivacky /* do channel selective muting if volume is zero */ 224204642Srdivacky if (left == AD1816_MUTE) reg |= 0x8000; 225204642Srdivacky if (right == AD1816_MUTE) reg |= 0x0080; 226204642Srdivacky 227218893Sdim ad1816_lock(ad1816); 228203954Srdivacky switch (dev) { 229203954Srdivacky case SOUND_MIXER_VOLUME: /* Register 14 master volume */ 230203954Srdivacky ad1816_write(ad1816, 14, reg); 231203954Srdivacky break; 232203954Srdivacky 233218893Sdim case SOUND_MIXER_CD: /* Register 15 cd */ 234203954Srdivacky case SOUND_MIXER_LINE1: 235203954Srdivacky ad1816_write(ad1816, 15, reg); 236218893Sdim break; 237203954Srdivacky 238208599Srdivacky case SOUND_MIXER_SYNTH: /* Register 16 synth */ 239203954Srdivacky ad1816_write(ad1816, 16, reg); 240203954Srdivacky break; 241203954Srdivacky 242204642Srdivacky case SOUND_MIXER_PCM: /* Register 4 pcm */ 243204642Srdivacky ad1816_write(ad1816, 4, reg); 244218893Sdim break; 245204642Srdivacky 246204642Srdivacky case SOUND_MIXER_LINE: 247204642Srdivacky case SOUND_MIXER_LINE3: /* Register 18 line in */ 248204642Srdivacky ad1816_write(ad1816, 18, reg); 249204642Srdivacky break; 250204642Srdivacky 251218893Sdim case SOUND_MIXER_MIC: /* Register 19 mic volume */ 252203954Srdivacky ad1816_write(ad1816, 19, reg & ~0xff); /* mic is mono */ 253204642Srdivacky break; 254218893Sdim 255203954Srdivacky case SOUND_MIXER_IGAIN: 256204642Srdivacky /* and now to something completely different ... */ 257218893Sdim ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f) 258203954Srdivacky | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ 259204642Srdivacky | ((AD1816_MUTE - right) / 2))); 260204642Srdivacky break; 261204642Srdivacky 262204642Srdivacky default: 263204642Srdivacky printf("ad1816_mixer_set(): unknown device.\n"); 264204642Srdivacky break; 265204642Srdivacky } 266204792Srdivacky ad1816_unlock(ad1816); 267204792Srdivacky 268204792Srdivacky left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE; 269204642Srdivacky right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE; 270203954Srdivacky 271218893Sdim return left | (right << 8); 272203954Srdivacky} 273203954Srdivacky 274203954Srdivackystatic int 275203954Srdivackyad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 276203954Srdivacky{ 277203954Srdivacky struct ad1816_info *ad1816 = mix_getdevinfo(m); 278203954Srdivacky int dev; 279203954Srdivacky 280218893Sdim switch (src) { 281203954Srdivacky case SOUND_MASK_LINE: 282203954Srdivacky case SOUND_MASK_LINE3: 283203954Srdivacky dev = 0x00; 284203954Srdivacky break; 285203954Srdivacky 286203954Srdivacky case SOUND_MASK_CD: 287203954Srdivacky case SOUND_MASK_LINE1: 288203954Srdivacky dev = 0x20; 289203954Srdivacky break; 290203954Srdivacky 291218893Sdim case SOUND_MASK_MIC: 292203954Srdivacky default: 293204642Srdivacky dev = 0x50; 294204642Srdivacky src = SOUND_MASK_MIC; 295203954Srdivacky } 296203954Srdivacky 297204642Srdivacky dev |= dev << 8; 298204642Srdivacky ad1816_lock(ad1816); 299204642Srdivacky ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev); 300204642Srdivacky ad1816_unlock(ad1816); 301204642Srdivacky return src; 302204642Srdivacky} 303204642Srdivacky 304203954Srdivackystatic kobj_method_t ad1816mixer_methods[] = { 305204642Srdivacky KOBJMETHOD(mixer_init, ad1816mix_init), 306203954Srdivacky KOBJMETHOD(mixer_set, ad1816mix_set), 307204642Srdivacky KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc), 308203954Srdivacky { 0, 0 } 309203954Srdivacky}; 310204642SrdivackyMIXER_DECLARE(ad1816mixer); 311203954Srdivacky 312204642Srdivacky/* -------------------------------------------------------------------- */ 313203954Srdivacky/* channel interface */ 314203954Srdivackystatic void * 315203954Srdivackyad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 316203954Srdivacky{ 317218893Sdim struct ad1816_info *ad1816 = devinfo; 318203954Srdivacky struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; 319204642Srdivacky 320218893Sdim ch->parent = ad1816; 321204642Srdivacky ch->channel = c; 322204642Srdivacky ch->buffer = b; 323204642Srdivacky if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, DSP_BUFFSIZE) == -1) return NULL; 324204642Srdivacky return ch; 325218893Sdim} 326203954Srdivacky 327203954Srdivackystatic int 328203954Srdivackyad1816chan_setdir(kobj_t obj, void *data, int dir) 329204642Srdivacky{ 330204642Srdivacky struct ad1816_chinfo *ch = data; 331204642Srdivacky struct ad1816_info *ad1816 = ch->parent; 332204642Srdivacky 333204642Srdivacky sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); 334204642Srdivacky ch->dir = dir; 335204642Srdivacky return 0; 336218893Sdim} 337204642Srdivacky 338204642Srdivackystatic int 339203954Srdivackyad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) 340203954Srdivacky{ 341204642Srdivacky struct ad1816_chinfo *ch = data; 342204642Srdivacky struct ad1816_info *ad1816 = ch->parent; 343204642Srdivacky int fmt = AD1816_U8, reg; 344204642Srdivacky 345204642Srdivacky ad1816_lock(ad1816); 346204642Srdivacky if (ch->dir == PCMDIR_PLAY) { 347204642Srdivacky reg = AD1816_PLAY; 348204642Srdivacky ad1816_write(ad1816, 8, 0x0000); /* reset base and current counter */ 349204642Srdivacky ad1816_write(ad1816, 9, 0x0000); /* for playback and capture */ 350204642Srdivacky } else { 351204642Srdivacky reg = AD1816_CAPT; 352204642Srdivacky ad1816_write(ad1816, 10, 0x0000); 353204642Srdivacky ad1816_write(ad1816, 11, 0x0000); 354204642Srdivacky } 355204642Srdivacky switch (format & ~AFMT_STEREO) { 356204642Srdivacky case AFMT_A_LAW: 357204642Srdivacky fmt = AD1816_ALAW; 358204642Srdivacky break; 359204642Srdivacky 360204642Srdivacky case AFMT_MU_LAW: 361204642Srdivacky fmt = AD1816_MULAW; 362204642Srdivacky break; 363204642Srdivacky 364203954Srdivacky case AFMT_S16_LE: 365203954Srdivacky fmt = AD1816_S16LE; 366203954Srdivacky break; 367218893Sdim 368203954Srdivacky case AFMT_S16_BE: 369203954Srdivacky fmt = AD1816_S16BE; 370203954Srdivacky break; 371218893Sdim 372203954Srdivacky case AFMT_U8: 373203954Srdivacky fmt = AD1816_U8; 374203954Srdivacky break; 375203954Srdivacky } 376203954Srdivacky if (format & AFMT_STEREO) fmt |= AD1816_STEREO; 377203954Srdivacky io_wr(ad1816, reg, fmt); 378203954Srdivacky ad1816_unlock(ad1816); 379203954Srdivacky return format; 380218893Sdim} 381218893Sdim 382203954Srdivackystatic int 383218893Sdimad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 384203954Srdivacky{ 385204642Srdivacky struct ad1816_chinfo *ch = data; 386203954Srdivacky struct ad1816_info *ad1816 = ch->parent; 387203954Srdivacky 388204642Srdivacky RANGE(speed, 4000, 55200); 389218893Sdim ad1816_lock(ad1816); 390218893Sdim ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); 391204642Srdivacky ad1816_unlock(ad1816); 392218893Sdim return speed; 393218893Sdim} 394204642Srdivacky 395204642Srdivackystatic int 396218893Sdimad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 397204642Srdivacky{ 398218893Sdim struct ad1816_chinfo *ch = data; 399218893Sdim 400204642Srdivacky ch->blksz = blocksize; 401218893Sdim return ch->blksz; 402218893Sdim} 403218893Sdim 404218893Sdimstatic int 405218893Sdimad1816chan_trigger(kobj_t obj, void *data, int go) 406218893Sdim{ 407218893Sdim struct ad1816_chinfo *ch = data; 408203954Srdivacky struct ad1816_info *ad1816 = ch->parent; 409203954Srdivacky int wr, reg; 410203954Srdivacky 411204642Srdivacky if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 412203954Srdivacky return 0; 413204642Srdivacky 414203954Srdivacky sndbuf_isadma(ch->buffer, go); 415203954Srdivacky wr = (ch->dir == PCMDIR_PLAY); 416203954Srdivacky reg = wr? AD1816_PLAY : AD1816_CAPT; 417203954Srdivacky ad1816_lock(ad1816); 418203954Srdivacky switch (go) { 419203954Srdivacky case PCMTRIG_START: 420203954Srdivacky /* start only if not already running */ 421203954Srdivacky if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) { 422203954Srdivacky int cnt = ((ch->blksz) >> 2) - 1; 423206083Srdivacky ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */ 424218893Sdim ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 425206083Srdivacky ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) | 426206083Srdivacky (wr? 0x8000 : 0x4000)); /* enable int */ 427206083Srdivacky /* enable playback */ 428203954Srdivacky io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE); 429206083Srdivacky if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) 430203954Srdivacky printf("ad1816: failed to start %s DMA!\n", 431218893Sdim wr? "play" : "rec"); 432203954Srdivacky } 433203954Srdivacky break; 434203954Srdivacky 435203954Srdivacky case PCMTRIG_STOP: 436203954Srdivacky case PCMTRIG_ABORT: /* XXX check this... */ 437204642Srdivacky /* we don't test here if it is running... */ 438204642Srdivacky if (wr) { 439203954Srdivacky ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) & 440203954Srdivacky ~(wr? 0x8000 : 0x4000)); 441203954Srdivacky /* disable int */ 442203954Srdivacky io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE); 443203954Srdivacky /* disable playback */ 444203954Srdivacky if (io_rd(ad1816, reg) & AD1816_ENABLE) 445204642Srdivacky printf("ad1816: failed to stop %s DMA!\n", 446203954Srdivacky wr? "play" : "rec"); 447203954Srdivacky ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */ 448203954Srdivacky ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 449218893Sdim } 450203954Srdivacky break; 451203954Srdivacky } 452203954Srdivacky ad1816_unlock(ad1816); 453203954Srdivacky return 0; 454218893Sdim} 455204961Srdivacky 456204961Srdivackystatic int 457204961Srdivackyad1816chan_getptr(kobj_t obj, void *data) 458218893Sdim{ 459206083Srdivacky struct ad1816_chinfo *ch = data; 460206083Srdivacky return sndbuf_isadmaptr(ch->buffer); 461206083Srdivacky} 462203954Srdivacky 463203954Srdivackystatic struct pcmchan_caps * 464204642Srdivackyad1816chan_getcaps(kobj_t obj, void *data) 465204642Srdivacky{ 466204642Srdivacky return &ad1816_caps; 467204642Srdivacky} 468204642Srdivacky 469204642Srdivackystatic kobj_method_t ad1816chan_methods[] = { 470204642Srdivacky KOBJMETHOD(channel_init, ad1816chan_init), 471204642Srdivacky KOBJMETHOD(channel_setdir, ad1816chan_setdir), 472204642Srdivacky KOBJMETHOD(channel_setformat, ad1816chan_setformat), 473204642Srdivacky KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), 474204642Srdivacky KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), 475204642Srdivacky KOBJMETHOD(channel_trigger, ad1816chan_trigger), 476204642Srdivacky KOBJMETHOD(channel_getptr, ad1816chan_getptr), 477218893Sdim KOBJMETHOD(channel_getcaps, ad1816chan_getcaps), 478204642Srdivacky { 0, 0 } 479204642Srdivacky}; 480204642SrdivackyCHANNEL_DECLARE(ad1816chan); 481204642Srdivacky 482218893Sdim/* -------------------------------------------------------------------- */ 483204792Srdivacky 484204792Srdivackystatic void 485218893Sdimad1816_release_resources(struct ad1816_info *ad1816, device_t dev) 486203954Srdivacky{ 487203954Srdivacky if (ad1816->irq) { 488203954Srdivacky if (ad1816->ih) 489204642Srdivacky bus_teardown_intr(dev, ad1816->irq, ad1816->ih); 490218893Sdim bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, 491204792Srdivacky ad1816->irq); 492204792Srdivacky ad1816->irq = 0; 493204792Srdivacky } 494204792Srdivacky if (ad1816->drq1) { 495204792Srdivacky bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, 496204792Srdivacky ad1816->drq1); 497218893Sdim ad1816->drq1 = 0; 498204792Srdivacky } 499204792Srdivacky if (ad1816->drq2) { 500204642Srdivacky bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, 501204792Srdivacky ad1816->drq2); 502204792Srdivacky ad1816->drq2 = 0; 503204792Srdivacky } 504204792Srdivacky if (ad1816->io_base) { 505204792Srdivacky bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, 506218893Sdim ad1816->io_base); 507204792Srdivacky ad1816->io_base = 0; 508204792Srdivacky } 509218893Sdim if (ad1816->parent_dmat) { 510204792Srdivacky bus_dma_tag_destroy(ad1816->parent_dmat); 511204792Srdivacky ad1816->parent_dmat = 0; 512204792Srdivacky } 513204792Srdivacky if (ad1816->lock) snd_mtxfree(ad1816->lock); 514218893Sdim free(ad1816, M_DEVBUF); 515204792Srdivacky} 516204792Srdivacky 517204792Srdivackystatic int 518204792Srdivackyad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev) 519204792Srdivacky{ 520204792Srdivacky int ok = 1, pdma, rdma; 521204792Srdivacky if (!ad1816->io_base) 522218893Sdim ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid, 523204792Srdivacky 0, ~0, 1, RF_ACTIVE); 524204792Srdivacky if (!ad1816->irq) 525204792Srdivacky ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid, 526204792Srdivacky 0, ~0, 1, RF_ACTIVE); 527204792Srdivacky if (!ad1816->drq1) 528218893Sdim ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid, 529218893Sdim 0, ~0, 1, RF_ACTIVE); 530204792Srdivacky if (!ad1816->drq2) 531204792Srdivacky ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid, 532218893Sdim 0, ~0, 1, RF_ACTIVE); 533204642Srdivacky 534203954Srdivacky if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0; 535203954Srdivacky 536203954Srdivacky if (ok) { 537204642Srdivacky pdma = rman_get_start(ad1816->drq1); 538204642Srdivacky isa_dma_acquire(pdma); 539204642Srdivacky isa_dmainit(pdma, DSP_BUFFSIZE); 540204642Srdivacky if (ad1816->drq2) { 541204642Srdivacky rdma = rman_get_start(ad1816->drq2); 542204642Srdivacky isa_dma_acquire(rdma); 543204642Srdivacky isa_dmainit(rdma, DSP_BUFFSIZE); 544218893Sdim } else rdma = pdma; 545204642Srdivacky if (pdma == rdma) 546204642Srdivacky pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 547204642Srdivacky } 548204792Srdivacky return ok; 549204792Srdivacky} 550204792Srdivacky 551204792Srdivackystatic int 552204792Srdivackyad1816_init(struct ad1816_info *ad1816, device_t dev) 553204792Srdivacky{ 554204792Srdivacky ad1816_write(ad1816, 1, 0x2); /* disable interrupts */ 555218893Sdim ad1816_write(ad1816, 32, 0x90F0); /* SoundSys Mode, split fmt */ 556204642Srdivacky 557204642Srdivacky ad1816_write(ad1816, 5, 0x8080); /* FM volume mute */ 558204642Srdivacky ad1816_write(ad1816, 6, 0x8080); /* I2S1 volume mute */ 559204792Srdivacky ad1816_write(ad1816, 7, 0x8080); /* I2S0 volume mute */ 560204642Srdivacky ad1816_write(ad1816, 17, 0x8888); /* VID Volume mute */ 561204642Srdivacky ad1816_write(ad1816, 20, 0x5050); /* recsrc mic, agc off */ 562204642Srdivacky /* adc gain is set to 0 */ 563204792Srdivacky 564204792Srdivacky return 0; 565204642Srdivacky} 566204642Srdivacky 567204642Srdivackystatic int 568204642Srdivackyad1816_probe(device_t dev) 569204642Srdivacky{ 570204642Srdivacky char *s = NULL; 571204642Srdivacky u_int32_t logical_id = isa_get_logicalid(dev); 572204642Srdivacky 573204642Srdivacky switch (logical_id) { 574204642Srdivacky case 0x80719304: /* ADS7180 */ 575204642Srdivacky s = "AD1816"; 576218893Sdim break; 577204642Srdivacky } 578204642Srdivacky 579204642Srdivacky if (s) { 580204642Srdivacky device_set_desc(dev, s); 581204642Srdivacky return 0; 582204642Srdivacky } 583218893Sdim return ENXIO; 584204642Srdivacky} 585205407Srdivacky 586204642Srdivackystatic int 587204642Srdivackyad1816_attach(device_t dev) 588204642Srdivacky{ 589218893Sdim struct ad1816_info *ad1816; 590204642Srdivacky char status[SND_STATUSLEN]; 591204642Srdivacky 592204642Srdivacky ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO); 593221345Sdim if (!ad1816) return ENXIO; 594221345Sdim 595204642Srdivacky ad1816->lock = snd_mtxcreate(device_get_nameunit(dev)); 596204642Srdivacky ad1816->io_rid = 2; 597204642Srdivacky ad1816->irq_rid = 0; 598218893Sdim ad1816->drq1_rid = 0; 599204642Srdivacky ad1816->drq2_rid = 1; 600205407Srdivacky 601204642Srdivacky if (!ad1816_alloc_resources(ad1816, dev)) goto no; 602204642Srdivacky ad1816_init(ad1816, dev); 603204642Srdivacky if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; 604218893Sdim 605204642Srdivacky snd_setup_intr(dev, ad1816->irq, INTR_MPSAFE, ad1816_intr, ad1816, &ad1816->ih); 606204642Srdivacky if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 607204642Srdivacky /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 608204642Srdivacky /*highaddr*/BUS_SPACE_MAXADDR, 609204642Srdivacky /*filter*/NULL, /*filterarg*/NULL, 610204642Srdivacky /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, 611204642Srdivacky /*maxsegz*/0x3ffff, 612204642Srdivacky /*flags*/0, &ad1816->parent_dmat) != 0) { 613208599Srdivacky device_printf(dev, "unable to create dma tag\n"); 614208599Srdivacky goto no; 615208599Srdivacky } 616208599Srdivacky snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", 617208599Srdivacky rman_get_start(ad1816->io_base), 618208599Srdivacky rman_get_start(ad1816->irq), 619208599Srdivacky rman_get_start(ad1816->drq1)); 620208599Srdivacky if (ad1816->drq2) snprintf(status + strlen(status), 621204642Srdivacky SND_STATUSLEN - strlen(status), ":%ld", 622218893Sdim rman_get_start(ad1816->drq2)); 623204642Srdivacky 624204642Srdivacky if (pcm_register(dev, ad1816, 1, 1)) goto no; 625204642Srdivacky pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816); 626204642Srdivacky pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816); 627204642Srdivacky pcm_setstatus(dev, status); 628218893Sdim 629204642Srdivacky return 0; 630204642Srdivackyno: 631204642Srdivacky ad1816_release_resources(ad1816, dev); 632218893Sdim 633204642Srdivacky return ENXIO; 634204642Srdivacky 635204642Srdivacky} 636204642Srdivacky 637204642Srdivackystatic int 638204642Srdivackyad1816_detach(device_t dev) 639204642Srdivacky{ 640204642Srdivacky int r; 641218893Sdim struct ad1816_info *ad1816; 642204642Srdivacky 643204642Srdivacky r = pcm_unregister(dev); 644204642Srdivacky if (r) 645218893Sdim return r; 646204642Srdivacky 647204642Srdivacky ad1816 = pcm_getdevinfo(dev); 648204642Srdivacky ad1816_release_resources(ad1816, dev); 649223017Sdim return 0; 650223017Sdim} 651223017Sdim 652223017Sdimstatic device_method_t ad1816_methods[] = { 653223017Sdim /* Device interface */ 654223017Sdim DEVMETHOD(device_probe, ad1816_probe), 655223017Sdim DEVMETHOD(device_attach, ad1816_attach), 656223017Sdim DEVMETHOD(device_detach, ad1816_detach), 657223017Sdim 658223017Sdim { 0, 0 } 659223017Sdim}; 660223017Sdim 661223017Sdimstatic driver_t ad1816_driver = { 662223017Sdim "pcm", 663223017Sdim ad1816_methods, 664223017Sdim sizeof(struct snddev_info), 665223017Sdim}; 666223017Sdim 667223017SdimDRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0); 668223017SdimMODULE_DEPEND(snd_ad1816, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 669223017SdimMODULE_VERSION(snd_ad1816, 1); 670223017Sdim 671223017Sdim 672223017Sdim