162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Matt Wu <Matt_Wu@acersoftech.com.cn> 462306a36Sopenharmony_ci * Apr 26, 2001 562306a36Sopenharmony_ci * Routines for control of ALi pci audio M5451 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * BUGS: 862306a36Sopenharmony_ci * -- 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * TODO: 1162306a36Sopenharmony_ci * -- 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/pcm.h> 2462306a36Sopenharmony_ci#include <sound/info.h> 2562306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2662306a36Sopenharmony_ci#include <sound/mpu401.h> 2762306a36Sopenharmony_ci#include <sound/initval.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>"); 3062306a36Sopenharmony_ciMODULE_DESCRIPTION("ALI M5451"); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index */ 3462306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 3562306a36Sopenharmony_cistatic int pcm_channels = 32; 3662306a36Sopenharmony_cistatic bool spdif; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cimodule_param(index, int, 0444); 3962306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); 4062306a36Sopenharmony_cimodule_param(id, charp, 0444); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio."); 4262306a36Sopenharmony_cimodule_param(pcm_channels, int, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "PCM Channels"); 4462306a36Sopenharmony_cimodule_param(spdif, bool, 0444); 4562306a36Sopenharmony_ciMODULE_PARM_DESC(spdif, "Support SPDIF I/O"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* just for backward compatibility */ 4862306a36Sopenharmony_cistatic bool enable; 4962306a36Sopenharmony_cimodule_param(enable, bool, 0444); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Constants definition 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define ALI_CHANNELS 32 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define ALI_PCM_IN_CHANNEL 31 6262306a36Sopenharmony_ci#define ALI_SPDIF_IN_CHANNEL 19 6362306a36Sopenharmony_ci#define ALI_SPDIF_OUT_CHANNEL 15 6462306a36Sopenharmony_ci#define ALI_CENTER_CHANNEL 24 6562306a36Sopenharmony_ci#define ALI_LEF_CHANNEL 23 6662306a36Sopenharmony_ci#define ALI_SURR_LEFT_CHANNEL 26 6762306a36Sopenharmony_ci#define ALI_SURR_RIGHT_CHANNEL 25 6862306a36Sopenharmony_ci#define ALI_MODEM_IN_CHANNEL 21 6962306a36Sopenharmony_ci#define ALI_MODEM_OUT_CHANNEL 20 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define SNDRV_ALI_VOICE_TYPE_PCM 01 7262306a36Sopenharmony_ci#define SNDRV_ALI_VOICE_TYPE_OTH 02 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define ALI_5451_V02 0x02 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Direct Registers 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define ALI_LEGACY_DMAR0 0x00 /* ADR0 */ 8162306a36Sopenharmony_ci#define ALI_LEGACY_DMAR4 0x04 /* CNT0 */ 8262306a36Sopenharmony_ci#define ALI_LEGACY_DMAR11 0x0b /* MOD */ 8362306a36Sopenharmony_ci#define ALI_LEGACY_DMAR15 0x0f /* MMR */ 8462306a36Sopenharmony_ci#define ALI_MPUR0 0x20 8562306a36Sopenharmony_ci#define ALI_MPUR1 0x21 8662306a36Sopenharmony_ci#define ALI_MPUR2 0x22 8762306a36Sopenharmony_ci#define ALI_MPUR3 0x23 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define ALI_AC97_WRITE 0x40 9062306a36Sopenharmony_ci#define ALI_AC97_READ 0x44 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define ALI_SCTRL 0x48 9362306a36Sopenharmony_ci#define ALI_SPDIF_OUT_ENABLE 0x20 9462306a36Sopenharmony_ci#define ALI_SCTRL_LINE_IN2 (1 << 9) 9562306a36Sopenharmony_ci#define ALI_SCTRL_GPIO_IN2 (1 << 13) 9662306a36Sopenharmony_ci#define ALI_SCTRL_LINE_OUT_EN (1 << 20) 9762306a36Sopenharmony_ci#define ALI_SCTRL_GPIO_OUT_EN (1 << 23) 9862306a36Sopenharmony_ci#define ALI_SCTRL_CODEC1_READY (1 << 24) 9962306a36Sopenharmony_ci#define ALI_SCTRL_CODEC2_READY (1 << 25) 10062306a36Sopenharmony_ci#define ALI_AC97_GPIO 0x4c 10162306a36Sopenharmony_ci#define ALI_AC97_GPIO_ENABLE 0x8000 10262306a36Sopenharmony_ci#define ALI_AC97_GPIO_DATA_SHIFT 16 10362306a36Sopenharmony_ci#define ALI_SPDIF_CS 0x70 10462306a36Sopenharmony_ci#define ALI_SPDIF_CTRL 0x74 10562306a36Sopenharmony_ci#define ALI_SPDIF_IN_FUNC_ENABLE 0x02 10662306a36Sopenharmony_ci#define ALI_SPDIF_IN_CH_STATUS 0x40 10762306a36Sopenharmony_ci#define ALI_SPDIF_OUT_CH_STATUS 0xbf 10862306a36Sopenharmony_ci#define ALI_START 0x80 10962306a36Sopenharmony_ci#define ALI_STOP 0x84 11062306a36Sopenharmony_ci#define ALI_CSPF 0x90 11162306a36Sopenharmony_ci#define ALI_AINT 0x98 11262306a36Sopenharmony_ci#define ALI_GC_CIR 0xa0 11362306a36Sopenharmony_ci #define ENDLP_IE 0x00001000 11462306a36Sopenharmony_ci #define MIDLP_IE 0x00002000 11562306a36Sopenharmony_ci#define ALI_AINTEN 0xa4 11662306a36Sopenharmony_ci#define ALI_VOLUME 0xa8 11762306a36Sopenharmony_ci#define ALI_SBDELTA_DELTA_R 0xac 11862306a36Sopenharmony_ci#define ALI_MISCINT 0xb0 11962306a36Sopenharmony_ci #define ADDRESS_IRQ 0x00000020 12062306a36Sopenharmony_ci #define TARGET_REACHED 0x00008000 12162306a36Sopenharmony_ci #define MIXER_OVERFLOW 0x00000800 12262306a36Sopenharmony_ci #define MIXER_UNDERFLOW 0x00000400 12362306a36Sopenharmony_ci #define GPIO_IRQ 0x01000000 12462306a36Sopenharmony_ci#define ALI_SBBL_SBCL 0xc0 12562306a36Sopenharmony_ci#define ALI_SBCTRL_SBE2R_SBDD 0xc4 12662306a36Sopenharmony_ci#define ALI_STIMER 0xc8 12762306a36Sopenharmony_ci#define ALI_GLOBAL_CONTROL 0xd4 12862306a36Sopenharmony_ci#define ALI_SPDIF_OUT_SEL_PCM 0x00000400 /* bit 10 */ 12962306a36Sopenharmony_ci#define ALI_SPDIF_IN_SUPPORT 0x00000800 /* bit 11 */ 13062306a36Sopenharmony_ci#define ALI_SPDIF_OUT_CH_ENABLE 0x00008000 /* bit 15 */ 13162306a36Sopenharmony_ci#define ALI_SPDIF_IN_CH_ENABLE 0x00080000 /* bit 19 */ 13262306a36Sopenharmony_ci#define ALI_PCM_IN_ENABLE 0x80000000 /* bit 31 */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define ALI_CSO_ALPHA_FMS 0xe0 13562306a36Sopenharmony_ci#define ALI_LBA 0xe4 13662306a36Sopenharmony_ci#define ALI_ESO_DELTA 0xe8 13762306a36Sopenharmony_ci#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 13862306a36Sopenharmony_ci#define ALI_EBUF1 0xf4 13962306a36Sopenharmony_ci#define ALI_EBUF2 0xf8 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define ALI_REG(codec, x) ((codec)->port + x) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define MAX_CODECS 2 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct snd_ali; 14762306a36Sopenharmony_cistruct snd_ali_voice; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistruct snd_ali_channel_control { 15062306a36Sopenharmony_ci /* register data */ 15162306a36Sopenharmony_ci struct REGDATA { 15262306a36Sopenharmony_ci unsigned int start; 15362306a36Sopenharmony_ci unsigned int stop; 15462306a36Sopenharmony_ci unsigned int aint; 15562306a36Sopenharmony_ci unsigned int ainten; 15662306a36Sopenharmony_ci } data; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* register addresses */ 15962306a36Sopenharmony_ci struct REGS { 16062306a36Sopenharmony_ci unsigned int start; 16162306a36Sopenharmony_ci unsigned int stop; 16262306a36Sopenharmony_ci unsigned int aint; 16362306a36Sopenharmony_ci unsigned int ainten; 16462306a36Sopenharmony_ci unsigned int ac97read; 16562306a36Sopenharmony_ci unsigned int ac97write; 16662306a36Sopenharmony_ci } regs; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistruct snd_ali_voice { 17162306a36Sopenharmony_ci unsigned int number; 17262306a36Sopenharmony_ci unsigned int use :1, 17362306a36Sopenharmony_ci pcm :1, 17462306a36Sopenharmony_ci midi :1, 17562306a36Sopenharmony_ci mode :1, 17662306a36Sopenharmony_ci synth :1, 17762306a36Sopenharmony_ci running :1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* PCM data */ 18062306a36Sopenharmony_ci struct snd_ali *codec; 18162306a36Sopenharmony_ci struct snd_pcm_substream *substream; 18262306a36Sopenharmony_ci struct snd_ali_voice *extra; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci int eso; /* final ESO value for channel */ 18562306a36Sopenharmony_ci int count; /* runtime->period_size */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* --- */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci void *private_data; 19062306a36Sopenharmony_ci void (*private_free)(void *private_data); 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistruct snd_alidev { 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci struct snd_ali_voice voices[ALI_CHANNELS]; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci unsigned int chcnt; /* num of opened channels */ 19962306a36Sopenharmony_ci unsigned int chmap; /* bitmap for opened channels */ 20062306a36Sopenharmony_ci unsigned int synthcount; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci#define ALI_GLOBAL_REGS 56 20662306a36Sopenharmony_ci#define ALI_CHANNEL_REGS 8 20762306a36Sopenharmony_cistruct snd_ali_image { 20862306a36Sopenharmony_ci u32 regs[ALI_GLOBAL_REGS]; 20962306a36Sopenharmony_ci u32 channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistruct snd_ali { 21462306a36Sopenharmony_ci int irq; 21562306a36Sopenharmony_ci unsigned long port; 21662306a36Sopenharmony_ci unsigned char revision; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci unsigned int hw_initialized :1; 21962306a36Sopenharmony_ci unsigned int spdif_support :1; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci struct pci_dev *pci; 22262306a36Sopenharmony_ci struct pci_dev *pci_m1533; 22362306a36Sopenharmony_ci struct pci_dev *pci_m7101; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci struct snd_card *card; 22662306a36Sopenharmony_ci struct snd_pcm *pcm[MAX_CODECS]; 22762306a36Sopenharmony_ci struct snd_alidev synth; 22862306a36Sopenharmony_ci struct snd_ali_channel_control chregs; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* S/PDIF Mask */ 23162306a36Sopenharmony_ci unsigned int spdif_mask; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci unsigned int spurious_irq_count; 23462306a36Sopenharmony_ci unsigned int spurious_irq_max_delta; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci unsigned int num_of_codecs; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 23962306a36Sopenharmony_ci struct snd_ac97 *ac97[MAX_CODECS]; 24062306a36Sopenharmony_ci unsigned short ac97_ext_id; 24162306a36Sopenharmony_ci unsigned short ac97_ext_status; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci spinlock_t reg_lock; 24462306a36Sopenharmony_ci spinlock_t voice_alloc; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 24762306a36Sopenharmony_ci struct snd_ali_image *image; 24862306a36Sopenharmony_ci#endif 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic const struct pci_device_id snd_ali_ids[] = { 25262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0}, 25362306a36Sopenharmony_ci {0, } 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ali_ids); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *, unsigned int, unsigned int); 25862306a36Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *, int, unsigned short); 25962306a36Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *, int, unsigned short, 26062306a36Sopenharmony_ci unsigned short); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * AC97 ACCESS 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic inline unsigned int snd_ali_5451_peek(struct snd_ali *codec, 26762306a36Sopenharmony_ci unsigned int port) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return (unsigned int)inl(ALI_REG(codec, port)); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic inline void snd_ali_5451_poke(struct snd_ali *codec, 27362306a36Sopenharmony_ci unsigned int port, 27462306a36Sopenharmony_ci unsigned int val) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci outl((unsigned int)val, ALI_REG(codec, port)); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int snd_ali_codec_ready(struct snd_ali *codec, 28062306a36Sopenharmony_ci unsigned int port) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci unsigned long end_time; 28362306a36Sopenharmony_ci unsigned int res; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(250); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci for (;;) { 28862306a36Sopenharmony_ci res = snd_ali_5451_peek(codec,port); 28962306a36Sopenharmony_ci if (!(res & 0x8000)) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci if (!time_after_eq(end_time, jiffies)) 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci snd_ali_5451_poke(codec, port, res & ~0x8000); 29762306a36Sopenharmony_ci dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n "); 29862306a36Sopenharmony_ci return -EIO; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int snd_ali_stimer_ready(struct snd_ali *codec) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci unsigned long end_time; 30462306a36Sopenharmony_ci unsigned long dwChk1,dwChk2; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); 30762306a36Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(250); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci for (;;) { 31062306a36Sopenharmony_ci dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); 31162306a36Sopenharmony_ci if (dwChk2 != dwChk1) 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci if (!time_after_eq(end_time, jiffies)) 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_stimer_read: stimer is not ready.\n"); 31962306a36Sopenharmony_ci return -EIO; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *codec,int secondary, 32362306a36Sopenharmony_ci unsigned short reg, 32462306a36Sopenharmony_ci unsigned short val) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned int dwVal; 32762306a36Sopenharmony_ci unsigned int port; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (reg >= 0x80) { 33062306a36Sopenharmony_ci dev_err(codec->card->dev, 33162306a36Sopenharmony_ci "ali_codec_poke: reg(%xh) invalid.\n", reg); 33262306a36Sopenharmony_ci return; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci port = codec->chregs.regs.ac97write; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci dwVal = (unsigned int) (reg & 0xff); 34362306a36Sopenharmony_ci dwVal |= 0x8000 | (val << 16); 34462306a36Sopenharmony_ci if (secondary) 34562306a36Sopenharmony_ci dwVal |= 0x0080; 34662306a36Sopenharmony_ci if (codec->revision == ALI_5451_V02) 34762306a36Sopenharmony_ci dwVal |= 0x0100; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci snd_ali_5451_poke(codec, port, dwVal); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return ; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *codec, 35562306a36Sopenharmony_ci int secondary, 35662306a36Sopenharmony_ci unsigned short reg) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned int dwVal; 35962306a36Sopenharmony_ci unsigned int port; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (reg >= 0x80) { 36262306a36Sopenharmony_ci dev_err(codec->card->dev, 36362306a36Sopenharmony_ci "ali_codec_peek: reg(%xh) invalid.\n", reg); 36462306a36Sopenharmony_ci return ~0; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci port = codec->chregs.regs.ac97read; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 37062306a36Sopenharmony_ci return ~0; 37162306a36Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 37262306a36Sopenharmony_ci return ~0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci dwVal = (unsigned int) (reg & 0xff); 37562306a36Sopenharmony_ci dwVal |= 0x8000; /* bit 15*/ 37662306a36Sopenharmony_ci if (secondary) 37762306a36Sopenharmony_ci dwVal |= 0x0080; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci snd_ali_5451_poke(codec, port, dwVal); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 38262306a36Sopenharmony_ci return ~0; 38362306a36Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 38462306a36Sopenharmony_ci return ~0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return (snd_ali_5451_peek(codec, port) & 0xffff0000) >> 16; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void snd_ali_codec_write(struct snd_ac97 *ac97, 39062306a36Sopenharmony_ci unsigned short reg, 39162306a36Sopenharmony_ci unsigned short val ) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct snd_ali *codec = ac97->private_data; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dev_dbg(codec->card->dev, "codec_write: reg=%xh data=%xh.\n", reg, val); 39662306a36Sopenharmony_ci if (reg == AC97_GPIO_STATUS) { 39762306a36Sopenharmony_ci outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE, 39862306a36Sopenharmony_ci ALI_REG(codec, ALI_AC97_GPIO)); 39962306a36Sopenharmony_ci return; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci snd_ali_codec_poke(codec, ac97->num, reg, val); 40262306a36Sopenharmony_ci return ; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic unsigned short snd_ali_codec_read(struct snd_ac97 *ac97, 40762306a36Sopenharmony_ci unsigned short reg) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct snd_ali *codec = ac97->private_data; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci dev_dbg(codec->card->dev, "codec_read reg=%xh.\n", reg); 41262306a36Sopenharmony_ci return snd_ali_codec_peek(codec, ac97->num, reg); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* 41662306a36Sopenharmony_ci * AC97 Reset 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int snd_ali_reset_5451(struct snd_ali *codec) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct pci_dev *pci_dev; 42262306a36Sopenharmony_ci unsigned short wCount, wReg; 42362306a36Sopenharmony_ci unsigned int dwVal; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci pci_dev = codec->pci_m1533; 42662306a36Sopenharmony_ci if (pci_dev) { 42762306a36Sopenharmony_ci pci_read_config_dword(pci_dev, 0x7c, &dwVal); 42862306a36Sopenharmony_ci pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); 42962306a36Sopenharmony_ci mdelay(5); 43062306a36Sopenharmony_ci pci_read_config_dword(pci_dev, 0x7c, &dwVal); 43162306a36Sopenharmony_ci pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); 43262306a36Sopenharmony_ci mdelay(5); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci pci_dev = codec->pci; 43662306a36Sopenharmony_ci pci_read_config_dword(pci_dev, 0x44, &dwVal); 43762306a36Sopenharmony_ci pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); 43862306a36Sopenharmony_ci udelay(500); 43962306a36Sopenharmony_ci pci_read_config_dword(pci_dev, 0x44, &dwVal); 44062306a36Sopenharmony_ci pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); 44162306a36Sopenharmony_ci mdelay(5); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci wCount = 200; 44462306a36Sopenharmony_ci while(wCount--) { 44562306a36Sopenharmony_ci wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); 44662306a36Sopenharmony_ci if ((wReg & 0x000f) == 0x000f) 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci mdelay(5); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* non-fatal if you have a non PM capable codec */ 45262306a36Sopenharmony_ci /* dev_warn(codec->card->dev, "ali5451: reset time out\n"); */ 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* 45762306a36Sopenharmony_ci * ALI 5451 Controller 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void snd_ali_enable_special_channel(struct snd_ali *codec, 46162306a36Sopenharmony_ci unsigned int channel) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci unsigned long dwVal; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 46662306a36Sopenharmony_ci dwVal |= 1 << (channel & 0x0000001f); 46762306a36Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void snd_ali_disable_special_channel(struct snd_ali *codec, 47162306a36Sopenharmony_ci unsigned int channel) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci unsigned long dwVal; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 47662306a36Sopenharmony_ci dwVal &= ~(1 << (channel & 0x0000001f)); 47762306a36Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void snd_ali_enable_address_interrupt(struct snd_ali *codec) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci unsigned int gc; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci gc = inl(ALI_REG(codec, ALI_GC_CIR)); 48562306a36Sopenharmony_ci gc |= ENDLP_IE; 48662306a36Sopenharmony_ci gc |= MIDLP_IE; 48762306a36Sopenharmony_ci outl( gc, ALI_REG(codec, ALI_GC_CIR)); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void snd_ali_disable_address_interrupt(struct snd_ali *codec) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci unsigned int gc; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci gc = inl(ALI_REG(codec, ALI_GC_CIR)); 49562306a36Sopenharmony_ci gc &= ~ENDLP_IE; 49662306a36Sopenharmony_ci gc &= ~MIDLP_IE; 49762306a36Sopenharmony_ci outl(gc, ALI_REG(codec, ALI_GC_CIR)); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void snd_ali_disable_voice_irq(struct snd_ali *codec, 50162306a36Sopenharmony_ci unsigned int channel) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci unsigned int mask; 50462306a36Sopenharmony_ci struct snd_ali_channel_control *pchregs = &(codec->chregs); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci dev_dbg(codec->card->dev, "disable_voice_irq channel=%d\n", channel); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci mask = 1 << (channel & 0x1f); 50962306a36Sopenharmony_ci pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten)); 51062306a36Sopenharmony_ci pchregs->data.ainten &= ~mask; 51162306a36Sopenharmony_ci outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci unsigned int idx = channel & 0x1f; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (codec->synth.chcnt >= ALI_CHANNELS){ 51962306a36Sopenharmony_ci dev_err(codec->card->dev, 52062306a36Sopenharmony_ci "ali_alloc_pcm_channel: no free channels.\n"); 52162306a36Sopenharmony_ci return -1; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!(codec->synth.chmap & (1 << idx))) { 52562306a36Sopenharmony_ci codec->synth.chmap |= 1 << idx; 52662306a36Sopenharmony_ci codec->synth.chcnt++; 52762306a36Sopenharmony_ci dev_dbg(codec->card->dev, "alloc_pcm_channel no. %d.\n", idx); 52862306a36Sopenharmony_ci return idx; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci return -1; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int snd_ali_find_free_channel(struct snd_ali * codec, int rec) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci int idx; 53662306a36Sopenharmony_ci int result = -1; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci dev_dbg(codec->card->dev, 53962306a36Sopenharmony_ci "find_free_channel: for %s\n", rec ? "rec" : "pcm"); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* recording */ 54262306a36Sopenharmony_ci if (rec) { 54362306a36Sopenharmony_ci if (codec->spdif_support && 54462306a36Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 54562306a36Sopenharmony_ci ALI_SPDIF_IN_SUPPORT)) 54662306a36Sopenharmony_ci idx = ALI_SPDIF_IN_CHANNEL; 54762306a36Sopenharmony_ci else 54862306a36Sopenharmony_ci idx = ALI_PCM_IN_CHANNEL; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 55162306a36Sopenharmony_ci if (result >= 0) 55262306a36Sopenharmony_ci return result; 55362306a36Sopenharmony_ci else { 55462306a36Sopenharmony_ci dev_err(codec->card->dev, 55562306a36Sopenharmony_ci "ali_find_free_channel: record channel is busy now.\n"); 55662306a36Sopenharmony_ci return -1; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* playback... */ 56162306a36Sopenharmony_ci if (codec->spdif_support && 56262306a36Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 56362306a36Sopenharmony_ci ALI_SPDIF_OUT_CH_ENABLE)) { 56462306a36Sopenharmony_ci idx = ALI_SPDIF_OUT_CHANNEL; 56562306a36Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 56662306a36Sopenharmony_ci if (result >= 0) 56762306a36Sopenharmony_ci return result; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci dev_err(codec->card->dev, 57062306a36Sopenharmony_ci "ali_find_free_channel: S/PDIF out channel is in busy now.\n"); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci for (idx = 0; idx < ALI_CHANNELS; idx++) { 57462306a36Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 57562306a36Sopenharmony_ci if (result >= 0) 57662306a36Sopenharmony_ci return result; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_find_free_channel: no free channels.\n"); 57962306a36Sopenharmony_ci return -1; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci unsigned int idx = channel & 0x0000001f; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci dev_dbg(codec->card->dev, "free_channel_pcm channel=%d\n", channel); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (channel < 0 || channel >= ALI_CHANNELS) 58962306a36Sopenharmony_ci return; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (!(codec->synth.chmap & (1 << idx))) { 59262306a36Sopenharmony_ci dev_err(codec->card->dev, 59362306a36Sopenharmony_ci "ali_free_channel_pcm: channel %d is not in use.\n", 59462306a36Sopenharmony_ci channel); 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci } else { 59762306a36Sopenharmony_ci codec->synth.chmap &= ~(1 << idx); 59862306a36Sopenharmony_ci codec->synth.chcnt--; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci unsigned int mask = 1 << (channel & 0x1f); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci dev_dbg(codec->card->dev, "stop_voice: channel=%d\n", channel); 60762306a36Sopenharmony_ci outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* 61162306a36Sopenharmony_ci * S/PDIF Part 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void snd_ali_delay(struct snd_ali *codec,int interval) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci unsigned long begintimer,currenttimer; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci begintimer = inl(ALI_REG(codec, ALI_STIMER)); 61962306a36Sopenharmony_ci currenttimer = inl(ALI_REG(codec, ALI_STIMER)); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci while (currenttimer < begintimer + interval) { 62262306a36Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci currenttimer = inl(ALI_REG(codec, ALI_STIMER)); 62562306a36Sopenharmony_ci cpu_relax(); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void snd_ali_detect_spdif_rate(struct snd_ali *codec) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci u16 wval; 63262306a36Sopenharmony_ci u16 count = 0; 63362306a36Sopenharmony_ci u8 bval, R1 = 0, R2; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 63662306a36Sopenharmony_ci bval |= 0x1F; 63762306a36Sopenharmony_ci outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci while ((R1 < 0x0b || R1 > 0x0e) && R1 != 0x12 && count <= 50000) { 64062306a36Sopenharmony_ci count ++; 64162306a36Sopenharmony_ci snd_ali_delay(codec, 6); 64262306a36Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 64362306a36Sopenharmony_ci R1 = bval & 0x1F; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (count > 50000) { 64762306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n"); 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci for (count = 0; count <= 50000; count++) { 65262306a36Sopenharmony_ci snd_ali_delay(codec, 6); 65362306a36Sopenharmony_ci bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); 65462306a36Sopenharmony_ci R2 = bval & 0x1F; 65562306a36Sopenharmony_ci if (R2 != R1) 65662306a36Sopenharmony_ci R1 = R2; 65762306a36Sopenharmony_ci else 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (count > 50000) { 66262306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n"); 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (R2 >= 0x0b && R2 <= 0x0e) { 66762306a36Sopenharmony_ci wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 66862306a36Sopenharmony_ci wval &= 0xe0f0; 66962306a36Sopenharmony_ci wval |= (0x09 << 8) | 0x05; 67062306a36Sopenharmony_ci outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)) & 0xf0; 67362306a36Sopenharmony_ci outb(bval | 0x02, ALI_REG(codec, ALI_SPDIF_CS + 3)); 67462306a36Sopenharmony_ci } else if (R2 == 0x12) { 67562306a36Sopenharmony_ci wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 67662306a36Sopenharmony_ci wval &= 0xe0f0; 67762306a36Sopenharmony_ci wval |= (0x0e << 8) | 0x08; 67862306a36Sopenharmony_ci outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)) & 0xf0; 68162306a36Sopenharmony_ci outb(bval | 0x03, ALI_REG(codec, ALI_SPDIF_CS + 3)); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic unsigned int snd_ali_get_spdif_in_rate(struct snd_ali *codec) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci u32 dwRate; 68862306a36Sopenharmony_ci u8 bval; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 69162306a36Sopenharmony_ci bval &= 0x7f; 69262306a36Sopenharmony_ci bval |= 0x40; 69362306a36Sopenharmony_ci outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL)); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci snd_ali_detect_spdif_rate(codec); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)); 69862306a36Sopenharmony_ci bval &= 0x0f; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci switch (bval) { 70162306a36Sopenharmony_ci case 0: dwRate = 44100; break; 70262306a36Sopenharmony_ci case 1: dwRate = 48000; break; 70362306a36Sopenharmony_ci case 2: dwRate = 32000; break; 70462306a36Sopenharmony_ci default: dwRate = 0; break; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return dwRate; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic void snd_ali_enable_spdif_in(struct snd_ali *codec) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci unsigned int dwVal; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 71562306a36Sopenharmony_ci dwVal |= ALI_SPDIF_IN_SUPPORT; 71662306a36Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 71962306a36Sopenharmony_ci dwVal |= 0x02; 72062306a36Sopenharmony_ci outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void snd_ali_disable_spdif_in(struct snd_ali *codec) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci unsigned int dwVal; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 73062306a36Sopenharmony_ci dwVal &= ~ALI_SPDIF_IN_SUPPORT; 73162306a36Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic void snd_ali_set_spdif_out_rate(struct snd_ali *codec, unsigned int rate) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci unsigned char bVal; 74062306a36Sopenharmony_ci unsigned int dwRate; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci switch (rate) { 74362306a36Sopenharmony_ci case 32000: dwRate = 0x300; break; 74462306a36Sopenharmony_ci case 48000: dwRate = 0x200; break; 74562306a36Sopenharmony_ci default: dwRate = 0; break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 74962306a36Sopenharmony_ci bVal &= (unsigned char)(~(1<<6)); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci bVal |= 0x80; /* select right */ 75262306a36Sopenharmony_ci outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 75362306a36Sopenharmony_ci outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci bVal &= ~0x80; /* select left */ 75662306a36Sopenharmony_ci outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 75762306a36Sopenharmony_ci outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic void snd_ali_enable_spdif_out(struct snd_ali *codec) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci unsigned short wVal; 76362306a36Sopenharmony_ci unsigned char bVal; 76462306a36Sopenharmony_ci struct pci_dev *pci_dev; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci pci_dev = codec->pci_m1533; 76762306a36Sopenharmony_ci if (pci_dev == NULL) 76862306a36Sopenharmony_ci return; 76962306a36Sopenharmony_ci pci_read_config_byte(pci_dev, 0x61, &bVal); 77062306a36Sopenharmony_ci bVal |= 0x40; 77162306a36Sopenharmony_ci pci_write_config_byte(pci_dev, 0x61, bVal); 77262306a36Sopenharmony_ci pci_read_config_byte(pci_dev, 0x7d, &bVal); 77362306a36Sopenharmony_ci bVal |= 0x01; 77462306a36Sopenharmony_ci pci_write_config_byte(pci_dev, 0x7d, bVal); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci pci_read_config_byte(pci_dev, 0x7e, &bVal); 77762306a36Sopenharmony_ci bVal &= (~0x20); 77862306a36Sopenharmony_ci bVal |= 0x10; 77962306a36Sopenharmony_ci pci_write_config_byte(pci_dev, 0x7e, bVal); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SCTRL)); 78262306a36Sopenharmony_ci outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 78562306a36Sopenharmony_ci outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL)); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 78862306a36Sopenharmony_ci wVal |= ALI_SPDIF_OUT_SEL_PCM; 78962306a36Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 79062306a36Sopenharmony_ci snd_ali_disable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void snd_ali_enable_spdif_chnout(struct snd_ali *codec) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci unsigned short wVal; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 79862306a36Sopenharmony_ci wVal &= ~ALI_SPDIF_OUT_SEL_PCM; 79962306a36Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 80062306a36Sopenharmony_ci/* 80162306a36Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); 80262306a36Sopenharmony_ci if (flag & ALI_SPDIF_OUT_NON_PCM) 80362306a36Sopenharmony_ci wVal |= 0x0002; 80462306a36Sopenharmony_ci else 80562306a36Sopenharmony_ci wVal &= (~0x0002); 80662306a36Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); 80762306a36Sopenharmony_ci*/ 80862306a36Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void snd_ali_disable_spdif_chnout(struct snd_ali *codec) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci unsigned short wVal; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 81662306a36Sopenharmony_ci wVal |= ALI_SPDIF_OUT_SEL_PCM; 81762306a36Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic void snd_ali_disable_spdif_out(struct snd_ali *codec) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci unsigned char bVal; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SCTRL)); 82762306a36Sopenharmony_ci outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci snd_ali_disable_spdif_chnout(codec); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic void snd_ali_update_ptr(struct snd_ali *codec, int channel) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct snd_ali_voice *pvoice; 83562306a36Sopenharmony_ci struct snd_ali_channel_control *pchregs; 83662306a36Sopenharmony_ci unsigned int old, mask; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pchregs = &(codec->chregs); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* check if interrupt occurred for channel */ 84162306a36Sopenharmony_ci old = pchregs->data.aint; 84262306a36Sopenharmony_ci mask = 1U << (channel & 0x1f); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (!(old & mask)) 84562306a36Sopenharmony_ci return; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci pvoice = &codec->synth.voices[channel]; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci udelay(100); 85062306a36Sopenharmony_ci spin_lock(&codec->reg_lock); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (pvoice->pcm && pvoice->substream) { 85362306a36Sopenharmony_ci /* pcm interrupt */ 85462306a36Sopenharmony_ci if (pvoice->running) { 85562306a36Sopenharmony_ci dev_dbg(codec->card->dev, 85662306a36Sopenharmony_ci "update_ptr: cso=%4.4x cspf=%d.\n", 85762306a36Sopenharmony_ci inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)), 85862306a36Sopenharmony_ci (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask); 85962306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 86062306a36Sopenharmony_ci snd_pcm_period_elapsed(pvoice->substream); 86162306a36Sopenharmony_ci spin_lock(&codec->reg_lock); 86262306a36Sopenharmony_ci } else { 86362306a36Sopenharmony_ci snd_ali_stop_voice(codec, channel); 86462306a36Sopenharmony_ci snd_ali_disable_voice_irq(codec, channel); 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci } else if (codec->synth.voices[channel].synth) { 86762306a36Sopenharmony_ci /* synth interrupt */ 86862306a36Sopenharmony_ci } else if (codec->synth.voices[channel].midi) { 86962306a36Sopenharmony_ci /* midi interrupt */ 87062306a36Sopenharmony_ci } else { 87162306a36Sopenharmony_ci /* unknown interrupt */ 87262306a36Sopenharmony_ci snd_ali_stop_voice(codec, channel); 87362306a36Sopenharmony_ci snd_ali_disable_voice_irq(codec, channel); 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 87662306a36Sopenharmony_ci outl(mask,ALI_REG(codec,pchregs->regs.aint)); 87762306a36Sopenharmony_ci pchregs->data.aint = old & (~mask); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic irqreturn_t snd_ali_card_interrupt(int irq, void *dev_id) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct snd_ali *codec = dev_id; 88362306a36Sopenharmony_ci int channel; 88462306a36Sopenharmony_ci unsigned int audio_int; 88562306a36Sopenharmony_ci struct snd_ali_channel_control *pchregs; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (codec == NULL || !codec->hw_initialized) 88862306a36Sopenharmony_ci return IRQ_NONE; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci audio_int = inl(ALI_REG(codec, ALI_MISCINT)); 89162306a36Sopenharmony_ci if (!audio_int) 89262306a36Sopenharmony_ci return IRQ_NONE; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci pchregs = &(codec->chregs); 89562306a36Sopenharmony_ci if (audio_int & ADDRESS_IRQ) { 89662306a36Sopenharmony_ci /* get interrupt status for all channels */ 89762306a36Sopenharmony_ci pchregs->data.aint = inl(ALI_REG(codec, pchregs->regs.aint)); 89862306a36Sopenharmony_ci for (channel = 0; channel < ALI_CHANNELS; channel++) 89962306a36Sopenharmony_ci snd_ali_update_ptr(codec, channel); 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), 90262306a36Sopenharmony_ci ALI_REG(codec, ALI_MISCINT)); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return IRQ_HANDLED; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec, 90962306a36Sopenharmony_ci int type, int rec, int channel) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct snd_ali_voice *pvoice; 91262306a36Sopenharmony_ci int idx; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci spin_lock_irq(&codec->voice_alloc); 91762306a36Sopenharmony_ci if (type == SNDRV_ALI_VOICE_TYPE_PCM) { 91862306a36Sopenharmony_ci idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) : 91962306a36Sopenharmony_ci snd_ali_find_free_channel(codec,rec); 92062306a36Sopenharmony_ci if (idx < 0) { 92162306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_alloc_voice: err.\n"); 92262306a36Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 92362306a36Sopenharmony_ci return NULL; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci pvoice = &(codec->synth.voices[idx]); 92662306a36Sopenharmony_ci pvoice->codec = codec; 92762306a36Sopenharmony_ci pvoice->use = 1; 92862306a36Sopenharmony_ci pvoice->pcm = 1; 92962306a36Sopenharmony_ci pvoice->mode = rec; 93062306a36Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 93162306a36Sopenharmony_ci return pvoice; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 93462306a36Sopenharmony_ci return NULL; 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void snd_ali_free_voice(struct snd_ali * codec, 93962306a36Sopenharmony_ci struct snd_ali_voice *pvoice) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci void (*private_free)(void *); 94262306a36Sopenharmony_ci void *private_data; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci dev_dbg(codec->card->dev, "free_voice: channel=%d\n", pvoice->number); 94562306a36Sopenharmony_ci if (!pvoice->use) 94662306a36Sopenharmony_ci return; 94762306a36Sopenharmony_ci snd_ali_clear_voices(codec, pvoice->number, pvoice->number); 94862306a36Sopenharmony_ci spin_lock_irq(&codec->voice_alloc); 94962306a36Sopenharmony_ci private_free = pvoice->private_free; 95062306a36Sopenharmony_ci private_data = pvoice->private_data; 95162306a36Sopenharmony_ci pvoice->private_free = NULL; 95262306a36Sopenharmony_ci pvoice->private_data = NULL; 95362306a36Sopenharmony_ci if (pvoice->pcm) 95462306a36Sopenharmony_ci snd_ali_free_channel_pcm(codec, pvoice->number); 95562306a36Sopenharmony_ci pvoice->use = pvoice->pcm = pvoice->synth = 0; 95662306a36Sopenharmony_ci pvoice->substream = NULL; 95762306a36Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 95862306a36Sopenharmony_ci if (private_free) 95962306a36Sopenharmony_ci private_free(private_data); 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *codec, 96462306a36Sopenharmony_ci unsigned int v_min, 96562306a36Sopenharmony_ci unsigned int v_max) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci unsigned int i; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci for (i = v_min; i <= v_max; i++) { 97062306a36Sopenharmony_ci snd_ali_stop_voice(codec, i); 97162306a36Sopenharmony_ci snd_ali_disable_voice_irq(codec, i); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void snd_ali_write_voice_regs(struct snd_ali *codec, 97662306a36Sopenharmony_ci unsigned int Channel, 97762306a36Sopenharmony_ci unsigned int LBA, 97862306a36Sopenharmony_ci unsigned int CSO, 97962306a36Sopenharmony_ci unsigned int ESO, 98062306a36Sopenharmony_ci unsigned int DELTA, 98162306a36Sopenharmony_ci unsigned int ALPHA_FMS, 98262306a36Sopenharmony_ci unsigned int GVSEL, 98362306a36Sopenharmony_ci unsigned int PAN, 98462306a36Sopenharmony_ci unsigned int VOL, 98562306a36Sopenharmony_ci unsigned int CTRL, 98662306a36Sopenharmony_ci unsigned int EC) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci unsigned int ctlcmds[4]; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci outb((unsigned char)(Channel & 0x001f), ALI_REG(codec, ALI_GC_CIR)); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); 99362306a36Sopenharmony_ci ctlcmds[1] = LBA; 99462306a36Sopenharmony_ci ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); 99562306a36Sopenharmony_ci ctlcmds[3] = (GVSEL << 31) | 99662306a36Sopenharmony_ci ((PAN & 0x0000007f) << 24) | 99762306a36Sopenharmony_ci ((VOL & 0x000000ff) << 16) | 99862306a36Sopenharmony_ci ((CTRL & 0x0000000f) << 12) | 99962306a36Sopenharmony_ci (EC & 0x00000fff); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci outb(Channel, ALI_REG(codec, ALI_GC_CIR)); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci outl(ctlcmds[0], ALI_REG(codec, ALI_CSO_ALPHA_FMS)); 100462306a36Sopenharmony_ci outl(ctlcmds[1], ALI_REG(codec, ALI_LBA)); 100562306a36Sopenharmony_ci outl(ctlcmds[2], ALI_REG(codec, ALI_ESO_DELTA)); 100662306a36Sopenharmony_ci outl(ctlcmds[3], ALI_REG(codec, ALI_GVSEL_PAN_VOC_CTRL_EC)); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ 100962306a36Sopenharmony_ci outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic unsigned int snd_ali_convert_rate(unsigned int rate, int rec) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci unsigned int delta; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (rate < 4000) 101762306a36Sopenharmony_ci rate = 4000; 101862306a36Sopenharmony_ci if (rate > 48000) 101962306a36Sopenharmony_ci rate = 48000; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (rec) { 102262306a36Sopenharmony_ci if (rate == 44100) 102362306a36Sopenharmony_ci delta = 0x116a; 102462306a36Sopenharmony_ci else if (rate == 8000) 102562306a36Sopenharmony_ci delta = 0x6000; 102662306a36Sopenharmony_ci else if (rate == 48000) 102762306a36Sopenharmony_ci delta = 0x1000; 102862306a36Sopenharmony_ci else 102962306a36Sopenharmony_ci delta = ((48000 << 12) / rate) & 0x0000ffff; 103062306a36Sopenharmony_ci } else { 103162306a36Sopenharmony_ci if (rate == 44100) 103262306a36Sopenharmony_ci delta = 0xeb3; 103362306a36Sopenharmony_ci else if (rate == 8000) 103462306a36Sopenharmony_ci delta = 0x2ab; 103562306a36Sopenharmony_ci else if (rate == 48000) 103662306a36Sopenharmony_ci delta = 0x1000; 103762306a36Sopenharmony_ci else 103862306a36Sopenharmony_ci delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return delta; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic unsigned int snd_ali_control_mode(struct snd_pcm_substream *substream) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci unsigned int CTRL; 104762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* set ctrl mode 105062306a36Sopenharmony_ci CTRL default: 8-bit (unsigned) mono, loop mode enabled 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ci CTRL = 0x00000001; 105362306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 105462306a36Sopenharmony_ci CTRL |= 0x00000008; /* 16-bit data */ 105562306a36Sopenharmony_ci if (!snd_pcm_format_unsigned(runtime->format)) 105662306a36Sopenharmony_ci CTRL |= 0x00000002; /* signed data */ 105762306a36Sopenharmony_ci if (runtime->channels > 1) 105862306a36Sopenharmony_ci CTRL |= 0x00000004; /* stereo data */ 105962306a36Sopenharmony_ci return CTRL; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci/* 106362306a36Sopenharmony_ci * PCM part 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic int snd_ali_trigger(struct snd_pcm_substream *substream, 106762306a36Sopenharmony_ci int cmd) 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 107162306a36Sopenharmony_ci struct snd_pcm_substream *s; 107262306a36Sopenharmony_ci unsigned int what, whati; 107362306a36Sopenharmony_ci struct snd_ali_voice *pvoice, *evoice; 107462306a36Sopenharmony_ci unsigned int val; 107562306a36Sopenharmony_ci int do_start; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci switch (cmd) { 107862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 107962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 108062306a36Sopenharmony_ci do_start = 1; 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 108362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 108462306a36Sopenharmony_ci do_start = 0; 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci default: 108762306a36Sopenharmony_ci return -EINVAL; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci what = whati = 0; 109162306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 109262306a36Sopenharmony_ci if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) { 109362306a36Sopenharmony_ci pvoice = s->runtime->private_data; 109462306a36Sopenharmony_ci evoice = pvoice->extra; 109562306a36Sopenharmony_ci what |= 1 << (pvoice->number & 0x1f); 109662306a36Sopenharmony_ci if (evoice == NULL) 109762306a36Sopenharmony_ci whati |= 1 << (pvoice->number & 0x1f); 109862306a36Sopenharmony_ci else { 109962306a36Sopenharmony_ci whati |= 1 << (evoice->number & 0x1f); 110062306a36Sopenharmony_ci what |= 1 << (evoice->number & 0x1f); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci if (do_start) { 110362306a36Sopenharmony_ci pvoice->running = 1; 110462306a36Sopenharmony_ci if (evoice != NULL) 110562306a36Sopenharmony_ci evoice->running = 1; 110662306a36Sopenharmony_ci } else { 110762306a36Sopenharmony_ci pvoice->running = 0; 110862306a36Sopenharmony_ci if (evoice != NULL) 110962306a36Sopenharmony_ci evoice->running = 0; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci snd_pcm_trigger_done(s, substream); 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci spin_lock(&codec->reg_lock); 111562306a36Sopenharmony_ci if (!do_start) 111662306a36Sopenharmony_ci outl(what, ALI_REG(codec, ALI_STOP)); 111762306a36Sopenharmony_ci val = inl(ALI_REG(codec, ALI_AINTEN)); 111862306a36Sopenharmony_ci if (do_start) 111962306a36Sopenharmony_ci val |= whati; 112062306a36Sopenharmony_ci else 112162306a36Sopenharmony_ci val &= ~whati; 112262306a36Sopenharmony_ci outl(val, ALI_REG(codec, ALI_AINTEN)); 112362306a36Sopenharmony_ci if (do_start) 112462306a36Sopenharmony_ci outl(what, ALI_REG(codec, ALI_START)); 112562306a36Sopenharmony_ci dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati); 112662306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci return 0; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic int snd_ali_playback_hw_params(struct snd_pcm_substream *substream, 113262306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 113562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 113662306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 113762306a36Sopenharmony_ci struct snd_ali_voice *evoice = pvoice->extra; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* voice management */ 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (params_buffer_size(hw_params) / 2 != 114262306a36Sopenharmony_ci params_period_size(hw_params)) { 114362306a36Sopenharmony_ci if (!evoice) { 114462306a36Sopenharmony_ci evoice = snd_ali_alloc_voice(codec, 114562306a36Sopenharmony_ci SNDRV_ALI_VOICE_TYPE_PCM, 114662306a36Sopenharmony_ci 0, -1); 114762306a36Sopenharmony_ci if (!evoice) 114862306a36Sopenharmony_ci return -ENOMEM; 114962306a36Sopenharmony_ci pvoice->extra = evoice; 115062306a36Sopenharmony_ci evoice->substream = substream; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci if (evoice) { 115462306a36Sopenharmony_ci snd_ali_free_voice(codec, evoice); 115562306a36Sopenharmony_ci pvoice->extra = evoice = NULL; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci return 0; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic int snd_ali_playback_hw_free(struct snd_pcm_substream *substream) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 116562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 116662306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 116762306a36Sopenharmony_ci struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (evoice) { 117062306a36Sopenharmony_ci snd_ali_free_voice(codec, evoice); 117162306a36Sopenharmony_ci pvoice->extra = NULL; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic int snd_ali_playback_prepare(struct snd_pcm_substream *substream) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 117962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 118062306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 118162306a36Sopenharmony_ci struct snd_ali_voice *evoice = pvoice->extra; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci unsigned int LBA; 118462306a36Sopenharmony_ci unsigned int Delta; 118562306a36Sopenharmony_ci unsigned int ESO; 118662306a36Sopenharmony_ci unsigned int CTRL; 118762306a36Sopenharmony_ci unsigned int GVSEL; 118862306a36Sopenharmony_ci unsigned int PAN; 118962306a36Sopenharmony_ci unsigned int VOL; 119062306a36Sopenharmony_ci unsigned int EC; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare ...\n"); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* set Delta (rate) value */ 119762306a36Sopenharmony_ci Delta = snd_ali_convert_rate(runtime->rate, 0); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (pvoice->number == ALI_SPDIF_IN_CHANNEL || 120062306a36Sopenharmony_ci pvoice->number == ALI_PCM_IN_CHANNEL) 120162306a36Sopenharmony_ci snd_ali_disable_special_channel(codec, pvoice->number); 120262306a36Sopenharmony_ci else if (codec->spdif_support && 120362306a36Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 120462306a36Sopenharmony_ci ALI_SPDIF_OUT_CH_ENABLE) 120562306a36Sopenharmony_ci && pvoice->number == ALI_SPDIF_OUT_CHANNEL) { 120662306a36Sopenharmony_ci snd_ali_set_spdif_out_rate(codec, runtime->rate); 120762306a36Sopenharmony_ci Delta = 0x1000; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* set Loop Back Address */ 121162306a36Sopenharmony_ci LBA = runtime->dma_addr; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* set interrupt count size */ 121462306a36Sopenharmony_ci pvoice->count = runtime->period_size; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* set target ESO for channel */ 121762306a36Sopenharmony_ci pvoice->eso = runtime->buffer_size; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare: eso=%xh count=%xh\n", 122062306a36Sopenharmony_ci pvoice->eso, pvoice->count); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci /* set ESO to capture first MIDLP interrupt */ 122362306a36Sopenharmony_ci ESO = pvoice->eso -1; 122462306a36Sopenharmony_ci /* set ctrl mode */ 122562306a36Sopenharmony_ci CTRL = snd_ali_control_mode(substream); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci GVSEL = 1; 122862306a36Sopenharmony_ci PAN = 0; 122962306a36Sopenharmony_ci VOL = 0; 123062306a36Sopenharmony_ci EC = 0; 123162306a36Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare:\n"); 123262306a36Sopenharmony_ci dev_dbg(codec->card->dev, 123362306a36Sopenharmony_ci "ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n", 123462306a36Sopenharmony_ci pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); 123562306a36Sopenharmony_ci snd_ali_write_voice_regs(codec, 123662306a36Sopenharmony_ci pvoice->number, 123762306a36Sopenharmony_ci LBA, 123862306a36Sopenharmony_ci 0, /* cso */ 123962306a36Sopenharmony_ci ESO, 124062306a36Sopenharmony_ci Delta, 124162306a36Sopenharmony_ci 0, /* alpha */ 124262306a36Sopenharmony_ci GVSEL, 124362306a36Sopenharmony_ci PAN, 124462306a36Sopenharmony_ci VOL, 124562306a36Sopenharmony_ci CTRL, 124662306a36Sopenharmony_ci EC); 124762306a36Sopenharmony_ci if (evoice) { 124862306a36Sopenharmony_ci evoice->count = pvoice->count; 124962306a36Sopenharmony_ci evoice->eso = pvoice->count << 1; 125062306a36Sopenharmony_ci ESO = evoice->eso - 1; 125162306a36Sopenharmony_ci snd_ali_write_voice_regs(codec, 125262306a36Sopenharmony_ci evoice->number, 125362306a36Sopenharmony_ci LBA, 125462306a36Sopenharmony_ci 0, /* cso */ 125562306a36Sopenharmony_ci ESO, 125662306a36Sopenharmony_ci Delta, 125762306a36Sopenharmony_ci 0, /* alpha */ 125862306a36Sopenharmony_ci GVSEL, 125962306a36Sopenharmony_ci 0x7f, 126062306a36Sopenharmony_ci 0x3ff, 126162306a36Sopenharmony_ci CTRL, 126262306a36Sopenharmony_ci EC); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int snd_ali_prepare(struct snd_pcm_substream *substream) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 127262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 127362306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 127462306a36Sopenharmony_ci unsigned int LBA; 127562306a36Sopenharmony_ci unsigned int Delta; 127662306a36Sopenharmony_ci unsigned int ESO; 127762306a36Sopenharmony_ci unsigned int CTRL; 127862306a36Sopenharmony_ci unsigned int GVSEL; 127962306a36Sopenharmony_ci unsigned int PAN; 128062306a36Sopenharmony_ci unsigned int VOL; 128162306a36Sopenharmony_ci unsigned int EC; 128262306a36Sopenharmony_ci u8 bValue; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci dev_dbg(codec->card->dev, "ali_prepare...\n"); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci snd_ali_enable_special_channel(codec,pvoice->number); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL || 129162306a36Sopenharmony_ci pvoice->number == ALI_MODEM_OUT_CHANNEL) ? 129262306a36Sopenharmony_ci 0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* Prepare capture intr channel */ 129562306a36Sopenharmony_ci if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci unsigned int rate; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 130062306a36Sopenharmony_ci if (codec->revision != ALI_5451_V02) 130162306a36Sopenharmony_ci return -1; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci rate = snd_ali_get_spdif_in_rate(codec); 130462306a36Sopenharmony_ci if (rate == 0) { 130562306a36Sopenharmony_ci dev_warn(codec->card->dev, 130662306a36Sopenharmony_ci "ali_capture_prepare: spdif rate detect err!\n"); 130762306a36Sopenharmony_ci rate = 48000; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 131062306a36Sopenharmony_ci bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); 131162306a36Sopenharmony_ci if (bValue & 0x10) { 131262306a36Sopenharmony_ci outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); 131362306a36Sopenharmony_ci dev_warn(codec->card->dev, 131462306a36Sopenharmony_ci "clear SPDIF parity error flag.\n"); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (rate != 48000) 131862306a36Sopenharmony_ci Delta = ((rate << 12) / runtime->rate) & 0x00ffff; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* set target ESO for channel */ 132262306a36Sopenharmony_ci pvoice->eso = runtime->buffer_size; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* set interrupt count size */ 132562306a36Sopenharmony_ci pvoice->count = runtime->period_size; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* set Loop Back Address */ 132862306a36Sopenharmony_ci LBA = runtime->dma_addr; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* set ESO to capture first MIDLP interrupt */ 133162306a36Sopenharmony_ci ESO = pvoice->eso - 1; 133262306a36Sopenharmony_ci CTRL = snd_ali_control_mode(substream); 133362306a36Sopenharmony_ci GVSEL = 0; 133462306a36Sopenharmony_ci PAN = 0x00; 133562306a36Sopenharmony_ci VOL = 0x00; 133662306a36Sopenharmony_ci EC = 0; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci snd_ali_write_voice_regs( codec, 133962306a36Sopenharmony_ci pvoice->number, 134062306a36Sopenharmony_ci LBA, 134162306a36Sopenharmony_ci 0, /* cso */ 134262306a36Sopenharmony_ci ESO, 134362306a36Sopenharmony_ci Delta, 134462306a36Sopenharmony_ci 0, /* alpha */ 134562306a36Sopenharmony_ci GVSEL, 134662306a36Sopenharmony_ci PAN, 134762306a36Sopenharmony_ci VOL, 134862306a36Sopenharmony_ci CTRL, 134962306a36Sopenharmony_ci EC); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return 0; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cistatic snd_pcm_uframes_t 135862306a36Sopenharmony_cisnd_ali_playback_pointer(struct snd_pcm_substream *substream) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 136162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 136262306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 136362306a36Sopenharmony_ci unsigned int cso; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci spin_lock(&codec->reg_lock); 136662306a36Sopenharmony_ci if (!pvoice->running) { 136762306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); 137162306a36Sopenharmony_ci cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); 137262306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 137362306a36Sopenharmony_ci dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci cso %= runtime->buffer_size; 137662306a36Sopenharmony_ci return cso; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 138362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 138462306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 138562306a36Sopenharmony_ci unsigned int cso; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci spin_lock(&codec->reg_lock); 138862306a36Sopenharmony_ci if (!pvoice->running) { 138962306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); 139362306a36Sopenharmony_ci cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); 139462306a36Sopenharmony_ci spin_unlock(&codec->reg_lock); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci cso %= runtime->buffer_size; 139762306a36Sopenharmony_ci return cso; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_playback = 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 140362306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 140462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 140562306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 140662306a36Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 140762306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 140862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 140962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 141062306a36Sopenharmony_ci .rate_min = 4000, 141162306a36Sopenharmony_ci .rate_max = 48000, 141262306a36Sopenharmony_ci .channels_min = 1, 141362306a36Sopenharmony_ci .channels_max = 2, 141462306a36Sopenharmony_ci .buffer_bytes_max = (256*1024), 141562306a36Sopenharmony_ci .period_bytes_min = 64, 141662306a36Sopenharmony_ci .period_bytes_max = (256*1024), 141762306a36Sopenharmony_ci .periods_min = 1, 141862306a36Sopenharmony_ci .periods_max = 1024, 141962306a36Sopenharmony_ci .fifo_size = 0, 142062306a36Sopenharmony_ci}; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci/* 142362306a36Sopenharmony_ci * Capture support device description 142462306a36Sopenharmony_ci */ 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_capture = 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 142962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 143062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 143162306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 143262306a36Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 143362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 143462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 143562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 143662306a36Sopenharmony_ci .rate_min = 4000, 143762306a36Sopenharmony_ci .rate_max = 48000, 143862306a36Sopenharmony_ci .channels_min = 1, 143962306a36Sopenharmony_ci .channels_max = 2, 144062306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 144162306a36Sopenharmony_ci .period_bytes_min = 64, 144262306a36Sopenharmony_ci .period_bytes_max = (128*1024), 144362306a36Sopenharmony_ci .periods_min = 1, 144462306a36Sopenharmony_ci .periods_max = 1024, 144562306a36Sopenharmony_ci .fifo_size = 0, 144662306a36Sopenharmony_ci}; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (pvoice) 145362306a36Sopenharmony_ci snd_ali_free_voice(pvoice->codec, pvoice); 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_cistatic int snd_ali_open(struct snd_pcm_substream *substream, int rec, 145762306a36Sopenharmony_ci int channel, const struct snd_pcm_hardware *phw) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 146062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 146162306a36Sopenharmony_ci struct snd_ali_voice *pvoice; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec, 146462306a36Sopenharmony_ci channel); 146562306a36Sopenharmony_ci if (!pvoice) 146662306a36Sopenharmony_ci return -EAGAIN; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci pvoice->substream = substream; 146962306a36Sopenharmony_ci runtime->private_data = pvoice; 147062306a36Sopenharmony_ci runtime->private_free = snd_ali_pcm_free_substream; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci runtime->hw = *phw; 147362306a36Sopenharmony_ci snd_pcm_set_sync(substream); 147462306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 147562306a36Sopenharmony_ci 0, 64*1024); 147662306a36Sopenharmony_ci return 0; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic int snd_ali_playback_open(struct snd_pcm_substream *substream) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci return snd_ali_open(substream, 0, -1, &snd_ali_playback); 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cistatic int snd_ali_capture_open(struct snd_pcm_substream *substream) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci return snd_ali_open(substream, 1, -1, &snd_ali_capture); 148762306a36Sopenharmony_ci} 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_cistatic int snd_ali_playback_close(struct snd_pcm_substream *substream) 149062306a36Sopenharmony_ci{ 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int snd_ali_close(struct snd_pcm_substream *substream) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 149762306a36Sopenharmony_ci struct snd_ali_voice *pvoice = substream->runtime->private_data; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci snd_ali_disable_special_channel(codec,pvoice->number); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return 0; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_playback_ops = { 150562306a36Sopenharmony_ci .open = snd_ali_playback_open, 150662306a36Sopenharmony_ci .close = snd_ali_playback_close, 150762306a36Sopenharmony_ci .hw_params = snd_ali_playback_hw_params, 150862306a36Sopenharmony_ci .hw_free = snd_ali_playback_hw_free, 150962306a36Sopenharmony_ci .prepare = snd_ali_playback_prepare, 151062306a36Sopenharmony_ci .trigger = snd_ali_trigger, 151162306a36Sopenharmony_ci .pointer = snd_ali_playback_pointer, 151262306a36Sopenharmony_ci}; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_capture_ops = { 151562306a36Sopenharmony_ci .open = snd_ali_capture_open, 151662306a36Sopenharmony_ci .close = snd_ali_close, 151762306a36Sopenharmony_ci .prepare = snd_ali_prepare, 151862306a36Sopenharmony_ci .trigger = snd_ali_trigger, 151962306a36Sopenharmony_ci .pointer = snd_ali_pointer, 152062306a36Sopenharmony_ci}; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci/* 152362306a36Sopenharmony_ci * Modem PCM 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic int snd_ali_modem_hw_params(struct snd_pcm_substream *substream, 152762306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci struct snd_ali *chip = snd_pcm_substream_chip(substream); 153062306a36Sopenharmony_ci unsigned int modem_num = chip->num_of_codecs - 1; 153162306a36Sopenharmony_ci snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE, 153262306a36Sopenharmony_ci params_rate(hw_params)); 153362306a36Sopenharmony_ci snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0); 153462306a36Sopenharmony_ci return 0; 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_modem = 153862306a36Sopenharmony_ci{ 153962306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 154062306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 154162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 154262306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 154362306a36Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 154462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 154562306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000 | 154662306a36Sopenharmony_ci SNDRV_PCM_RATE_16000), 154762306a36Sopenharmony_ci .rate_min = 8000, 154862306a36Sopenharmony_ci .rate_max = 16000, 154962306a36Sopenharmony_ci .channels_min = 1, 155062306a36Sopenharmony_ci .channels_max = 1, 155162306a36Sopenharmony_ci .buffer_bytes_max = (256*1024), 155262306a36Sopenharmony_ci .period_bytes_min = 64, 155362306a36Sopenharmony_ci .period_bytes_max = (256*1024), 155462306a36Sopenharmony_ci .periods_min = 1, 155562306a36Sopenharmony_ci .periods_max = 1024, 155662306a36Sopenharmony_ci .fifo_size = 0, 155762306a36Sopenharmony_ci}; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec, 156062306a36Sopenharmony_ci int channel) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci static const unsigned int rates[] = {8000, 9600, 12000, 16000}; 156362306a36Sopenharmony_ci static const struct snd_pcm_hw_constraint_list hw_constraint_rates = { 156462306a36Sopenharmony_ci .count = ARRAY_SIZE(rates), 156562306a36Sopenharmony_ci .list = rates, 156662306a36Sopenharmony_ci .mask = 0, 156762306a36Sopenharmony_ci }; 156862306a36Sopenharmony_ci int err = snd_ali_open(substream, rec, channel, &snd_ali_modem); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (err) 157162306a36Sopenharmony_ci return err; 157262306a36Sopenharmony_ci return snd_pcm_hw_constraint_list(substream->runtime, 0, 157362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_cistatic int snd_ali_modem_playback_open(struct snd_pcm_substream *substream) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL); 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic int snd_ali_modem_capture_open(struct snd_pcm_substream *substream) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL); 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_playback_ops = { 158762306a36Sopenharmony_ci .open = snd_ali_modem_playback_open, 158862306a36Sopenharmony_ci .close = snd_ali_close, 158962306a36Sopenharmony_ci .hw_params = snd_ali_modem_hw_params, 159062306a36Sopenharmony_ci .prepare = snd_ali_prepare, 159162306a36Sopenharmony_ci .trigger = snd_ali_trigger, 159262306a36Sopenharmony_ci .pointer = snd_ali_pointer, 159362306a36Sopenharmony_ci}; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_capture_ops = { 159662306a36Sopenharmony_ci .open = snd_ali_modem_capture_open, 159762306a36Sopenharmony_ci .close = snd_ali_close, 159862306a36Sopenharmony_ci .hw_params = snd_ali_modem_hw_params, 159962306a36Sopenharmony_ci .prepare = snd_ali_prepare, 160062306a36Sopenharmony_ci .trigger = snd_ali_trigger, 160162306a36Sopenharmony_ci .pointer = snd_ali_pointer, 160262306a36Sopenharmony_ci}; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistruct ali_pcm_description { 160662306a36Sopenharmony_ci char *name; 160762306a36Sopenharmony_ci unsigned int playback_num; 160862306a36Sopenharmony_ci unsigned int capture_num; 160962306a36Sopenharmony_ci const struct snd_pcm_ops *playback_ops; 161062306a36Sopenharmony_ci const struct snd_pcm_ops *capture_ops; 161162306a36Sopenharmony_ci unsigned short class; 161262306a36Sopenharmony_ci}; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic void snd_ali_pcm_free(struct snd_pcm *pcm) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct snd_ali *codec = pcm->private_data; 161862306a36Sopenharmony_ci codec->pcm[pcm->device] = NULL; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic int snd_ali_pcm(struct snd_ali *codec, int device, 162362306a36Sopenharmony_ci struct ali_pcm_description *desc) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct snd_pcm *pcm; 162662306a36Sopenharmony_ci int err; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci err = snd_pcm_new(codec->card, desc->name, device, 162962306a36Sopenharmony_ci desc->playback_num, desc->capture_num, &pcm); 163062306a36Sopenharmony_ci if (err < 0) { 163162306a36Sopenharmony_ci dev_err(codec->card->dev, 163262306a36Sopenharmony_ci "snd_ali_pcm: err called snd_pcm_new.\n"); 163362306a36Sopenharmony_ci return err; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci pcm->private_data = codec; 163662306a36Sopenharmony_ci pcm->private_free = snd_ali_pcm_free; 163762306a36Sopenharmony_ci if (desc->playback_ops) 163862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 163962306a36Sopenharmony_ci desc->playback_ops); 164062306a36Sopenharmony_ci if (desc->capture_ops) 164162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 164262306a36Sopenharmony_ci desc->capture_ops); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 164562306a36Sopenharmony_ci &codec->pci->dev, 64*1024, 128*1024); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci pcm->info_flags = 0; 164862306a36Sopenharmony_ci pcm->dev_class = desc->class; 164962306a36Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 165062306a36Sopenharmony_ci strcpy(pcm->name, desc->name); 165162306a36Sopenharmony_ci codec->pcm[0] = pcm; 165262306a36Sopenharmony_ci return 0; 165362306a36Sopenharmony_ci} 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_cistatic struct ali_pcm_description ali_pcms[] = { 165662306a36Sopenharmony_ci { .name = "ALI 5451", 165762306a36Sopenharmony_ci .playback_num = ALI_CHANNELS, 165862306a36Sopenharmony_ci .capture_num = 1, 165962306a36Sopenharmony_ci .playback_ops = &snd_ali_playback_ops, 166062306a36Sopenharmony_ci .capture_ops = &snd_ali_capture_ops 166162306a36Sopenharmony_ci }, 166262306a36Sopenharmony_ci { .name = "ALI 5451 modem", 166362306a36Sopenharmony_ci .playback_num = 1, 166462306a36Sopenharmony_ci .capture_num = 1, 166562306a36Sopenharmony_ci .playback_ops = &snd_ali_modem_playback_ops, 166662306a36Sopenharmony_ci .capture_ops = &snd_ali_modem_capture_ops, 166762306a36Sopenharmony_ci .class = SNDRV_PCM_CLASS_MODEM 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci}; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic int snd_ali_build_pcms(struct snd_ali *codec) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci int i, err; 167462306a36Sopenharmony_ci for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) { 167562306a36Sopenharmony_ci err = snd_ali_pcm(codec, i, &ali_pcms[i]); 167662306a36Sopenharmony_ci if (err < 0) 167762306a36Sopenharmony_ci return err; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci#define ALI5451_SPDIF(xname, xindex, value) \ 168462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ 168562306a36Sopenharmony_ci.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ 168662306a36Sopenharmony_ci.put = snd_ali5451_spdif_put, .private_value = value} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci#define snd_ali5451_spdif_info snd_ctl_boolean_mono_info 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, 169162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 169262306a36Sopenharmony_ci{ 169362306a36Sopenharmony_ci struct snd_ali *codec = kcontrol->private_data; 169462306a36Sopenharmony_ci unsigned int spdif_enable; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 169962306a36Sopenharmony_ci switch (kcontrol->private_value) { 170062306a36Sopenharmony_ci case 0: 170162306a36Sopenharmony_ci spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0; 170262306a36Sopenharmony_ci break; 170362306a36Sopenharmony_ci case 1: 170462306a36Sopenharmony_ci spdif_enable = ((codec->spdif_mask & 0x02) && 170562306a36Sopenharmony_ci (codec->spdif_mask & 0x04)) ? 1 : 0; 170662306a36Sopenharmony_ci break; 170762306a36Sopenharmony_ci case 2: 170862306a36Sopenharmony_ci spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0; 170962306a36Sopenharmony_ci break; 171062306a36Sopenharmony_ci default: 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = spdif_enable; 171462306a36Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 171562306a36Sopenharmony_ci return 0; 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, 171962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci struct snd_ali *codec = kcontrol->private_data; 172262306a36Sopenharmony_ci unsigned int change = 0, spdif_enable = 0; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 172762306a36Sopenharmony_ci switch (kcontrol->private_value) { 172862306a36Sopenharmony_ci case 0: 172962306a36Sopenharmony_ci change = (codec->spdif_mask & 0x02) ? 1 : 0; 173062306a36Sopenharmony_ci change = change ^ spdif_enable; 173162306a36Sopenharmony_ci if (change) { 173262306a36Sopenharmony_ci if (spdif_enable) { 173362306a36Sopenharmony_ci codec->spdif_mask |= 0x02; 173462306a36Sopenharmony_ci snd_ali_enable_spdif_out(codec); 173562306a36Sopenharmony_ci } else { 173662306a36Sopenharmony_ci codec->spdif_mask &= ~(0x02); 173762306a36Sopenharmony_ci codec->spdif_mask &= ~(0x04); 173862306a36Sopenharmony_ci snd_ali_disable_spdif_out(codec); 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci break; 174262306a36Sopenharmony_ci case 1: 174362306a36Sopenharmony_ci change = (codec->spdif_mask & 0x04) ? 1 : 0; 174462306a36Sopenharmony_ci change = change ^ spdif_enable; 174562306a36Sopenharmony_ci if (change && (codec->spdif_mask & 0x02)) { 174662306a36Sopenharmony_ci if (spdif_enable) { 174762306a36Sopenharmony_ci codec->spdif_mask |= 0x04; 174862306a36Sopenharmony_ci snd_ali_enable_spdif_chnout(codec); 174962306a36Sopenharmony_ci } else { 175062306a36Sopenharmony_ci codec->spdif_mask &= ~(0x04); 175162306a36Sopenharmony_ci snd_ali_disable_spdif_chnout(codec); 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci break; 175562306a36Sopenharmony_ci case 2: 175662306a36Sopenharmony_ci change = (codec->spdif_mask & 0x01) ? 1 : 0; 175762306a36Sopenharmony_ci change = change ^ spdif_enable; 175862306a36Sopenharmony_ci if (change) { 175962306a36Sopenharmony_ci if (spdif_enable) { 176062306a36Sopenharmony_ci codec->spdif_mask |= 0x01; 176162306a36Sopenharmony_ci snd_ali_enable_spdif_in(codec); 176262306a36Sopenharmony_ci } else { 176362306a36Sopenharmony_ci codec->spdif_mask &= ~(0x01); 176462306a36Sopenharmony_ci snd_ali_disable_spdif_in(codec); 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci break; 176862306a36Sopenharmony_ci default: 176962306a36Sopenharmony_ci break; 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return change; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = { 177762306a36Sopenharmony_ci /* spdif aplayback switch */ 177862306a36Sopenharmony_ci /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */ 177962306a36Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0), 178062306a36Sopenharmony_ci /* spdif out to spdif channel */ 178162306a36Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1), 178262306a36Sopenharmony_ci /* spdif in from spdif channel */ 178362306a36Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) 178462306a36Sopenharmony_ci}; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cistatic int snd_ali_mixer(struct snd_ali *codec) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci struct snd_ac97_template ac97; 178962306a36Sopenharmony_ci unsigned int idx; 179062306a36Sopenharmony_ci int i, err; 179162306a36Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 179262306a36Sopenharmony_ci .write = snd_ali_codec_write, 179362306a36Sopenharmony_ci .read = snd_ali_codec_read, 179462306a36Sopenharmony_ci }; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus); 179762306a36Sopenharmony_ci if (err < 0) 179862306a36Sopenharmony_ci return err; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 180162306a36Sopenharmony_ci ac97.private_data = codec; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci for (i = 0; i < codec->num_of_codecs; i++) { 180462306a36Sopenharmony_ci ac97.num = i; 180562306a36Sopenharmony_ci err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]); 180662306a36Sopenharmony_ci if (err < 0) { 180762306a36Sopenharmony_ci dev_err(codec->card->dev, 180862306a36Sopenharmony_ci "ali mixer %d creating error.\n", i); 180962306a36Sopenharmony_ci if (i == 0) 181062306a36Sopenharmony_ci return err; 181162306a36Sopenharmony_ci codec->num_of_codecs = 1; 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci if (codec->spdif_support) { 181762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) { 181862306a36Sopenharmony_ci err = snd_ctl_add(codec->card, 181962306a36Sopenharmony_ci snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); 182062306a36Sopenharmony_ci if (err < 0) 182162306a36Sopenharmony_ci return err; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci return 0; 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 182862306a36Sopenharmony_cistatic int ali_suspend(struct device *dev) 182962306a36Sopenharmony_ci{ 183062306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 183162306a36Sopenharmony_ci struct snd_ali *chip = card->private_data; 183262306a36Sopenharmony_ci struct snd_ali_image *im; 183362306a36Sopenharmony_ci int i, j; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci im = chip->image; 183662306a36Sopenharmony_ci if (!im) 183762306a36Sopenharmony_ci return 0; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 184062306a36Sopenharmony_ci for (i = 0; i < chip->num_of_codecs; i++) 184162306a36Sopenharmony_ci snd_ac97_suspend(chip->ac97[i]); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); 184662306a36Sopenharmony_ci /* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */ 184762306a36Sopenharmony_ci im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* disable all IRQ bits */ 185062306a36Sopenharmony_ci outl(0, ALI_REG(chip, ALI_MISCINT)); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci for (i = 0; i < ALI_GLOBAL_REGS; i++) { 185362306a36Sopenharmony_ci if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) 185462306a36Sopenharmony_ci continue; 185562306a36Sopenharmony_ci im->regs[i] = inl(ALI_REG(chip, i*4)); 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) { 185962306a36Sopenharmony_ci outb(i, ALI_REG(chip, ALI_GC_CIR)); 186062306a36Sopenharmony_ci for (j = 0; j < ALI_CHANNEL_REGS; j++) 186162306a36Sopenharmony_ci im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci /* stop all HW channel */ 186562306a36Sopenharmony_ci outl(0xffffffff, ALI_REG(chip, ALI_STOP)); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 186862306a36Sopenharmony_ci return 0; 186962306a36Sopenharmony_ci} 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic int ali_resume(struct device *dev) 187262306a36Sopenharmony_ci{ 187362306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 187462306a36Sopenharmony_ci struct snd_ali *chip = card->private_data; 187562306a36Sopenharmony_ci struct snd_ali_image *im; 187662306a36Sopenharmony_ci int i, j; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci im = chip->image; 187962306a36Sopenharmony_ci if (!im) 188062306a36Sopenharmony_ci return 0; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) { 188562306a36Sopenharmony_ci outb(i, ALI_REG(chip, ALI_GC_CIR)); 188662306a36Sopenharmony_ci for (j = 0; j < ALI_CHANNEL_REGS; j++) 188762306a36Sopenharmony_ci outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci for (i = 0; i < ALI_GLOBAL_REGS; i++) { 189162306a36Sopenharmony_ci if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || 189262306a36Sopenharmony_ci (i*4 == ALI_START)) 189362306a36Sopenharmony_ci continue; 189462306a36Sopenharmony_ci outl(im->regs[i], ALI_REG(chip, i*4)); 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* start HW channel */ 189862306a36Sopenharmony_ci outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); 189962306a36Sopenharmony_ci /* restore IRQ enable bits */ 190062306a36Sopenharmony_ci outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci for (i = 0 ; i < chip->num_of_codecs; i++) 190562306a36Sopenharmony_ci snd_ac97_resume(chip->ac97[i]); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 190862306a36Sopenharmony_ci return 0; 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); 191262306a36Sopenharmony_ci#define ALI_PM_OPS &ali_pm 191362306a36Sopenharmony_ci#else 191462306a36Sopenharmony_ci#define ALI_PM_OPS NULL 191562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic void snd_ali_free(struct snd_card *card) 191862306a36Sopenharmony_ci{ 191962306a36Sopenharmony_ci struct snd_ali *codec = card->private_data; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci if (codec->hw_initialized) 192262306a36Sopenharmony_ci snd_ali_disable_address_interrupt(codec); 192362306a36Sopenharmony_ci pci_dev_put(codec->pci_m1533); 192462306a36Sopenharmony_ci pci_dev_put(codec->pci_m7101); 192562306a36Sopenharmony_ci} 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_cistatic int snd_ali_chip_init(struct snd_ali *codec) 192862306a36Sopenharmony_ci{ 192962306a36Sopenharmony_ci unsigned int legacy; 193062306a36Sopenharmony_ci unsigned char temp; 193162306a36Sopenharmony_ci struct pci_dev *pci_dev; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci dev_dbg(codec->card->dev, "chip initializing ...\n"); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci if (snd_ali_reset_5451(codec)) { 193662306a36Sopenharmony_ci dev_err(codec->card->dev, "ali_chip_init: reset 5451 error.\n"); 193762306a36Sopenharmony_ci return -1; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci if (codec->revision == ALI_5451_V02) { 194162306a36Sopenharmony_ci pci_dev = codec->pci_m1533; 194262306a36Sopenharmony_ci pci_read_config_byte(pci_dev, 0x59, &temp); 194362306a36Sopenharmony_ci temp |= 0x80; 194462306a36Sopenharmony_ci pci_write_config_byte(pci_dev, 0x59, temp); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci pci_dev = codec->pci_m7101; 194762306a36Sopenharmony_ci pci_read_config_byte(pci_dev, 0xb8, &temp); 194862306a36Sopenharmony_ci temp |= 0x20; 194962306a36Sopenharmony_ci pci_write_config_byte(pci_dev, 0xB8, temp); 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci pci_read_config_dword(codec->pci, 0x44, &legacy); 195362306a36Sopenharmony_ci legacy &= 0xff00ff00; 195462306a36Sopenharmony_ci legacy |= 0x000800aa; 195562306a36Sopenharmony_ci pci_write_config_dword(codec->pci, 0x44, legacy); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 195862306a36Sopenharmony_ci outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); 195962306a36Sopenharmony_ci outl(0xffffffff, ALI_REG(codec, ALI_AINT)); 196062306a36Sopenharmony_ci outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); 196162306a36Sopenharmony_ci outb(0x10, ALI_REG(codec, ALI_MPUR2)); 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); 196462306a36Sopenharmony_ci codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, 196562306a36Sopenharmony_ci AC97_EXTENDED_STATUS); 196662306a36Sopenharmony_ci if (codec->spdif_support) { 196762306a36Sopenharmony_ci snd_ali_enable_spdif_out(codec); 196862306a36Sopenharmony_ci codec->spdif_mask = 0x00000002; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci codec->num_of_codecs = 1; 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci /* secondary codec - modem */ 197462306a36Sopenharmony_ci if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) { 197562306a36Sopenharmony_ci codec->num_of_codecs++; 197662306a36Sopenharmony_ci outl(inl(ALI_REG(codec, ALI_SCTRL)) | 197762306a36Sopenharmony_ci (ALI_SCTRL_LINE_IN2 | ALI_SCTRL_GPIO_IN2 | 197862306a36Sopenharmony_ci ALI_SCTRL_LINE_OUT_EN), 197962306a36Sopenharmony_ci ALI_REG(codec, ALI_SCTRL)); 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci dev_dbg(codec->card->dev, "chip initialize succeed.\n"); 198362306a36Sopenharmony_ci return 0; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci/* proc for register dump */ 198862306a36Sopenharmony_cistatic void snd_ali_proc_read(struct snd_info_entry *entry, 198962306a36Sopenharmony_ci struct snd_info_buffer *buf) 199062306a36Sopenharmony_ci{ 199162306a36Sopenharmony_ci struct snd_ali *codec = entry->private_data; 199262306a36Sopenharmony_ci int i; 199362306a36Sopenharmony_ci for (i = 0; i < 256 ; i+= 4) 199462306a36Sopenharmony_ci snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i))); 199562306a36Sopenharmony_ci} 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_cistatic void snd_ali_proc_init(struct snd_ali *codec) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read); 200062306a36Sopenharmony_ci} 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_cistatic int snd_ali_resources(struct snd_ali *codec) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci int err; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci dev_dbg(codec->card->dev, "resources allocation ...\n"); 200762306a36Sopenharmony_ci err = pci_request_regions(codec->pci, "ALI 5451"); 200862306a36Sopenharmony_ci if (err < 0) 200962306a36Sopenharmony_ci return err; 201062306a36Sopenharmony_ci codec->port = pci_resource_start(codec->pci, 0); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (devm_request_irq(&codec->pci->dev, codec->pci->irq, 201362306a36Sopenharmony_ci snd_ali_card_interrupt, 201462306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, codec)) { 201562306a36Sopenharmony_ci dev_err(codec->card->dev, "Unable to request irq.\n"); 201662306a36Sopenharmony_ci return -EBUSY; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci codec->irq = codec->pci->irq; 201962306a36Sopenharmony_ci codec->card->sync_irq = codec->irq; 202062306a36Sopenharmony_ci dev_dbg(codec->card->dev, "resources allocated.\n"); 202162306a36Sopenharmony_ci return 0; 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic int snd_ali_create(struct snd_card *card, 202562306a36Sopenharmony_ci struct pci_dev *pci, 202662306a36Sopenharmony_ci int pcm_streams, 202762306a36Sopenharmony_ci int spdif_support) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci struct snd_ali *codec = card->private_data; 203062306a36Sopenharmony_ci int i, err; 203162306a36Sopenharmony_ci unsigned short cmdw; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci dev_dbg(card->dev, "creating ...\n"); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci /* enable PCI device */ 203662306a36Sopenharmony_ci err = pcim_enable_device(pci); 203762306a36Sopenharmony_ci if (err < 0) 203862306a36Sopenharmony_ci return err; 203962306a36Sopenharmony_ci /* check, if we can restrict PCI DMA transfers to 31 bits */ 204062306a36Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31))) { 204162306a36Sopenharmony_ci dev_err(card->dev, 204262306a36Sopenharmony_ci "architecture does not support 31bit PCI busmaster DMA\n"); 204362306a36Sopenharmony_ci return -ENXIO; 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci spin_lock_init(&codec->reg_lock); 204762306a36Sopenharmony_ci spin_lock_init(&codec->voice_alloc); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci codec->card = card; 205062306a36Sopenharmony_ci codec->pci = pci; 205162306a36Sopenharmony_ci codec->irq = -1; 205262306a36Sopenharmony_ci codec->revision = pci->revision; 205362306a36Sopenharmony_ci codec->spdif_support = spdif_support; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (pcm_streams < 1) 205662306a36Sopenharmony_ci pcm_streams = 1; 205762306a36Sopenharmony_ci if (pcm_streams > 32) 205862306a36Sopenharmony_ci pcm_streams = 32; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci pci_set_master(pci); 206162306a36Sopenharmony_ci pci_read_config_word(pci, PCI_COMMAND, &cmdw); 206262306a36Sopenharmony_ci if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { 206362306a36Sopenharmony_ci cmdw |= PCI_COMMAND_IO; 206462306a36Sopenharmony_ci pci_write_config_word(pci, PCI_COMMAND, cmdw); 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (snd_ali_resources(codec)) 206862306a36Sopenharmony_ci return -EBUSY; 206962306a36Sopenharmony_ci card->private_free = snd_ali_free; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci codec->synth.chmap = 0; 207262306a36Sopenharmony_ci codec->synth.chcnt = 0; 207362306a36Sopenharmony_ci codec->spdif_mask = 0; 207462306a36Sopenharmony_ci codec->synth.synthcount = 0; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci if (codec->revision == ALI_5451_V02) 207762306a36Sopenharmony_ci codec->chregs.regs.ac97read = ALI_AC97_WRITE; 207862306a36Sopenharmony_ci else 207962306a36Sopenharmony_ci codec->chregs.regs.ac97read = ALI_AC97_READ; 208062306a36Sopenharmony_ci codec->chregs.regs.ac97write = ALI_AC97_WRITE; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci codec->chregs.regs.start = ALI_START; 208362306a36Sopenharmony_ci codec->chregs.regs.stop = ALI_STOP; 208462306a36Sopenharmony_ci codec->chregs.regs.aint = ALI_AINT; 208562306a36Sopenharmony_ci codec->chregs.regs.ainten = ALI_AINTEN; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci codec->chregs.data.start = 0x00; 208862306a36Sopenharmony_ci codec->chregs.data.stop = 0x00; 208962306a36Sopenharmony_ci codec->chregs.data.aint = 0x00; 209062306a36Sopenharmony_ci codec->chregs.data.ainten = 0x00; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci /* M1533: southbridge */ 209362306a36Sopenharmony_ci codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL); 209462306a36Sopenharmony_ci if (!codec->pci_m1533) { 209562306a36Sopenharmony_ci dev_err(card->dev, "cannot find ALi 1533 chip.\n"); 209662306a36Sopenharmony_ci return -ENODEV; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci /* M7101: power management */ 209962306a36Sopenharmony_ci codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL); 210062306a36Sopenharmony_ci if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) { 210162306a36Sopenharmony_ci dev_err(card->dev, "cannot find ALi 7101 chip.\n"); 210262306a36Sopenharmony_ci return -ENODEV; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci /* initialise synth voices*/ 210662306a36Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) 210762306a36Sopenharmony_ci codec->synth.voices[i].number = i; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci err = snd_ali_chip_init(codec); 211062306a36Sopenharmony_ci if (err < 0) { 211162306a36Sopenharmony_ci dev_err(card->dev, "ali create: chip init error.\n"); 211262306a36Sopenharmony_ci return err; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 211662306a36Sopenharmony_ci codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image), 211762306a36Sopenharmony_ci GFP_KERNEL); 211862306a36Sopenharmony_ci if (!codec->image) 211962306a36Sopenharmony_ci dev_warn(card->dev, "can't allocate apm buffer\n"); 212062306a36Sopenharmony_ci#endif 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci snd_ali_enable_address_interrupt(codec); 212362306a36Sopenharmony_ci codec->hw_initialized = 1; 212462306a36Sopenharmony_ci return 0; 212562306a36Sopenharmony_ci} 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_cistatic int __snd_ali_probe(struct pci_dev *pci, 212862306a36Sopenharmony_ci const struct pci_device_id *pci_id) 212962306a36Sopenharmony_ci{ 213062306a36Sopenharmony_ci struct snd_card *card; 213162306a36Sopenharmony_ci struct snd_ali *codec; 213262306a36Sopenharmony_ci int err; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci dev_dbg(&pci->dev, "probe ...\n"); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE, 213762306a36Sopenharmony_ci sizeof(*codec), &card); 213862306a36Sopenharmony_ci if (err < 0) 213962306a36Sopenharmony_ci return err; 214062306a36Sopenharmony_ci codec = card->private_data; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci err = snd_ali_create(card, pci, pcm_channels, spdif); 214362306a36Sopenharmony_ci if (err < 0) 214462306a36Sopenharmony_ci return err; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci dev_dbg(&pci->dev, "mixer building ...\n"); 214762306a36Sopenharmony_ci err = snd_ali_mixer(codec); 214862306a36Sopenharmony_ci if (err < 0) 214962306a36Sopenharmony_ci return err; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci dev_dbg(&pci->dev, "pcm building ...\n"); 215262306a36Sopenharmony_ci err = snd_ali_build_pcms(codec); 215362306a36Sopenharmony_ci if (err < 0) 215462306a36Sopenharmony_ci return err; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci snd_ali_proc_init(codec); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci strcpy(card->driver, "ALI5451"); 215962306a36Sopenharmony_ci strcpy(card->shortname, "ALI 5451"); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i", 216262306a36Sopenharmony_ci card->shortname, codec->port, codec->irq); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci dev_dbg(&pci->dev, "register card.\n"); 216562306a36Sopenharmony_ci err = snd_card_register(card); 216662306a36Sopenharmony_ci if (err < 0) 216762306a36Sopenharmony_ci return err; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci pci_set_drvdata(pci, card); 217062306a36Sopenharmony_ci return 0; 217162306a36Sopenharmony_ci} 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_cistatic int snd_ali_probe(struct pci_dev *pci, 217462306a36Sopenharmony_ci const struct pci_device_id *pci_id) 217562306a36Sopenharmony_ci{ 217662306a36Sopenharmony_ci return snd_card_free_on_error(&pci->dev, __snd_ali_probe(pci, pci_id)); 217762306a36Sopenharmony_ci} 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_cistatic struct pci_driver ali5451_driver = { 218062306a36Sopenharmony_ci .name = KBUILD_MODNAME, 218162306a36Sopenharmony_ci .id_table = snd_ali_ids, 218262306a36Sopenharmony_ci .probe = snd_ali_probe, 218362306a36Sopenharmony_ci .driver = { 218462306a36Sopenharmony_ci .pm = ALI_PM_OPS, 218562306a36Sopenharmony_ci }, 218662306a36Sopenharmony_ci}; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_cimodule_pci_driver(ali5451_driver); 2189