/* Aureal AU8830 Solaris Kernel Driver * * Written by Phil Jensen (philj@csufresno.edu) * * * * Contains code written and * * Copyright (c) 2002, 2003 Jeff Muizelaar * * and the OpenVortex Project * * * * Thu Apr 10 23:39:30 PDT 2003 * * First Revision: * * Mon Mar 31 14:16:48 PST 2003 */ #include "driver.h" /* Operations Required by Autoconfiguration */ static int aureal_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int aureal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int aureal_detach(dev_info_t *dip, ddi_attach_cmd_t cmd); /* Interrupt Handler */ uint_t aureal_intr(caddr_t intr_arg); /* Streams Module Info */ static struct module_info aureal_minfo = { 0x8830, /* module ID number */ "au8830", /* module name */ 0, /* minimum packet size accepted */ 8192, /* maximum packet size accepted */ 48000*4*2, /* hi-water mark: ~2 seconds */ 0x8000 /* lo-water mark */ }; /* Read Queue Streams Table */ static struct qinit aureal_rqueue = { audio_hlp_rput, audio_hlp_rsvc, audio_hlp_open, audio_hlp_close, NULL, &aureal_minfo }; /* Write Queue Streams Table */ static struct qinit aureal_wqueue = { audio_hlp_wput, audio_hlp_wsvc, NULL, NULL, NULL, &aureal_minfo }; /* Streams Table */ static struct streamtab aureal_str_info = { &aureal_rqueue, &aureal_wqueue, NULL, NULL }; /* Callback Operations Structure */ static struct cb_ops aureal_cb_ops = { nulldev, /* open(9E) */ nulldev, /* close(9E) */ nodev, /* strategy(9E) */ nodev, /* print(9E) */ nodev, /* dump(9E) */ nodev, /* read(9E) */ nodev, /* write(9E) */ nodev, /* ioctl(9E) */ nodev, /* devmap(9E) */ nodev, /* mmap(9E) */ nodev, /* segmap(9E) */ nochpoll, /* chpoll(9E) */ ddi_prop_op, /* prop_op(9E) */ &aureal_str_info, /* streamtab(9S) */ D_MP | D_64BIT /* cb_flag */ }; /* entry points to dev_ops include attach, detach, getinfo, probe and power */ static struct dev_ops aureal_dev_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ aureal_getinfo, /* getinfo(9E) */ nulldev, /* identity(9E) */ nulldev, /* probe(9E) */ aureal_attach, /* attach(9E) */ aureal_detach, /* detach(9E) */ nodev, /* devo_reset */ &aureal_cb_ops, /* devo_cb_ops */ NULL, /* devo_bus_ops */ NULL /* power(9E) */ }; static struct modldrv aureal_modldrv = { &mod_driverops, /* drv_modops */ "Aureal AU8830 Driver", /* drv_linkinfo */ &aureal_dev_ops /* drv_dev_ops */ }; /* entry points to modlinkage include _info, _fini and _init */ static struct modlinkage aureal_modlinkage = { MODREV_1, /* ml_rev */ &aureal_modldrv, /* ml_linkage */ NULL }; /* Used to describe byte order for the PCI card */ static struct ddi_device_acc_attr dev_attr = { DDI_DEVICE_ATTR_V0, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC }; void *statep; /* for soft state routines */ static ah_ad_info_t aureal_ad_info; /* here's an attempt at mapping the ac97 codec registers */ /* taken verbatim from the linux driver header file */ struct aureal_regs { uint16_t reg1; uint16_t reg2; uint16_t reg3; uint16_t reg4; }; struct aureal_dma_regs { uint32_t dma_addr; uint32_t dma_size; }; static ddi_dma_attr_t dma_attr = { DMA_ATTR_V0, 0, /* low address */ 0xffffffff, /* high address */ 0xffffffff, /* counter register max */ 2, /* byte alignment */ 0x3c, /* DMA burst sizes */ 4, /* min-xfer effective DMA sizes */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* address register max | segment boundary */ 1, /* s/g length / no scatter-gather */ 4, /* granularity of device, 4 byte transfers */ 0 /* attr flag: set to 0 */ }; static audio_info_t aureal_defaults = { { 8000, /* samples per second */ AUDIO_CHANNELS_MONO, /* number of interleaved channels */ AUDIO_PRECISION_8, /* bit-width of each sample */ AUDIO_ENCODING_ULAW, /* data encoding method */ AUDIO_MID_GAIN, /* gain level: 0 - 255 */ AUDIO_HEADPHONE, /* selected I/O port */ AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT, #ifdef AUDIOIO_HAVE_MODPORTS AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT, #endif 0, /* Reserved for future use */ 0, /* I/O buffer size */ 0, /* number of samples converted */ 0, /* End Of File counter (play only) */ 0, 0, 0, AUDIO_MID_BALANCE, /* stereo channel balance */ }, { 8000, /* samples per second */ AUDIO_CHANNELS_MONO, /* number of interleaved channels */ AUDIO_PRECISION_8, /* bit-width of each sample */ AUDIO_ENCODING_ULAW, /* data encoding method */ AUDIO_MID_GAIN, /* gain level: 0 - 255 */ AUDIO_CD, /* selected I/O port */ AUDIO_MICROPHONE | AUDIO_LINE_IN | AUDIO_CD | AUDIO_AUX1_IN | AUDIO_AUX2_IN | AUDIO_CODEC_LOOPB_IN, #ifdef AUDIOIO_HAVE_MODPORTS AUDIO_MICROPHONE | AUDIO_LINE_IN | AUDIO_CD | AUDIO_AUX1_IN | AUDIO_AUX2_IN | AUDIO_CODEC_LOOPB_IN, #endif 0, /* Reserved for future use */ 0, /* I/O buffer size */ 0, /* number of samples converted */ 0, /* End Of File counter (play only) */ 0, 0, 0, AUDIO_MID_BALANCE, /* stereo channel balance */ }, AUDIO_MID_GAIN, /* monitor_gain */ 0, /* output_muted */ #ifdef AUDIOIO_HAS_REFCNT 0, /* driver reference count */ #endif {0, 0}, /* reserved fields */ #ifdef AUDIOIO_HAS_FEATURES AUDIO_HWFEATURE_DUPLEX | AUDIO_HWFEATURE_IN2OUT | \ AUDIO_HWFEATURE_PLAY | AUDIO_HWFEATURE_RECORD, AUDIO_SWFEATURE_MIXER, 0, #endif }; /* Functions */ static int aureal_init(dev_info_t *dip); static int aureal_cleanup(struct aureal_state *softstate); static int aureal_pci_setup(struct aureal_state *sofstate); static int aureal_ac97_read(void *closure, uint16_t sub_addr, uint16_t *data); static int aureal_ac97_write(void *closure, uint16_t sub_addr, uint16_t value); int _init(void) { int error; ddi_soft_state_init(&statep, sizeof (struct aureal_state), 4); error = mod_install(&aureal_modlinkage); if (error != 0) { ddi_soft_state_fini(&statep); cmn_err(CE_NOTE, "_init:mod_install() failed\n"); return error; } return DDI_SUCCESS; } int _fini(void) { int error; error = mod_remove(&aureal_modlinkage); if (error != 0) { cmn_err(CE_NOTE, "_fini:mod_remove() failed\n"); return error; } ddi_soft_state_fini(&statep); return DDI_SUCCESS; } int _info(struct modinfo *modinfop) { return mod_info(&aureal_modlinkage, modinfop); } /* Autoconfiguration Functions */ int aureal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance; struct aureal_state *softstate; int numberRegs; /* number of registers */ int regMapStatus; /* used for status of register mapping */ off_t regCount; /* used for getting size of registers */ uint_t testReg; /* just used for testing */ if (cmd != DDI_ATTACH) return DDI_FAILURE; instance = ddi_get_instance(dip); cmn_err(CE_NOTE, "aureal_attach(): attach for instance %d\n", instance); /* Allocate Per-Instance Soft State Structure */ if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) { cmn_err(CE_NOTE, "aureal_attach(): ddi_soft_state_zalloc failed\n"); return DDI_FAILURE; } /* Initialize Soft State Structure */ softstate = ddi_get_soft_state(statep, instance); if (softstate == NULL) { cmn_err(CE_NOTE, "aureal_attach(): ddi_get_soft_state failed\n"); ddi_soft_state_free(statep, instance); return DDI_FAILURE; } softstate->dip = dip; /* Attach Audio Helper Module */ softstate->audio_handle = audio_hlp_attach(dip, cmd, &aureal_ad_info); if (!softstate->audio_handle) { cmn_err(CE_NOTE, "aureal_attach(): audio_hlp_attach failed\n"); ddi_soft_state_free(statep, instance); return DDI_FAILURE; } audio_hlp_set_private(softstate->audio_handle, softstate); /* Map Interrupt */ if (ddi_get_iblock_cookie(dip, 0, &softstate->intr_cookie) != DDI_SUCCESS) { cmn_err(CE_NOTE, "aureal_attach(): ddi_get_intr_cookie failed\n"); return DDI_FAILURE; } mutex_init(&softstate->intr_mutex, NULL, MUTEX_DRIVER, softstate->intr_cookie); if (ddi_add_intr(dip, 0, NULL, NULL, aureal_intr, (caddr_t) softstate) != DDI_SUCCESS) { cmn_err(CE_NOTE, "aureal_attach(): ddi_add_intr failed\n"); mutex_destroy(&softstate->intr_mutex); ddi_soft_state_free(statep, instance); return DDI_FAILURE; } /* Setup the PCI configuration on the card */ regMapStatus = pci_config_setup(dip, &softstate->pci_conf_handle); if (regMapStatus == DDI_FAILURE) { cmn_err(CE_NOTE, "aureal_attach(): pci_config_setup failed\n"); return DDI_FAILURE; } aureal_pci_setup(softstate); /* Map the register sets */ regMapStatus = ddi_regs_map_setup(dip, 1, (caddr_t *)&softstate->aureal_ac97_regs, 0, 0, &dev_attr, &softstate->aureal_ac97); if (regMapStatus == DDI_FAILURE) { cmn_err(CE_NOTE, "aureal_attach(): ddi_regs_map_setup failed\n"); return DDI_FAILURE; } regMapStatus = ddi_regs_map_setup(dip, 2, (caddr_t *)&softstate->aureal_mixer_regs, 0, 0, &dev_attr, &softstate->aureal_mixer); if (regMapStatus == DDI_FAILURE) { cmn_err(CE_NOTE, "aureal_attach(): ddi_regs_map_setup failed\n"); return DDI_FAILURE; } regMapStatus = ddi_regs_map_setup(dip, 3, (caddr_t *)&softstate->aureal_bus_regs, 0, 0, &dev_attr, &softstate->aureal_bus); if (regMapStatus == DDI_FAILURE) { cmn_err(CE_NOTE, "aureal_attach(): ddi_regs_map_setup failed\n"); return DDI_FAILURE; } /* Initialize the card */ aureal_init(dip); return DDI_SUCCESS; } int aureal_detach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance; struct aureal_state *softstate; instance = ddi_get_instance(dip); softstate = ddi_get_soft_state(statep, instance); if (softstate == NULL) { cmn_err(CE_NOTE, "aureal_detach(): ddi_get_soft_state failed\n"); return DDI_FAILURE; } aureal_cleanup(softstate); return DDI_SUCCESS; } int aureal_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { return DDI_FAILURE; } /* Interrupt Handler */ static uint_t aureal_intr(caddr_t intr_arg) { /* interrupt context. don't use cmn_err() */ struct aureal_state *softstate = (struct aureal_state *)intr_arg; int position_read; int byte_diff; if (! ((AUREAL_GET32(0x2a008) & 0x1))) return DDI_INTR_CLAIMED; if (! AUREAL_GET32(0x2a000)) return DDI_INTR_CLAIMED; /* s = card->states[0]; * check if s == NULL */ position_read = AUREAL_GET32(0x27e00) & 0xfff; if (position_read < softstate->last_position) { byte_diff = softstate->dmalen - softstate->last_position + position_read; } else { byte_diff = position_read - softstate->last_position; } softstate->last_position = position_read; softstate->dmacount -= byte_diff; AUREAL_GET32(0x2919c); AUREAL_PUT32(0x2919c, 0x60); return DDI_INTR_CLAIMED; } /* Aureal Init Routine */ int aureal_init(dev_info_t *dip) { int instance; struct aureal_state *softstate; int ready = 0, i = 0, count = 0; instance = ddi_get_instance(dip); /* find out which instance we are */ softstate = ddi_get_soft_state(statep, instance); /* let's get our softstate */ /* Hardware Initialization */ AUREAL_PUT32(VORTEX_IRQ_GLOB, 0xffffffff); drv_usecwait(5000); AUREAL_PUT32(VORTEX_IRQ_GLOB, AUREAL_GET32(VORTEX_IRQ_GLOB) & 0xffdfffff); /* AC97 Codec Initialization */ AUREAL_PUT32(VORTEX_CODEC_CTRL, 0x8068); drv_usecwait(1000); AUREAL_PUT32(VORTEX_CODEC_CTRL, 0x00e8); for (i=0; i<32; i++) { AUREAL_PUT32(VORTEX_CODEC_CHN + (i<<2), 0); } AUREAL_PUT32(VORTEX_CODEC_CTRL, 0x00e8); drv_usecwait(1000); /* Enable Channel 0 and 1 */ AUREAL_PUT32(VORTEX_CODEC_EN, (AUREAL_GET32(VORTEX_CODEC_EN) | 0x300)); /* Setup AC97 Codec */ { static struct ac97_codec_ops codec_ops = { aureal_ac97_read, aureal_ac97_write }; softstate->codec_handle = ac97_codec_init(softstate->audio_handle, &codec_ops, softstate); } if (!CODEC_RESET(softstate->codec_handle, &aureal_defaults, 1)) { cmn_err(CE_NOTE, "aureal_attach(): Audio CODEC reset failed\n"); } /* Audio Data Bus Init */ vortex2_mixerinit(softstate); vortex2_fifoinit(softstate); vortex2_dmainit(softstate); vortex2_srcblockinit(softstate); AUREAL_PUT32(VORTEX_ADB_CTRL, 0); for (i=0; i<173; i++) AUREAL_PUT32( (0x28000 + (i<<2)), 0xffff); for (i=0; i<24; i++) AUREAL_PUT32( (0x282b4 + (i<<2)), 0xffff); /* Enable Interrupts */ AUREAL_PUT32(0x2a004, 0x24); AUREAL_PUT32(0x2a00c, AUREAL_GET32(0x2a00c) | 0x1000000); /* Add Default Routes */ vortex2_addroute(softstate, 17, 0x88, 0x0); vortex2_addroute(softstate, 17, 0x89, 0x0); AUREAL_PUT32(VORTEX_IRQ_GLOB, AUREAL_GET32(VORTEX_IRQ_GLOB) | VORTEX_IRQ_ENABLE); AUREAL_PUT32( (0x282b4 + (18<<2)), 0x3251); /* I think we're done. * What's pci_set_srvdata() and list_add? Increment devindex. */ if (ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_DONTWAIT, NULL, &softstate->dmahandle) != 0) { cmn_err(CE_NOTE, "aureal_init(): ddi_dma_alloc_handle did not return 0.\n"); } alloc_dmabuf(softstate); return DDI_SUCCESS; } /* Aureal Cleanup Routine */ int aureal_cleanup(struct aureal_state *softstate) { if (softstate->dmahandle != NULL) ddi_dma_free_handle(&softstate->dmahandle); /* detach audio helper audio_handle */ if (softstate->audio_handle != NULL) audio_hlp_detach(softstate->audio_handle, DDI_DETACH); if (softstate->aureal_mixer != NULL) ddi_regs_map_free(&softstate->aureal_mixer); if (softstate->aureal_bus != NULL) ddi_regs_map_free(&softstate->aureal_bus); if (softstate->intr_cookie != NULL) ddi_remove_intr(softstate->dip, 0, softstate->intr_cookie); mutex_destroy(&softstate->intr_mutex); ddi_soft_state_free(statep, ddi_get_instance(softstate->dip)); return DDI_SUCCESS; } /* PCI Setup Routine */ int aureal_pci_setup(struct aureal_state *softstate) { dev_info_t *dip = softstate->dip; ddi_acc_handle_t conf_handle = softstate->pci_conf_handle; uint16_t pci_status; uint16_t pci_vendorid; uint16_t pci_deviceid; uint8_t pci_revision; uint8_t pci_intrinfo; uint16_t pci_commandreg; char *revstr = ""; /* Grab the card info from the PCI command registers */ pci_status = pci_config_get16(conf_handle, PCI_CONF_STAT); pci_vendorid = pci_config_get16(conf_handle, PCI_CONF_VENID); pci_deviceid = pci_config_get16(conf_handle, PCI_CONF_DEVID); pci_revision = pci_config_get8 (conf_handle, PCI_CONF_REVID); pci_intrinfo = pci_config_get8 (conf_handle, PCI_CONF_ILINE); switch (pci_vendorid) { case 0x12eb: /* aureal: */ switch (pci_deviceid) { case 0x2: revstr = "AU8830"; break; } } /* Place the text description in the softstate struct along with the revision */ softstate->rev = pci_revision; softstate->revstr = revstr; /* Announce the hardware */ ddi_report_dev(dip); cmn_err(CE_CONT, "?PCI-device: %s, pajau%d, %s%s(rev. 0x%02x) at irq %d\n", ddi_node_name(dip), ddi_get_instance(dip), revstr, revstr[0] ? " " : "", pci_revision, pci_intrinfo); pci_commandreg = pci_config_get16(conf_handle, PCI_CONF_COMM); /* cmn_err(CE_CONT, "pci command register 0x%x %s%s%s%s", pci_commandreg, pci_commandreg & PCI_COMM_SERR_ENABLE ? " serr#_en" : "", pci_commandreg & PCI_COMM_ME ? " busmaster" : "", pci_commandreg & PCI_COMM_MAE ? " mem_access" : "", pci_commandreg & PCI_COMM_IO ? " io_access" : ""); */ if ((pci_commandreg & (PCI_COMM_IO|PCI_COMM_ME)) == (PCI_COMM_IO|PCI_COMM_ME)) { } else { pci_config_put16(conf_handle, PCI_CONF_COMM, pci_commandreg | PCI_COMM_IO | PCI_COMM_ME); } return DDI_SUCCESS; } /* Audio Driver functions for Audio Helper */ static int aureal_setup(audiohdl_t handle, int stream, int flags) { struct aureal_state *softstate = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_setup(): handle %p, stream %d, flags %x\n", handle, stream, flags); AUREAL_PUT32(0x2919c, 0x7ff); vortex2_setadbvalid(softstate, 1); AUREAL_PUT32(0x28400, 0x20000); /* alloc_dmabuf(softstate); */ vortex2_enable_timer_int(softstate); return AUDIO_SUCCESS; } static void aureal_teardown(audiohdl_t handle, int stream, int dir) { struct aureal_state *softstate = audio_hlp_get_private(handle); vortex2_disable_int(softstate); vortex2_flushbuffers(softstate); vortex2_disablefifo(softstate); vortex2_stopfifo(softstate); vortex2_flushbuffers(softstate); AUREAL_PUT32(0x28400, 0); dealloc_dmabuf(softstate); AUREAL_PUT32(0x27a00, 0x22000); cmn_err(CE_NOTE, "aureal_teardown(): handle %p, stream %d, dir %d\n", handle, stream, dir); } static int aureal_set_config(audiohdl_t handle, int stream, int command, int dir, int arg1, int arg2) { struct aureal_state *softstate = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_set_config(): handle %p, stream %d, command %d, dir %d, arg1 %d, arg2 %d\n", handle, stream, command, dir, arg1, arg2); CODEC_SET_CONFIG(softstate->codec_handle, command, dir, arg1, arg2); return AUDIO_SUCCESS; } static int aureal_set_format(audiohdl_t handle, int stream, int dir, int sample_rate, int channels, int precision, int encoding) { struct aureal_state *softstate = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_set_format(): handle %p, stream %d, dir %d, sample_rate %d, channels %d, precision %d, encoding %d\n", handle, stream, dir, sample_rate, channels, precision, encoding); switch (encoding) { case AUDIO_ENCODING_LINEAR8: softstate->format = 0x06000; break; case AUDIO_ENCODING_ULAW: softstate->format = 0x0a000; break; case AUDIO_ENCODING_ALAW: softstate->format = 0x0e000; break; case AUDIO_ENCODING_LINEAR: softstate->format = 0x22000; break; default: return 0; } AUREAL_PUT32(VORTEX_ADB_DMA_CTRL, (AUREAL_GET32(0x27e00) & ~0xff000) | softstate->format); return AUDIO_SUCCESS; } static void aureal_play_chunk(void *arg) { audiohdl_t handle = arg; struct aureal_state *state = audio_hlp_get_private(handle); #define CHUNK_SIZE 512 unsigned short buf[CHUNK_SIZE]; int len; cmn_err(CE_NOTE, "aureal_play_chunk(): handle %p, stream %d\n", handle, state->playing_stream); len = audio_hlp_get_audio(handle, buf, state->playing_stream, CHUNK_SIZE); cmn_err(CE_NOTE, " read %d samples with audio_hlp_get_audio\n", len); #if 0 { int i; char line[132]; for (i = 0; i < len+4; i++) { if (i % 16 == 0) line[0] = 0; sprintf(line + strlen(line), "%04x ", buf[i]); if (i % 16 == 15) cmn_err(CE_NOTE, "%s\n", line); } if (i % 16) cmn_err("%s\n", line); } #endif if (len > 0 && ++state->num_timeouts < 100) { state->samples_played += len; state->play_timeout_id = timeout(aureal_play_chunk, handle, drv_usectohz(10000)); } else { state->play_timeout_id = 0; } } static int aureal_start_play(audiohdl_t handle, int stream) { struct aureal_state *state = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_start_play(): handle %p, stream %d\n", handle, stream); if (!state->play_timeout_id) { state->playing_stream = stream; state->num_timeouts = 0; state->samples_played = 0; state->play_timeout_id = timeout(aureal_play_chunk, handle, drv_usectohz(10000)); } vortex2_setupbuffer(state, state->cookie.dmac_address); vortex2_startfifo(state); return AUDIO_SUCCESS; } static void aureal_pause_play(audiohdl_t handle, int stream) { cmn_err(CE_NOTE, "aureal_pause_play(): handle %p, stream %d\n", handle, stream); } static void aureal_stop_play(audiohdl_t handle, int stream) { struct aureal_state *state = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_stop_play(): handle %p, stream %d\n", handle, stream); cmn_err(CE_NOTE, "aureal_stop_play(): played %d samples\n", state->samples_played); } static void aureal_record_chunk(void *arg) { audiohdl_t handle = arg; struct aureal_state *state = audio_hlp_get_private(handle); #define CHUNK_SIZE 512 unsigned short buf[CHUNK_SIZE]; int i; cmn_err(CE_NOTE, "aureal_record_chunk(): handle %p, stream %d\n", handle, state->playing_stream); for (i = 0; i < CHUNK_SIZE; i++) buf[i] = i; audio_hlp_send_audio(handle, buf, state->playing_stream, CHUNK_SIZE); if (++state->num_record_timeouts < 100) { state->record_timeout_id = timeout(aureal_record_chunk, handle, drv_usectohz(10000)); } else { state->record_timeout_id = 0; } } static int aureal_start_record(audiohdl_t handle, int stream) { struct aureal_state *state = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_start_record(): handle %p, stream %d\n", handle, stream); if (!state->record_timeout_id) { state->recording_stream = stream; state->num_record_timeouts = 0; state->record_timeout_id = timeout(aureal_record_chunk, handle, drv_usectohz(10000)); } return AUDIO_SUCCESS; } static void aureal_stop_record(audiohdl_t handle, int stream) { struct aureal_state *state = audio_hlp_get_private(handle); cmn_err(CE_NOTE, "aureal_stop_record(): handle %p, stream %d\n", handle, stream); if (state->record_timeout_id) { untimeout(state->record_timeout_id); state->record_timeout_id = 0; } } static int aureal_ioctl(audiohdl_t handle, int channel, queue_t *q, mblk_t *mp, int *error) { struct iocblk *iocp = (struct iocblk *)mp->b_rptr; cmn_err(CE_NOTE, "audionull_ioctl ioc_cmd=%x\n", iocp->ioc_cmd); switch (iocp->ioc_cmd) { default: return AD_NACK; } } static int aureal_iocdata(audiohdl_t handle, int channel, queue_t *q, mblk_t *mp, int *error) { struct copyresp *copyresp = (struct copyresp *)mp->b_rptr; mblk_t *data_mp; caddr_t arg = (caddr_t) copyresp->cp_private; if (mp->b_cont == NULL) return AD_NACK; data_mp = mp->b_cont; cmn_err(CE_NOTE, "aureal_iocdata cp_cmd=%x, arg=%x\n", copyresp->cp_cmd, arg); switch (copyresp->cp_cmd) { default: return AD_NACK; } } static ah_ad_entry_t aureal_entry = { aureal_setup, aureal_teardown, aureal_set_config, aureal_set_format, aureal_start_play, aureal_pause_play, aureal_stop_play, aureal_start_record, aureal_stop_record, aureal_ioctl, aureal_iocdata, }; static audio_device_t aureal_dev_info = { "au8830", /* name */ "1.0", /* version */ "null" /* config */ }; static uint_t aureal_srs[] = { 8000, 48000, 0 }; static uint_t aureal_chs[] = { AUDIO_CHANNELS_MONO, AUDIO_CHANNELS_STEREO, 0, }; static ah_ad_cap_comb_t aureal_cap_comb[] = { { AUDIO_PRECISION_16, AUDIO_ENCODING_LINEAR }, /* 16-bit signed PCM */ { AUDIO_PRECISION_8, AUDIO_ENCODING_LINEAR8 }, /* 8-bit unsigned PCM */ { AUDIO_PRECISION_8, AUDIO_ENCODING_ULAW }, { 0 }, }; static ah_ad_info_t aureal_ad_info = { AD_VERS1, /* Audio Driver interface version */ &aureal_defaults, /* Audio Driver audio_info_t struct */ { /* play capabilities */ { AD_SRS_FLAG_SR_LIMITS, aureal_srs }, aureal_chs, 0, 2, 16384, /* buffer size */ }, { /* record capabilities */ { AD_SRS_FLAG_SR_LIMITS, aureal_srs }, aureal_chs, 0, 2, 16384, /* buffer size */ }, aureal_cap_comb, /* list of play cap. combinations */ aureal_cap_comb, /* list of rec cap. combinations */ &aureal_entry, /* Audio Driver entry points */ &aureal_dev_info, /* device information */ }; static int aureal_ac97_read(void *closure, uint16_t sub_addr, uint16_t *data) { struct aureal_state *softstate = closure; int reader, t; for (t = 0; t < POLL_COUNT; t++) { /* cmn_err(CE_NOTE, "ac97_read(): ctrl_check: t is %d\n", t); */ if ( (AUREAL_GET32(VORTEX_CODEC_CTRL) & 0x100) ) break; } reader = ( (sub_addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK); AUREAL_PUT32(VORTEX_CODEC_IO, reader); for (t = 0; t < POLL_COUNT; t++) { /* cmn_err(CE_NOTE, "ac97_read(): io_check: t is %d\n", t); */ if ( ((AUREAL_GET32(VORTEX_CODEC_IO) & VORTEX_CODEC_ADDMASK)) == (sub_addr << VORTEX_CODEC_ADDSHIFT) ) break; drv_usecwait(1000); } *data = AUREAL_GET32(VORTEX_CODEC_IO) & VORTEX_CODEC_DATMASK; /* cmn_err(CE_NOTE, "ac97_read(): read %d from ac97 register %d\n", *data, sub_addr); */ return 1; } static int aureal_ac97_write(void *closure, uint16_t sub_addr, uint16_t value) { struct aureal_state *softstate = closure; int t; for (t = 0; t < POLL_COUNT; t++) { /* cmn_err(CE_NOTE, "ac97_write(): ctrl_check: t is %d\n", t); */ if ((AUREAL_GET32(VORTEX_CODEC_CTRL) & 0x100) ) break; } /* cmn_err(CE_NOTE, "ac97_write(): writing %d to ac97 register %d\n", value, sub_addr); */ AUREAL_PUT32(VORTEX_CODEC_IO, ((sub_addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) | ((value << VORTEX_CODEC_DATSHIFT) & VORTEX_CODEC_DATMASK) | VORTEX_CODEC_WRITE); return 1; }