ad1816.c revision 168847
1323530Savg/*- 2323530Savg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3323530Savg * Copyright (c) 1997,1998 Luigi Rizzo 4323530Savg * Copyright (c) 1994,1995 Hannu Savolainen 5323530Savg * All rights reserved. 6323530Savg * 7323530Savg * Redistribution and use in source and binary forms, with or without 8323530Savg * modification, are permitted provided that the following conditions 9323530Savg * are met: 10323530Savg * 1. Redistributions of source code must retain the above copyright 11323530Savg * notice, this list of conditions and the following disclaimer. 12323530Savg * 2. Redistributions in binary form must reproduce the above copyright 13323530Savg * notice, this list of conditions and the following disclaimer in the 14323530Savg * documentation and/or other materials provided with the distribution. 15323530Savg * 16323530Savg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17323530Savg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18323530Savg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19323530Savg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20323530Savg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21323530Savg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22323530Savg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23323530Savg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24323530Savg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25323530Savg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26323530Savg * SUCH DAMAGE. 27323530Savg */ 28323530Savg 29323530Savg#include <dev/sound/pcm/sound.h> 30323530Savg#include <dev/sound/isa/ad1816.h> 31323530Savg 32323530Savg#include <isa/isavar.h> 33323530Savg 34323530Savg#include "mixer_if.h" 35323530Savg 36323530SavgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/ad1816.c 168847 2007-04-18 18:26:41Z ariff $"); 37323530Savg 38323530Savgstruct ad1816_info; 39323530Savg 40323530Savgstruct ad1816_chinfo { 41323530Savg struct ad1816_info *parent; 42323530Savg struct pcm_channel *channel; 43323530Savg struct snd_dbuf *buffer; 44323530Savg int dir, blksz; 45323530Savg}; 46323530Savg 47323530Savgstruct ad1816_info { 48323530Savg struct resource *io_base; /* primary I/O address for the board */ 49323530Savg int io_rid; 50323530Savg struct resource *irq; 51323530Savg int irq_rid; 52323530Savg struct resource *drq1; /* play */ 53323530Savg int drq1_rid; 54323530Savg struct resource *drq2; /* rec */ 55323530Savg int drq2_rid; 56323530Savg void *ih; 57323530Savg bus_dma_tag_t parent_dmat; 58323530Savg struct mtx *lock; 59323530Savg 60323530Savg unsigned int bufsize; 61323530Savg struct ad1816_chinfo pch, rch; 62323530Savg}; 63323530Savg 64323530Savgstatic u_int32_t ad1816_fmt[] = { 65323530Savg AFMT_U8, 66323530Savg AFMT_STEREO | AFMT_U8, 67323530Savg AFMT_S16_LE, 68323530Savg AFMT_STEREO | AFMT_S16_LE, 69323530Savg AFMT_MU_LAW, 70323530Savg AFMT_STEREO | AFMT_MU_LAW, 71323530Savg AFMT_A_LAW, 72323530Savg AFMT_STEREO | AFMT_A_LAW, 73323530Savg 0 74323530Savg}; 75323530Savg 76323530Savgstatic struct pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0}; 77323530Savg 78323530Savg#define AD1816_MUTE 31 /* value for mute */ 79323530Savg 80323530Savgstatic void 81323530Savgad1816_lock(struct ad1816_info *ad1816) 82323530Savg{ 83323530Savg snd_mtxlock(ad1816->lock); 84323530Savg} 85323530Savg 86323530Savgstatic void 87323530Savgad1816_unlock(struct ad1816_info *ad1816) 88323530Savg{ 89323530Savg snd_mtxunlock(ad1816->lock); 90323530Savg} 91323530Savg 92323530Savgstatic int 93323530Savgport_rd(struct resource *port, int off) 94323530Savg{ 95323530Savg if (port) 96323530Savg return bus_space_read_1(rman_get_bustag(port), 97323530Savg rman_get_bushandle(port), 98323530Savg off); 99323530Savg else 100323530Savg return -1; 101323530Savg} 102323530Savg 103323530Savgstatic void 104323530Savgport_wr(struct resource *port, int off, u_int8_t data) 105323530Savg{ 106323530Savg if (port) 107323530Savg bus_space_write_1(rman_get_bustag(port), 108323530Savg rman_get_bushandle(port), 109323530Savg off, data); 110323530Savg} 111323530Savg 112323530Savgstatic int 113323530Savgio_rd(struct ad1816_info *ad1816, int reg) 114323530Savg{ 115323530Savg return port_rd(ad1816->io_base, reg); 116323530Savg} 117323530Savg 118323530Savgstatic void 119323530Savgio_wr(struct ad1816_info *ad1816, int reg, u_int8_t data) 120323530Savg{ 121323530Savg port_wr(ad1816->io_base, reg, data); 122323530Savg} 123323530Savg 124323530Savgstatic void 125323530Savgad1816_intr(void *arg) 126323530Savg{ 127323530Savg struct ad1816_info *ad1816 = (struct ad1816_info *)arg; 128323530Savg unsigned char c, served = 0; 129323530Savg 130323530Savg ad1816_lock(ad1816); 131323530Savg /* get interupt status */ 132323530Savg c = io_rd(ad1816, AD1816_INT); 133323530Savg 134323530Savg /* check for stray interupts */ 135323530Savg if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { 136323530Savg printf("pcm: stray int (%x)\n", c); 137323530Savg c &= AD1816_INTRCI | AD1816_INTRPI; 138323530Savg } 139323530Savg /* check for capture interupt */ 140323530Savg if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) { 141323530Savg ad1816_unlock(ad1816); 142323530Savg chn_intr(ad1816->rch.channel); 143323530Savg ad1816_lock(ad1816); 144323530Savg served |= AD1816_INTRCI; /* cp served */ 145323530Savg } 146323530Savg /* check for playback interupt */ 147323530Savg if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) { 148323530Savg ad1816_unlock(ad1816); 149323530Savg chn_intr(ad1816->pch.channel); 150323530Savg ad1816_lock(ad1816); 151323530Savg served |= AD1816_INTRPI; /* pb served */ 152323530Savg } 153323530Savg if (served == 0) { 154323530Savg /* this probably means this is not a (working) ad1816 chip, */ 155323530Savg /* or an error in dma handling */ 156323530Savg printf("pcm: int without reason (%x)\n", c); 157323530Savg c = 0; 158323530Savg } else c &= ~served; 159323530Savg io_wr(ad1816, AD1816_INT, c); 160323530Savg c = io_rd(ad1816, AD1816_INT); 161323530Savg if (c != 0) printf("pcm: int clear failed (%x)\n", c); 162323530Savg ad1816_unlock(ad1816); 163323530Savg} 164323530Savg 165323530Savgstatic int 166323530Savgad1816_wait_init(struct ad1816_info *ad1816, int x) 167323530Savg{ 168323530Savg int n = 0; /* to shut up the compiler... */ 169323530Savg 170323530Savg for (; x--;) 171323530Savg if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); 172323530Savg else return n; 173323530Savg printf("ad1816_wait_init failed 0x%02x.\n", n); 174323530Savg return -1; 175323530Savg} 176323530Savg 177323530Savgstatic unsigned short 178323530Savgad1816_read(struct ad1816_info *ad1816, unsigned int reg) 179323530Savg{ 180323530Savg u_short x = 0; 181323530Savg 182323530Savg if (ad1816_wait_init(ad1816, 100) == -1) return 0; 183323530Savg io_wr(ad1816, AD1816_ALE, 0); 184323530Savg io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 185323530Savg if (ad1816_wait_init(ad1816, 100) == -1) return 0; 186323530Savg x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW); 187323530Savg return x; 188323530Savg} 189323530Savg 190323530Savgstatic void 191323530Savgad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data) 192323530Savg{ 193323530Savg if (ad1816_wait_init(ad1816, 100) == -1) return; 194323530Savg io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); 195323530Savg io_wr(ad1816, AD1816_LOW, (data & 0x000000ff)); 196323530Savg io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8); 197323530Savg} 198323530Savg 199323530Savg/* -------------------------------------------------------------------- */ 200323530Savg 201323530Savgstatic int 202323530Savgad1816mix_init(struct snd_mixer *m) 203323530Savg{ 204323530Savg mix_setdevs(m, AD1816_MIXER_DEVICES); 205323530Savg mix_setrecdevs(m, AD1816_REC_DEVICES); 206323530Savg return 0; 207323530Savg} 208323530Savg 209323530Savgstatic int 210323530Savgad1816mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 211323530Savg{ 212323530Savg struct ad1816_info *ad1816 = mix_getdevinfo(m); 213323530Savg u_short reg = 0; 214323530Savg 215323530Savg /* Scale volumes */ 216323530Savg left = AD1816_MUTE - (AD1816_MUTE * left) / 100; 217323530Savg right = AD1816_MUTE - (AD1816_MUTE * right) / 100; 218323530Savg 219323530Savg reg = (left << 8) | right; 220323530Savg 221323530Savg /* do channel selective muting if volume is zero */ 222323530Savg if (left == AD1816_MUTE) reg |= 0x8000; 223323530Savg if (right == AD1816_MUTE) reg |= 0x0080; 224323530Savg 225323530Savg ad1816_lock(ad1816); 226323530Savg switch (dev) { 227323530Savg case SOUND_MIXER_VOLUME: /* Register 14 master volume */ 228323530Savg ad1816_write(ad1816, 14, reg); 229323530Savg break; 230323530Savg 231323530Savg case SOUND_MIXER_CD: /* Register 15 cd */ 232323530Savg case SOUND_MIXER_LINE1: 233323530Savg ad1816_write(ad1816, 15, reg); 234323530Savg break; 235323530Savg 236323530Savg case SOUND_MIXER_SYNTH: /* Register 16 synth */ 237323530Savg ad1816_write(ad1816, 16, reg); 238323530Savg break; 239323530Savg 240323530Savg case SOUND_MIXER_PCM: /* Register 4 pcm */ 241323530Savg ad1816_write(ad1816, 4, reg); 242323530Savg break; 243323530Savg 244323530Savg case SOUND_MIXER_LINE: 245323530Savg case SOUND_MIXER_LINE3: /* Register 18 line in */ 246323530Savg ad1816_write(ad1816, 18, reg); 247323530Savg break; 248323530Savg 249323530Savg case SOUND_MIXER_MIC: /* Register 19 mic volume */ 250323530Savg ad1816_write(ad1816, 19, reg & ~0xff); /* mic is mono */ 251323530Savg break; 252323530Savg 253323530Savg case SOUND_MIXER_IGAIN: 254323530Savg /* and now to something completely different ... */ 255323530Savg ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f) 256323530Savg | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ 257323530Savg | ((AD1816_MUTE - right) / 2))); 258323530Savg break; 259323530Savg 260323530Savg default: 261323530Savg printf("ad1816_mixer_set(): unknown device.\n"); 262323530Savg break; 263323530Savg } 264323530Savg ad1816_unlock(ad1816); 265323530Savg 266323530Savg left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE; 267323530Savg right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE; 268323530Savg 269323530Savg return left | (right << 8); 270323530Savg} 271323530Savg 272323530Savgstatic int 273323530Savgad1816mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 274323530Savg{ 275323530Savg struct ad1816_info *ad1816 = mix_getdevinfo(m); 276323530Savg int dev; 277323530Savg 278323530Savg switch (src) { 279323530Savg case SOUND_MASK_LINE: 280323530Savg case SOUND_MASK_LINE3: 281323530Savg dev = 0x00; 282323530Savg break; 283323530Savg 284323530Savg case SOUND_MASK_CD: 285323530Savg case SOUND_MASK_LINE1: 286323530Savg dev = 0x20; 287323530Savg break; 288323530Savg 289323530Savg case SOUND_MASK_MIC: 290323530Savg default: 291323530Savg dev = 0x50; 292323530Savg src = SOUND_MASK_MIC; 293323530Savg } 294323530Savg 295323530Savg dev |= dev << 8; 296323530Savg ad1816_lock(ad1816); 297323530Savg ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev); 298323530Savg ad1816_unlock(ad1816); 299323530Savg return src; 300323530Savg} 301323530Savg 302323530Savgstatic kobj_method_t ad1816mixer_methods[] = { 303323530Savg KOBJMETHOD(mixer_init, ad1816mix_init), 304323530Savg KOBJMETHOD(mixer_set, ad1816mix_set), 305323530Savg KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc), 306323530Savg { 0, 0 } 307323530Savg}; 308323530SavgMIXER_DECLARE(ad1816mixer); 309323530Savg 310323530Savg/* -------------------------------------------------------------------- */ 311323530Savg/* channel interface */ 312323530Savgstatic void * 313323530Savgad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 314323530Savg{ 315323530Savg struct ad1816_info *ad1816 = devinfo; 316323530Savg struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; 317323530Savg 318323530Savg ch->parent = ad1816; 319323530Savg ch->channel = c; 320323530Savg ch->buffer = b; 321323530Savg if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0) 322323530Savg return NULL; 323323530Savg return ch; 324323530Savg} 325323530Savg 326323530Savgstatic int 327323530Savgad1816chan_setdir(kobj_t obj, void *data, int dir) 328323530Savg{ 329323530Savg struct ad1816_chinfo *ch = data; 330323530Savg struct ad1816_info *ad1816 = ch->parent; 331323530Savg 332323530Savg sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); 333323530Savg ch->dir = dir; 334323530Savg return 0; 335323530Savg} 336323530Savg 337323530Savgstatic int 338323530Savgad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) 339323530Savg{ 340323530Savg struct ad1816_chinfo *ch = data; 341323530Savg struct ad1816_info *ad1816 = ch->parent; 342323530Savg int fmt = AD1816_U8, reg; 343323530Savg 344323530Savg ad1816_lock(ad1816); 345323530Savg if (ch->dir == PCMDIR_PLAY) { 346323530Savg reg = AD1816_PLAY; 347323530Savg ad1816_write(ad1816, 8, 0x0000); /* reset base and current counter */ 348323530Savg ad1816_write(ad1816, 9, 0x0000); /* for playback and capture */ 349323530Savg } else { 350323530Savg reg = AD1816_CAPT; 351323530Savg ad1816_write(ad1816, 10, 0x0000); 352323530Savg ad1816_write(ad1816, 11, 0x0000); 353323530Savg } 354323530Savg switch (format & ~AFMT_STEREO) { 355323530Savg case AFMT_A_LAW: 356323530Savg fmt = AD1816_ALAW; 357323530Savg break; 358323530Savg 359323530Savg case AFMT_MU_LAW: 360323530Savg fmt = AD1816_MULAW; 361323530Savg break; 362323530Savg 363323530Savg case AFMT_S16_LE: 364323530Savg fmt = AD1816_S16LE; 365323530Savg break; 366323530Savg 367323530Savg case AFMT_S16_BE: 368323530Savg fmt = AD1816_S16BE; 369323530Savg break; 370323530Savg 371323530Savg case AFMT_U8: 372323530Savg fmt = AD1816_U8; 373323530Savg break; 374323530Savg } 375323530Savg if (format & AFMT_STEREO) fmt |= AD1816_STEREO; 376323530Savg io_wr(ad1816, reg, fmt); 377323530Savg ad1816_unlock(ad1816); 378323530Savg#if 0 379323530Savg return format; 380323530Savg#else 381323530Savg return 0; 382323530Savg#endif 383323530Savg} 384323530Savg 385323530Savgstatic int 386323530Savgad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 387323530Savg{ 388323530Savg struct ad1816_chinfo *ch = data; 389323530Savg struct ad1816_info *ad1816 = ch->parent; 390323530Savg 391323530Savg RANGE(speed, 4000, 55200); 392323530Savg ad1816_lock(ad1816); 393323530Savg ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); 394323530Savg ad1816_unlock(ad1816); 395323530Savg return speed; 396323530Savg} 397323530Savg 398323530Savgstatic int 399323530Savgad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 400323530Savg{ 401323530Savg struct ad1816_chinfo *ch = data; 402323530Savg 403323530Savg ch->blksz = blocksize; 404323530Savg return ch->blksz; 405323530Savg} 406323530Savg 407323530Savgstatic int 408323530Savgad1816chan_trigger(kobj_t obj, void *data, int go) 409323530Savg{ 410323530Savg struct ad1816_chinfo *ch = data; 411323530Savg struct ad1816_info *ad1816 = ch->parent; 412323530Savg int wr, reg; 413323530Savg 414323530Savg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 415323530Savg return 0; 416323530Savg 417323530Savg sndbuf_dma(ch->buffer, go); 418323530Savg wr = (ch->dir == PCMDIR_PLAY); 419323530Savg reg = wr? AD1816_PLAY : AD1816_CAPT; 420323530Savg ad1816_lock(ad1816); 421323530Savg switch (go) { 422323530Savg case PCMTRIG_START: 423323530Savg /* start only if not already running */ 424323530Savg if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) { 425323530Savg int cnt = ((ch->blksz) >> 2) - 1; 426323530Savg ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */ 427323530Savg ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 428323530Savg ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) | 429323530Savg (wr? 0x8000 : 0x4000)); /* enable int */ 430323530Savg /* enable playback */ 431323530Savg io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE); 432323530Savg if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) 433323530Savg printf("ad1816: failed to start %s DMA!\n", 434323530Savg wr? "play" : "rec"); 435323530Savg } 436323530Savg break; 437323530Savg 438323530Savg case PCMTRIG_STOP: 439323530Savg case PCMTRIG_ABORT: /* XXX check this... */ 440323530Savg /* we don't test here if it is running... */ 441323530Savg if (wr) { 442323530Savg ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) & 443323530Savg ~(wr? 0x8000 : 0x4000)); 444323530Savg /* disable int */ 445323530Savg io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE); 446323530Savg /* disable playback */ 447323530Savg if (io_rd(ad1816, reg) & AD1816_ENABLE) 448323530Savg printf("ad1816: failed to stop %s DMA!\n", 449323530Savg wr? "play" : "rec"); 450323530Savg ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */ 451323530Savg ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ 452323530Savg } 453323530Savg break; 454323530Savg } 455323530Savg ad1816_unlock(ad1816); 456323530Savg return 0; 457323530Savg} 458323530Savg 459323530Savgstatic int 460323530Savgad1816chan_getptr(kobj_t obj, void *data) 461323530Savg{ 462323530Savg struct ad1816_chinfo *ch = data; 463323530Savg return sndbuf_dmaptr(ch->buffer); 464323530Savg} 465323530Savg 466323530Savgstatic struct pcmchan_caps * 467323530Savgad1816chan_getcaps(kobj_t obj, void *data) 468323530Savg{ 469323530Savg return &ad1816_caps; 470323530Savg} 471323530Savg 472323530Savgstatic kobj_method_t ad1816chan_methods[] = { 473323530Savg KOBJMETHOD(channel_init, ad1816chan_init), 474323530Savg KOBJMETHOD(channel_setdir, ad1816chan_setdir), 475323530Savg KOBJMETHOD(channel_setformat, ad1816chan_setformat), 476323530Savg KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), 477323530Savg KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), 478323530Savg KOBJMETHOD(channel_trigger, ad1816chan_trigger), 479323530Savg KOBJMETHOD(channel_getptr, ad1816chan_getptr), 480323530Savg KOBJMETHOD(channel_getcaps, ad1816chan_getcaps), 481323530Savg { 0, 0 } 482323530Savg}; 483323530SavgCHANNEL_DECLARE(ad1816chan); 484323530Savg 485323530Savg/* -------------------------------------------------------------------- */ 486323530Savg 487323530Savgstatic void 488323530Savgad1816_release_resources(struct ad1816_info *ad1816, device_t dev) 489323530Savg{ 490323530Savg if (ad1816->irq) { 491323530Savg if (ad1816->ih) 492323530Savg bus_teardown_intr(dev, ad1816->irq, ad1816->ih); 493323530Savg bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, ad1816->irq); 494323530Savg ad1816->irq = 0; 495323530Savg } 496323530Savg if (ad1816->drq1) { 497323530Savg isa_dma_release(rman_get_start(ad1816->drq1)); 498323530Savg bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, ad1816->drq1); 499323530Savg ad1816->drq1 = 0; 500323530Savg } 501323530Savg if (ad1816->drq2) { 502323530Savg isa_dma_release(rman_get_start(ad1816->drq2)); 503323530Savg bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, ad1816->drq2); 504323530Savg ad1816->drq2 = 0; 505323530Savg } 506323530Savg if (ad1816->io_base) { 507323530Savg bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, ad1816->io_base); 508323530Savg ad1816->io_base = 0; 509323530Savg } 510323530Savg if (ad1816->parent_dmat) { 511323530Savg bus_dma_tag_destroy(ad1816->parent_dmat); 512323530Savg ad1816->parent_dmat = 0; 513323530Savg } 514323530Savg if (ad1816->lock) 515323530Savg snd_mtxfree(ad1816->lock); 516323530Savg 517323530Savg free(ad1816, M_DEVBUF); 518323530Savg} 519323530Savg 520323530Savgstatic int 521323530Savgad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev) 522323530Savg{ 523323530Savg int ok = 1, pdma, rdma; 524323530Savg 525323530Savg if (!ad1816->io_base) 526323530Savg ad1816->io_base = bus_alloc_resource_any(dev, 527323530Savg SYS_RES_IOPORT, &ad1816->io_rid, RF_ACTIVE); 528323530Savg if (!ad1816->irq) 529323530Savg ad1816->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 530323530Savg &ad1816->irq_rid, RF_ACTIVE); 531323530Savg if (!ad1816->drq1) 532323530Savg ad1816->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, 533323530Savg &ad1816->drq1_rid, RF_ACTIVE); 534323530Savg if (!ad1816->drq2) 535323530Savg ad1816->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, 536323530Savg &ad1816->drq2_rid, RF_ACTIVE); 537323530Savg 538323530Savg if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0; 539323530Savg 540323530Savg if (ok) { 541323530Savg pdma = rman_get_start(ad1816->drq1); 542323530Savg isa_dma_acquire(pdma); 543323530Savg isa_dmainit(pdma, ad1816->bufsize); 544323530Savg if (ad1816->drq2) { 545323530Savg rdma = rman_get_start(ad1816->drq2); 546323530Savg isa_dma_acquire(rdma); 547323530Savg isa_dmainit(rdma, ad1816->bufsize); 548323530Savg } else 549323530Savg rdma = pdma; 550323530Savg if (pdma == rdma) 551323530Savg pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 552323530Savg } 553323530Savg 554323530Savg return ok; 555323530Savg} 556323530Savg 557323530Savgstatic int 558323530Savgad1816_init(struct ad1816_info *ad1816, device_t dev) 559323530Savg{ 560323530Savg ad1816_write(ad1816, 1, 0x2); /* disable interrupts */ 561323530Savg ad1816_write(ad1816, 32, 0x90F0); /* SoundSys Mode, split fmt */ 562323530Savg 563323530Savg ad1816_write(ad1816, 5, 0x8080); /* FM volume mute */ 564323530Savg ad1816_write(ad1816, 6, 0x8080); /* I2S1 volume mute */ 565323530Savg ad1816_write(ad1816, 7, 0x8080); /* I2S0 volume mute */ 566323530Savg ad1816_write(ad1816, 17, 0x8888); /* VID Volume mute */ 567323530Savg ad1816_write(ad1816, 20, 0x5050); /* recsrc mic, agc off */ 568323530Savg /* adc gain is set to 0 */ 569323530Savg 570323530Savg return 0; 571323530Savg} 572323530Savg 573323530Savgstatic int 574323530Savgad1816_probe(device_t dev) 575323530Savg{ 576323530Savg char *s = NULL; 577323530Savg u_int32_t logical_id = isa_get_logicalid(dev); 578323530Savg 579323530Savg switch (logical_id) { 580323530Savg case 0x80719304: /* ADS7180 */ 581323530Savg s = "AD1816"; 582323530Savg break; 583323530Savg case 0x50719304: /* ADS7150 */ 584323530Savg s = "AD1815"; 585323530Savg break; 586323530Savg } 587323530Savg 588323530Savg if (s) { 589323530Savg device_set_desc(dev, s); 590323530Savg return BUS_PROBE_DEFAULT; 591323530Savg } 592323530Savg return ENXIO; 593323530Savg} 594323530Savg 595323530Savgstatic int 596323530Savgad1816_attach(device_t dev) 597323530Savg{ 598323530Savg struct ad1816_info *ad1816; 599323530Savg char status[SND_STATUSLEN], status2[SND_STATUSLEN]; 600323530Savg 601323530Savg ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT | M_ZERO); 602323530Savg if (!ad1816) return ENXIO; 603323530Savg 604323530Savg ad1816->lock = snd_mtxcreate(device_get_nameunit(dev), 605323530Savg "snd_ad1816 softc"); 606323530Savg ad1816->io_rid = 2; 607323530Savg ad1816->irq_rid = 0; 608323530Savg ad1816->drq1_rid = 0; 609323530Savg ad1816->drq2_rid = 1; 610323530Savg ad1816->bufsize = pcm_getbuffersize(dev, 4096, DSP_BUFFSIZE, 65536); 611323530Savg 612323530Savg if (!ad1816_alloc_resources(ad1816, dev)) goto no; 613323530Savg ad1816_init(ad1816, dev); 614323530Savg if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; 615323530Savg 616323530Savg snd_setup_intr(dev, ad1816->irq, 0, ad1816_intr, ad1816, &ad1816->ih); 617323530Savg if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 618323530Savg /*boundary*/0, 619323530Savg /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 620323530Savg /*highaddr*/BUS_SPACE_MAXADDR, 621323530Savg /*filter*/NULL, /*filterarg*/NULL, 622323530Savg /*maxsize*/ad1816->bufsize, /*nsegments*/1, 623323530Savg /*maxsegz*/0x3ffff, 624323530Savg /*flags*/0, /*lockfunc*/busdma_lock_mutex, 625323530Savg /*lockarg*/ &Giant, &ad1816->parent_dmat) != 0) { 626323530Savg device_printf(dev, "unable to create dma tag\n"); 627323530Savg goto no; 628323530Savg } 629323530Savg if (ad1816->drq2) 630323530Savg snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(ad1816->drq2)); 631323530Savg else 632323530Savg status2[0] = '\0'; 633323530Savg 634323530Savg snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", 635323530Savg rman_get_start(ad1816->io_base), 636323530Savg rman_get_start(ad1816->irq), 637323530Savg rman_get_start(ad1816->drq1), 638323530Savg status2, 639323530Savg ad1816->bufsize, 640323530Savg PCM_KLDSTRING(snd_ad1816)); 641323530Savg 642323530Savg if (pcm_register(dev, ad1816, 1, 1)) goto no; 643323530Savg pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816); 644323530Savg pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816); 645323530Savg pcm_setstatus(dev, status); 646323530Savg 647323530Savg return 0; 648323530Savgno: 649323530Savg ad1816_release_resources(ad1816, dev); 650323530Savg 651323530Savg return ENXIO; 652323530Savg 653323530Savg} 654323530Savg 655323530Savgstatic int 656323530Savgad1816_detach(device_t dev) 657323530Savg{ 658323530Savg int r; 659323530Savg struct ad1816_info *ad1816; 660323530Savg 661323530Savg r = pcm_unregister(dev); 662323530Savg if (r) 663323530Savg return r; 664323530Savg 665323530Savg ad1816 = pcm_getdevinfo(dev); 666323530Savg ad1816_release_resources(ad1816, dev); 667323530Savg return 0; 668323530Savg} 669323530Savg 670323530Savgstatic device_method_t ad1816_methods[] = { 671323530Savg /* Device interface */ 672323530Savg DEVMETHOD(device_probe, ad1816_probe), 673323530Savg DEVMETHOD(device_attach, ad1816_attach), 674323530Savg DEVMETHOD(device_detach, ad1816_detach), 675323530Savg 676323530Savg { 0, 0 } 677323530Savg}; 678323530Savg 679323530Savgstatic driver_t ad1816_driver = { 680323530Savg "pcm", 681323530Savg ad1816_methods, 682323530Savg PCM_SOFTC_SIZE, 683323530Savg}; 684323530Savg 685323530SavgDRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0); 686323530SavgDRIVER_MODULE(snd_ad1816, acpi, ad1816_driver, pcm_devclass, 0, 0); 687323530SavgMODULE_DEPEND(snd_ad1816, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 688323530SavgMODULE_VERSION(snd_ad1816, 1); 689323530Savg 690323530Savg 691323530Savg