18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA driver for ATI IXP 150/200/250/300 AC97 controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <sound/core.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 198c2ecf20Sopenharmony_ci#include <sound/info.h> 208c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 218c2ecf20Sopenharmony_ci#include <sound/initval.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ATI IXP AC97 controller"); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400/600}}"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 298c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 308c2ecf20Sopenharmony_cistatic int ac97_clock = 48000; 318c2ecf20Sopenharmony_cistatic char *ac97_quirk; 328c2ecf20Sopenharmony_cistatic bool spdif_aclink = 1; 338c2ecf20Sopenharmony_cistatic int ac97_codec = -1; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ATI IXP controller."); 378c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ATI IXP controller."); 398c2ecf20Sopenharmony_cimodule_param(ac97_clock, int, 0444); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); 418c2ecf20Sopenharmony_cimodule_param(ac97_quirk, charp, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); 438c2ecf20Sopenharmony_cimodule_param(ac97_codec, int, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ac97_codec, "Specify codec instead of probing."); 458c2ecf20Sopenharmony_cimodule_param(spdif_aclink, bool, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link."); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* just for backward compatibility */ 498c2ecf20Sopenharmony_cistatic bool enable; 508c2ecf20Sopenharmony_cimodule_param(enable, bool, 0444); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define ATI_REG_ISR 0x00 /* interrupt source */ 578c2ecf20Sopenharmony_ci#define ATI_REG_ISR_IN_XRUN (1U<<0) 588c2ecf20Sopenharmony_ci#define ATI_REG_ISR_IN_STATUS (1U<<1) 598c2ecf20Sopenharmony_ci#define ATI_REG_ISR_OUT_XRUN (1U<<2) 608c2ecf20Sopenharmony_ci#define ATI_REG_ISR_OUT_STATUS (1U<<3) 618c2ecf20Sopenharmony_ci#define ATI_REG_ISR_SPDF_XRUN (1U<<4) 628c2ecf20Sopenharmony_ci#define ATI_REG_ISR_SPDF_STATUS (1U<<5) 638c2ecf20Sopenharmony_ci#define ATI_REG_ISR_PHYS_INTR (1U<<8) 648c2ecf20Sopenharmony_ci#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9) 658c2ecf20Sopenharmony_ci#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10) 668c2ecf20Sopenharmony_ci#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11) 678c2ecf20Sopenharmony_ci#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12) 688c2ecf20Sopenharmony_ci#define ATI_REG_ISR_NEW_FRAME (1U<<13) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define ATI_REG_IER 0x04 /* interrupt enable */ 718c2ecf20Sopenharmony_ci#define ATI_REG_IER_IN_XRUN_EN (1U<<0) 728c2ecf20Sopenharmony_ci#define ATI_REG_IER_IO_STATUS_EN (1U<<1) 738c2ecf20Sopenharmony_ci#define ATI_REG_IER_OUT_XRUN_EN (1U<<2) 748c2ecf20Sopenharmony_ci#define ATI_REG_IER_OUT_XRUN_COND (1U<<3) 758c2ecf20Sopenharmony_ci#define ATI_REG_IER_SPDF_XRUN_EN (1U<<4) 768c2ecf20Sopenharmony_ci#define ATI_REG_IER_SPDF_STATUS_EN (1U<<5) 778c2ecf20Sopenharmony_ci#define ATI_REG_IER_PHYS_INTR_EN (1U<<8) 788c2ecf20Sopenharmony_ci#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9) 798c2ecf20Sopenharmony_ci#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10) 808c2ecf20Sopenharmony_ci#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11) 818c2ecf20Sopenharmony_ci#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12) 828c2ecf20Sopenharmony_ci#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO */ 838c2ecf20Sopenharmony_ci#define ATI_REG_IER_SET_BUS_BUSY (1U<<14) /* (WO) audio is running */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define ATI_REG_CMD 0x08 /* command */ 868c2ecf20Sopenharmony_ci#define ATI_REG_CMD_POWERDOWN (1U<<0) 878c2ecf20Sopenharmony_ci#define ATI_REG_CMD_RECEIVE_EN (1U<<1) 888c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SEND_EN (1U<<2) 898c2ecf20Sopenharmony_ci#define ATI_REG_CMD_STATUS_MEM (1U<<3) 908c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_OUT_EN (1U<<4) 918c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_STATUS_MEM (1U<<5) 928c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_THRESHOLD (3U<<6) 938c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_THRESHOLD_SHIFT 6 948c2ecf20Sopenharmony_ci#define ATI_REG_CMD_IN_DMA_EN (1U<<8) 958c2ecf20Sopenharmony_ci#define ATI_REG_CMD_OUT_DMA_EN (1U<<9) 968c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_DMA_EN (1U<<10) 978c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_OUT_STOPPED (1U<<11) 988c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_CONFIG_MASK (7U<<12) 998c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_CONFIG_34 (1U<<12) 1008c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_CONFIG_78 (2U<<12) 1018c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_CONFIG_69 (3U<<12) 1028c2ecf20Sopenharmony_ci#define ATI_REG_CMD_SPDF_CONFIG_01 (4U<<12) 1038c2ecf20Sopenharmony_ci#define ATI_REG_CMD_INTERLEAVE_SPDF (1U<<16) 1048c2ecf20Sopenharmony_ci#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20) 1058c2ecf20Sopenharmony_ci#define ATI_REG_CMD_INTERLEAVE_IN (1U<<21) 1068c2ecf20Sopenharmony_ci#define ATI_REG_CMD_INTERLEAVE_OUT (1U<<22) 1078c2ecf20Sopenharmony_ci#define ATI_REG_CMD_LOOPBACK_EN (1U<<23) 1088c2ecf20Sopenharmony_ci#define ATI_REG_CMD_PACKED_DIS (1U<<24) 1098c2ecf20Sopenharmony_ci#define ATI_REG_CMD_BURST_EN (1U<<25) 1108c2ecf20Sopenharmony_ci#define ATI_REG_CMD_PANIC_EN (1U<<26) 1118c2ecf20Sopenharmony_ci#define ATI_REG_CMD_MODEM_PRESENT (1U<<27) 1128c2ecf20Sopenharmony_ci#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28) 1138c2ecf20Sopenharmony_ci#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29) 1148c2ecf20Sopenharmony_ci#define ATI_REG_CMD_AC_SYNC (1U<<30) 1158c2ecf20Sopenharmony_ci#define ATI_REG_CMD_AC_RESET (1U<<31) 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_ADDR 0x0c 1188c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0) 1198c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_RW (1U<<2) 1208c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8) 1218c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9 1228c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_OUT_DATA_SHIFT 16 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_IN_ADDR 0x10 1258c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8) 1268c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_IN_ADDR_SHIFT 9 1278c2ecf20Sopenharmony_ci#define ATI_REG_PHYS_IN_DATA_SHIFT 16 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define ATI_REG_SLOTREQ 0x14 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define ATI_REG_COUNTER 0x18 1328c2ecf20Sopenharmony_ci#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */ 1338c2ecf20Sopenharmony_ci#define ATI_REG_COUNTER_BITCLOCK (31U<<8) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define ATI_REG_IN_FIFO_THRESHOLD 0x1c 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define ATI_REG_IN_DMA_LINKPTR 0x20 1388c2ecf20Sopenharmony_ci#define ATI_REG_IN_DMA_DT_START 0x24 /* RO */ 1398c2ecf20Sopenharmony_ci#define ATI_REG_IN_DMA_DT_NEXT 0x28 /* RO */ 1408c2ecf20Sopenharmony_ci#define ATI_REG_IN_DMA_DT_CUR 0x2c /* RO */ 1418c2ecf20Sopenharmony_ci#define ATI_REG_IN_DMA_DT_SIZE 0x30 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_SLOT 0x34 1448c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_SLOT_BIT(x) (1U << ((x) - 3)) 1458c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_SLOT_MASK 0x1ff 1468c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_THRESHOLD_MASK 0xf800 1478c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_THRESHOLD_SHIFT 11 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_LINKPTR 0x38 1508c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_DT_START 0x3c /* RO */ 1518c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_DT_NEXT 0x40 /* RO */ 1528c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_DT_CUR 0x44 /* RO */ 1538c2ecf20Sopenharmony_ci#define ATI_REG_OUT_DMA_DT_SIZE 0x48 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_CMD 0x4c 1568c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_CMD_LFSR (1U<<4) 1578c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_CMD_SINGLE_CH (1U<<5) 1588c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_CMD_LFSR_ACC (0xff<<8) /* RO */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_DMA_LINKPTR 0x50 1618c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_DMA_DT_START 0x54 /* RO */ 1628c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_DMA_DT_NEXT 0x58 /* RO */ 1638c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_DMA_DT_CUR 0x5c /* RO */ 1648c2ecf20Sopenharmony_ci#define ATI_REG_SPDF_DMA_DT_SIZE 0x60 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#define ATI_REG_MODEM_MIRROR 0x7c 1678c2ecf20Sopenharmony_ci#define ATI_REG_AUDIO_MIRROR 0x80 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#define ATI_REG_6CH_REORDER 0x84 /* reorder slots for 6ch */ 1708c2ecf20Sopenharmony_ci#define ATI_REG_6CH_REORDER_EN (1U<<0) /* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define ATI_REG_FIFO_FLUSH 0x88 1738c2ecf20Sopenharmony_ci#define ATI_REG_FIFO_OUT_FLUSH (1U<<0) 1748c2ecf20Sopenharmony_ci#define ATI_REG_FIFO_IN_FLUSH (1U<<1) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* LINKPTR */ 1778c2ecf20Sopenharmony_ci#define ATI_REG_LINKPTR_EN (1U<<0) 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* [INT|OUT|SPDIF]_DMA_DT_SIZE */ 1808c2ecf20Sopenharmony_ci#define ATI_REG_DMA_DT_SIZE (0xffffU<<0) 1818c2ecf20Sopenharmony_ci#define ATI_REG_DMA_FIFO_USED (0x1fU<<16) 1828c2ecf20Sopenharmony_ci#define ATI_REG_DMA_FIFO_FREE (0x1fU<<21) 1838c2ecf20Sopenharmony_ci#define ATI_REG_DMA_STATE (7U<<26) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistruct atiixp; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * DMA packate descriptor 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistruct atiixp_dma_desc { 1968c2ecf20Sopenharmony_ci __le32 addr; /* DMA buffer address */ 1978c2ecf20Sopenharmony_ci u16 status; /* status bits */ 1988c2ecf20Sopenharmony_ci u16 size; /* size of the packet in dwords */ 1998c2ecf20Sopenharmony_ci __le32 next; /* address of the next packet descriptor */ 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * stream enum 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cienum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, ATI_DMA_SPDIF, NUM_ATI_DMAS }; /* DMAs */ 2068c2ecf20Sopenharmony_cienum { ATI_PCM_OUT, ATI_PCM_IN, ATI_PCM_SPDIF, NUM_ATI_PCMS }; /* AC97 pcm slots */ 2078c2ecf20Sopenharmony_cienum { ATI_PCMDEV_ANALOG, ATI_PCMDEV_DIGITAL, NUM_ATI_PCMDEVS }; /* pcm devices */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define NUM_ATI_CODECS 3 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * constants and callbacks for each DMA type 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistruct atiixp_dma_ops { 2168c2ecf20Sopenharmony_ci int type; /* ATI_DMA_XXX */ 2178c2ecf20Sopenharmony_ci unsigned int llp_offset; /* LINKPTR offset */ 2188c2ecf20Sopenharmony_ci unsigned int dt_cur; /* DT_CUR offset */ 2198c2ecf20Sopenharmony_ci /* called from open callback */ 2208c2ecf20Sopenharmony_ci void (*enable_dma)(struct atiixp *chip, int on); 2218c2ecf20Sopenharmony_ci /* called from trigger (START/STOP) */ 2228c2ecf20Sopenharmony_ci void (*enable_transfer)(struct atiixp *chip, int on); 2238c2ecf20Sopenharmony_ci /* called from trigger (STOP only) */ 2248c2ecf20Sopenharmony_ci void (*flush_dma)(struct atiixp *chip); 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * DMA stream 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistruct atiixp_dma { 2318c2ecf20Sopenharmony_ci const struct atiixp_dma_ops *ops; 2328c2ecf20Sopenharmony_ci struct snd_dma_buffer desc_buf; 2338c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; /* assigned PCM substream */ 2348c2ecf20Sopenharmony_ci unsigned int buf_addr, buf_bytes; /* DMA buffer address, bytes */ 2358c2ecf20Sopenharmony_ci unsigned int period_bytes, periods; 2368c2ecf20Sopenharmony_ci int opened; 2378c2ecf20Sopenharmony_ci int running; 2388c2ecf20Sopenharmony_ci int suspended; 2398c2ecf20Sopenharmony_ci int pcm_open_flag; 2408c2ecf20Sopenharmony_ci int ac97_pcm_type; /* index # of ac97_pcm to access, -1 = not used */ 2418c2ecf20Sopenharmony_ci unsigned int saved_curptr; 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * ATI IXP chip 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistruct atiixp { 2488c2ecf20Sopenharmony_ci struct snd_card *card; 2498c2ecf20Sopenharmony_ci struct pci_dev *pci; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci unsigned long addr; 2528c2ecf20Sopenharmony_ci void __iomem *remap_addr; 2538c2ecf20Sopenharmony_ci int irq; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 2568c2ecf20Sopenharmony_ci struct snd_ac97 *ac97[NUM_ATI_CODECS]; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spinlock_t reg_lock; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci struct atiixp_dma dmas[NUM_ATI_DMAS]; 2618c2ecf20Sopenharmony_ci struct ac97_pcm *pcms[NUM_ATI_PCMS]; 2628c2ecf20Sopenharmony_ci struct snd_pcm *pcmdevs[NUM_ATI_PCMDEVS]; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci int max_channels; /* max. channels for PCM out */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci unsigned int codec_not_ready_bits; /* for codec detection */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci int spdif_over_aclink; /* passed from the module option */ 2698c2ecf20Sopenharmony_ci struct mutex open_mutex; /* playback open mutex */ 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_atiixp_ids[] = { 2768c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */ 2778c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */ 2788c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */ 2798c2ecf20Sopenharmony_ci { PCI_VDEVICE(ATI, 0x4382), 0 }, /* SB600 */ 2808c2ecf20Sopenharmony_ci { 0, } 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_atiixp_ids); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk atiixp_quirks[] = { 2868c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x105b, 0x0c81, "Foxconn RC4107MA-RS2", 0), 2878c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0), 2888c2ecf20Sopenharmony_ci { } /* terminator */ 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/* 2928c2ecf20Sopenharmony_ci * lowlevel functions 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * update the bits of the given register. 2978c2ecf20Sopenharmony_ci * return 1 if the bits changed. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_cistatic int snd_atiixp_update_bits(struct atiixp *chip, unsigned int reg, 3008c2ecf20Sopenharmony_ci unsigned int mask, unsigned int value) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci void __iomem *addr = chip->remap_addr + reg; 3038c2ecf20Sopenharmony_ci unsigned int data, old_data; 3048c2ecf20Sopenharmony_ci old_data = data = readl(addr); 3058c2ecf20Sopenharmony_ci data &= ~mask; 3068c2ecf20Sopenharmony_ci data |= value; 3078c2ecf20Sopenharmony_ci if (old_data == data) 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci writel(data, addr); 3108c2ecf20Sopenharmony_ci return 1; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* 3148c2ecf20Sopenharmony_ci * macros for easy use 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci#define atiixp_write(chip,reg,value) \ 3178c2ecf20Sopenharmony_ci writel(value, chip->remap_addr + ATI_REG_##reg) 3188c2ecf20Sopenharmony_ci#define atiixp_read(chip,reg) \ 3198c2ecf20Sopenharmony_ci readl(chip->remap_addr + ATI_REG_##reg) 3208c2ecf20Sopenharmony_ci#define atiixp_update(chip,reg,mask,val) \ 3218c2ecf20Sopenharmony_ci snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val) 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * handling DMA packets 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * we allocate a linear buffer for the DMA, and split it to each packet. 3278c2ecf20Sopenharmony_ci * in a future version, a scatter-gather buffer should be implemented. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#define ATI_DESC_LIST_SIZE \ 3318c2ecf20Sopenharmony_ci PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(struct atiixp_dma_desc)) 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * build packets ring for the given buffer size. 3358c2ecf20Sopenharmony_ci * 3368c2ecf20Sopenharmony_ci * IXP handles the buffer descriptors, which are connected as a linked 3378c2ecf20Sopenharmony_ci * list. although we can change the list dynamically, in this version, 3388c2ecf20Sopenharmony_ci * a static RING of buffer descriptors is used. 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * the ring is built in this function, and is set up to the hardware. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_cistatic int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma, 3438c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 3448c2ecf20Sopenharmony_ci unsigned int periods, 3458c2ecf20Sopenharmony_ci unsigned int period_bytes) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci unsigned int i; 3488c2ecf20Sopenharmony_ci u32 addr, desc_addr; 3498c2ecf20Sopenharmony_ci unsigned long flags; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (periods > ATI_MAX_DESCRIPTORS) 3528c2ecf20Sopenharmony_ci return -ENOMEM; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (dma->desc_buf.area == NULL) { 3558c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, 3568c2ecf20Sopenharmony_ci &chip->pci->dev, 3578c2ecf20Sopenharmony_ci ATI_DESC_LIST_SIZE, 3588c2ecf20Sopenharmony_ci &dma->desc_buf) < 0) 3598c2ecf20Sopenharmony_ci return -ENOMEM; 3608c2ecf20Sopenharmony_ci dma->period_bytes = dma->periods = 0; /* clear */ 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (dma->periods == periods && dma->period_bytes == period_bytes) 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* reset DMA before changing the descriptor table */ 3678c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 3688c2ecf20Sopenharmony_ci writel(0, chip->remap_addr + dma->ops->llp_offset); 3698c2ecf20Sopenharmony_ci dma->ops->enable_dma(chip, 0); 3708c2ecf20Sopenharmony_ci dma->ops->enable_dma(chip, 1); 3718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* fill the entries */ 3748c2ecf20Sopenharmony_ci addr = (u32)substream->runtime->dma_addr; 3758c2ecf20Sopenharmony_ci desc_addr = (u32)dma->desc_buf.addr; 3768c2ecf20Sopenharmony_ci for (i = 0; i < periods; i++) { 3778c2ecf20Sopenharmony_ci struct atiixp_dma_desc *desc; 3788c2ecf20Sopenharmony_ci desc = &((struct atiixp_dma_desc *)dma->desc_buf.area)[i]; 3798c2ecf20Sopenharmony_ci desc->addr = cpu_to_le32(addr); 3808c2ecf20Sopenharmony_ci desc->status = 0; 3818c2ecf20Sopenharmony_ci desc->size = period_bytes >> 2; /* in dwords */ 3828c2ecf20Sopenharmony_ci desc_addr += sizeof(struct atiixp_dma_desc); 3838c2ecf20Sopenharmony_ci if (i == periods - 1) 3848c2ecf20Sopenharmony_ci desc->next = cpu_to_le32((u32)dma->desc_buf.addr); 3858c2ecf20Sopenharmony_ci else 3868c2ecf20Sopenharmony_ci desc->next = cpu_to_le32(desc_addr); 3878c2ecf20Sopenharmony_ci addr += period_bytes; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, 3918c2ecf20Sopenharmony_ci chip->remap_addr + dma->ops->llp_offset); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dma->period_bytes = period_bytes; 3948c2ecf20Sopenharmony_ci dma->periods = periods; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* 4008c2ecf20Sopenharmony_ci * remove the ring buffer and release it if assigned 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic void atiixp_clear_dma_packets(struct atiixp *chip, struct atiixp_dma *dma, 4038c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci if (dma->desc_buf.area) { 4068c2ecf20Sopenharmony_ci writel(0, chip->remap_addr + dma->ops->llp_offset); 4078c2ecf20Sopenharmony_ci snd_dma_free_pages(&dma->desc_buf); 4088c2ecf20Sopenharmony_ci dma->desc_buf.area = NULL; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/* 4138c2ecf20Sopenharmony_ci * AC97 interface 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_cistatic int snd_atiixp_acquire_codec(struct atiixp *chip) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci int timeout = 1000; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) { 4208c2ecf20Sopenharmony_ci if (! timeout--) { 4218c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "codec acquire timeout\n"); 4228c2ecf20Sopenharmony_ci return -EBUSY; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci udelay(1); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic unsigned short snd_atiixp_codec_read(struct atiixp *chip, unsigned short codec, unsigned short reg) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci unsigned int data; 4328c2ecf20Sopenharmony_ci int timeout; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (snd_atiixp_acquire_codec(chip) < 0) 4358c2ecf20Sopenharmony_ci return 0xffff; 4368c2ecf20Sopenharmony_ci data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | 4378c2ecf20Sopenharmony_ci ATI_REG_PHYS_OUT_ADDR_EN | 4388c2ecf20Sopenharmony_ci ATI_REG_PHYS_OUT_RW | 4398c2ecf20Sopenharmony_ci codec; 4408c2ecf20Sopenharmony_ci atiixp_write(chip, PHYS_OUT_ADDR, data); 4418c2ecf20Sopenharmony_ci if (snd_atiixp_acquire_codec(chip) < 0) 4428c2ecf20Sopenharmony_ci return 0xffff; 4438c2ecf20Sopenharmony_ci timeout = 1000; 4448c2ecf20Sopenharmony_ci do { 4458c2ecf20Sopenharmony_ci data = atiixp_read(chip, PHYS_IN_ADDR); 4468c2ecf20Sopenharmony_ci if (data & ATI_REG_PHYS_IN_READ_FLAG) 4478c2ecf20Sopenharmony_ci return data >> ATI_REG_PHYS_IN_DATA_SHIFT; 4488c2ecf20Sopenharmony_ci udelay(1); 4498c2ecf20Sopenharmony_ci } while (--timeout); 4508c2ecf20Sopenharmony_ci /* time out may happen during reset */ 4518c2ecf20Sopenharmony_ci if (reg < 0x7c) 4528c2ecf20Sopenharmony_ci dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg); 4538c2ecf20Sopenharmony_ci return 0xffff; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void snd_atiixp_codec_write(struct atiixp *chip, unsigned short codec, 4588c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci unsigned int data; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (snd_atiixp_acquire_codec(chip) < 0) 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) | 4658c2ecf20Sopenharmony_ci ((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | 4668c2ecf20Sopenharmony_ci ATI_REG_PHYS_OUT_ADDR_EN | codec; 4678c2ecf20Sopenharmony_ci atiixp_write(chip, PHYS_OUT_ADDR, data); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic unsigned short snd_atiixp_ac97_read(struct snd_ac97 *ac97, 4728c2ecf20Sopenharmony_ci unsigned short reg) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct atiixp *chip = ac97->private_data; 4758c2ecf20Sopenharmony_ci return snd_atiixp_codec_read(chip, ac97->num, reg); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void snd_atiixp_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 4808c2ecf20Sopenharmony_ci unsigned short val) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct atiixp *chip = ac97->private_data; 4838c2ecf20Sopenharmony_ci snd_atiixp_codec_write(chip, ac97->num, reg, val); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* 4878c2ecf20Sopenharmony_ci * reset AC link 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_cistatic int snd_atiixp_aclink_reset(struct atiixp *chip) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int timeout; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* reset powerdoewn */ 4948c2ecf20Sopenharmony_ci if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0)) 4958c2ecf20Sopenharmony_ci udelay(10); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* perform a software reset */ 4988c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET); 4998c2ecf20Sopenharmony_ci atiixp_read(chip, CMD); 5008c2ecf20Sopenharmony_ci udelay(10); 5018c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci timeout = 10; 5048c2ecf20Sopenharmony_ci while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) { 5058c2ecf20Sopenharmony_ci /* do a hard reset */ 5068c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET, 5078c2ecf20Sopenharmony_ci ATI_REG_CMD_AC_SYNC); 5088c2ecf20Sopenharmony_ci atiixp_read(chip, CMD); 5098c2ecf20Sopenharmony_ci mdelay(1); 5108c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); 5118c2ecf20Sopenharmony_ci if (!--timeout) { 5128c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "codec reset timeout\n"); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* deassert RESET and assert SYNC to make sure */ 5188c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET, 5198c2ecf20Sopenharmony_ci ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5258c2ecf20Sopenharmony_cistatic int snd_atiixp_aclink_down(struct atiixp *chip) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ 5288c2ecf20Sopenharmony_ci // return -EBUSY; 5298c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, 5308c2ecf20Sopenharmony_ci ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET, 5318c2ecf20Sopenharmony_ci ATI_REG_CMD_POWERDOWN); 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/* 5378c2ecf20Sopenharmony_ci * auto-detection of codecs 5388c2ecf20Sopenharmony_ci * 5398c2ecf20Sopenharmony_ci * the IXP chip can generate interrupts for the non-existing codecs. 5408c2ecf20Sopenharmony_ci * NEW_FRAME interrupt is used to make sure that the interrupt is generated 5418c2ecf20Sopenharmony_ci * even if all three codecs are connected. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci#define ALL_CODEC_NOT_READY \ 5458c2ecf20Sopenharmony_ci (ATI_REG_ISR_CODEC0_NOT_READY |\ 5468c2ecf20Sopenharmony_ci ATI_REG_ISR_CODEC1_NOT_READY |\ 5478c2ecf20Sopenharmony_ci ATI_REG_ISR_CODEC2_NOT_READY) 5488c2ecf20Sopenharmony_ci#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME) 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int ac97_probing_bugs(struct pci_dev *pci) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci const struct snd_pci_quirk *q; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci q = snd_pci_quirk_lookup(pci, atiixp_quirks); 5558c2ecf20Sopenharmony_ci if (q) { 5568c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "atiixp quirk for %s. Forcing codec %d\n", 5578c2ecf20Sopenharmony_ci snd_pci_quirk_name(q), q->value); 5588c2ecf20Sopenharmony_ci return q->value; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci /* this hardware doesn't need workarounds. Probe for codec */ 5618c2ecf20Sopenharmony_ci return -1; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int snd_atiixp_codec_detect(struct atiixp *chip) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci int timeout; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci chip->codec_not_ready_bits = 0; 5698c2ecf20Sopenharmony_ci if (ac97_codec == -1) 5708c2ecf20Sopenharmony_ci ac97_codec = ac97_probing_bugs(chip->pci); 5718c2ecf20Sopenharmony_ci if (ac97_codec >= 0) { 5728c2ecf20Sopenharmony_ci chip->codec_not_ready_bits |= 5738c2ecf20Sopenharmony_ci CODEC_CHECK_BITS ^ (1 << (ac97_codec + 10)); 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci atiixp_write(chip, IER, CODEC_CHECK_BITS); 5788c2ecf20Sopenharmony_ci /* wait for the interrupts */ 5798c2ecf20Sopenharmony_ci timeout = 50; 5808c2ecf20Sopenharmony_ci while (timeout-- > 0) { 5818c2ecf20Sopenharmony_ci mdelay(1); 5828c2ecf20Sopenharmony_ci if (chip->codec_not_ready_bits) 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci atiixp_write(chip, IER, 0); /* disable irqs */ 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) { 5888c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "no codec detected!\n"); 5898c2ecf20Sopenharmony_ci return -ENXIO; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/* 5968c2ecf20Sopenharmony_ci * enable DMA and irqs 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_cistatic int snd_atiixp_chip_start(struct atiixp *chip) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci unsigned int reg; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* set up spdif, enable burst mode */ 6038c2ecf20Sopenharmony_ci reg = atiixp_read(chip, CMD); 6048c2ecf20Sopenharmony_ci reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT; 6058c2ecf20Sopenharmony_ci reg |= ATI_REG_CMD_BURST_EN; 6068c2ecf20Sopenharmony_ci atiixp_write(chip, CMD, reg); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci reg = atiixp_read(chip, SPDF_CMD); 6098c2ecf20Sopenharmony_ci reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH); 6108c2ecf20Sopenharmony_ci atiixp_write(chip, SPDF_CMD, reg); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* clear all interrupt source */ 6138c2ecf20Sopenharmony_ci atiixp_write(chip, ISR, 0xffffffff); 6148c2ecf20Sopenharmony_ci /* enable irqs */ 6158c2ecf20Sopenharmony_ci atiixp_write(chip, IER, 6168c2ecf20Sopenharmony_ci ATI_REG_IER_IO_STATUS_EN | 6178c2ecf20Sopenharmony_ci ATI_REG_IER_IN_XRUN_EN | 6188c2ecf20Sopenharmony_ci ATI_REG_IER_OUT_XRUN_EN | 6198c2ecf20Sopenharmony_ci ATI_REG_IER_SPDF_XRUN_EN | 6208c2ecf20Sopenharmony_ci ATI_REG_IER_SPDF_STATUS_EN); 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/* 6268c2ecf20Sopenharmony_ci * disable DMA and IRQs 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_cistatic int snd_atiixp_chip_stop(struct atiixp *chip) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci /* clear interrupt source */ 6318c2ecf20Sopenharmony_ci atiixp_write(chip, ISR, atiixp_read(chip, ISR)); 6328c2ecf20Sopenharmony_ci /* disable irqs */ 6338c2ecf20Sopenharmony_ci atiixp_write(chip, IER, 0); 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/* 6398c2ecf20Sopenharmony_ci * PCM section 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* 6438c2ecf20Sopenharmony_ci * pointer callback simplly reads XXX_DMA_DT_CUR register as the current 6448c2ecf20Sopenharmony_ci * position. when SG-buffer is implemented, the offset must be calculated 6458c2ecf20Sopenharmony_ci * correctly... 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substream) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 6508c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6518c2ecf20Sopenharmony_ci struct atiixp_dma *dma = runtime->private_data; 6528c2ecf20Sopenharmony_ci unsigned int curptr; 6538c2ecf20Sopenharmony_ci int timeout = 1000; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci while (timeout--) { 6568c2ecf20Sopenharmony_ci curptr = readl(chip->remap_addr + dma->ops->dt_cur); 6578c2ecf20Sopenharmony_ci if (curptr < dma->buf_addr) 6588c2ecf20Sopenharmony_ci continue; 6598c2ecf20Sopenharmony_ci curptr -= dma->buf_addr; 6608c2ecf20Sopenharmony_ci if (curptr >= dma->buf_bytes) 6618c2ecf20Sopenharmony_ci continue; 6628c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, curptr); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n", 6658c2ecf20Sopenharmony_ci readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr); 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* 6708c2ecf20Sopenharmony_ci * XRUN detected, and stop the PCM substream 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_cistatic void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci if (! dma->substream || ! dma->running) 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); 6778c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(dma->substream); 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci/* 6818c2ecf20Sopenharmony_ci * the period ack. update the substream. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_cistatic void snd_atiixp_update_dma(struct atiixp *chip, struct atiixp_dma *dma) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci if (! dma->substream || ! dma->running) 6868c2ecf20Sopenharmony_ci return; 6878c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(dma->substream); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci/* set BUS_BUSY interrupt bit if any DMA is running */ 6918c2ecf20Sopenharmony_ci/* call with spinlock held */ 6928c2ecf20Sopenharmony_cistatic void snd_atiixp_check_bus_busy(struct atiixp *chip) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci unsigned int bus_busy; 6958c2ecf20Sopenharmony_ci if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN | 6968c2ecf20Sopenharmony_ci ATI_REG_CMD_RECEIVE_EN | 6978c2ecf20Sopenharmony_ci ATI_REG_CMD_SPDF_OUT_EN)) 6988c2ecf20Sopenharmony_ci bus_busy = ATI_REG_IER_SET_BUS_BUSY; 6998c2ecf20Sopenharmony_ci else 7008c2ecf20Sopenharmony_ci bus_busy = 0; 7018c2ecf20Sopenharmony_ci atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* common trigger callback 7058c2ecf20Sopenharmony_ci * calling the lowlevel callbacks in it 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 7108c2ecf20Sopenharmony_ci struct atiixp_dma *dma = substream->runtime->private_data; 7118c2ecf20Sopenharmony_ci int err = 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (snd_BUG_ON(!dma->ops->enable_transfer || 7148c2ecf20Sopenharmony_ci !dma->ops->flush_dma)) 7158c2ecf20Sopenharmony_ci return -EINVAL; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 7188c2ecf20Sopenharmony_ci switch (cmd) { 7198c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7208c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7218c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7228c2ecf20Sopenharmony_ci if (dma->running && dma->suspended && 7238c2ecf20Sopenharmony_ci cmd == SNDRV_PCM_TRIGGER_RESUME) 7248c2ecf20Sopenharmony_ci writel(dma->saved_curptr, chip->remap_addr + 7258c2ecf20Sopenharmony_ci dma->ops->dt_cur); 7268c2ecf20Sopenharmony_ci dma->ops->enable_transfer(chip, 1); 7278c2ecf20Sopenharmony_ci dma->running = 1; 7288c2ecf20Sopenharmony_ci dma->suspended = 0; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 7318c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7328c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 7338c2ecf20Sopenharmony_ci dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND; 7348c2ecf20Sopenharmony_ci if (dma->running && dma->suspended) 7358c2ecf20Sopenharmony_ci dma->saved_curptr = readl(chip->remap_addr + 7368c2ecf20Sopenharmony_ci dma->ops->dt_cur); 7378c2ecf20Sopenharmony_ci dma->ops->enable_transfer(chip, 0); 7388c2ecf20Sopenharmony_ci dma->running = 0; 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci default: 7418c2ecf20Sopenharmony_ci err = -EINVAL; 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci if (! err) { 7458c2ecf20Sopenharmony_ci snd_atiixp_check_bus_busy(chip); 7468c2ecf20Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_STOP) { 7478c2ecf20Sopenharmony_ci dma->ops->flush_dma(chip); 7488c2ecf20Sopenharmony_ci snd_atiixp_check_bus_busy(chip); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 7528c2ecf20Sopenharmony_ci return err; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci/* 7578c2ecf20Sopenharmony_ci * lowlevel callbacks for each DMA type 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * every callback is supposed to be called in chip->reg_lock spinlock 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* flush FIFO of analog OUT DMA */ 7638c2ecf20Sopenharmony_cistatic void atiixp_out_flush_dma(struct atiixp *chip) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/* enable/disable analog OUT DMA */ 7698c2ecf20Sopenharmony_cistatic void atiixp_out_enable_dma(struct atiixp *chip, int on) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci unsigned int data; 7728c2ecf20Sopenharmony_ci data = atiixp_read(chip, CMD); 7738c2ecf20Sopenharmony_ci if (on) { 7748c2ecf20Sopenharmony_ci if (data & ATI_REG_CMD_OUT_DMA_EN) 7758c2ecf20Sopenharmony_ci return; 7768c2ecf20Sopenharmony_ci atiixp_out_flush_dma(chip); 7778c2ecf20Sopenharmony_ci data |= ATI_REG_CMD_OUT_DMA_EN; 7788c2ecf20Sopenharmony_ci } else 7798c2ecf20Sopenharmony_ci data &= ~ATI_REG_CMD_OUT_DMA_EN; 7808c2ecf20Sopenharmony_ci atiixp_write(chip, CMD, data); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci/* start/stop transfer over OUT DMA */ 7848c2ecf20Sopenharmony_cistatic void atiixp_out_enable_transfer(struct atiixp *chip, int on) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN, 7878c2ecf20Sopenharmony_ci on ? ATI_REG_CMD_SEND_EN : 0); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/* enable/disable analog IN DMA */ 7918c2ecf20Sopenharmony_cistatic void atiixp_in_enable_dma(struct atiixp *chip, int on) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN, 7948c2ecf20Sopenharmony_ci on ? ATI_REG_CMD_IN_DMA_EN : 0); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/* start/stop analog IN DMA */ 7988c2ecf20Sopenharmony_cistatic void atiixp_in_enable_transfer(struct atiixp *chip, int on) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci if (on) { 8018c2ecf20Sopenharmony_ci unsigned int data = atiixp_read(chip, CMD); 8028c2ecf20Sopenharmony_ci if (! (data & ATI_REG_CMD_RECEIVE_EN)) { 8038c2ecf20Sopenharmony_ci data |= ATI_REG_CMD_RECEIVE_EN; 8048c2ecf20Sopenharmony_ci#if 0 /* FIXME: this causes the endless loop */ 8058c2ecf20Sopenharmony_ci /* wait until slot 3/4 are finished */ 8068c2ecf20Sopenharmony_ci while ((atiixp_read(chip, COUNTER) & 8078c2ecf20Sopenharmony_ci ATI_REG_COUNTER_SLOT) != 5) 8088c2ecf20Sopenharmony_ci ; 8098c2ecf20Sopenharmony_ci#endif 8108c2ecf20Sopenharmony_ci atiixp_write(chip, CMD, data); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } else 8138c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci/* flush FIFO of analog IN DMA */ 8178c2ecf20Sopenharmony_cistatic void atiixp_in_flush_dma(struct atiixp *chip) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/* enable/disable SPDIF OUT DMA */ 8238c2ecf20Sopenharmony_cistatic void atiixp_spdif_enable_dma(struct atiixp *chip, int on) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN, 8268c2ecf20Sopenharmony_ci on ? ATI_REG_CMD_SPDF_DMA_EN : 0); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci/* start/stop SPDIF OUT DMA */ 8308c2ecf20Sopenharmony_cistatic void atiixp_spdif_enable_transfer(struct atiixp *chip, int on) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci unsigned int data; 8338c2ecf20Sopenharmony_ci data = atiixp_read(chip, CMD); 8348c2ecf20Sopenharmony_ci if (on) 8358c2ecf20Sopenharmony_ci data |= ATI_REG_CMD_SPDF_OUT_EN; 8368c2ecf20Sopenharmony_ci else 8378c2ecf20Sopenharmony_ci data &= ~ATI_REG_CMD_SPDF_OUT_EN; 8388c2ecf20Sopenharmony_ci atiixp_write(chip, CMD, data); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/* flush FIFO of SPDIF OUT DMA */ 8428c2ecf20Sopenharmony_cistatic void atiixp_spdif_flush_dma(struct atiixp *chip) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci int timeout; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* DMA off, transfer on */ 8478c2ecf20Sopenharmony_ci atiixp_spdif_enable_dma(chip, 0); 8488c2ecf20Sopenharmony_ci atiixp_spdif_enable_transfer(chip, 1); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci timeout = 100; 8518c2ecf20Sopenharmony_ci do { 8528c2ecf20Sopenharmony_ci if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED)) 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci udelay(1); 8558c2ecf20Sopenharmony_ci } while (timeout-- > 0); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci atiixp_spdif_enable_transfer(chip, 0); 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci/* set up slots and formats for SPDIF OUT */ 8618c2ecf20Sopenharmony_cistatic int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 8668c2ecf20Sopenharmony_ci if (chip->spdif_over_aclink) { 8678c2ecf20Sopenharmony_ci unsigned int data; 8688c2ecf20Sopenharmony_ci /* enable slots 10/11 */ 8698c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 8708c2ecf20Sopenharmony_ci ATI_REG_CMD_SPDF_CONFIG_01); 8718c2ecf20Sopenharmony_ci data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK; 8728c2ecf20Sopenharmony_ci data |= ATI_REG_OUT_DMA_SLOT_BIT(10) | 8738c2ecf20Sopenharmony_ci ATI_REG_OUT_DMA_SLOT_BIT(11); 8748c2ecf20Sopenharmony_ci data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; 8758c2ecf20Sopenharmony_ci atiixp_write(chip, OUT_DMA_SLOT, data); 8768c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT, 8778c2ecf20Sopenharmony_ci substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? 8788c2ecf20Sopenharmony_ci ATI_REG_CMD_INTERLEAVE_OUT : 0); 8798c2ecf20Sopenharmony_ci } else { 8808c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0); 8818c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0); 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 8848c2ecf20Sopenharmony_ci return 0; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci/* set up slots and formats for analog OUT */ 8888c2ecf20Sopenharmony_cistatic int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 8918c2ecf20Sopenharmony_ci unsigned int data; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 8948c2ecf20Sopenharmony_ci data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK; 8958c2ecf20Sopenharmony_ci switch (substream->runtime->channels) { 8968c2ecf20Sopenharmony_ci case 8: 8978c2ecf20Sopenharmony_ci data |= ATI_REG_OUT_DMA_SLOT_BIT(10) | 8988c2ecf20Sopenharmony_ci ATI_REG_OUT_DMA_SLOT_BIT(11); 8998c2ecf20Sopenharmony_ci fallthrough; 9008c2ecf20Sopenharmony_ci case 6: 9018c2ecf20Sopenharmony_ci data |= ATI_REG_OUT_DMA_SLOT_BIT(7) | 9028c2ecf20Sopenharmony_ci ATI_REG_OUT_DMA_SLOT_BIT(8); 9038c2ecf20Sopenharmony_ci fallthrough; 9048c2ecf20Sopenharmony_ci case 4: 9058c2ecf20Sopenharmony_ci data |= ATI_REG_OUT_DMA_SLOT_BIT(6) | 9068c2ecf20Sopenharmony_ci ATI_REG_OUT_DMA_SLOT_BIT(9); 9078c2ecf20Sopenharmony_ci fallthrough; 9088c2ecf20Sopenharmony_ci default: 9098c2ecf20Sopenharmony_ci data |= ATI_REG_OUT_DMA_SLOT_BIT(3) | 9108c2ecf20Sopenharmony_ci ATI_REG_OUT_DMA_SLOT_BIT(4); 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* set output threshold */ 9158c2ecf20Sopenharmony_ci data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; 9168c2ecf20Sopenharmony_ci atiixp_write(chip, OUT_DMA_SLOT, data); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT, 9198c2ecf20Sopenharmony_ci substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? 9208c2ecf20Sopenharmony_ci ATI_REG_CMD_INTERLEAVE_OUT : 0); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* 9238c2ecf20Sopenharmony_ci * enable 6 channel re-ordering bit if needed 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_ci atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN, 9268c2ecf20Sopenharmony_ci substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 9298c2ecf20Sopenharmony_ci return 0; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/* set up slots and formats for analog IN */ 9338c2ecf20Sopenharmony_cistatic int snd_atiixp_capture_prepare(struct snd_pcm_substream *substream) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 9388c2ecf20Sopenharmony_ci atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN, 9398c2ecf20Sopenharmony_ci substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? 9408c2ecf20Sopenharmony_ci ATI_REG_CMD_INTERLEAVE_IN : 0); 9418c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci/* 9468c2ecf20Sopenharmony_ci * hw_params - allocate the buffer and set up buffer descriptors 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream, 9498c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 9528c2ecf20Sopenharmony_ci struct atiixp_dma *dma = substream->runtime->private_data; 9538c2ecf20Sopenharmony_ci int err; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci dma->buf_addr = substream->runtime->dma_addr; 9568c2ecf20Sopenharmony_ci dma->buf_bytes = params_buffer_bytes(hw_params); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci err = atiixp_build_dma_packets(chip, dma, substream, 9598c2ecf20Sopenharmony_ci params_periods(hw_params), 9608c2ecf20Sopenharmony_ci params_period_bytes(hw_params)); 9618c2ecf20Sopenharmony_ci if (err < 0) 9628c2ecf20Sopenharmony_ci return err; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (dma->ac97_pcm_type >= 0) { 9658c2ecf20Sopenharmony_ci struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type]; 9668c2ecf20Sopenharmony_ci /* PCM is bound to AC97 codec(s) 9678c2ecf20Sopenharmony_ci * set up the AC97 codecs 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci if (dma->pcm_open_flag) { 9708c2ecf20Sopenharmony_ci snd_ac97_pcm_close(pcm); 9718c2ecf20Sopenharmony_ci dma->pcm_open_flag = 0; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci err = snd_ac97_pcm_open(pcm, params_rate(hw_params), 9748c2ecf20Sopenharmony_ci params_channels(hw_params), 9758c2ecf20Sopenharmony_ci pcm->r[0].slots); 9768c2ecf20Sopenharmony_ci if (err >= 0) 9778c2ecf20Sopenharmony_ci dma->pcm_open_flag = 1; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return err; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 9868c2ecf20Sopenharmony_ci struct atiixp_dma *dma = substream->runtime->private_data; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (dma->pcm_open_flag) { 9898c2ecf20Sopenharmony_ci struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type]; 9908c2ecf20Sopenharmony_ci snd_ac97_pcm_close(pcm); 9918c2ecf20Sopenharmony_ci dma->pcm_open_flag = 0; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci atiixp_clear_dma_packets(chip, dma, substream); 9948c2ecf20Sopenharmony_ci return 0; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci/* 9998c2ecf20Sopenharmony_ci * pcm hardware definition, identical for all DMA types 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_atiixp_pcm_hw = 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 10048c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 10058c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 10068c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 10078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 10088c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 10098c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 10108c2ecf20Sopenharmony_ci .rate_min = 48000, 10118c2ecf20Sopenharmony_ci .rate_max = 48000, 10128c2ecf20Sopenharmony_ci .channels_min = 2, 10138c2ecf20Sopenharmony_ci .channels_max = 2, 10148c2ecf20Sopenharmony_ci .buffer_bytes_max = 256 * 1024, 10158c2ecf20Sopenharmony_ci .period_bytes_min = 32, 10168c2ecf20Sopenharmony_ci .period_bytes_max = 128 * 1024, 10178c2ecf20Sopenharmony_ci .periods_min = 2, 10188c2ecf20Sopenharmony_ci .periods_max = ATI_MAX_DESCRIPTORS, 10198c2ecf20Sopenharmony_ci}; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_open(struct snd_pcm_substream *substream, 10228c2ecf20Sopenharmony_ci struct atiixp_dma *dma, int pcm_type) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 10258c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10268c2ecf20Sopenharmony_ci int err; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) 10298c2ecf20Sopenharmony_ci return -EINVAL; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (dma->opened) 10328c2ecf20Sopenharmony_ci return -EBUSY; 10338c2ecf20Sopenharmony_ci dma->substream = substream; 10348c2ecf20Sopenharmony_ci runtime->hw = snd_atiixp_pcm_hw; 10358c2ecf20Sopenharmony_ci dma->ac97_pcm_type = pcm_type; 10368c2ecf20Sopenharmony_ci if (pcm_type >= 0) { 10378c2ecf20Sopenharmony_ci runtime->hw.rates = chip->pcms[pcm_type]->rates; 10388c2ecf20Sopenharmony_ci snd_pcm_limit_hw_rates(runtime); 10398c2ecf20Sopenharmony_ci } else { 10408c2ecf20Sopenharmony_ci /* direct SPDIF */ 10418c2ecf20Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 10448c2ecf20Sopenharmony_ci return err; 10458c2ecf20Sopenharmony_ci runtime->private_data = dma; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* enable DMA bits */ 10488c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10498c2ecf20Sopenharmony_ci dma->ops->enable_dma(chip, 1); 10508c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10518c2ecf20Sopenharmony_ci dma->opened = 1; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_close(struct snd_pcm_substream *substream, 10578c2ecf20Sopenharmony_ci struct atiixp_dma *dma) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 10608c2ecf20Sopenharmony_ci /* disable DMA bits */ 10618c2ecf20Sopenharmony_ci if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) 10628c2ecf20Sopenharmony_ci return -EINVAL; 10638c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10648c2ecf20Sopenharmony_ci dma->ops->enable_dma(chip, 0); 10658c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10668c2ecf20Sopenharmony_ci dma->substream = NULL; 10678c2ecf20Sopenharmony_ci dma->opened = 0; 10688c2ecf20Sopenharmony_ci return 0; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci/* 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic int snd_atiixp_playback_open(struct snd_pcm_substream *substream) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 10768c2ecf20Sopenharmony_ci int err; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci mutex_lock(&chip->open_mutex); 10798c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0); 10808c2ecf20Sopenharmony_ci mutex_unlock(&chip->open_mutex); 10818c2ecf20Sopenharmony_ci if (err < 0) 10828c2ecf20Sopenharmony_ci return err; 10838c2ecf20Sopenharmony_ci substream->runtime->hw.channels_max = chip->max_channels; 10848c2ecf20Sopenharmony_ci if (chip->max_channels > 2) 10858c2ecf20Sopenharmony_ci /* channels must be even */ 10868c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(substream->runtime, 0, 10878c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 2); 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int snd_atiixp_playback_close(struct snd_pcm_substream *substream) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 10948c2ecf20Sopenharmony_ci int err; 10958c2ecf20Sopenharmony_ci mutex_lock(&chip->open_mutex); 10968c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]); 10978c2ecf20Sopenharmony_ci mutex_unlock(&chip->open_mutex); 10988c2ecf20Sopenharmony_ci return err; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic int snd_atiixp_capture_open(struct snd_pcm_substream *substream) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 11048c2ecf20Sopenharmony_ci return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1); 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_cistatic int snd_atiixp_capture_close(struct snd_pcm_substream *substream) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 11108c2ecf20Sopenharmony_ci return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int snd_atiixp_spdif_open(struct snd_pcm_substream *substream) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 11168c2ecf20Sopenharmony_ci int err; 11178c2ecf20Sopenharmony_ci mutex_lock(&chip->open_mutex); 11188c2ecf20Sopenharmony_ci if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */ 11198c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2); 11208c2ecf20Sopenharmony_ci else 11218c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1); 11228c2ecf20Sopenharmony_ci mutex_unlock(&chip->open_mutex); 11238c2ecf20Sopenharmony_ci return err; 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic int snd_atiixp_spdif_close(struct snd_pcm_substream *substream) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct atiixp *chip = snd_pcm_substream_chip(substream); 11298c2ecf20Sopenharmony_ci int err; 11308c2ecf20Sopenharmony_ci mutex_lock(&chip->open_mutex); 11318c2ecf20Sopenharmony_ci if (chip->spdif_over_aclink) 11328c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]); 11338c2ecf20Sopenharmony_ci else 11348c2ecf20Sopenharmony_ci err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]); 11358c2ecf20Sopenharmony_ci mutex_unlock(&chip->open_mutex); 11368c2ecf20Sopenharmony_ci return err; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci/* AC97 playback */ 11408c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_atiixp_playback_ops = { 11418c2ecf20Sopenharmony_ci .open = snd_atiixp_playback_open, 11428c2ecf20Sopenharmony_ci .close = snd_atiixp_playback_close, 11438c2ecf20Sopenharmony_ci .hw_params = snd_atiixp_pcm_hw_params, 11448c2ecf20Sopenharmony_ci .hw_free = snd_atiixp_pcm_hw_free, 11458c2ecf20Sopenharmony_ci .prepare = snd_atiixp_playback_prepare, 11468c2ecf20Sopenharmony_ci .trigger = snd_atiixp_pcm_trigger, 11478c2ecf20Sopenharmony_ci .pointer = snd_atiixp_pcm_pointer, 11488c2ecf20Sopenharmony_ci}; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci/* AC97 capture */ 11518c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_atiixp_capture_ops = { 11528c2ecf20Sopenharmony_ci .open = snd_atiixp_capture_open, 11538c2ecf20Sopenharmony_ci .close = snd_atiixp_capture_close, 11548c2ecf20Sopenharmony_ci .hw_params = snd_atiixp_pcm_hw_params, 11558c2ecf20Sopenharmony_ci .hw_free = snd_atiixp_pcm_hw_free, 11568c2ecf20Sopenharmony_ci .prepare = snd_atiixp_capture_prepare, 11578c2ecf20Sopenharmony_ci .trigger = snd_atiixp_pcm_trigger, 11588c2ecf20Sopenharmony_ci .pointer = snd_atiixp_pcm_pointer, 11598c2ecf20Sopenharmony_ci}; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci/* SPDIF playback */ 11628c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_atiixp_spdif_ops = { 11638c2ecf20Sopenharmony_ci .open = snd_atiixp_spdif_open, 11648c2ecf20Sopenharmony_ci .close = snd_atiixp_spdif_close, 11658c2ecf20Sopenharmony_ci .hw_params = snd_atiixp_pcm_hw_params, 11668c2ecf20Sopenharmony_ci .hw_free = snd_atiixp_pcm_hw_free, 11678c2ecf20Sopenharmony_ci .prepare = snd_atiixp_spdif_prepare, 11688c2ecf20Sopenharmony_ci .trigger = snd_atiixp_pcm_trigger, 11698c2ecf20Sopenharmony_ci .pointer = snd_atiixp_pcm_pointer, 11708c2ecf20Sopenharmony_ci}; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic const struct ac97_pcm atiixp_pcm_defs[] = { 11738c2ecf20Sopenharmony_ci /* front PCM */ 11748c2ecf20Sopenharmony_ci { 11758c2ecf20Sopenharmony_ci .exclusive = 1, 11768c2ecf20Sopenharmony_ci .r = { { 11778c2ecf20Sopenharmony_ci .slots = (1 << AC97_SLOT_PCM_LEFT) | 11788c2ecf20Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT) | 11798c2ecf20Sopenharmony_ci (1 << AC97_SLOT_PCM_CENTER) | 11808c2ecf20Sopenharmony_ci (1 << AC97_SLOT_PCM_SLEFT) | 11818c2ecf20Sopenharmony_ci (1 << AC97_SLOT_PCM_SRIGHT) | 11828c2ecf20Sopenharmony_ci (1 << AC97_SLOT_LFE) 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci }, 11868c2ecf20Sopenharmony_ci /* PCM IN #1 */ 11878c2ecf20Sopenharmony_ci { 11888c2ecf20Sopenharmony_ci .stream = 1, 11898c2ecf20Sopenharmony_ci .exclusive = 1, 11908c2ecf20Sopenharmony_ci .r = { { 11918c2ecf20Sopenharmony_ci .slots = (1 << AC97_SLOT_PCM_LEFT) | 11928c2ecf20Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT) 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci }, 11968c2ecf20Sopenharmony_ci /* S/PDIF OUT (optional) */ 11978c2ecf20Sopenharmony_ci { 11988c2ecf20Sopenharmony_ci .exclusive = 1, 11998c2ecf20Sopenharmony_ci .spdif = 1, 12008c2ecf20Sopenharmony_ci .r = { { 12018c2ecf20Sopenharmony_ci .slots = (1 << AC97_SLOT_SPDIF_LEFT2) | 12028c2ecf20Sopenharmony_ci (1 << AC97_SLOT_SPDIF_RIGHT2) 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci }, 12068c2ecf20Sopenharmony_ci}; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = { 12098c2ecf20Sopenharmony_ci .type = ATI_DMA_PLAYBACK, 12108c2ecf20Sopenharmony_ci .llp_offset = ATI_REG_OUT_DMA_LINKPTR, 12118c2ecf20Sopenharmony_ci .dt_cur = ATI_REG_OUT_DMA_DT_CUR, 12128c2ecf20Sopenharmony_ci .enable_dma = atiixp_out_enable_dma, 12138c2ecf20Sopenharmony_ci .enable_transfer = atiixp_out_enable_transfer, 12148c2ecf20Sopenharmony_ci .flush_dma = atiixp_out_flush_dma, 12158c2ecf20Sopenharmony_ci}; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = { 12188c2ecf20Sopenharmony_ci .type = ATI_DMA_CAPTURE, 12198c2ecf20Sopenharmony_ci .llp_offset = ATI_REG_IN_DMA_LINKPTR, 12208c2ecf20Sopenharmony_ci .dt_cur = ATI_REG_IN_DMA_DT_CUR, 12218c2ecf20Sopenharmony_ci .enable_dma = atiixp_in_enable_dma, 12228c2ecf20Sopenharmony_ci .enable_transfer = atiixp_in_enable_transfer, 12238c2ecf20Sopenharmony_ci .flush_dma = atiixp_in_flush_dma, 12248c2ecf20Sopenharmony_ci}; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic const struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = { 12278c2ecf20Sopenharmony_ci .type = ATI_DMA_SPDIF, 12288c2ecf20Sopenharmony_ci .llp_offset = ATI_REG_SPDF_DMA_LINKPTR, 12298c2ecf20Sopenharmony_ci .dt_cur = ATI_REG_SPDF_DMA_DT_CUR, 12308c2ecf20Sopenharmony_ci .enable_dma = atiixp_spdif_enable_dma, 12318c2ecf20Sopenharmony_ci .enable_transfer = atiixp_spdif_enable_transfer, 12328c2ecf20Sopenharmony_ci .flush_dma = atiixp_spdif_flush_dma, 12338c2ecf20Sopenharmony_ci}; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic int snd_atiixp_pcm_new(struct atiixp *chip) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 12398c2ecf20Sopenharmony_ci struct snd_pcm_chmap *chmap; 12408c2ecf20Sopenharmony_ci struct snd_ac97_bus *pbus = chip->ac97_bus; 12418c2ecf20Sopenharmony_ci int err, i, num_pcms; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* initialize constants */ 12448c2ecf20Sopenharmony_ci chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops; 12458c2ecf20Sopenharmony_ci chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops; 12468c2ecf20Sopenharmony_ci if (! chip->spdif_over_aclink) 12478c2ecf20Sopenharmony_ci chip->dmas[ATI_DMA_SPDIF].ops = &snd_atiixp_spdif_dma_ops; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* assign AC97 pcm */ 12508c2ecf20Sopenharmony_ci if (chip->spdif_over_aclink) 12518c2ecf20Sopenharmony_ci num_pcms = 3; 12528c2ecf20Sopenharmony_ci else 12538c2ecf20Sopenharmony_ci num_pcms = 2; 12548c2ecf20Sopenharmony_ci err = snd_ac97_pcm_assign(pbus, num_pcms, atiixp_pcm_defs); 12558c2ecf20Sopenharmony_ci if (err < 0) 12568c2ecf20Sopenharmony_ci return err; 12578c2ecf20Sopenharmony_ci for (i = 0; i < num_pcms; i++) 12588c2ecf20Sopenharmony_ci chip->pcms[i] = &pbus->pcms[i]; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci chip->max_channels = 2; 12618c2ecf20Sopenharmony_ci if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) { 12628c2ecf20Sopenharmony_ci if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_LFE)) 12638c2ecf20Sopenharmony_ci chip->max_channels = 6; 12648c2ecf20Sopenharmony_ci else 12658c2ecf20Sopenharmony_ci chip->max_channels = 4; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* PCM #0: analog I/O */ 12698c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, "ATI IXP AC97", 12708c2ecf20Sopenharmony_ci ATI_PCMDEV_ANALOG, 1, 1, &pcm); 12718c2ecf20Sopenharmony_ci if (err < 0) 12728c2ecf20Sopenharmony_ci return err; 12738c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); 12748c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); 12758c2ecf20Sopenharmony_ci pcm->private_data = chip; 12768c2ecf20Sopenharmony_ci strcpy(pcm->name, "ATI IXP AC97"); 12778c2ecf20Sopenharmony_ci chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 12808c2ecf20Sopenharmony_ci &chip->pci->dev, 64*1024, 128*1024); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, 12838c2ecf20Sopenharmony_ci snd_pcm_alt_chmaps, chip->max_channels, 0, 12848c2ecf20Sopenharmony_ci &chmap); 12858c2ecf20Sopenharmony_ci if (err < 0) 12868c2ecf20Sopenharmony_ci return err; 12878c2ecf20Sopenharmony_ci chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; 12888c2ecf20Sopenharmony_ci chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* no SPDIF support on codec? */ 12918c2ecf20Sopenharmony_ci if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates) 12928c2ecf20Sopenharmony_ci return 0; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* FIXME: non-48k sample rate doesn't work on my test machine with AD1888 */ 12958c2ecf20Sopenharmony_ci if (chip->pcms[ATI_PCM_SPDIF]) 12968c2ecf20Sopenharmony_ci chip->pcms[ATI_PCM_SPDIF]->rates = SNDRV_PCM_RATE_48000; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* PCM #1: spdif playback */ 12998c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, "ATI IXP IEC958", 13008c2ecf20Sopenharmony_ci ATI_PCMDEV_DIGITAL, 1, 0, &pcm); 13018c2ecf20Sopenharmony_ci if (err < 0) 13028c2ecf20Sopenharmony_ci return err; 13038c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops); 13048c2ecf20Sopenharmony_ci pcm->private_data = chip; 13058c2ecf20Sopenharmony_ci if (chip->spdif_over_aclink) 13068c2ecf20Sopenharmony_ci strcpy(pcm->name, "ATI IXP IEC958 (AC97)"); 13078c2ecf20Sopenharmony_ci else 13088c2ecf20Sopenharmony_ci strcpy(pcm->name, "ATI IXP IEC958 (Direct)"); 13098c2ecf20Sopenharmony_ci chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 13128c2ecf20Sopenharmony_ci &chip->pci->dev, 64*1024, 128*1024); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* pre-select AC97 SPDIF slots 10/11 */ 13158c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ATI_CODECS; i++) { 13168c2ecf20Sopenharmony_ci if (chip->ac97[i]) 13178c2ecf20Sopenharmony_ci snd_ac97_update_bits(chip->ac97[i], 13188c2ecf20Sopenharmony_ci AC97_EXTENDED_STATUS, 13198c2ecf20Sopenharmony_ci 0x03 << 4, 0x03 << 4); 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci return 0; 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci/* 13288c2ecf20Sopenharmony_ci * interrupt handler 13298c2ecf20Sopenharmony_ci */ 13308c2ecf20Sopenharmony_cistatic irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct atiixp *chip = dev_id; 13338c2ecf20Sopenharmony_ci unsigned int status; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci status = atiixp_read(chip, ISR); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (! status) 13388c2ecf20Sopenharmony_ci return IRQ_NONE; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* process audio DMA */ 13418c2ecf20Sopenharmony_ci if (status & ATI_REG_ISR_OUT_XRUN) 13428c2ecf20Sopenharmony_ci snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); 13438c2ecf20Sopenharmony_ci else if (status & ATI_REG_ISR_OUT_STATUS) 13448c2ecf20Sopenharmony_ci snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); 13458c2ecf20Sopenharmony_ci if (status & ATI_REG_ISR_IN_XRUN) 13468c2ecf20Sopenharmony_ci snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); 13478c2ecf20Sopenharmony_ci else if (status & ATI_REG_ISR_IN_STATUS) 13488c2ecf20Sopenharmony_ci snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); 13498c2ecf20Sopenharmony_ci if (! chip->spdif_over_aclink) { 13508c2ecf20Sopenharmony_ci if (status & ATI_REG_ISR_SPDF_XRUN) 13518c2ecf20Sopenharmony_ci snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_SPDIF]); 13528c2ecf20Sopenharmony_ci else if (status & ATI_REG_ISR_SPDF_STATUS) 13538c2ecf20Sopenharmony_ci snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_SPDIF]); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* for codec detection */ 13578c2ecf20Sopenharmony_ci if (status & CODEC_CHECK_BITS) { 13588c2ecf20Sopenharmony_ci unsigned int detected; 13598c2ecf20Sopenharmony_ci detected = status & CODEC_CHECK_BITS; 13608c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 13618c2ecf20Sopenharmony_ci chip->codec_not_ready_bits |= detected; 13628c2ecf20Sopenharmony_ci atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */ 13638c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* ack */ 13678c2ecf20Sopenharmony_ci atiixp_write(chip, ISR, status); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci/* 13748c2ecf20Sopenharmony_ci * ac97 mixer section 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic const struct ac97_quirk ac97_quirks[] = { 13788c2ecf20Sopenharmony_ci { 13798c2ecf20Sopenharmony_ci .subvendor = 0x103c, 13808c2ecf20Sopenharmony_ci .subdevice = 0x006b, 13818c2ecf20Sopenharmony_ci .name = "HP Pavilion ZV5030US", 13828c2ecf20Sopenharmony_ci .type = AC97_TUNE_MUTE_LED 13838c2ecf20Sopenharmony_ci }, 13848c2ecf20Sopenharmony_ci { 13858c2ecf20Sopenharmony_ci .subvendor = 0x103c, 13868c2ecf20Sopenharmony_ci .subdevice = 0x308b, 13878c2ecf20Sopenharmony_ci .name = "HP nx6125", 13888c2ecf20Sopenharmony_ci .type = AC97_TUNE_MUTE_LED 13898c2ecf20Sopenharmony_ci }, 13908c2ecf20Sopenharmony_ci { 13918c2ecf20Sopenharmony_ci .subvendor = 0x103c, 13928c2ecf20Sopenharmony_ci .subdevice = 0x3091, 13938c2ecf20Sopenharmony_ci .name = "unknown HP", 13948c2ecf20Sopenharmony_ci .type = AC97_TUNE_MUTE_LED 13958c2ecf20Sopenharmony_ci }, 13968c2ecf20Sopenharmony_ci { } /* terminator */ 13978c2ecf20Sopenharmony_ci}; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int snd_atiixp_mixer_new(struct atiixp *chip, int clock, 14008c2ecf20Sopenharmony_ci const char *quirk_override) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct snd_ac97_bus *pbus; 14038c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 14048c2ecf20Sopenharmony_ci int i, err; 14058c2ecf20Sopenharmony_ci int codec_count; 14068c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 14078c2ecf20Sopenharmony_ci .write = snd_atiixp_ac97_write, 14088c2ecf20Sopenharmony_ci .read = snd_atiixp_ac97_read, 14098c2ecf20Sopenharmony_ci }; 14108c2ecf20Sopenharmony_ci static const unsigned int codec_skip[NUM_ATI_CODECS] = { 14118c2ecf20Sopenharmony_ci ATI_REG_ISR_CODEC0_NOT_READY, 14128c2ecf20Sopenharmony_ci ATI_REG_ISR_CODEC1_NOT_READY, 14138c2ecf20Sopenharmony_ci ATI_REG_ISR_CODEC2_NOT_READY, 14148c2ecf20Sopenharmony_ci }; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (snd_atiixp_codec_detect(chip) < 0) 14178c2ecf20Sopenharmony_ci return -ENXIO; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) 14208c2ecf20Sopenharmony_ci return err; 14218c2ecf20Sopenharmony_ci pbus->clock = clock; 14228c2ecf20Sopenharmony_ci chip->ac97_bus = pbus; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci codec_count = 0; 14258c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ATI_CODECS; i++) { 14268c2ecf20Sopenharmony_ci if (chip->codec_not_ready_bits & codec_skip[i]) 14278c2ecf20Sopenharmony_ci continue; 14288c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 14298c2ecf20Sopenharmony_ci ac97.private_data = chip; 14308c2ecf20Sopenharmony_ci ac97.pci = chip->pci; 14318c2ecf20Sopenharmony_ci ac97.num = i; 14328c2ecf20Sopenharmony_ci ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; 14338c2ecf20Sopenharmony_ci if (! chip->spdif_over_aclink) 14348c2ecf20Sopenharmony_ci ac97.scaps |= AC97_SCAP_NO_SPDIF; 14358c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { 14368c2ecf20Sopenharmony_ci chip->ac97[i] = NULL; /* to be sure */ 14378c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 14388c2ecf20Sopenharmony_ci "codec %d not available for audio\n", i); 14398c2ecf20Sopenharmony_ci continue; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci codec_count++; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (! codec_count) { 14458c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "no codec available\n"); 14468c2ecf20Sopenharmony_ci return -ENODEV; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci return 0; 14528c2ecf20Sopenharmony_ci} 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 14568c2ecf20Sopenharmony_ci/* 14578c2ecf20Sopenharmony_ci * power management 14588c2ecf20Sopenharmony_ci */ 14598c2ecf20Sopenharmony_cistatic int snd_atiixp_suspend(struct device *dev) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14628c2ecf20Sopenharmony_ci struct atiixp *chip = card->private_data; 14638c2ecf20Sopenharmony_ci int i; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 14668c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ATI_CODECS; i++) 14678c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97[i]); 14688c2ecf20Sopenharmony_ci snd_atiixp_aclink_down(chip); 14698c2ecf20Sopenharmony_ci snd_atiixp_chip_stop(chip); 14708c2ecf20Sopenharmony_ci return 0; 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic int snd_atiixp_resume(struct device *dev) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14768c2ecf20Sopenharmony_ci struct atiixp *chip = card->private_data; 14778c2ecf20Sopenharmony_ci int i; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci snd_atiixp_aclink_reset(chip); 14808c2ecf20Sopenharmony_ci snd_atiixp_chip_start(chip); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ATI_CODECS; i++) 14838c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97[i]); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ATI_PCMDEVS; i++) 14868c2ecf20Sopenharmony_ci if (chip->pcmdevs[i]) { 14878c2ecf20Sopenharmony_ci struct atiixp_dma *dma = &chip->dmas[i]; 14888c2ecf20Sopenharmony_ci if (dma->substream && dma->suspended) { 14898c2ecf20Sopenharmony_ci dma->ops->enable_dma(chip, 1); 14908c2ecf20Sopenharmony_ci dma->substream->ops->prepare(dma->substream); 14918c2ecf20Sopenharmony_ci writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, 14928c2ecf20Sopenharmony_ci chip->remap_addr + dma->ops->llp_offset); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 14978c2ecf20Sopenharmony_ci return 0; 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); 15018c2ecf20Sopenharmony_ci#define SND_ATIIXP_PM_OPS &snd_atiixp_pm 15028c2ecf20Sopenharmony_ci#else 15038c2ecf20Sopenharmony_ci#define SND_ATIIXP_PM_OPS NULL 15048c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci/* 15088c2ecf20Sopenharmony_ci * proc interface for register dump 15098c2ecf20Sopenharmony_ci */ 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic void snd_atiixp_proc_read(struct snd_info_entry *entry, 15128c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct atiixp *chip = entry->private_data; 15158c2ecf20Sopenharmony_ci int i; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci for (i = 0; i < 256; i += 4) 15188c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i)); 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic void snd_atiixp_proc_init(struct atiixp *chip) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci snd_card_ro_proc_new(chip->card, "atiixp", chip, snd_atiixp_proc_read); 15248c2ecf20Sopenharmony_ci} 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci/* 15288c2ecf20Sopenharmony_ci * destructor 15298c2ecf20Sopenharmony_ci */ 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_cistatic int snd_atiixp_free(struct atiixp *chip) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci if (chip->irq < 0) 15348c2ecf20Sopenharmony_ci goto __hw_end; 15358c2ecf20Sopenharmony_ci snd_atiixp_chip_stop(chip); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci __hw_end: 15388c2ecf20Sopenharmony_ci if (chip->irq >= 0) 15398c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 15408c2ecf20Sopenharmony_ci iounmap(chip->remap_addr); 15418c2ecf20Sopenharmony_ci pci_release_regions(chip->pci); 15428c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 15438c2ecf20Sopenharmony_ci kfree(chip); 15448c2ecf20Sopenharmony_ci return 0; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic int snd_atiixp_dev_free(struct snd_device *device) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci struct atiixp *chip = device->device_data; 15508c2ecf20Sopenharmony_ci return snd_atiixp_free(chip); 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci/* 15548c2ecf20Sopenharmony_ci * constructor for chip instance 15558c2ecf20Sopenharmony_ci */ 15568c2ecf20Sopenharmony_cistatic int snd_atiixp_create(struct snd_card *card, 15578c2ecf20Sopenharmony_ci struct pci_dev *pci, 15588c2ecf20Sopenharmony_ci struct atiixp **r_chip) 15598c2ecf20Sopenharmony_ci{ 15608c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 15618c2ecf20Sopenharmony_ci .dev_free = snd_atiixp_dev_free, 15628c2ecf20Sopenharmony_ci }; 15638c2ecf20Sopenharmony_ci struct atiixp *chip; 15648c2ecf20Sopenharmony_ci int err; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 15678c2ecf20Sopenharmony_ci return err; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 15708c2ecf20Sopenharmony_ci if (chip == NULL) { 15718c2ecf20Sopenharmony_ci pci_disable_device(pci); 15728c2ecf20Sopenharmony_ci return -ENOMEM; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 15768c2ecf20Sopenharmony_ci mutex_init(&chip->open_mutex); 15778c2ecf20Sopenharmony_ci chip->card = card; 15788c2ecf20Sopenharmony_ci chip->pci = pci; 15798c2ecf20Sopenharmony_ci chip->irq = -1; 15808c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) { 15818c2ecf20Sopenharmony_ci pci_disable_device(pci); 15828c2ecf20Sopenharmony_ci kfree(chip); 15838c2ecf20Sopenharmony_ci return err; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci chip->addr = pci_resource_start(pci, 0); 15868c2ecf20Sopenharmony_ci chip->remap_addr = pci_ioremap_bar(pci, 0); 15878c2ecf20Sopenharmony_ci if (chip->remap_addr == NULL) { 15888c2ecf20Sopenharmony_ci dev_err(card->dev, "AC'97 space ioremap problem\n"); 15898c2ecf20Sopenharmony_ci snd_atiixp_free(chip); 15908c2ecf20Sopenharmony_ci return -EIO; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED, 15948c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 15958c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 15968c2ecf20Sopenharmony_ci snd_atiixp_free(chip); 15978c2ecf20Sopenharmony_ci return -EBUSY; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci chip->irq = pci->irq; 16008c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 16018c2ecf20Sopenharmony_ci pci_set_master(pci); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 16048c2ecf20Sopenharmony_ci snd_atiixp_free(chip); 16058c2ecf20Sopenharmony_ci return err; 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci *r_chip = chip; 16098c2ecf20Sopenharmony_ci return 0; 16108c2ecf20Sopenharmony_ci} 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic int snd_atiixp_probe(struct pci_dev *pci, 16148c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci struct snd_card *card; 16178c2ecf20Sopenharmony_ci struct atiixp *chip; 16188c2ecf20Sopenharmony_ci int err; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card); 16218c2ecf20Sopenharmony_ci if (err < 0) 16228c2ecf20Sopenharmony_ci return err; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); 16258c2ecf20Sopenharmony_ci strcpy(card->shortname, "ATI IXP"); 16268c2ecf20Sopenharmony_ci if ((err = snd_atiixp_create(card, pci, &chip)) < 0) 16278c2ecf20Sopenharmony_ci goto __error; 16288c2ecf20Sopenharmony_ci card->private_data = chip; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if ((err = snd_atiixp_aclink_reset(chip)) < 0) 16318c2ecf20Sopenharmony_ci goto __error; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci chip->spdif_over_aclink = spdif_aclink; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if ((err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk)) < 0) 16368c2ecf20Sopenharmony_ci goto __error; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if ((err = snd_atiixp_pcm_new(chip)) < 0) 16398c2ecf20Sopenharmony_ci goto __error; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci snd_atiixp_proc_init(chip); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci snd_atiixp_chip_start(chip); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 16468c2ecf20Sopenharmony_ci "%s rev %x with %s at %#lx, irq %i", card->shortname, 16478c2ecf20Sopenharmony_ci pci->revision, 16488c2ecf20Sopenharmony_ci chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?", 16498c2ecf20Sopenharmony_ci chip->addr, chip->irq); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) 16528c2ecf20Sopenharmony_ci goto __error; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 16558c2ecf20Sopenharmony_ci return 0; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci __error: 16588c2ecf20Sopenharmony_ci snd_card_free(card); 16598c2ecf20Sopenharmony_ci return err; 16608c2ecf20Sopenharmony_ci} 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistatic void snd_atiixp_remove(struct pci_dev *pci) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 16658c2ecf20Sopenharmony_ci} 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_cistatic struct pci_driver atiixp_driver = { 16688c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 16698c2ecf20Sopenharmony_ci .id_table = snd_atiixp_ids, 16708c2ecf20Sopenharmony_ci .probe = snd_atiixp_probe, 16718c2ecf20Sopenharmony_ci .remove = snd_atiixp_remove, 16728c2ecf20Sopenharmony_ci .driver = { 16738c2ecf20Sopenharmony_ci .pm = SND_ATIIXP_PM_OPS, 16748c2ecf20Sopenharmony_ci }, 16758c2ecf20Sopenharmony_ci}; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_cimodule_pci_driver(atiixp_driver); 1678