ATI IXP documentation extraction, R.P.Zandijk 2004, 2005 Chip overview ------------ The ATI IXP audio part consists of 3 DMA channels (two output, one input), several FIFO's for output streams. The FIFO's are not clear to me yet; it might be a separate I/O/S FIFO or a FIFO per ADC rate. Channel numbers used are dictated by the AC'97 standard. The modem part of the chip is unknown to me but could also be handled by the codec. Chip init and closedown - - - - - - - - - - - - For operation without SPDIF, it's enough to clear all interrupt sources and to enable the irq's we need. With SPDIF it's preferable to enable burst-mode, setup thresholding for spdif and setting up other protocol choices when spdif is not fed trough the codec. On chip closedown, all interrupt sources need to be cleared and all IRQ's be disabled. Interrupts --------- [Status : tested ok autodetection, ISR and IER] There is one interrupt source register (ATI_REG_ISR) and one interrupt enable/disable register (ATI_REG_IER). Interrupts are acknowledged by writing the bits back in the status register. Each in/out/spdf subsystem has two interrupt flags: ATI_REG_ISR_XXX_XRUN and ATI_REG_ISR_XXX_STATUS. For codec detection, an NEW_FRAME interrupt is to be used to ensure that an interrupt is generated when all three codecs are disconnected. When a codec is not there, it's flag will be set and the resulting mask can then be used to disable the codec interrupt sources in the ATI_REG_IER) DMA --- [Status: works fine] The IXP chip has support for upto three simultaneous DMA channels operating; one for audio playback, one for capturing audio and one for SPDIF output. This last SPDIF one is only to be used when the spdif output is not passed trough the aclink. This is probably codec related and thus is free for dedicated SPDIF codec use. A DMA channel has two interrupts associated with it as mentioned in the interrupts section. An ATI_REG_ISR_XXX_XRUN and an ATI_REG_ISR_XXX_STATUS. The ATI_REG_ISR_XXX_XRUN signals the end of the buffers and that the PCM substream ought to stop. The ATI_REG_ISR_XXX_STATUS signals the periodic update of the substream. For NetBSD this can be used to call the (*intr)(intrarg) function provided to us by the trigger_output() call to make sure the buffer gets filled (again). The `mysterious' bus-busy bit has to be set to get access as busmaster? (XXX check me XXX) To get audio output interrupts generated, ATI_REG_IEAR_OUT_XRUN_EN and/or ATI_REG_IER_IO_STATUS_EN needs to be set. DMA buffers - - - - - - DMA buffers are arranged in a linked list of small DMA descriptors each consisting of three 32 bit values indicating data-area, packet size (upto 64k 32 bit values i.e. 256 kb RAM) and the address of the next DMA descriptor. These DMA descriptors are read in automatically by the IXP chip and only need to be set up by writing a link register to start with. A link address has to have its lower bit set to indicate a valid link address stored. This enables the chip to detect the end of a DMA descriptor list. The ATI_REG_ISR_XXX_XRUN interrupt most likely signals the end of the chain, whereas the ATI_REG_ISR_XXX_STATUS interrupt most likely signals the end of one descriptor. DMA rates - - - - - All DMA rates are controlled by the codec interface. Rates can be selected for audio sampling, SPDIF output rate. For normal audio output there is the choice of either all sampled on the same audio rate as the front DAC or split up in separate front DAC, surround DAC and LFE DAC rates. Audio can be either interleaved in one DMA stream or dealt with separately for each channel. Interleaving might force the same output rates setting. When using different rates, the interface is not clear but its probably related to the ATI_REG_SLOTREQ variable and possibly ATI_REG_ISR_OUT_XRUN interrupts to fill up the FIFO's for each channel. Audio format and channels - - - - - - - - - - - - - Channel numbers used are dictated by the AC'97 standard. The AC'97 standard (or the ATI chip) dictates configuration for either two, four, six or eight channels. [6,9 not confirmed, might be swapped with 7,8] channel | normal func 0 } 1 } AC-link 2 } 3 | L 4 | R 5 | (input) 6 | CENTER 7 | sL 8 | sR 9 | LFE 10 | SPDIF-C1 11 | SPDIF-C2 12 } 13 } AC-link ? 14 } 15 } Audio output slots need to set up by setting the relevant ATI_REG_OUT_DMA_SLOT_BIT's in the ATI_IXP_OUT_DMA_SLOT register. The setting of the bits most likely dictate the audio stream when interleaved. Normal interleaving order for enabled channels is (3,4,7,8,6,9,10,11) wich resolves into (L,R, sL, sR, CENTER, LFE, SPDIF-C1, SPDIF-C2). This order can be swapped by setting the ATI_REG_6CH_REORDER_EN flag in the ATI_REG_6CH_REORDER register. The order then switches to (3,4,6,9,7,8,10,11) wich then resolves into (L,R, CENTER, LFE, sL, sR, SPDIF-C1, SPDIF-C2). The ATI chip supports 16 or 32 bit samples indicated by the ATI_REG_CMD_INTERLEAVE_{IN/OUT} to be set for 16 bit samples or it will pass on 32 bits/channel. XXX 32 bit sound not tested yet XXX Codecs ------ [Status : tested ok for autodetection, reading and writing codecs] [Not tested: resetting AC-link] The chip supports upto three codecs. To my knowledge no multi-codec boards have been found so far. All codecs are fed by the same DMA channels so i guess only the mixer stuff is really independent and/or is an additional/separate SPDIF. Codecs are detected by the procedure explained in the Interrupts section and can also be switched off individually. Before reading or writing can be performed on a codec, access to the codec has to be acquired by making sure the transfers to and from the codec(s) are finished by making sure the ATI_REG_PHYS_OUT_ADDR_EN flag is cleared when reading the ATI_REG_PHYS_OUT_ADD register. This delay can be upto a millisecond or so; it prolly indicates transfers to/from the codec happening. Reading a codec variable: - - - - - - - - - - - - - To read a codec variable, a value consisting of register address and codec number needs to written in the ATI_REG_PHYS_OUT_ADDR register together with the ATI_REG_PHYS_OUT_ADDR_EN and ATI_REG_PHYS_OUT_RW flags; this value is computed as : data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | /* shifted 9 bits */ ATI_REG_PHYS_OUT_ADDR_EN | /* bit 8 */ ATI_REG_PHYS_OUT_RW | /* bit 2 */ codec; /* bits 0-1 */ after writing this value, the codec has to be aquired again. i.e. all transfers need to be finished. The resulting value can be read in from the ATI_IXP_PHYS_IN_ADDR register that is only valid when the ATI_IXP_PHYS_IN_READ_FLAG is set. The value read has to be shifted ATI_REG_PHYS_IN_DATA_SHIFT (16) bits. Timeouts normally only happen during reset, whereas 0xffff needs to be returned. Writing a codec variable: - - - - - - - - - - - - - To write a codec variable, a value has to be written into the ATI_REG_PHYS_OUT register. This value is computed from the value to write and the register needed to be written : data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | /* shifted 9 bits */ (val << ATI_REG_PHYS_OUT_DATA_SHIFT) | /* shifted 16 bits */ ATI_REG_PHYS_OUT_ADDR_EN | /* bit 8 */ codec; /* bits 0-1 */ No aquiring has to be done after this. AC link with IXP: - - - - - - - - - The AC link with the IXP is signalled by the ATI_REG_CMD_ACLINK_ACTIVE flag. All Codecs apparently share the same link. The ATI_REG_CMD_AC_SYNC flag signals a sync signal to the codecs and ought to be on all the time but at hard reset needs to be asserted to be able to get a link. The AC-link with the IXP can either be soft resetted. When this fails, it can be hard-resetted. A reset-sequence begins with a reset powerdown by clearing the ATI_REG_CMD_POWERDOWN flag. If this flag was set, wait for about 10 usec. A reset-sequence ends with asserting the ATI_REG_CMD_AC_SYNC flag and clearing the ATI_REG_CMD_AC_RESET flag. A soft reset begins with asserting the ATI_REG_CMD_AC_SOFT_RESET flag and reading the ATI_REG_CMD register, waiting another 10 usec and clearing the ATI_REG_CMD_AC_SOFT_RESET flag. The reason for the extra `read' is not clear but prolly to assert AC link. The AC link should then be up again wich can be asserted by reading in the ATI_REG_CMD flag and checking the ATI_REG_CMD_ACLINK_ACTIVE flag. A hard reset starts by asseting the ATI_REG_CMD_AC_SYNC and clearing the ATI_REG_CMD_AC_RESET flag in one write and then reading the chip out again, waiting for some time and asserting the ATI_REG_CMD_AC_RESET flag. If the ATI_REG_CMD_ACLINK_ACTIVE flag is still down, repeat the hard reset a few times. SPDIF ----- [Not tested: all] SPDIF can either be shared with the normal output DMA or with the dedicated SPDIF DMA. SPDIF data can be interleaved in the audio stream by setting the ATI_REG_CMD_SPDF_CONFIG_01 flag in the ATI_REG_CMD flag and clearing the other SPDF_CONFIG flags to setup DMA slot 10 and 11 for SPDIF data. All SPDIF data will then be put into channel 7 and 8. This shared/not shared has to be setup in the AC97 pcm_type; 2 (?) indicates interleaving, otherwise AC'97 format has to be set to PCM_FMTBIT_IEC958_SUBFRAME_LE (?) ================================================= DMA interrupt sources and masks #define ATI_REG_ISR 0x00 /* interrupt source */ #define ATI_REG_ISR_IN_XRUN (1U<<0) #define ATI_REG_ISR_IN_STATUS (1U<<1) #define ATI_REG_ISR_OUT_XRUN (1U<<2) #define ATI_REG_ISR_OUT_STATUS (1U<<3) #define ATI_REG_ISR_SPDF_XRUN (1U<<4) #define ATI_REG_ISR_SPDF_STATUS (1U<<5) #define ATI_REG_ISR_PHYS_INTR (1U<<8) #define ATI_REG_ISR_PHYS_MISMATCH (1U<<9) #define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10) #define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11) #define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12) #define ATI_REG_ISR_NEW_FRAME (1U<<13) #define ATI_REG_IER 0x04 /* interrupt enable */ #define ATI_REG_IER_IN_XRUN_EN (1U<<0) #define ATI_REG_IER_IO_STATUS_EN (1U<<1) #define ATI_REG_IER_OUT_XRUN_EN (1U<<2) #define ATI_REG_IER_OUT_XRUN_COND (1U<<3) #define ATI_REG_IER_SPDF_XRUN_EN (1U<<4) #define ATI_REG_IER_SPDF_STATUS_EN (1U<<5) #define ATI_REG_IER_PHYS_INTR_EN (1U<<8) #define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9) #define ATI_REG_IER_CODEC0_INTR_EN (1U<<10) #define ATI_REG_IER_CODEC1_INTR_EN (1U<<11) #define ATI_REG_IER_CODEC2_INTR_EN (1U<<12) #define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO */ #define ATI_REG_IER_SET_BUS_BUSY (1U<<14) /* (WO) audio is running */ ----------------------- ATI command's, to be orr'd together, set is action to be taken (prolly) #define ATI_REG_CMD 0x08 /* command */ #define ATI_REG_CMD_POWERDOWN (1U<<0) #define ATI_REG_CMD_RECEIVE_EN (1U<<1) #define ATI_REG_CMD_SEND_EN (1U<<2) #define ATI_REG_CMD_STATUS_MEM (1U<<3) #define ATI_REG_CMD_SPDF_OUT_EN (1U<<4) #define ATI_REG_CMD_SPDF_STATUS_MEM (1U<<5) #define ATI_REG_CMD_SPDF_THRESHOLD (3U<<6) #define ATI_REG_CMD_SPDF_THRESHOLD_SHIFT 6 #define ATI_REG_CMD_IN_DMA_EN (1U<<8) #define ATI_REG_CMD_OUT_DMA_EN (1U<<9) #define ATI_REG_CMD_SPDF_DMA_EN (1U<<10) #define ATI_REG_CMD_SPDF_OUT_STOPPED (1U<<11) #define ATI_REG_CMD_SPDF_CONFIG_MASK (7U<<12) #define ATI_REG_CMD_SPDF_CONFIG_34 (1U<<12) #define ATI_REG_CMD_SPDF_CONFIG_78 (2U<<12) #define ATI_REG_CMD_SPDF_CONFIG_69 (3U<<12) #define ATI_REG_CMD_SPDF_CONFIG_01 (4U<<12) #define ATI_REG_CMD_INTERLEAVE_SPDF (1U<<16) #define ATI_REG_CMD_AUDIO_PRESENT (1U<<20) #define ATI_REG_CMD_INTERLEAVE_IN (1U<<21) #define ATI_REG_CMD_INTERLEAVE_OUT (1U<<22) #define ATI_REG_CMD_LOOPBACK_EN (1U<<23) #define ATI_REG_CMD_PACKED_DIS (1U<<24) #define ATI_REG_CMD_BURST_EN (1U<<25) #define ATI_REG_CMD_PANIC_EN (1U<<26) #define ATI_REG_CMD_MODEM_PRESENT (1U<<27) #define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28) #define ATI_REG_CMD_AC_SOFT_RESET (1U<<29) #define ATI_REG_CMD_AC_SYNC (1U<<30) #define ATI_REG_CMD_AC_RESET (1U<<31) ---------------------- ATI physical out/in select?? #define ATI_REG_PHYS_OUT_ADDR 0x0c /* UNKNOWN */ #define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0) #define ATI_REG_PHYS_OUT_RW (1U<<2) #define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8) #define ATI_REG_PHYS_OUT_ADDR_SHIFT 9 #define ATI_REG_PHYS_OUT_DATA_SHIFT 16 #define ATI_REG_PHYS_IN_ADDR 0x10 /* UNKNOWN */ #define ATI_REG_PHYS_IN_READ_FLAG (1U<<8) #define ATI_REG_PHYS_IN_ADDR_SHIFT 9 #define ATI_REG_PHYS_IN_DATA_SHIFT 16 ------------------------ ATI slot req? /* UNKNOWN */ #define ATI_REG_SLOTREQ 0x14 ------------------------- ATI counter /* UNKNOWN */ #define ATI_REG_COUNTER 0x18 #define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */ #define ATI_REG_COUNTER_BITCLOCK (31U<<8) ------------------------- ATI input current status #define ATI_REG_IN_FIFO_THRESHOLD 0x1c #define ATI_REG_IN_DMA_LINKPTR 0x20 /* linked list? */ #define ATI_REG_IN_DMA_DT_START 0x24 /* RO */ #define ATI_REG_IN_DMA_DT_NEXT 0x28 /* RO */ #define ATI_REG_IN_DMA_DT_CUR 0x2c /* RO */ #define ATI_REG_IN_DMA_DT_SIZE 0x30 ATI output threshold, ATI output DMA area #define ATI_REG_OUT_DMA_SLOT 0x34 #define ATI_REG_OUT_DMA_SLOT_BIT(x) (1U << ((x) - 3)) #define ATI_REG_OUT_DMA_SLOT_MASK 0x1ff #define ATI_REG_OUT_DMA_THRESHOLD_MASK 0xf800 #define ATI_REG_OUT_DMA_THRESHOLD_SHIFT 11 #define ATI_REG_OUT_DMA_LINKPTR 0x38 /* linked list? */ #define ATI_REG_OUT_DMA_DT_START 0x3c /* RO */ #define ATI_REG_OUT_DMA_DT_NEXT 0x40 /* RO */ #define ATI_REG_OUT_DMA_DT_CUR 0x44 /* RO */ #define ATI_REG_OUT_DMA_DT_SIZE 0x48 ATI SPDF output threshold, ATI output DMA area #define ATI_REG_SPDF_CMD 0x4c /* SPDF control register? */ #define ATI_REG_SPDF_CMD_LFSR (1U<<4) #define ATI_REG_SPDF_CMD_SINGLE_CH (1U<<5) #define ATI_REG_SPDF_CMD_LFSR_ACC (0xff<<8) /* RO */ #define ATI_REG_SPDF_DMA_LINKPTR 0x50 /* linked list ? */ #define ATI_REG_SPDF_DMA_DT_START 0x54 /* RO */ #define ATI_REG_SPDF_DMA_DT_NEXT 0x58 /* RO */ #define ATI_REG_SPDF_DMA_DT_CUR 0x5c /* RO */ #define ATI_REG_SPDF_DMA_DT_SIZE 0x60 ----------------------- ATI modem en audio stuff? #define ATI_REG_MODEM_MIRROR 0x7c #define ATI_REG_AUDIO_MIRROR 0x80 ----------------------- ATI channel reordering, to allow standard AC'97 data to be output in the NON-AC'97 compat mode #define ATI_REG_FIFO_FLUSH 0x88 /* flush remaining stuff in the FIFO between output and DMA? */ #define ATI_REG_FIFO_OUT_FLUSH (1U<<0) #define ATI_REG_FIFO_IN_FLUSH (1U<<1) /* LINKPTR */ #define ATI_REG_LINKPTR_EN (1U<<0) /* flag to signal valid linkptr */ /* [INT|OUT|SPDIF]_DMA_DT_SIZE */ /* what register? */ #define ATI_REG_DMA_DT_SIZE (0xffffU<<0) /* max size 64kb ? */ #define ATI_REG_DMA_FIFO_USED (0x1fU<<16) #define ATI_REG_DMA_FIFO_FREE (0x1fU<<21) #define ATI_REG_DMA_STATE (7U<<26) ------------------------- DMA descriptors and enums /* * DMA packate descriptor */ typedef struct atiixp_dma_desc { u32 addr; /* DMA buffer address */ u16 status; /* status bits */ u16 size; /* size of the packet in dwords */ u32 next; /* address of the next packet descriptor */ } atiixp_dma_desc_t; #define NUM_ATI_CODECS 3 ==================================================