/* ns_geode.c */ /* * Copyright (c) 2001 Bruce R. Montague * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. */ /* *----------------------------------------------------------------------- * 1-August-2002 * * FreeBSD driver for National Semiconductor Geode "Native Audio". * This is the PCI audio function found on the NatSemi Cx5530 * "southbridge" and integrated into some current NatSemi single chip * Geode systems. This hardware originally supported Cyrix * "hypervisor"-level software emulation of Soundblaster hardware * (which was driven by SMI interrupts). * * This driver is operational. It can be built using either interrupts, * or (because not all Geode hardware apparently can map SMI to IRQs), * it can be built without requiring interrupts. If built to not use * interrupts, either "timeout()" calls can be used or, alternatively, * a "hardclock()" hack similar to what's used by polling network * drivers can be used. * * This code is based on a study of Cameron Grant's "template.c", the * current "ich.c" bridge driver, other PCM bridge drivers, miscellaneous * doc, and the /usr/src/sys/dev/sound/pcm new PCM driver code. * *-------- * This is a "newpcm bridge" driver, that is, a hardware-specific * low-level driver that works with the higher-level generic PCM * driver. The generic PCM sound driver manages logical "channels" * that are connected to a "codec". The channels are virtual sinks * and sources of data buffers to be DMAd (either transmitted * out, or filled with input). The "codec" can be considered a * number of A/Ds and D/As that connect to the DMAs. The "mixer" * is a multiplier (volume control) for the analog A/D & D/A * signals that correspond to the channels. Multiple output and * input channels can be active concurrently. * * This bridge driver interacts with the generic PCM driver using * standard FreeBSD "kernel objects". The use of kernel objects * provides a means to do a degree of dynamic object-oriented * programming in C. To use FreeBSD kernel objects, parent code * defines "interfaces" specified in ".m" files. Child code then * implements the required methods of the interface in C (there * can, of course, be multiple implementations of a given interface). * Routines with a "kobj" pointer as first argument are FreeBSD * method implementations. Methods are invoked in parent code using * the "method name" of the routine in UPPER CASE. The C routines * that comprise an interface implementation are typically tied to * their (lower case) method name by KOBJMETHOD() or DEVMETHOD() * macros. * * The Geode audio mixer device adheres to the AC97 standard. The * PCM driver specifies an AC97 ".m" interface. This interface * simply specifies methods to read and write the standard AC97 * registers. Actual managment of these registers is done by the * generic PCM driver, although it uses a callback to this driver * to set the speed of a channel ("geode_pchan_setspeed()"). * *-------- * See datasheet "Geode SC1200 Set-Top Box on a Chip", Jan 2002, * and "LM4549 AC'97 Rev 2.1 Codec with Sample Rate Conversion * and National 3D Sound", September 2000. *----------------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * To build this driver (as a loadable driver), assuming a source tree * in "/usr/src": * * Create a build directory "/usr/src/sys/modules/sound/driver/ns_geode" * and put the following "Makefile" there: *---- * |.PATH: ${.CURDIR}/../../../../dev/sound/pci * |KMOD = snd_ns_geode * |SRCS = device_if.h bus_if.h isa_if.h pci_if.h * |SRCS += ns_geode.c * |KMODDEPS = snd_pcm * | * |.include *---- * Put this "ns_geode.c" and the "ns_geode.h" file in * "/usr/src/sys/dev/sound/pci". * * Build and load the driver: *--- * >su * #cd /usr/src/sys/modules/sound/driver/ns_geode * #make clean * #make depend * #make * #kldunload snd_ns_geode.ko * #kldload /usr/src/sys/modules/sound/driver/ns_geode/snd_ns_geode.ko * * >kldstat * >cat /dev/sndstat * >mixer vol 100 * >mixer rec 100 * >mixer * * >cat /kernel >/dev/audio * * *-------- * To build the driver into the kernel (for instance, when building a * custom version of PicoBSD), assuming as above that the source * tree is in "/usr/src": * *--- * As above, put files "ns_geode.c", "ns_geode.h", and "Makefile" * in "/usr/src/sys/dev/sound/pci". * *--- * Edit "/usr/src/sys/conf/files" * * Add the line: * *|dev/sound/pci/ns_geode.c optional pcm pci * * Perhaps under following the "ich.c" driver line: *|dev/sound/pci/ich.c optional pcm pci * *--- * Build a new kernel, with the following lines in your custom * kernel config file: * *|device pcm * *----------------------------------------------------------------------- */ /* *----------------------------------------------------------------------- * As a crutch that can be used if the particular Geode hardware does not * support mapping bus master "DMA" completion interrupts to IRQs, polling * can be used (set USE_TIMER_POLL to 1). There are 2 types of polling * supported, one that uses the "timeout()" routine (set HARDCLOCK_POLL to * 0) and one that uses a call in the kernels "hardclock()" routine, * similar to what is done for polling network drivers. Because the * audio interrupt rate is quite slow, use of "timeout()" usually * works as well as calling from "hardclock()" and does not require * modifying kernel source. * *-------- * To use hard clock polling (not timeout()), recompile the kernel after * modifying "kern_clock.c" as follows: * * ---> Before the hardclock routine: * void * hardclock(frame) * * ---> Add: * #if 1 * int geode_dma_poll = 0; * void (*geode_audio_poll)( void ); * #endif * * * ----> After this code in "hardclock()": * #ifdef DEVICE_POLLING * hardclock_device_poll(); * #endif * * ----> Add: * #if 1 * if( geode_dma_poll ) { * (*geode_audio_poll)(); * } * #endif * * #endif *-------------------------------------------------------------------- */ /* *-------------------------------------------------------------------- * CONTENTS. This file is organized into the following sections, * identified by the following comment lines: * * @@ Includes. * @@ Debug enables. * @@ Defines. * @@ Structures. * @@ Channel Formats. * @@ Prototypes. * @@ Globals (only if polling). * * @@@ Hardware access functions. * @@ Memory-mapped PCI Audio port (F3BAR) access routines. * @@ Codec access routines. * @@ Polling and DMA routines. * * ______________ * @@@ Implementation of the AC97 Interface (PCM codec support). * @@@ Geode Audio Initializataion routines (support attach function). * * __________________ * @@@ Implementation of the Channel Interfaces. * @@ Instantiation of the Play Channel. * @@ Instantiation of the Stereo Receive Channel. * @@ Instantiation of the Mono Receive Channel. * * @@@ The interrupt handler. * ________________ * @@@ Implementation of the Device Interface. * * @@@ Debug Routines. *-------------------------------------------------------------------- */ /* @@ Includes. */ #include #include #include #include #include #include #include #include /*---- @@ Debug enables. ---------------------------------------------------*/ #define GEODE_DEBUG 0 /* Compile dump routines (for DDB calls). */ #define GEODE_DEBUG_INIT 0 /* Call dump routines during init. */ #define F3BAR_TRACE 0 #define DMA_TRACE 0 #define AC97_TRACE 0 #define OP_TRACE 0 #define CRASH_ON_ATTACH 0 /* Trap to DDB on initial driver attach.*/ /*---- @@ Defines. --------------------------------------------------------*/ /* Use HARDCLOCK_POLL 1 with added code in "kern_clock.c/hardclock()". * Use HARDCLOCK 0 if "timeout()" is to be used instead. */ #define USE_TIMER_POLL 0 #define HARDCLOCK_POLL 0 #define GEODE_DEFAULT_BUFSZ 65536 /* (was 16384) */ #define GEODE_MAX_BUFSZ 65536 /* Use double-buffering for each channel. */ #define NUM_PRD_ELEMS (2+1) /* Number of DMA descriptors + 1 entry for loop. */ /*---- @@ Structures. ----------------------------------------------------*/ struct sc_info; /* Forward ref for sc_chinfo. */ /* * The sc_chinfo "ch_dtbl" pointer points to an array of PRD (Physical * Region Descriptor) elements. Each channel has an array of these * that describe the buffers to be "DMAed". PRD values need to be * "really" written to memory. Careful, no struct padding, etc.. * Descriptors must be 2 32-bit words. Each descriptor has the following * format: * * u_int32_t prd_buffer; DMA buffer address, low 2 bits must be 0. * u_int32_t prd_length; Top 3 bits are EOT, EOP, LOOP, low 16 are buf len. * * EOT: "End of Table" (last PRD elem) * EOP: "End of Page" (interrupt when finished processing this page (block)) * LOOP: "prd_buffer" contains physical address of next PRD element to process. *-------- * The driver supports 3 channels, "stereo play" (output), "stereo record" (input), * and "mono play". These correspond to hardwired PCI audio function hardware. * Channel buffers are allocated in "geode_pchan_init()". The driver's buffering * scheme for each channel is: * * base = sndbuf_getbuf( ch->ch_snd_dbuf ) * * base----> [ Blk 0 ] * [ Blk 1 ] * * There are 3 PRD entries for each channel, with the following format: * * addr:[prd_buffer ]-------> [Blk 0] * ^ [EOP, len ] * | [prd_buffer ]-------> [Blk 1] * | [EOP, len ] * \-<-[ addr ] * [JMP ] * * The addresses in the PRD entries are physical memory addresss. * * Although each channel (struct sc_chinfo) has a pointer (ch_dtbl) * to its first PRD entry (that is, a pointer to its "addr:" in the * above diagram), all PRDs for all channels are allocated successively * from a single array, ("sc_info.sc_dtbl"). This array is allocated * at the start of "geode_init()" using "bus_*()" allocation functions * that can allocate to the requirements of DMA controllers. */ /* * Private channel info. One of these structures exists for each channel * supported by the device, that is, each play or record data stream * that can be doing "DMA" concurrently (each "PCI bus master", or "DMA engine"). * * The driver allocates a single buffer containing a "block array" * for each channel. Blocks are the "sub-buffers" that correspond * to a single DMA operation. Each channel currently gets two * contiguous blocks, thus supporting ping-pong double buffering. * Blocks are numbered 0 origin (0,1). A large part of this driver's * operation consists of synchronizing activation and completion of * block DMA with requests to the PCM driver to process a block * (either write a full buffer out to application level (receive, * record), or fill a buffer (send, play)). */ struct sc_chinfo { int ch_num; /* Channel number, matches DMA engine number.*/ int ch_run; /* 1=DMA in progress.*/ int ch_was_running; /* DMA was aborted when last suspend occured. */ u_int32_t ch_speed; /* Channel speed.*/ u_int32_t ch_fmt; /* Channel format.*/ struct sc_info *ch_sc; /* Backptr to our driver's private device info. */ struct pcm_channel *ch_channel; /* PCM channel that generated buffer.*/ struct snd_dbuf *ch_snd_dbuf; /* PCM Control struct for buffer to be DMAed.*/ /* DMA blocks are in 1 buf attached to snd_dbuf*/ u_int32_t ch_num_blks; /* Number of DMA blocks in buffer.*/ u_int32_t ch_blk_size; /* Size of each block. An interrupt is triggered.*/ /* at the end of each block.*/ u_int32_t *ch_dtbl; /* Start PRD descriptor for this channel.*/ int ch_cur_dma_blk; /* Num of blk just DMAed (or being DMAed). */ /*----- Below are bus master specific for this channel. */ u_int32_t ch_last_prd_dma_adr; /* Last PRD DMA address of operational DMA. */ u_int32_t ch_dma_cmd; /* Offset of DMA_CMD PCI F3BAR audio register. */ u_int32_t ch_dma_status; /* " DMA STATUS register. */ u_int32_t ch_dma_prd_adr; /* " address of DMA PRD register, contains. */ /* physcial adr of next PRD to process. */ int ch_dir; /* DMA_INPUT,DMA_OUTPUT */ }; /* ch_dir */ #define CH_INPUT 2 /* Channel is an input (record) channel. */ #define CH_OUTPUT 3 /* Channel is an output (play) channel. */ /* * Geode audio device private data. There is one of these for each instance of the * device (codec). In theory, there could be more than one codec. This driver currently does * not support multiple codecs. This structure contains 3 sc_chinfo structures that control * the 3 possible concurrent DMA operations of a single codec. */ struct sc_info { device_t sc_dev; /* Backpointer to our device.*/ u_int32_t sc_type; /* Device id.*/ void *sc_mmregs_base; /* Base virt adr of memory-mapped PCI audio regs.*/ bus_dma_tag_t sc_parent_dmat; /* Our "parent" TAG for allocating DMAable memory.*/ /* Phys mem requirements can be further restricted*/ /* by creating child tags, if need be. */ bus_dmamap_t sc_parent_dtmap; /* Default DMAable mem MAP (requirements). */ bus_dmamap_t sc_prd_dt_map; /* PRD descriptor table map. */ struct resource *sc_reg, *sc_irq_res; /* Standard driver vars. */ int sc_regtype, sc_regid, sc_irqid; void *sc_ih; void *sc_lock; /* Lock device for SMP. */ unsigned int sc_buf_size; /* Total size of single buf attached to a channel.*/ /* This includes all the ping-pong DMA blocks.*/ struct ac97_info *sc_ac97_codec; /* An AC97 Codec KOBJ allocated by AC97_CREATE().*/ int sc_ch_num; /* Initialization counter, indexes sc_ch[].*/ struct sc_chinfo sc_ch[3]; /* Priv Channel info for PLAY, REC, and MONO REC channels.*/ u_int8_t *sc_dtbl; /* Single PRD descriptor table, divided for all channels.*/ }; /* (this is the virtual address, not physical.)*/ /*---- @@ Channel Formats. -----------------------------------------------------*/ /* * Channel Format Descriptors. When an application program needs * an audio channel with particular attributes, the PCM driver * (accessed via ioctls()) locates an appropriate CHANNEL class. The * classes supported by this driver are instantiated in * "geode_pci_attach()" via "pcm_addchan()" calls. The capabilities * of each CHANNEL class are defined by a "recfmt" structure. The * PCM driver obtains this capability information using the * "channel_getcaps()" method ("geode_pchan_getcaps()", * "geode_rcv_chan_getcaps()", and "geode_rcv_mono_chan_getcaps()"). * These methods return a pointer to the appropriate "pcmchan_caps" * defined below (low-speed, high-speed, supported-formats, 0). * * See "/usr/src/sys/sys/soundcard.h" for bit-encoding of the fmt-word. * A channel can support multiple formats. */ /*--------------------------*/ static u_int32_t geode_recfmt[] = { AFMT_U16_LE, /* Unsigned, 16-bit, little-endian. */ AFMT_STEREO | AFMT_U16_LE, AFMT_S16_LE, /* Signed, 16-bit, little-endian. */ AFMT_STEREO | AFMT_S16_LE, 0 }; static struct pcmchan_caps geode_reccaps = { 4000, 48000, geode_recfmt, 0 }; /*--------------------------------*/ static u_int32_t geode_mono_recfmt[] = { AFMT_U16_LE, AFMT_S16_LE, 0 }; static struct pcmchan_caps geode_mono_reccaps = { 4000, 48000, geode_mono_recfmt, 0 }; /*--------------------------*/ static u_int32_t geode_playfmt[] = { AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static struct pcmchan_caps geode_playcaps = { 4000, 48000, geode_playfmt, 0 }; /*---- @@ Prototypes. ------------------------------------------------------*/ static void audio_port_write_32( u_int32_t reg, u_int32_t val ); static void audio_port_write_8( u_int32_t reg, u_int8_t val ); static u_int32_t audio_port_read_32( u_int32_t reg ); static u_int8_t audio_port_read_8( u_int32_t reg ); #if 0 static void audio_port_write_16( u_int32_t reg, u_int16_t val ); #endif static u_int32_t get_codec_status( void ); static void set_codec_control( u_int32_t val ); static u_int16_t codec_direct_reg_read( int codec_port ); static int verify_codec_present( void ); static int reset_codec( void ); static void codec_reg_write( u_int32_t reg_idx, u_int32_t val ); static void await_codec_ready( void ); static void geode_free_all( device_t dev, struct sc_info *sc ); static void start_geode_dma( struct sc_chinfo *ch ); static void stop_geode_dma( struct sc_chinfo *ch ); static void prd_filldtbl( struct sc_chinfo *ch ); static void set_codec_AD_rate( void ); static void geode_intr( void *p ); static void enable_audio_irq( void ); static void await_operational_codec_channel_regs( void ); #if GEODE_DEBUG int do_dmp_mmregs( void ); void dmp_codec_regs( void ); static void dmp_audio_pci_hdr( device_t dev ); static void dmp_audio_mem_regs( char *audio_regs_adr ); #endif #if CRASH_ON_ATTACH void crash( void ); #endif #if 0 static int is_dma_done( void ); #endif /*---- @@ Globals. --------------------------------------------*/ struct sc_info *sc; #if USE_TIMER_POLL #if HARDCLOCK_POLL extern int geode_dma_poll; extern void (*geode_audio_poll)( void * ); #else static struct callout_handle tick_poll; static int geode_dma_poll = 0; #endif #endif /* *---------------------------------------------------------------- * @@@ Hardware access functions. *---------------------------------------------------------------- */ /*-- @@ Memory-mapped PCI Audio port (F3BAR) access routines. -----------*/ /*-----------------------------------------------------------------------*/ static void audio_port_write_32( u_int32_t reg, u_int32_t val ) { volatile u_int32_t *reg_loc; reg_loc = (u_int32_t *) ( ((u_int8_t *)(sc->sc_mmregs_base)) + reg); *reg_loc = val; #if F3BAR_TRACE printf( "\n ap_write_32: reg=%x val=%x \n", reg, val ); #endif } /* Not currently used. */ #if 0 /*-----------------------------------------------------------------------*/ static void audio_port_write_16( u_int32_t reg, u_int16_t val ) { volatile u_int16_t *reg_loc; reg_loc = (u_int16_t *) ( ((u_int8_t *)(sc->sc_mmregs_base)) + reg); *reg_loc = val; #if F3BAR_TRACE printf( "\n ap_write_16: reg=%x val=%x \n", reg, val ); #endif } #endif /*-----------------------------------------------------------------------*/ static void audio_port_write_8( u_int32_t reg, u_int8_t val ) { volatile u_int8_t *reg_loc; reg_loc = (u_int8_t *) ( ((u_int8_t *)(sc->sc_mmregs_base)) + reg); *reg_loc = val; #if F3BAR_TRACE printf( "\n ap_write_8: reg=%x val=%x \n", reg, val ); #endif } /*-----------------------------------------------------------------------*/ static u_int32_t audio_port_read_32( u_int32_t reg ) { volatile u_int32_t *reg_loc,val; reg_loc = (u_int32_t *) ( ((unsigned char *)(sc->sc_mmregs_base)) + reg); val = *reg_loc; #if F3BAR_TRACE printf( "\n ap_read_32: reg=%x val=%x \n", reg, val ); #endif return( val ); } /*-----------------------------------------------------------------------*/ static u_int8_t audio_port_read_8( u_int32_t reg ) { volatile u_int8_t *reg_loc,val; reg_loc = (u_int8_t *) ( ((unsigned char *)(sc->sc_mmregs_base)) + reg); val = *reg_loc; #if F3BAR_TRACE printf( "\n ap_read_8: reg=%x val=%x \n", reg, val ); #endif return( val ); } /*------- @@ Codec access routines. -------------------------------------*/ /*--- Spin awaiting last command to be shifted out serially. */ /*-----------------------------------------------------------------------*/ static void await_codec_ready( void ) { int timeout; u_int32_t status; status = 0; for (timeout=0; timeout<2000; timeout++) { status = audio_port_read_32(CODEC_CMD_REG); /* Read the CMD (not status) reg.*/ if( 0 == (status & CODEC_CMD_VALID) ) break; /* If CMD_VALID, last cmd still*/ } /* is transmitting.*/ if( status & CODEC_CMD_VALID ) device_printf( sc->sc_dev, "\n codec cmd not ready." ); } /* * Read the 32-bit F3BAR codec status register when it is valid. * This "status" is always one of the internal codec AC'97 registers. */ /*-----------------------------------------------------------------------*/ static u_int32_t get_codec_status( void ) { u_int32_t status; int i; DELAY(500); await_codec_ready(); for (i=0;i<300;i++) { status = audio_port_read_32( CODEC_STATUS_REG ); if( status & CODEC_STATUS_VALID ) { /* Data (reg value) has been received. */ return( status ); } } device_printf( sc->sc_dev, "\n codec underflow?" ); return( status ); } /* * Write the codec command register. Must spin waiting for * any current codec command register serial transfer to complete. */ /*-----------------------------------------------------------------------*/ static void set_codec_control( u_int32_t val ) { u_int32_t cmd_val; DELAY(500); await_codec_ready(); cmd_val = val & CODEC_CMD_MASK; audio_port_write_32( CODEC_CMD_REG, cmd_val ); DELAY(30); await_codec_ready(); } /* * Read a codec register ("codec_port") over the serial link, with the codec * register specified as a 0-origin index. Returns the 8-bit AC97 reg from * the codec (as 16 bits). */ /*-----------------------------------------------------------------------*/ static u_int16_t codec_direct_reg_read( int codec_port ) { u_int32_t cmd,val; cmd = 0; cmd = ((u_int32_t)codec_port) << 24; /* High cmd byte is register index.*/ cmd |= 0x80000000; /* High cmd byte bit set is codec*/ /* register read cmd.*/ do { set_codec_control( cmd ); /* Issue read command. */ val = get_codec_status(); /* Read 32-bit F3BAR codec status reg result. */ } while ( /* Repeat if result is for wrong reg (ugly serial proto)*/ (0x000000FF & ((u_int32_t)(codec_port)) ) != (0x000000FF & (val >> 24) ) ); return( (u_int16_t)val ); /* return AC97 codec reg value.*/ } /* Write value to codec register specified by index. */ /*-----------------------------------------------------------------------*/ static void codec_reg_write( u_int32_t reg_idx, u_int32_t val ) { u_int32_t cmd; cmd = 0; cmd = reg_idx << 24; cmd |= val; /* Hit bit of 0 means _write_ */ set_codec_control( cmd ); /* Write specified codec register. */ get_codec_status(); set_codec_control( 0x80000000 | cmd ); /* Read it back... */ get_codec_status(); } /* * Initialize codec to enable setting AD and DA rate (speeds), and set * default codec AD and DA rate (22050 Hz). */ /*-----------------------------------------------------------------------*/ static void set_codec_AD_rate( void ) { u_int16_t b2; /* Enable Variable Rate Audio (VRA).*/ b2 = codec_direct_reg_read( 0x2A ); b2 |= 0x01; /* VRA on (can now change speed).*/ codec_reg_write( 0x2A, b2 ); /* Set default, 16-bit uint.*/ codec_reg_write( 0x2C, 0x5622 ); /* 22050 Hz D/A rate.*/ codec_reg_write( 0x32, 0x5622 ); /* 22050 Hz A/D rate.*/ } /*---- @@ Polling and DMA routines. -------------------------------------*/ #if USE_TIMER_POLL /* * Poll to see if any active DMA has completed, and if so fake an interrupt * by calling the driver's interrupt routine. Polling for DMA completion is * done by examining the PCI memory-mapped "DMA PRD address" register for * each bus master. This register is updated to point to the "next PRD" * element upon completion of each DMA transfer. * * This routine is either called from "timeout()" or custom code in "hardclock()". */ /*-----------------------------------------------------------------------*/ static void do_audio_poll( void *foo ) { struct sc_chinfo *ch; u_int32_t cur_dma_adr; int i; /* printf( "\n\n *** do_audio_poll!!!\n\n" ); */ if( !geode_dma_poll ) goto end; for (i=0;i<3;i++) { /* See if any channel DMA has completed.*/ ch = &sc->sc_ch[i]; cur_dma_adr = audio_port_read_32( ch->ch_dma_prd_adr ); /* DMA PRD adr value.*/ if( -1 == ch->ch_last_prd_dma_adr ) { ch->ch_last_prd_dma_adr = cur_dma_adr; } else { if( ch->ch_last_prd_dma_adr != cur_dma_adr ) { geode_intr( sc ); /* If completed, go handle it.*/ } } } end:; #if HARDCLOCK_POLL #else tick_poll = timeout( do_audio_poll, (void *)0, 1 ); /* 1==Every tick. */ #endif } #endif /* * Start F3BAR audio PCI "DMA engines" (bus masters). Each channel has a * dedicated bus master. * * A bus master is controlled by 3 mem-mapped regs in the PCI audio function. * These are a PRD table address register, a command register, and a status register. * * The DMA status register (one for each bus master) is read-to-clear, so the first * time bit 0x01 (End of Page) is read, it is cleared. * *--- * * DMA Start. We always start at the 1st PRD descriptor, after which DMA * loops forever. ch_cur_dma_blk tracks the block being DMAed, so on a * completion trigger it points to the "finished" block. */ /*-----------------------------------------------------------------------*/ static void start_geode_dma( struct sc_chinfo *ch ) { u_int32_t prd_phy_adr; u_int8_t status; #if USE_TIMER_POLL #if DMA_TRACE /* printf( "\n !!start DMA, geode_dma_poll=%x\n", geode_dma_poll ); */ #endif #endif status = audio_port_read_8( ch->ch_dma_status ); /* DMA Status, Paranoid Read clears EOP flgs*/ #if USE_TIMER_POLL geode_dma_poll = 1; /* Start polling. */ #endif prd_phy_adr = (u_int32_t)vtophys( ch->ch_dtbl ); audio_port_write_32( ch->ch_dma_prd_adr, prd_phy_adr ); /* DMA PRD. Physical adr PRD tbl.*/ ch->ch_cur_dma_blk = 0; ch->ch_last_prd_dma_adr = prd_phy_adr; if( CH_OUTPUT == ch->ch_dir ) { audio_port_write_8( ch->ch_dma_cmd, 0x01 ); /* DMA CMD. Bus Master ON. Starts write DMA.*/ } else if( CH_INPUT == ch->ch_dir ) { audio_port_write_8( ch->ch_dma_cmd, 0x09 ); /* DMA CMD. Bus Master ON. Starts read DMA.*/ } ch->ch_run = 1; } /* * Stop a bus master; called on abort and at end of overall DMA operation * (that is, when there is no more data to be transfered). */ /*-----------------------------------------------------------------------*/ static void stop_geode_dma( struct sc_chinfo *ch ) { u_int8_t status,cmd; status = audio_port_read_8( ch->ch_dma_status );/* DMA Status, Paranoid Read clears EOP flgs.*/ cmd = audio_port_read_8( ch->ch_dma_cmd ); /* DMA CMD. */ cmd &= 0xFE; /* Clear enable bit. */ audio_port_write_8( ch->ch_dma_cmd, cmd ); /* DMA CMD. Write Clear enable bit. */ /* WORRY: can corrupt PCI state? */ #if USE_TIMER_POLL geode_dma_poll = 0; /* Stop polling. */ #endif ch->ch_run = 0; } /* *---------------------------------------------------------------- * @@@ Implementation of the AC97 Interface (PCM codec support). *---------------------------------------------------------------- */ /* Initialize LM4549 codec operations. */ #if 0 static u_int32_t geode_initcd( kobj_t obj, void *devinfo ) { return( 1 ); /* 1 codec. */ } #endif /*--- Read standard AC'97 specification codec register. */ /*-----------------------------------------------------------------------*/ static int geode_rdcd( kobj_t obj, void *devinfo, int regno ) { u_int16_t b2; #if AC97_TRACE printf( "\n[ac97]rdcd, regno=%x\n", regno ); #endif regno &= 0xFF; b2 = codec_direct_reg_read( regno ); return( b2 ); } /*--- Write standard AC'97 specification codec register. */ /*-----------------------------------------------------------------------*/ static int geode_wrcd( kobj_t obj, void *devinfo, int regno, u_int16_t data ) { #if AC97_TRACE printf( "\n[ac97]wrcd, regno=%x data=%x\n", regno, data ); #endif regno &= 0xFF; codec_reg_write( regno, data ); return( 0 ); } /* * This is where the driver specifies which routines comprise its * methods implementing the AC97 interface. This interface is simply * the means to read and write the (standard) codec registers. * * The AC97_DECLARE() macro wraps a DEFINE_CLASS() and is defined * in "/usr/src/sys/dev/sound/pcm/ac97.h". */ /*-----------------------------------------------------------------------*/ static kobj_method_t geode_ac97_methods[] = { /* KOBJMETHOD(ac97_init, geode_initcd), */ KOBJMETHOD(ac97_read, geode_rdcd), KOBJMETHOD(ac97_write, geode_wrcd), { 0, 0 } }; AC97_DECLARE(geode_ac97); /* *----------------------------------------------------------------------- * @@@ Geode Audio Initializataion routines (support attach function). * * The attach function is "geode_pci_attach()". It is called to activate * the driver, and in doing so uses the routines in this section. *----------------------------------------------------------------------- */ /*--- Verify the LM4549 codec's internal vendor ID. */ /*-----------------------------------------------------------------------*/ static int verify_codec_present( void ) { u_int16_t b2; int fail; fail = 0; b2 = codec_direct_reg_read( LM4549_VENDOR_ID1 ); if( NSC_ID1 != b2 ) fail++; b2 = codec_direct_reg_read( LM4549_VENDOR_ID2 ); if( NSC_ID2 != b2 ) fail++; if( fail ) return( 0 ); return( 1 ); } /*--- Reset an LM4549 codec, verify its there, and set defaults. */ /*-----------------------------------------------------------------------*/ static int reset_codec( void ) { u_int16_t b2; codec_reg_write( LM4549_RESET, 0 ); /* Reset.*/ b2 = codec_direct_reg_read( LM4549_RESET ); if( b2 != LM_RESET_VAL ) { device_printf( sc->sc_dev, "\n unexpected codec reset value=%x", b2 ); return( 0 ); } /* Just to be paranoid, PCM AC97 will do all this at mixer_init() call.*/ codec_reg_write( LM4549_MASTER_VOLUME, 0x0808 ); codec_reg_write( LM4549_BEEP_VOLUME, 0x0008 ); /* Enable PC beep.*/ return( 1 ); } /* * The following is undocumented magic required to get the VSA * (hypervisor software) to trigger an IRQ 5 on an audio SMI, * that is, we need to do this to get an interrupt. Even if not * using interrupts (polling) this routine needs to be called * for the bus engine hardware to activate. * * The "irq = 5" below must correspond to the * "sc->sc_irq_res = bus_alloc_resource( ... 5,5,1,...)" * in "geode_pci_attach()". */ /*-----------------------------------------------------------------------*/ #if 1 static void enable_audio_irq( void ) { u_int16_t irq; irq = 5; outw( 0xAC1C, 0xFC53 ); outw( 0xAC1C, 0x108 ); outw( 0xAC1E, irq ); outl( 0x800090D0, PCI_CONFIG_ADR ); outl( PCI_CONFIG_DATA, ((irq<<16) | 0xA00A) ); } #endif /* * This routine is a delay after "enable_audio_irq()" until the * codec registers "work". * * This is important since the ac97 PCM code uses the same technique to * determine what channels exist, so we want it to find things... * That is, it writes 0x3F into AC'97 registers, and sees if it can * read 0x3F back. If the register works, it restores the original * register value. * * If this isn't done, sometimes the pcm driver will not find any channels * (the DELAY() settings can mask this). */ void await_operational_codec_channel_regs( void ) { int i; u_int16_t b2,b2_new; for (i=0;i<400;i++) { DELAY(250); b2 = codec_direct_reg_read( 0x2 ); codec_reg_write( 0x2, 0x3F ); b2_new = codec_direct_reg_read( 0x2 ); /* printf( "\n b2=%x b2_new=%x\n", b2, b2_new ); */ if( b2 != b2_new ) { codec_reg_write( 0x2, b2 ); break; } } } /* * The following routine (init hardware to DMA a page address) is called for * every page in bus_dmamem_load(), which is called in geode_init() below. */ /*-----------------------------------------------------------------------*/ static void geode_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { return; } /* * Called from "geode_pci_attach()", the following is the first routine to hit * the hardware, reset, verify it's there, set hardware defaults, and * allocate descriptor table memory that can be accessed by the controller. * * A "TAG" can be considered a handle to requirements for DMAable memory. * A "MAP" is a handle to memory allocated to the requirements of a specific * TAG. A MAP must be "loaded" before the memory can be accessed. (You can * think of a MAP as similar to a subset of page table entries). */ /*-----------------------------------------------------------------------*/ static int geode_init( struct sc_info *sc ) { int byte_size; audio_port_write_32( CODEC_CMD_REG, 0 ); /* HW CMD: reset codec. */ DELAY(100); if( !verify_codec_present() ) return( -1 ); if( !reset_codec() ) return( -1 ); /* Get a "MAP" for hardware accessible memory as specified by a valid "TAG". * This MAP describes memory where the PRD table will go. This table contains * PRD descriptors. Each descriptor contains 2 32-bit words, one of which is * a physical h/w mem addresses of a buffer to be DMAed. * The PRD table is processed autonomously by the geode audio PCI hardware. * The allocated MAP needs to be "loaded" before the memory can actually be used. */ if (bus_dmamem_alloc( sc->sc_parent_dmat, /* DMA mem "tag" (rqmts) */ (void **)&sc->sc_dtbl, /* Virtual address of allocated mem. */ BUS_DMA_NOWAIT, /* get it now. */ &sc->sc_prd_dt_map) /* Result MAP to use to reference this mem.*/ ) return( ENOSPC ); byte_size = (sizeof(u_int32_t) * 2) /* Byte size of 1 PRD descritpor. */ * (NUM_PRD_ELEMS * 3); /* Allocate a PRD table for all */ /* 3 channels. */ /* Actually "activate" physical memory buffer for device access via the TAG/MAP. */ /* After this call, sc_dtbl can be used. */ if (bus_dmamap_load( sc->sc_parent_dmat, /* DMA mem TAG (rqmts) */ sc->sc_prd_dt_map, /* DMA mem MAP of mem we want. */ sc->sc_dtbl, /* Buffer virtual address. */ byte_size, /* Buffer len. */ geode_setmap, NULL, /* per buf seg callback/arg. */ 0) /* flags. */ ) { bus_dmamem_free( sc->sc_parent_dmat, (void **)&sc->sc_dtbl, sc->sc_prd_dt_map ); return( ENOSPC ); } /* printf( "\n\n *** dtbl=%x len=%d. padr=%x\n\n", (int)sc->sc_dtbl, byte_size, vtophys( sc->sc_dtbl ) ); */ set_codec_AD_rate(); enable_audio_irq(); DELAY(100); await_operational_codec_channel_regs(); /* enable_audio_irq() must require time... */ return( 0 ); /* OK */ } /* Called from geode_detach(), restore "normal" beep. */ /*-----------------------------------------------------------------------*/ static int geode_uninit( struct sc_info *sc ) { codec_reg_write( LM4549_MASTER_VOLUME, 0x0808 ); codec_reg_write( LM4549_BEEP_VOLUME, 0x0008 ); /* Enable PC beep. */ return( 0 ); } /* * This routine is called when a "resume" occurs after a "suspend", * in which case it reactivates the native audio hardware (codec) * in preparation for resumption of DMA that was active (and aborted) * when the suspend occured. All memory data structures should still * be valid... this code acts on the assumption that all the I/O * hardware should be reinitialized to the last active state. * This code is called by driver method "geode_pci_resume()" to * set the initial codec state. */ /*-----------------------------------------------------------------------*/ static int geode_reinit( struct sc_info *sc ) { audio_port_write_32( CODEC_CMD_REG, 0 ); /* HW CMD: reset codec. */ DELAY(100); if( !verify_codec_present() ) return( -1 ); if( !reset_codec() ) return( -1 ); set_codec_AD_rate(); enable_audio_irq(); DELAY(100); await_operational_codec_channel_regs(); /* enable_audio_irq() must require time... */ return( 0 ); /* OK */ } /* *----------------------------------------------------------------------- * @@@ Implementation of the Channel Interfaces. *----------------------------------------------------------------------- */ /*-----------------------------------------------------------------------*\ * The Channel Interface Methods. *------------------------------ * * The routines in this section are methods, that is, all have a pointer to a * kernel kobj structure as their first argument. * * Communication between these methods and the PCM driver is largely via * the snd_dbuf structure associated with a specific channel. Our methods must * use access methods defined by the PCM "snd" class to access data from the * channel's "snd_dbuf". * * The "geode_pchan_init()" method returns our private channel info structure, * (struct sc_chinfo *). This "opaque pointer" is then passed back to us, * providing data context, when the other channel interface methods are called. * * The first set of methods here are used by all 3 channels - stereo play, * stereo recieve, and mono-play. Strictly speaking, each of the 3 instantiations * of the channel methods could have its own set of implementations, but * in practice this seems to lead to a lot of duplicate code. *----------------------------------------------------------------------*/ /*---- @@ Instantiation of the Play Channel. ---------------------------*/ /* * Called by PCM when a "pcm_addchan()" call is made in "geode_attach()". * The "devinfo" arg is the pointer passed in by "pcm_addchan()" as its last arg; * the "dir" arg is PCMDIR_PLAY or PCMDIR_REC. This routine is called * for all channels. * * This routine is called after the PCM driver has allocated a pcm_channel * structure. It is our responsibility to allocate the data buffer that is to * be used to service this channel, in conjunction with the associated snd_dbuf * control structure. This is done by calling "sndbuf_alloc()". */ /*-----------------------------------------------------------------------*/ static void * geode_pchan_init( kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir ) { struct sc_info *sc = devinfo; struct sc_chinfo *ch; int num; #if OP_TRACE printf( "\n[pchan]init\n" ); #endif num = sc->sc_ch_num++; /* For each channel instantiation,*/ ch = &sc->sc_ch[num]; /* point to our private channel data.*/ ch->ch_sc = sc; /* Save backpointer to our private driver data,*/ ch->ch_snd_dbuf = b; /* attach control buffer struct,*/ ch->ch_channel = c; /* and ptr to PCM channel struct.*/ ch->ch_num = num; ch->ch_run = 0; /* Get start of our PRD array.*/ ch->ch_dtbl = (u_int32_t *)sc->sc_dtbl + (num * (NUM_PRD_ELEMS * 2 * sizeof(u_int32_t)) ); ch->ch_num_blks = 2; /* Default is always dbl-buffering,*/ ch->ch_blk_size = sc->sc_buf_size / 2; /* user can change latter. sc_buf_size */ /* from pcm_get_buffersize() in "_attach()".*/ ch->ch_cur_dma_blk = 0; /* Current DMA target.*/ ch->ch_fmt = AFMT_U16_LE; /* Set default format and*/ ch->ch_speed = DSP_DEFAULT_SPEED; /* speed.*/ /* Allocate a data buffer for DMA using associated DMA tag.*/ if ( sndbuf_alloc( ch->ch_snd_dbuf, /* For this control buffer*/ sc->sc_parent_dmat, /* using this TAG (mem rqmts).*/ sc->sc_buf_size ) == -1) /* Allocate this size buffer.*/ return( NULL ); prd_filldtbl( ch ); /* Write default DMA descriptors.*/ ch->ch_last_prd_dma_adr = -1; /* No DMA adr yet.*/ /* Channel num corresponds directly to fixed-function bus master (DMA engine). * Save indices of correct PCI memory-mapped registers to control the channel's * bus master. */ switch( num ) { case 0: /* Stero, 16-bit*2, output */ ch->ch_dir = CH_OUTPUT; ch->ch_dma_cmd = DMA_PLAY_CMD; /* Offset of DMA_CMD audio reg. */ ch->ch_dma_status = DMA_PLAY_STATUS; /* " DMA STATUS reg. */ ch->ch_dma_prd_adr = DMA_PLAY_PRD_ADR; /* " address of DMA PRD table. */ break; case 1: /* Stero, 16-bit*2, input. */ ch->ch_dir = CH_INPUT; ch->ch_dma_cmd = DMA_RECORD_CMD; /* Offset of DMA_CMD audio reg. */ ch->ch_dma_status = DMA_RECORD_STATUS; /* " DMA STATUS reg. */ ch->ch_dma_prd_adr = DMA_RECORD_PRD_ADR; /* " address of DMA PRD table. */ break; case 2: /* Mono, 16-bit, output. */ ch->ch_dir = CH_OUTPUT; ch->ch_dma_cmd = DMA_PLAY_MONO_CMD; /* Offset of DMA_CMD audio reg. */ ch->ch_dma_status = DMA_PLAY_MONO_STATUS; /* " DMA STATUS reg. */ ch->ch_dma_prd_adr = DMA_PLAY_MONO_PRD_ADR; /* " address of DMA PRD table. */ break; } return( ch ); } /* * Free up control buffer and DMA buffer - called after the channel * is stopped. */ /*-----------------------------------------------------------------------*/ static int geode_pchan_free( kobj_t obj, void *data ) { struct sc_chinfo *ch = data; /* Our channel */ #if OP_TRACE printf( "\n[pchan]free\n" ); #endif sndbuf_free( ch->ch_snd_dbuf ); return( 0 ); /* OK */ } /* * Construct a 3-element PRD table to control bus-engine DMA. The first 2 PRD entries each * point to countiguous data buffers, while the last PRD entry points back to the first * PRD entry. The bus-engine thus runs in a loop. The EOP flag in the 2 data buffer * descriptors causes an interrupt when the corresponding DMA is complete. The * two buffers provide a double-buffering scheme in conjunction with the PCM driver. */ /*-----------------------------------------------------------------------*/ static void prd_filldtbl( struct sc_chinfo *ch ) { u_int32_t base; base = vtophys( sndbuf_getbuf( ch->ch_snd_dbuf )); /* Get memory physical address */ /* of DMA buffer. */ ch->ch_num_blks = sndbuf_getsize( ch->ch_snd_dbuf ) / ch->ch_blk_size; /* Num blks in DMA buf.*/ if (ch->ch_num_blks != 2 ) { ch->ch_num_blks = 2; ch->ch_blk_size = sndbuf_getsize( ch->ch_snd_dbuf ) / ch->ch_num_blks; } /* printf( "\n prd_tbl: ch_blk_size=%d.\n", ch->ch_blk_size );*/ ch->ch_dtbl[0] = base + (0 * ch->ch_blk_size); ch->ch_dtbl[1] = ( 0x40000000 | ch->ch_blk_size); /* EOP (DMA completion interrupt)*/ ch->ch_dtbl[2] = base + (1 * ch->ch_blk_size); ch->ch_dtbl[3] = ( 0x40000000 | ch->ch_blk_size); /* EOP (DMA completion interrupt)*/ /* * Make the last entry "loop" back to the first descriptor. * No interrupt or DMA buffer is associated with this entry. */ ch->ch_dtbl[4] = vtophys( ch->ch_dtbl ); ch->ch_dtbl[5] = 0x20000000; /* JMP (loop back to top of PRD descriptor table.)*/ /* NOTE: EOT and JMP bit cannot both be set,*/ /* if JMP, no EOT required.*/ } /* * The "channel_setblocksize()" method returns the number of bytes expected * to be processed by a single DMA operation, that is, bytes processed * between DMA completion interrupts. This value will evenly divide * the total length of the buffer associated with the channel. * * This routine is a filter for the blocksize, which is passed in by the * PCM driver. If the blocksize selected and returned by this routine is * not the same as that passed in by PCM, this routine is responsible * for calling "sndbuf_resize()" to keep the PCM driver consistent. * * This routine should return the closest possible block size less than * or equal to the input blocksize. This method will not be called when * the channel is running. */ /*-----------------------------------------------------------------------*/ static int geode_pchan_setblocksize( kobj_t obj, void *data, u_int32_t blocksize ) { struct sc_chinfo *ch = data; #if OP_TRACE printf( "\n[pchan]setblocksize, blocksize=%d.\n", blocksize ); #endif /* printf( "\n setblocksize, blocksize=%d.\n", blocksize ); */ ch->ch_blk_size = blocksize; prd_filldtbl( ch ); return( blocksize ); } /* * The PCM driver, when it has output data to play or requires input * data, uses the channel buffers that have already been setup. * * To start playing, the PCM driver will fill the entire buffer * associated with the channel (that is, all play "blocks"), and * then will call the "channel_trigger" method with a "go" code of * PCMTRIG_START. The trigger method should start DMA operation, * with an interrupt after "ch_block_size" bytes have been DMAed * (that is, after one block in the buffer has been DMAed). The * first block to be DMAed (and interrupt at completion) will * thus be block 0. * * On PCMTRIG_START, the DMA transfer to be activated is specified * as block "sndbuf_getbuf()" with size "sndbuf_getsize()". */ /*-----------------------------------------------------------------------*/ static int geode_pchan_trigger( kobj_t obj, void *data, int go ) { struct sc_chinfo *ch = data; /* struct sc_info *sc = ch->ch_sc; */ #if OP_TRACE printf( "\n[pchan]trigger, go=%x\n", go ); #endif switch(go) { case PCMTRIG_START: /* start at beginning of buffer */ start_geode_dma( ch ); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: /* stop operation */ stop_geode_dma( ch ); break; default: break; } return( 0 ); /* OK */ } /* * Get an OFFSET (not a pointer despite the name) to the next channel * block that the PCM driver is to fill (after DMA completion on play * channels), or copy (after DMA completion on record channels). * * The "channel_getptr()" method is called by the PCM driver's * "chn_intr()" routine, which is called by this driver's interrupt * routine, "geode_intr()". The "channel_getptr()" routine should * return the offset of the data block (within the overall channel * buffer) that the PCM driver is to process _next_. For a * double-bufferd channel, this routine first returns the offset * of buffer 0, then 1, then 0, etc. When a play channel is started, * all blocks are filled before the first DMA is started. */ /*-----------------------------------------------------------------------*/ static int geode_pchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch; int pos; /* struct sc_info *sc = ch->ch_sc; */ #if OP_TRACE printf( "\n[pchan]getptr\n" ); #endif ch = data; pos = ch->ch_cur_dma_blk * ch->ch_blk_size; return( pos ); /* return current byte offset of channel.*/ } /*--- Return the address of the play channel capability structure. */ /*----------------------------------------------------------------------*/ static struct pcmchan_caps * geode_pchan_getcaps( kobj_t obj, void *data ) { #if OP_TRACE printf( "\n[pchan]getcaps\n" ); #endif return( &geode_playcaps ); } /* * Set the channel format (mono/stereo, 8-bit, 16-bit, etc.) from the * channel getcaps list. This method is not called if the channel running. The * Geode audio channels are "fixed-format". * $$$ Shouldn't this routine check what it's being asked to do and return * an error code if it doens't match the fixed setting? */ /*-----------------------------------------------------------------------*/ static int geode_pchan_setformat( kobj_t obj, void *data, u_int32_t format ) { #if OP_TRACE printf( "\n[pchan]setformat\n" ); #endif /* return 0 - format _must_ be settable if in list */ return( 0 ); } /* * Set the codec speed (rate) for a given channel. This method determines * which register is to be written (ADC_RATE or DAC_RATE) and then * calls the PCM driver's ac97_setrate() routine to do the work. */ /*-----------------------------------------------------------------------*/ static int geode_pchan_setspeed( kobj_t obj, void *data, u_int32_t speed ) { struct sc_chinfo *ch; struct sc_info *sc; int reg; #if OP_TRACE printf( "\n[pchan]setspeed\n" ); #endif ch = data; sc = ch->ch_sc; reg = LM4549_PCM_FRONT_DAC_RATE; if( CH_INPUT == ch->ch_dir ) { reg = LM4549_PCM_ADC_RATE; } else if( CH_OUTPUT == ch->ch_dir ) { reg = LM4549_PCM_FRONT_DAC_RATE; } /* ac97_create() writes the codec speed using the specified reg. */ ch->ch_speed = (u_int)ac97_setrate( sc->sc_ac97_codec, reg, speed ); return( ch->ch_speed ); } /*-----------------------------------------------------------------------*\ * Stereo Play Channel Interface * * This defines the routines (methods) implementing the CHANNEL interface for * the stereo "play" channel. * * The CHANNEL_DECLARE() macro is a wrapper for DECLARE_CLASS() and is defined * in "/usr/src/sys/dev/sound/pcm/channel.h". */ /*-----------------------------------------------------------------------*/ static kobj_method_t geode_pchan_methods[] = { KOBJMETHOD(channel_init, geode_pchan_init), KOBJMETHOD(channel_free, geode_pchan_free), KOBJMETHOD(channel_setformat, geode_pchan_setformat), KOBJMETHOD(channel_setspeed, geode_pchan_setspeed), KOBJMETHOD(channel_setblocksize, geode_pchan_setblocksize), KOBJMETHOD(channel_trigger, geode_pchan_trigger), KOBJMETHOD(channel_getptr, geode_pchan_getptr), KOBJMETHOD(channel_getcaps, geode_pchan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(geode_pchan); /*-----------------------------------------------------------------------*\ * @@ Instantiation of the Stereo Receive Channel. * * The following implements the routines (methods) of the CHANNEL * interface for the stereo "record" channel. The only difference * between the record and play channels is the "channel_getcaps()" * method that returns a pointer to a different channel capability * structure. */ /*-----------------------------------------------------------------------*/ /*--- Return the address of the receive channel capability structure. */ static struct pcmchan_caps * geode_rcv_chan_getcaps( kobj_t obj, void *data ) { #if OP_TRACE printf( "\n[rchan]getcaps\n" ); #endif return( &geode_reccaps ); } static kobj_method_t geode_rchan_methods[] = { KOBJMETHOD(channel_init, geode_pchan_init), KOBJMETHOD(channel_free, geode_pchan_free), KOBJMETHOD(channel_setformat, geode_pchan_setformat), KOBJMETHOD(channel_setspeed, geode_pchan_setspeed), KOBJMETHOD(channel_setblocksize, geode_pchan_setblocksize), KOBJMETHOD(channel_trigger, geode_pchan_trigger), KOBJMETHOD(channel_getptr, geode_pchan_getptr), KOBJMETHOD(channel_getcaps, geode_rcv_chan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(geode_rchan); /*----------------------------------------------------------------*\ * @@ Instantiation of the Mono Receive Channel. * * The routines (methods) implementing the CHANNEL interface for * the mono "record" channel. The only difference between this and * the previous interface implementations is the channel capability * structure. Of course, when the "channel_init()" method is called, * the differing fixed-feature channel-hardware bus-engines are * logically attached to each respective channel. \*----------------------------------------------------------------*/ static struct pcmchan_caps * geode_rcv_mono_chan_getcaps( kobj_t obj, void *data ) { #if OP_TRACE printf( "\n[rchan]getcaps\n" ); #endif return( &geode_mono_reccaps ); } static kobj_method_t geode_rmono_chan_methods[] = { KOBJMETHOD(channel_init, geode_pchan_init), KOBJMETHOD(channel_free, geode_pchan_free), KOBJMETHOD(channel_setformat, geode_pchan_setformat), KOBJMETHOD(channel_setspeed, geode_pchan_setspeed), KOBJMETHOD(channel_setblocksize, geode_pchan_setblocksize), KOBJMETHOD(channel_trigger, geode_pchan_trigger), KOBJMETHOD(channel_getptr, geode_pchan_getptr), KOBJMETHOD(channel_getcaps, geode_rcv_mono_chan_getcaps), { 0, 0 } }; CHANNEL_DECLARE(geode_rmono_chan); /* Instantiates "geode_rmono_chan_class" CHANNEL class,*/ /* using "geode_rmono_chan_methods".*/ /*-----------------------------------------------------------------------*\ * @@@ The interrupt handler. *--------------------------- * * Interrupt notification is by device (PCU audio function), not * channel, so the interrupt routine needs to figure out what channel * caused the interrupt. When the interrupt routine runs, multiple * channels may be ready for additional work. * * This routine is responsible for updating the channel's "current" * DMA buffer indicator (the "ch_cur_dma_blk" index). The PCM driver * determines which DMA buffer it should read or write by calling * the "channel_getptr()" method when the interrupt routine calls * "chn_intr()". Thus, buffer bookkeeping should happen after the * "chn_intr()" call in the following interrupt code. * * The PRD DMA descriptor table that corresponds to the data buffers * never needs to change (the DMA engine just keeps processing it * "in a circle"). The interrupt routine uses the change in the * "processed PRD address" contained in the memory-mapped audio * function registers of the appropriate bus-master to detect the * end of a "DMA" operation. * * The EOP bit needs to be read after DMA completion, or the bus * master (and associated transfer) will halted on "double EOP * write". The EOP apparently is not to be trusted due to a hardware * bug (it cannot be used to detect end of the DMA for the respective * bus-master). */ /*-----------------------------------------------------------------------*/ static void geode_intr( void *p ) { struct sc_info *sc; struct sc_chinfo *ch; u_int32_t cur_dma_adr; u_int8_t status; int i; sc = (struct sc_info *)p; /* printf( "\n GINT!\n" ); */ for (i=0; i<3; i++) { ch = &sc->sc_ch[i]; if( ch->ch_run ) { /* If DMA is running on channel...*/ cur_dma_adr = audio_port_read_32( ch->ch_dma_prd_adr ); /* Current DMA PRD.*/ if( cur_dma_adr != ch->ch_last_prd_dma_adr ) { ch->ch_last_prd_dma_adr = cur_dma_adr; status = audio_port_read_8( ch->ch_dma_status ); /* Clear EOP.*/ chn_intr( ch->ch_channel ); /* PCM generic int routine.*/ /* Interrupt has been processed, update pointer to next DMA buffer.*/ ch->ch_cur_dma_blk++; if (ch->ch_cur_dma_blk >= ch->ch_num_blks ) { ch->ch_cur_dma_blk = 0; } } /* Endif new DMA PRD adr (DMA blk completion).*/ } /* Endif channel running DMA.*/ } /* End for all 3 channels.*/ } /* *---------------------------------------------------------------------- * @@@ Implementation of the Device Interface. *-------------------------------------------- * * These routines are standard routines required of any FreeBSD * driver. They implement the methods of the "device interface", * see the structure "geode_methods[]" below. * * The "device_probe()" method in all drivers with the same parent * bus (driver) is called by the parent driver. The "device_probe()" * methods return a "vote" on their ability to handle the device. * A vote (return) of 0 means the driver should unequivocally get * the device, an ENXIO means the driver cannot handle the device, * and for negative numbers, if no driver votes 0, the driver with * the return closest to 0 gets the device. * * The parent (bus) driver calls the "device_attach()" method of * the driver that gets the device. The "device_attach()" is * responsible for driver and device initialization and preparing * for normal operation on the device. * * The "device_suspend()" method is called when the system is * entering a low-power suspend state, The system is to be halted * with memory contents maintained. The "device_resume()" method * is called later to reinitialize the device and resume operations * that were in progress when the previous suspend state was entered. *---------------------------------------------------------------------- */ /* * Determine if the Geode PCI audio device this driver handles is present. * * See the datasheet documentation for the Cx5530 PCI Configuration * space registers. 0x1078 is the National Semiconductor PCI Vendor ID. * * 0x103 identifies Function 3 PCI header registers for the XpressAudio subsystem. * 0x104 identifies Function 4 (video subsystem). * * Thus, 0x1031078 identifies the Geode audio vendor/func (our device). */ /*-----------------------------------------------------------------------*/ static int geode_pci_probe( device_t dev ) { #if OP_TRACE printf( "\n\n**** geode Probe!\n\n" ); #endif switch( pci_get_devid( dev ) ) { case 0x1031078: device_set_desc( dev, "NatSemi CS5530A (Geode)" ); return( 0 ); /* Yes, give it to us!*/ default: return( ENXIO ); } } #if CRASH_ON_ATTACH /* * This routine can be used to enter DDB, the kernel debugger. Be sure that * the kernel has been compiled with "options DDB". */ /*---------------*/ void crash( void ) { volatile int i,j,k; i = 0; j = 0; k = 1; k = i/j; } #endif /* * The driver's attach function is called if this driver was the * winner of all device probes for a particular device. * * A private sc_info structure is allocated to manage the device * (these structures are called a "softc" (software context) in * FreeBSD terminology), resources are set up to do direct hardware * access, and the interrupt routine is enabled. A call is made to * instantiate the AC97 interface used by the parent PCM driver to * interact with the codec (mixer). Calls are made to instantiate * a PCM Channel interface for each supported audio channel (these * interact with the PCI PCM DMA engine hardware). * * FreeBSD uses virtualizaton functions to access I/O related * hardware resources. Hardware such as an I/O port or memory-mapped * register is specified via a "virtual resource" consisting of a * (type, index) pair. * * PCI function header registers are accessed by index number. PCI * function headers have a standard format with the Command register * at index 2 and the Status register at index 3. * * The device structure passed in is known the "pcm" device, and * its parent device is "pci" (usually pci0). */ /*-----------------------------------------------------------------------*/ static int geode_pci_attach( device_t dev ) { u_int32_t data; int nplay; /* Number of play (output) DMA channels.*/ int nrec; /* Number of record channels.*/ char status[ SND_STATUSLEN ]; #if !USE_TIMER_POLL int err; #endif /* device_printf( dev, "\n geode_pci_attach()\n" );*/ #if OP_TRACE printf( "\n\n**** geode Attach!\n\n" ); #endif #if USE_TIMER_POLL geode_dma_poll = 0; #endif #if USE_TIMER_POLL #if HARDCLOCK_POLL geode_audio_poll = do_audio_poll; #else timeout( do_audio_poll, (void *)0, 1 ); /* Every tick.*/ #endif #endif /* Allocate and zero this device's sc_info (private softc).*/ if((sc = malloc( sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO)) == NULL ) { device_printf( dev, "cannot allocate softc\n" ); return( ENXIO ); } sc->sc_ac97_codec = 0; /* Should be zero, but...*/ sc->sc_reg = 0; sc->sc_ih = 0; sc->sc_irq_res = 0; sc->sc_parent_dmat = 0; sc->sc_lock = 0; sc->sc_ch_num = 0; /*-- Lock device, store dev backpointer and PCI hdr ID.*/ sc->sc_lock = snd_mtxcreate( device_get_nameunit(dev), "geode sound softc" ); sc->sc_dev = dev; sc->sc_type = pci_get_devid( dev ); /* * Enable the audio device to respond to memory accesses on the PCI bus * by setting the enable bit in the device PCI header PCIR_COMMAND register. * (for the function hdr cmd reg, see datasheet p.146/192). * The "dev" device is already setup for proper PCI header access by * "pcivar.h" functions ("pci_*()"). */ data = pci_read_config( dev, PCIR_COMMAND, 2 ); /* 2-byte reg*/ data |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); /* Enable PCI ops.*/ pci_write_config( dev, PCIR_COMMAND, data, 2 ); /* * Enable access to the PCI audio BAR registers, using BAR at PCI header idx 0x10. * The Bus Address Register (BAR) for F3 (the audio dev)) contains the memory * address of the audio function's memory-mapped registers in the PCI audio * device. The parent pci (bus) driver knows how to access the PCI header. */ #if GEODE_DEBUG_INIT data = pci_read_config( dev, GEODE_PCI_HDR_BAR, 4 ); /* 4-byte reg.*/ printf( "\n---> Geode mem regs at %x\n", data ); dmp_audio_pci_hdr( dev ); #endif /* * Map PCI memory so F3BAR memory-mapped registers can be accessed. Registers * in PCI function hardware can be either I/O mapped or memory-mapped. The * F3 Audio function uses memory-mapped registers. */ sc->sc_regid = PCIR_MAPS; sc->sc_regtype = SYS_RES_MEMORY; /* Not SYS_RES_IOPORT, enable access to mem in dev.*/ sc->sc_reg = bus_alloc_resource( dev, sc->sc_regtype, &sc->sc_regid, 0, ~0, 1, /* start, end, count.*/ RF_ACTIVE ); /* Activate rsrc w/o rman call.*/ if (!sc->sc_reg ) { device_printf( dev, "unable to map BAR reg\n" ); goto bad; } sc->sc_mmregs_base = rman_get_virtual( sc->sc_reg ); /* Get virtual address of F3BAR memory.*/ #if GEODE_DEBUG_INIT printf( "\n VIRTUAL adr of memmapped regs=%x\n", (int)sc->sc_mmregs_base ); dmp_audio_mem_regs( (char *)sc->sc_mmregs_base ); #endif /*------------------- Setup interrupt ------------------*/ #if !USE_TIMER_POLL /*--- Setup interrupt handler.*/ #if 0 printf( "\n dev name =<%s>\n", device_get_nameunit(dev) ); printf( "\n parent dev name =<%s>\n", device_get_nameunit( device_get_parent(dev) ) ); #endif #if CRASH_ON_ATTACH crash(); #endif /* Allocate an IRQ 5 interrupt resource so we can hang our handler off the IRQ 5 list.*/ sc->sc_irqid = 0; sc->sc_irq_res = bus_alloc_resource( device_get_parent(dev), SYS_RES_IRQ, &(sc->sc_irqid), 5, 5, 1, (RF_ACTIVE|RF_SHAREABLE) ); if (!sc->sc_irq_res ) { device_printf( dev, "unable to allocate interrupt\n" ); goto bad; } /* Put our "geode_intr()" interrupt handler on the chain of IRQ 5 handlers. * The "snd_setup_intr()" routine is a wrapper for "bus_setup_intr()". */ err = snd_setup_intr( dev, sc->sc_irq_res, INTR_MPSAFE, geode_intr, sc, &sc->sc_ih ); if( err ) { device_printf( dev, "unable to setup interrupt: %d.\n", err ); goto bad; } #endif /* * The following sets the "device buffersize" and stores it in "sc_buf_size". * The device buffersize is the overall buffer size that the PCM driver * creates when any channel associated with the device "dev" is created. All * other DMA buffers that this driver works with are calculated based on sc_buf_size. * * The "pcm_getbuffersize()" arguments are (dev, >). * The actual size is set to "default", unless an IVAR resource named * "buffersize" exists for the "dev" (the FreeBSD driver system manages * a "space" of dynamic variables named by "index" integers, these are * sometimes called IVARS). */ sc->sc_buf_size = pcm_getbuffersize(dev, 4096, GEODE_DEFAULT_BUFSZ, GEODE_MAX_BUFSZ); /* printf( "\n sc_buf_size=%d.\n", sc->sc_buf_size ); */ /* * Setup to allocate DMAable memory. A "tag" is a handle to memory that meets DMA * hardware restrictions. Tags can inherit restrictions from parent tags. * Arguments are confusing. * low-addr - for bus_dmamem_alloc, allocate below this address. * low-addr/highadr - for bus_dmamap_create, * pages below low-addr can be mapped, and pages above highadr * can be mapped. If a filter func is present, pages between * low and high adr might be mappable, if the filter func returns 1. */ if ( bus_dma_tag_create( NULL, /* Parent. */ 8, /* Alignment in bytes (thus, 64-bit long)*/ 64 * 1024, /* Span bndary. Can't DMA across 64K adr*/ /* low/high addrs that CANT be accessed: */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr (dont alloc above this)*/ BUS_SPACE_MAXADDR, /* highaddr(cant access above this)*/ NULL, NULL, /* filter, filterarg (per page Y/N)*/ sc->sc_buf_size, /* Maxsize in single alloc.*/ 1, 0x0000FFFF, /* Num scat/gath segs, Max such seg size.*/ 0, /* flags*/ &sc->sc_parent_dmat ) /* Returned TAG for DMA mem ops.*/ != 0 ) { device_printf( dev, "unable to create dma tag\n" ); goto bad; } /* Do detailed hardware initialization. Among other things, this * allocates PRD (DMA descriptor) buffers for all channels. */ if (geode_init( sc ) == -1 ) { device_printf( dev, "unable to init audio." ); goto bad; } /*-------- Now bring up the glue to the generic PCM audio driver. -------*/ /*--- Instantiate a PCM AC97 interface for standard mixer operations.*/ sc->sc_ac97_codec = AC97_CREATE( dev, sc, geode_ac97 ); if (NULL == sc->sc_ac97_codec ) goto bad; /* Use the AC97 interface to do codec (mixer) initialization. The PCM "mixer_init()" * routine uses our AC97 interface routines to init the LM4549 codec. */ mixer_init( dev, ac97_getmixerclass(), sc->sc_ac97_codec ); /* Calls geode_initcd()*/ nplay = 1; /* Number of channels: */ nrec = 2; /* * Finally, link the device, our softc, and the number of channels we will * allocate all together with "pcm_register()". The 2nd argument is the * "opaque ptr" passed back to our channel methods (our own softc). */ if( pcm_register( dev, sc, nplay, nrec ) ) goto bad; /* * Now allocate all the PCM channels we will use and attach them to "dev". * The following 3 calls to "pcm_addchan()" result in 3 "geode_pchan_init()" * calls in the same order, corresponding to the PLAY, REC, and MONO REC channels. * The correct instantiated channel interface and our softc are connected to * each channel created by PCM. */ pcm_addchan( dev, PCMDIR_PLAY, &geode_pchan_class, sc ); /* Calls geode_pchan_init()*/ pcm_addchan( dev, PCMDIR_REC, &geode_rchan_class, sc ); /* Calls geode_pchan_init()*/ pcm_addchan( dev, PCMDIR_REC, &geode_rmono_chan_class, sc ); /* Calls geode_pchan_init()*/ /*---------------------------------------------------------------- * Update sndstat driver text. * * The following snprintf() supports the "dev/sound/pcm/sndstat.c" * driver. This is a small non-loadable driver for a virtual device * that simply contains a text buffer describing the operational * audio hardware. The "snprintf()" adds formatted text to this * buffer. The user can display the buffer via ">cat /dev/sndstat". *---------------------------------------------------------------- */ #if USE_TIMER_POLL snprintf( status, SND_STATUSLEN, " at memory 0x%lx ", rman_get_start(sc->sc_reg) ); #else snprintf( status, SND_STATUSLEN, " at memory 0x%lx irq %ld", rman_get_start(sc->sc_reg), rman_get_start(sc->sc_irq_res) ); #endif pcm_setstatus( dev, status ); #if 0 dmp_codec_regs(); #endif return( 0 ); bad:; if( sc->sc_ac97_codec ) ac97_destroy( sc->sc_ac97_codec ); geode_free_all( dev, sc ); return( ENXIO ); } /* * Shutdown and stop the driver, release all resources. This is called * when a loadable version of the driver is unloaded (kldunload). */ /*-----------------------------------------------------------------------*/ static int geode_pci_detach( device_t dev ) { struct sc_info *sc; int err; /*--- Tell PCM we're history.*/ err = pcm_unregister( dev ); /* Call mix_uninit() */ if( err ) { return( err ); } sc = pcm_getdevinfo( dev ); /*--- Get our softc and,*/ geode_uninit( sc ); /* shutdown hardware (reset to just PC beep).*/ geode_free_all( dev, sc ); /*--- Release all system resources (irqs, etc.).*/ return( 0 ); } /* * Release system resources that were allocated to this driver in "geode_pci_attach()". * Normally this is called when unloading the driver, but it can also be called * when the driver receives an error in "geode_pci_attach()" that requires * the attach to abort. */ /*-----------------------------------------------------------------------*/ static void geode_free_all( device_t dev, struct sc_info *sc ) { #if USE_TIMER_POLL #if HARDCLOCK_POLL geode_dma_poll = 0; #else untimeout( do_audio_poll, (void *)0, tick_poll ); /* Cancel tick timer.*/ #endif #endif if( sc->sc_reg) bus_release_resource( dev, sc->sc_regtype, sc->sc_regid, sc->sc_reg ); if( sc->sc_ih) bus_teardown_intr( dev, sc->sc_irq_res, sc->sc_ih ); /* This crashes when snd_setup_intr() returns 0 (fails).*/ #if 0 if( sc->sc_irq_res ) bus_release_resource( dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq_res ); #endif if( sc->sc_parent_dmat ) bus_dma_tag_destroy( sc->sc_parent_dmat ); if (sc->sc_lock) snd_mtxfree( sc->sc_lock ); free( sc, M_DEVBUF ); /* Free out softc. We are done.*/ } /* * The suspend method aborts all DMAs in progress, setting a flag so each * aborted DMA can be restarted (a suspend is basically a halt with machine * state (memory) recorded/valid, awaiting a "resume"). */ /*-----------------------------------------------------------------------*/ static int geode_pci_suspend( device_t dev ) { struct sc_info *sc; int i; printf( "\n\n *** GEODE_PCI_SUSPEND\n\n" ); sc = pcm_getdevinfo( dev ); for (i=0; i<3; i++) { /* For all channels,*/ sc->sc_ch[i].ch_was_running = sc->sc_ch[i].ch_run; /* Flag state.*/ if( sc->sc_ch[i].ch_run ) { geode_pchan_trigger( 0, &sc->sc_ch[i], PCMTRIG_ABORT ); /* Abort running DMA.*/ } } return( 0 ); } /* * A "resume" method reactivates suspended hardware and restarts DMAs that * were aborted when the previous suspend occured. */ /*-----------------------------------------------------------------------*/ static int geode_pci_resume( device_t dev ) { struct sc_info *sc; struct sc_chinfo *ch; int i; printf( "\n\n *** GEODE_PCI_RESUME\n\n" ); sc = pcm_getdevinfo( dev ); /* Reinit geode PCI audio function (reactivate/reset the hardware).*/ if( geode_reinit( sc ) != 0 ) { device_printf( dev, "unable to reinit Audio.\n" ); return( ENXIO ); } /* Reinit the codec (mixer) */ if( mixer_reinit( dev ) == -1 ) { device_printf( dev, "unable to reinit mixer.\n" ); return( ENXIO ); } /* Restart DMA that was in progress (aborted) at last suspend.*/ for (i=0; i<3; i++) { /* For all channels, */ ch = &sc->sc_ch[i]; if( ch->ch_was_running ) { /* If DMA was active,*/ geode_pchan_setblocksize( 0, ch, ch->ch_blk_size ); /* Reset channel,*/ geode_pchan_setspeed( 0, ch, ch->ch_speed ); geode_pchan_trigger( 0, ch, PCMTRIG_START ); /* Restart DMA.*/ } } return( 0 ); } /*---------------------------------------------------------------*\ * Driver metadata. This roots instantiation of the kernel's * Device Method interface. These are the routines that the * kernel uses to interact with this driver. *---------------------------------------------------------------*/ static device_method_t geode_methods[] = { /* Device Interface */ DEVMETHOD(device_probe, geode_pci_probe), DEVMETHOD(device_attach, geode_pci_attach), DEVMETHOD(device_detach, geode_pci_detach), DEVMETHOD(device_suspend, geode_pci_suspend), DEVMETHOD(device_resume, geode_pci_resume), { 0, 0 } }; /*--- The driver_t structure roots the driver. It has a pointer to * the interface methods implementing the driver. */ static driver_t geode_driver = { "pcm", /* Class - This is a "pcm" driver. */ geode_methods, /* Our device methods. */ PCM_SOFTC_SIZE /* A generic PCM softc, not our */ }; /* private softc. */ /*--- The DRIVER_MODULE() macro defines this, the "snd_geode" driver, a * child of the "pci" driver (bus), with driver_t "geode_driver". */ DRIVER_MODULE( snd_geode, pci, geode_driver, pcm_devclass, 0, 0 ); MODULE_DEPEND( snd_geode, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER ); MODULE_VERSION( snd_geode, 1 ); /* *--------------------------------------------------------------- * @@@ Debug Routines. *--------------------------------------------------------------- */ #if GEODE_DEBUG /* Display the PCI Function 3 Header registers. These registers largely * follow the format specified for any PCI device, that is, the standard * PCI Header. Register 0x10 in the Header is the memory address at which * the device specific registers appear on the PCI bus. */ /*-----------------------------------------------------------------------*/ static void dmp_audio_pci_hdr( device_t dev ) { u_int32_t data; printf( "\n **** AUDIO PCI HDR ***\n" ); /* 00-01h. Vendo ID. */ data = pci_read_config( dev, 0x00, 2 ); printf( "-->Vendor ID =%x \n", data ); /* 02-03h. Dev ID. */ data = pci_read_config( dev, 0x02, 2 ); printf( "-->Dev ID =%x \n", data ); /* 04-05h. PCI CMD. */ data = pci_read_config( dev, 0x04, 2 ); printf( "-->PCI cmd =%x \n", data ); /* 06-07h. PCI Status. */ data = pci_read_config( dev, 0x06, 2 ); printf( "-->PCI status =%x \n", data ); /* 08h. Dev revision ID. */ data = pci_read_config( dev, 0x08, 1 ); printf( "-->Dev revision =%x \n", data ); /* 09-0Bh. PCI Class. */ data = pci_read_config( dev, 0x09, 4 ); printf( "-->PCI class =%x \n", data ); /* 0Ch. PCI cache line size. */ /* 0dh. PCI latency. */ data = pci_read_config( dev, 0x0D, 1 ); printf( "-->PCI latency =%x \n", data ); /* 0Eh. PCI Header type. */ data = pci_read_config( dev, 0x0E, 1 ); printf( "-->PCI header type =%x \n", data ); /* 0Fh. PCI BIST. */ data = pci_read_config( dev, 0x0F, 1 ); printf( "-->BIST =%x \n", data ); /* 10-13h. BASE REGISTER --> Geode Audio Regs are mem mapped at this loc. */ data = pci_read_config( dev, 0x10, 4 ); printf( "--> Register Base address =%x \n", data ); } /* Display the memory-mapped PCI Function 3 registers. These registers in * the PCI audio function appear in memory at the (fixed) location specified * by the BAR register in the PCI Function 3 header. The LM4549 codec (mixer) * is accessed via a synchronous serial line as controlled by the codec * command and status registers at offsets 0x0C and 0x08. */ /*-----------------------------------------------------------------------*/ static void dmp_audio_mem_regs( char *audio_regs_adr ) { u_int32_t b4; u_int16_t b2; u_int8_t b1; printf( "\n **** MEM-MAPPED AUDIO REGS at %x ***\n", (int)audio_regs_adr ); b4 = *(u_int32_t *)(audio_regs_adr + 0x00 ); printf( "--> CODEC GPIO status =%x \n", b4 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x04 ); printf( "--> CODEC GPIO control =%x \n", b4 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x08 ); printf( "--> CODEC status =%x \n", b4 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x0C ); printf( "--> CODEC command =%x \n", b4 ); b2 = *(u_int16_t *)(audio_regs_adr + 0x10 ); printf( "--> Audio SMI src =%x \n", b2 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x14 ); printf( "--> I/O trap SMI =%x \n", b4 ); b2 = *(u_int16_t *)(audio_regs_adr + 0x18 ); printf( "--> I/O trap SMI enable =%x \n", b2 ); b2 = *(u_int16_t *)(audio_regs_adr + 0x1A ); printf( "--> Internal IRQ enable=%x \n", b2 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x1C ); printf( "--> Internal IRQ control=%x \n", b4 ); b1 = *(u_int8_t *)(audio_regs_adr + 0x20 ); printf( "--> DMA 0 cmd =%x \n", b1 ); b1 = *(u_int8_t *)(audio_regs_adr + 0x21 ); printf( "--> DMA 0 SMI status =%x \n", b1 ); b4 = *(u_int32_t *)(audio_regs_adr + 0x24 ); printf( "--> DMA 0 PRD table adr =%x \n", b4 ); } /*--- Display the registers internal to the LM4549 codec. */ /*-----------------------------------------------------------------------*/ void dmp_codec_regs( void ) { u_int16_t b2; printf( "\n **** CODEC REGS ***\n" ); b2 = codec_direct_reg_read( LM4549_RESET ); printf( "Reset =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_MASTER_VOLUME ); printf( "master Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_LINE_OUT_VOLUME ); printf( "Line Out Volume=%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_MONO_VOLUME ); printf( "Mono Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_BEEP_VOLUME ); printf( "Beep Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_PHONE_VOLUME ); printf( "Phone Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_MIC_VOLUME ); printf( "Mic Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_LINE_IN_VOLUME ); printf( "Line in Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_CD_VOLUME ); printf( "Line CD Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_VIDEO_VOLUME ); printf( "Video Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_AUX_VOLUME ); printf( "Aux Volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_PCM_OUT_VOLUME ); printf( "PCM out volume =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_RECORD_SELECT ); printf( "record select =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_RECORD_GAIN ); printf( "record gain =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_GENERAL_PURPOSE ); printf( "general purpose=%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_3D ); printf( "National 3D =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_POWERDOWN ); printf( "Powerdown =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_EXTENDED_AUDIO ); printf( "Extended audio =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_EXTENDED_AUDIO_STATUS ); printf( "Extended audio status=%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_PCM_FRONT_DAC_RATE ); printf( "PCM DAC rate =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_PCM_ADC_RATE ); printf( "PCM ADC rate =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_VENDOR_ID1 ); printf( "Vendor ID1 =%x\n", b2 ); b2 = codec_direct_reg_read( LM4549_VENDOR_ID2 ); printf( "Vendor ID2 =%x\n\n", b2 ); } /* The following supports "call do_dmp_mmregs" from DDB. */ /*-----------------------------------------------------------------------*/ int do_dmp_mmregs( void ) { dmp_audio_mem_regs( (char *)sc->sc_mmregs_base ); return( 1 ); } #endif