18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Matt Wu <Matt_Wu@acersoftech.com.cn> 48c2ecf20Sopenharmony_ci * Apr 26, 2001 58c2ecf20Sopenharmony_ci * Routines for control of ALi pci audio M5451 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * BUGS: 88c2ecf20Sopenharmony_ci * -- 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * TODO: 118c2ecf20Sopenharmony_ci * -- 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm.h> 248c2ecf20Sopenharmony_ci#include <sound/info.h> 258c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 268c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>"); 308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALI M5451"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index */ 358c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 368c2ecf20Sopenharmony_cistatic int pcm_channels = 32; 378c2ecf20Sopenharmony_cistatic bool spdif; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); 418c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio."); 438c2ecf20Sopenharmony_cimodule_param(pcm_channels, int, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "PCM Channels"); 458c2ecf20Sopenharmony_cimodule_param(spdif, bool, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(spdif, "Support SPDIF I/O"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* just for backward compatibility */ 498c2ecf20Sopenharmony_cistatic bool enable; 508c2ecf20Sopenharmony_cimodule_param(enable, bool, 0444); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Constants definition 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define ALI_CHANNELS 32 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define ALI_PCM_IN_CHANNEL 31 638c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_CHANNEL 19 648c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_CHANNEL 15 658c2ecf20Sopenharmony_ci#define ALI_CENTER_CHANNEL 24 668c2ecf20Sopenharmony_ci#define ALI_LEF_CHANNEL 23 678c2ecf20Sopenharmony_ci#define ALI_SURR_LEFT_CHANNEL 26 688c2ecf20Sopenharmony_ci#define ALI_SURR_RIGHT_CHANNEL 25 698c2ecf20Sopenharmony_ci#define ALI_MODEM_IN_CHANNEL 21 708c2ecf20Sopenharmony_ci#define ALI_MODEM_OUT_CHANNEL 20 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define SNDRV_ALI_VOICE_TYPE_PCM 01 738c2ecf20Sopenharmony_ci#define SNDRV_ALI_VOICE_TYPE_OTH 02 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define ALI_5451_V02 0x02 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * Direct Registers 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR0 0x00 /* ADR0 */ 828c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR4 0x04 /* CNT0 */ 838c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR11 0x0b /* MOD */ 848c2ecf20Sopenharmony_ci#define ALI_LEGACY_DMAR15 0x0f /* MMR */ 858c2ecf20Sopenharmony_ci#define ALI_MPUR0 0x20 868c2ecf20Sopenharmony_ci#define ALI_MPUR1 0x21 878c2ecf20Sopenharmony_ci#define ALI_MPUR2 0x22 888c2ecf20Sopenharmony_ci#define ALI_MPUR3 0x23 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define ALI_AC97_WRITE 0x40 918c2ecf20Sopenharmony_ci#define ALI_AC97_READ 0x44 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define ALI_SCTRL 0x48 948c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_ENABLE 0x20 958c2ecf20Sopenharmony_ci#define ALI_SCTRL_LINE_IN2 (1 << 9) 968c2ecf20Sopenharmony_ci#define ALI_SCTRL_GPIO_IN2 (1 << 13) 978c2ecf20Sopenharmony_ci#define ALI_SCTRL_LINE_OUT_EN (1 << 20) 988c2ecf20Sopenharmony_ci#define ALI_SCTRL_GPIO_OUT_EN (1 << 23) 998c2ecf20Sopenharmony_ci#define ALI_SCTRL_CODEC1_READY (1 << 24) 1008c2ecf20Sopenharmony_ci#define ALI_SCTRL_CODEC2_READY (1 << 25) 1018c2ecf20Sopenharmony_ci#define ALI_AC97_GPIO 0x4c 1028c2ecf20Sopenharmony_ci#define ALI_AC97_GPIO_ENABLE 0x8000 1038c2ecf20Sopenharmony_ci#define ALI_AC97_GPIO_DATA_SHIFT 16 1048c2ecf20Sopenharmony_ci#define ALI_SPDIF_CS 0x70 1058c2ecf20Sopenharmony_ci#define ALI_SPDIF_CTRL 0x74 1068c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_FUNC_ENABLE 0x02 1078c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_CH_STATUS 0x40 1088c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_CH_STATUS 0xbf 1098c2ecf20Sopenharmony_ci#define ALI_START 0x80 1108c2ecf20Sopenharmony_ci#define ALI_STOP 0x84 1118c2ecf20Sopenharmony_ci#define ALI_CSPF 0x90 1128c2ecf20Sopenharmony_ci#define ALI_AINT 0x98 1138c2ecf20Sopenharmony_ci#define ALI_GC_CIR 0xa0 1148c2ecf20Sopenharmony_ci #define ENDLP_IE 0x00001000 1158c2ecf20Sopenharmony_ci #define MIDLP_IE 0x00002000 1168c2ecf20Sopenharmony_ci#define ALI_AINTEN 0xa4 1178c2ecf20Sopenharmony_ci#define ALI_VOLUME 0xa8 1188c2ecf20Sopenharmony_ci#define ALI_SBDELTA_DELTA_R 0xac 1198c2ecf20Sopenharmony_ci#define ALI_MISCINT 0xb0 1208c2ecf20Sopenharmony_ci #define ADDRESS_IRQ 0x00000020 1218c2ecf20Sopenharmony_ci #define TARGET_REACHED 0x00008000 1228c2ecf20Sopenharmony_ci #define MIXER_OVERFLOW 0x00000800 1238c2ecf20Sopenharmony_ci #define MIXER_UNDERFLOW 0x00000400 1248c2ecf20Sopenharmony_ci #define GPIO_IRQ 0x01000000 1258c2ecf20Sopenharmony_ci#define ALI_SBBL_SBCL 0xc0 1268c2ecf20Sopenharmony_ci#define ALI_SBCTRL_SBE2R_SBDD 0xc4 1278c2ecf20Sopenharmony_ci#define ALI_STIMER 0xc8 1288c2ecf20Sopenharmony_ci#define ALI_GLOBAL_CONTROL 0xd4 1298c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_SEL_PCM 0x00000400 /* bit 10 */ 1308c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_SUPPORT 0x00000800 /* bit 11 */ 1318c2ecf20Sopenharmony_ci#define ALI_SPDIF_OUT_CH_ENABLE 0x00008000 /* bit 15 */ 1328c2ecf20Sopenharmony_ci#define ALI_SPDIF_IN_CH_ENABLE 0x00080000 /* bit 19 */ 1338c2ecf20Sopenharmony_ci#define ALI_PCM_IN_ENABLE 0x80000000 /* bit 31 */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define ALI_CSO_ALPHA_FMS 0xe0 1368c2ecf20Sopenharmony_ci#define ALI_LBA 0xe4 1378c2ecf20Sopenharmony_ci#define ALI_ESO_DELTA 0xe8 1388c2ecf20Sopenharmony_ci#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 1398c2ecf20Sopenharmony_ci#define ALI_EBUF1 0xf4 1408c2ecf20Sopenharmony_ci#define ALI_EBUF2 0xf8 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define ALI_REG(codec, x) ((codec)->port + x) 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define MAX_CODECS 2 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistruct snd_ali; 1488c2ecf20Sopenharmony_cistruct snd_ali_voice; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct snd_ali_channel_control { 1518c2ecf20Sopenharmony_ci /* register data */ 1528c2ecf20Sopenharmony_ci struct REGDATA { 1538c2ecf20Sopenharmony_ci unsigned int start; 1548c2ecf20Sopenharmony_ci unsigned int stop; 1558c2ecf20Sopenharmony_ci unsigned int aint; 1568c2ecf20Sopenharmony_ci unsigned int ainten; 1578c2ecf20Sopenharmony_ci } data; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* register addresses */ 1608c2ecf20Sopenharmony_ci struct REGS { 1618c2ecf20Sopenharmony_ci unsigned int start; 1628c2ecf20Sopenharmony_ci unsigned int stop; 1638c2ecf20Sopenharmony_ci unsigned int aint; 1648c2ecf20Sopenharmony_ci unsigned int ainten; 1658c2ecf20Sopenharmony_ci unsigned int ac97read; 1668c2ecf20Sopenharmony_ci unsigned int ac97write; 1678c2ecf20Sopenharmony_ci } regs; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistruct snd_ali_voice { 1728c2ecf20Sopenharmony_ci unsigned int number; 1738c2ecf20Sopenharmony_ci unsigned int use :1, 1748c2ecf20Sopenharmony_ci pcm :1, 1758c2ecf20Sopenharmony_ci midi :1, 1768c2ecf20Sopenharmony_ci mode :1, 1778c2ecf20Sopenharmony_ci synth :1, 1788c2ecf20Sopenharmony_ci running :1; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* PCM data */ 1818c2ecf20Sopenharmony_ci struct snd_ali *codec; 1828c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1838c2ecf20Sopenharmony_ci struct snd_ali_voice *extra; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci int eso; /* final ESO value for channel */ 1868c2ecf20Sopenharmony_ci int count; /* runtime->period_size */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* --- */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci void *private_data; 1918c2ecf20Sopenharmony_ci void (*private_free)(void *private_data); 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistruct snd_alidev { 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci struct snd_ali_voice voices[ALI_CHANNELS]; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci unsigned int chcnt; /* num of opened channels */ 2008c2ecf20Sopenharmony_ci unsigned int chmap; /* bitmap for opened channels */ 2018c2ecf20Sopenharmony_ci unsigned int synthcount; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define ALI_GLOBAL_REGS 56 2078c2ecf20Sopenharmony_ci#define ALI_CHANNEL_REGS 8 2088c2ecf20Sopenharmony_cistruct snd_ali_image { 2098c2ecf20Sopenharmony_ci u32 regs[ALI_GLOBAL_REGS]; 2108c2ecf20Sopenharmony_ci u32 channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistruct snd_ali { 2158c2ecf20Sopenharmony_ci int irq; 2168c2ecf20Sopenharmony_ci unsigned long port; 2178c2ecf20Sopenharmony_ci unsigned char revision; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci unsigned int hw_initialized :1; 2208c2ecf20Sopenharmony_ci unsigned int spdif_support :1; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci struct pci_dev *pci; 2238c2ecf20Sopenharmony_ci struct pci_dev *pci_m1533; 2248c2ecf20Sopenharmony_ci struct pci_dev *pci_m7101; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci struct snd_card *card; 2278c2ecf20Sopenharmony_ci struct snd_pcm *pcm[MAX_CODECS]; 2288c2ecf20Sopenharmony_ci struct snd_alidev synth; 2298c2ecf20Sopenharmony_ci struct snd_ali_channel_control chregs; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* S/PDIF Mask */ 2328c2ecf20Sopenharmony_ci unsigned int spdif_mask; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci unsigned int spurious_irq_count; 2358c2ecf20Sopenharmony_ci unsigned int spurious_irq_max_delta; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci unsigned int num_of_codecs; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 2408c2ecf20Sopenharmony_ci struct snd_ac97 *ac97[MAX_CODECS]; 2418c2ecf20Sopenharmony_ci unsigned short ac97_ext_id; 2428c2ecf20Sopenharmony_ci unsigned short ac97_ext_status; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci spinlock_t reg_lock; 2458c2ecf20Sopenharmony_ci spinlock_t voice_alloc; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2488c2ecf20Sopenharmony_ci struct snd_ali_image *image; 2498c2ecf20Sopenharmony_ci#endif 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_ali_ids[] = { 2538c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0}, 2548c2ecf20Sopenharmony_ci {0, } 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ali_ids); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *, unsigned int, unsigned int); 2598c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *, int, unsigned short); 2608c2ecf20Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *, int, unsigned short, 2618c2ecf20Sopenharmony_ci unsigned short); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * AC97 ACCESS 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline unsigned int snd_ali_5451_peek(struct snd_ali *codec, 2688c2ecf20Sopenharmony_ci unsigned int port) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return (unsigned int)inl(ALI_REG(codec, port)); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic inline void snd_ali_5451_poke(struct snd_ali *codec, 2748c2ecf20Sopenharmony_ci unsigned int port, 2758c2ecf20Sopenharmony_ci unsigned int val) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci outl((unsigned int)val, ALI_REG(codec, port)); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int snd_ali_codec_ready(struct snd_ali *codec, 2818c2ecf20Sopenharmony_ci unsigned int port) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci unsigned long end_time; 2848c2ecf20Sopenharmony_ci unsigned int res; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(250); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci for (;;) { 2898c2ecf20Sopenharmony_ci res = snd_ali_5451_peek(codec,port); 2908c2ecf20Sopenharmony_ci if (!(res & 0x8000)) 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci if (!time_after_eq(end_time, jiffies)) 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci snd_ali_5451_poke(codec, port, res & ~0x8000); 2988c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n "); 2998c2ecf20Sopenharmony_ci return -EIO; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int snd_ali_stimer_ready(struct snd_ali *codec) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci unsigned long end_time; 3058c2ecf20Sopenharmony_ci unsigned long dwChk1,dwChk2; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); 3088c2ecf20Sopenharmony_ci end_time = jiffies + msecs_to_jiffies(250); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci for (;;) { 3118c2ecf20Sopenharmony_ci dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); 3128c2ecf20Sopenharmony_ci if (dwChk2 != dwChk1) 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci if (!time_after_eq(end_time, jiffies)) 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_stimer_read: stimer is not ready.\n"); 3208c2ecf20Sopenharmony_ci return -EIO; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void snd_ali_codec_poke(struct snd_ali *codec,int secondary, 3248c2ecf20Sopenharmony_ci unsigned short reg, 3258c2ecf20Sopenharmony_ci unsigned short val) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci unsigned int dwVal; 3288c2ecf20Sopenharmony_ci unsigned int port; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (reg >= 0x80) { 3318c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 3328c2ecf20Sopenharmony_ci "ali_codec_poke: reg(%xh) invalid.\n", reg); 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci port = codec->chregs.regs.ac97write; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 3418c2ecf20Sopenharmony_ci return; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci dwVal = (unsigned int) (reg & 0xff); 3448c2ecf20Sopenharmony_ci dwVal |= 0x8000 | (val << 16); 3458c2ecf20Sopenharmony_ci if (secondary) 3468c2ecf20Sopenharmony_ci dwVal |= 0x0080; 3478c2ecf20Sopenharmony_ci if (codec->revision == ALI_5451_V02) 3488c2ecf20Sopenharmony_ci dwVal |= 0x0100; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci snd_ali_5451_poke(codec, port, dwVal); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return ; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_peek(struct snd_ali *codec, 3568c2ecf20Sopenharmony_ci int secondary, 3578c2ecf20Sopenharmony_ci unsigned short reg) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci unsigned int dwVal; 3608c2ecf20Sopenharmony_ci unsigned int port; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (reg >= 0x80) { 3638c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 3648c2ecf20Sopenharmony_ci "ali_codec_peek: reg(%xh) invalid.\n", reg); 3658c2ecf20Sopenharmony_ci return ~0; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci port = codec->chregs.regs.ac97read; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 3718c2ecf20Sopenharmony_ci return ~0; 3728c2ecf20Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 3738c2ecf20Sopenharmony_ci return ~0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci dwVal = (unsigned int) (reg & 0xff); 3768c2ecf20Sopenharmony_ci dwVal |= 0x8000; /* bit 15*/ 3778c2ecf20Sopenharmony_ci if (secondary) 3788c2ecf20Sopenharmony_ci dwVal |= 0x0080; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci snd_ali_5451_poke(codec, port, dwVal); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 3838c2ecf20Sopenharmony_ci return ~0; 3848c2ecf20Sopenharmony_ci if (snd_ali_codec_ready(codec, port) < 0) 3858c2ecf20Sopenharmony_ci return ~0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return (snd_ali_5451_peek(codec, port) & 0xffff0000) >> 16; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void snd_ali_codec_write(struct snd_ac97 *ac97, 3918c2ecf20Sopenharmony_ci unsigned short reg, 3928c2ecf20Sopenharmony_ci unsigned short val ) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct snd_ali *codec = ac97->private_data; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "codec_write: reg=%xh data=%xh.\n", reg, val); 3978c2ecf20Sopenharmony_ci if (reg == AC97_GPIO_STATUS) { 3988c2ecf20Sopenharmony_ci outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE, 3998c2ecf20Sopenharmony_ci ALI_REG(codec, ALI_AC97_GPIO)); 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci snd_ali_codec_poke(codec, ac97->num, reg, val); 4038c2ecf20Sopenharmony_ci return ; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic unsigned short snd_ali_codec_read(struct snd_ac97 *ac97, 4088c2ecf20Sopenharmony_ci unsigned short reg) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct snd_ali *codec = ac97->private_data; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "codec_read reg=%xh.\n", reg); 4138c2ecf20Sopenharmony_ci return snd_ali_codec_peek(codec, ac97->num, reg); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * AC97 Reset 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int snd_ali_reset_5451(struct snd_ali *codec) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 4238c2ecf20Sopenharmony_ci unsigned short wCount, wReg; 4248c2ecf20Sopenharmony_ci unsigned int dwVal; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci pci_dev = codec->pci_m1533; 4278c2ecf20Sopenharmony_ci if (pci_dev) { 4288c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 0x7c, &dwVal); 4298c2ecf20Sopenharmony_ci pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); 4308c2ecf20Sopenharmony_ci mdelay(5); 4318c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 0x7c, &dwVal); 4328c2ecf20Sopenharmony_ci pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); 4338c2ecf20Sopenharmony_ci mdelay(5); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci pci_dev = codec->pci; 4378c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 0x44, &dwVal); 4388c2ecf20Sopenharmony_ci pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); 4398c2ecf20Sopenharmony_ci udelay(500); 4408c2ecf20Sopenharmony_ci pci_read_config_dword(pci_dev, 0x44, &dwVal); 4418c2ecf20Sopenharmony_ci pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); 4428c2ecf20Sopenharmony_ci mdelay(5); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci wCount = 200; 4458c2ecf20Sopenharmony_ci while(wCount--) { 4468c2ecf20Sopenharmony_ci wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); 4478c2ecf20Sopenharmony_ci if ((wReg & 0x000f) == 0x000f) 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci mdelay(5); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* non-fatal if you have a non PM capable codec */ 4538c2ecf20Sopenharmony_ci /* dev_warn(codec->card->dev, "ali5451: reset time out\n"); */ 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* 4588c2ecf20Sopenharmony_ci * ALI 5451 Controller 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void snd_ali_enable_special_channel(struct snd_ali *codec, 4628c2ecf20Sopenharmony_ci unsigned int channel) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci unsigned long dwVal; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 4678c2ecf20Sopenharmony_ci dwVal |= 1 << (channel & 0x0000001f); 4688c2ecf20Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void snd_ali_disable_special_channel(struct snd_ali *codec, 4728c2ecf20Sopenharmony_ci unsigned int channel) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci unsigned long dwVal; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 4778c2ecf20Sopenharmony_ci dwVal &= ~(1 << (channel & 0x0000001f)); 4788c2ecf20Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void snd_ali_enable_address_interrupt(struct snd_ali *codec) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci unsigned int gc; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci gc = inl(ALI_REG(codec, ALI_GC_CIR)); 4868c2ecf20Sopenharmony_ci gc |= ENDLP_IE; 4878c2ecf20Sopenharmony_ci gc |= MIDLP_IE; 4888c2ecf20Sopenharmony_ci outl( gc, ALI_REG(codec, ALI_GC_CIR)); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void snd_ali_disable_address_interrupt(struct snd_ali *codec) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci unsigned int gc; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci gc = inl(ALI_REG(codec, ALI_GC_CIR)); 4968c2ecf20Sopenharmony_ci gc &= ~ENDLP_IE; 4978c2ecf20Sopenharmony_ci gc &= ~MIDLP_IE; 4988c2ecf20Sopenharmony_ci outl(gc, ALI_REG(codec, ALI_GC_CIR)); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void snd_ali_disable_voice_irq(struct snd_ali *codec, 5028c2ecf20Sopenharmony_ci unsigned int channel) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci unsigned int mask; 5058c2ecf20Sopenharmony_ci struct snd_ali_channel_control *pchregs = &(codec->chregs); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "disable_voice_irq channel=%d\n", channel); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mask = 1 << (channel & 0x1f); 5108c2ecf20Sopenharmony_ci pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten)); 5118c2ecf20Sopenharmony_ci pchregs->data.ainten &= ~mask; 5128c2ecf20Sopenharmony_ci outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci unsigned int idx = channel & 0x1f; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (codec->synth.chcnt >= ALI_CHANNELS){ 5208c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 5218c2ecf20Sopenharmony_ci "ali_alloc_pcm_channel: no free channels.\n"); 5228c2ecf20Sopenharmony_ci return -1; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (!(codec->synth.chmap & (1 << idx))) { 5268c2ecf20Sopenharmony_ci codec->synth.chmap |= 1 << idx; 5278c2ecf20Sopenharmony_ci codec->synth.chcnt++; 5288c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "alloc_pcm_channel no. %d.\n", idx); 5298c2ecf20Sopenharmony_ci return idx; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci return -1; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int snd_ali_find_free_channel(struct snd_ali * codec, int rec) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int idx; 5378c2ecf20Sopenharmony_ci int result = -1; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, 5408c2ecf20Sopenharmony_ci "find_free_channel: for %s\n", rec ? "rec" : "pcm"); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* recording */ 5438c2ecf20Sopenharmony_ci if (rec) { 5448c2ecf20Sopenharmony_ci if (codec->spdif_support && 5458c2ecf20Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 5468c2ecf20Sopenharmony_ci ALI_SPDIF_IN_SUPPORT)) 5478c2ecf20Sopenharmony_ci idx = ALI_SPDIF_IN_CHANNEL; 5488c2ecf20Sopenharmony_ci else 5498c2ecf20Sopenharmony_ci idx = ALI_PCM_IN_CHANNEL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 5528c2ecf20Sopenharmony_ci if (result >= 0) 5538c2ecf20Sopenharmony_ci return result; 5548c2ecf20Sopenharmony_ci else { 5558c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 5568c2ecf20Sopenharmony_ci "ali_find_free_channel: record channel is busy now.\n"); 5578c2ecf20Sopenharmony_ci return -1; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* playback... */ 5628c2ecf20Sopenharmony_ci if (codec->spdif_support && 5638c2ecf20Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 5648c2ecf20Sopenharmony_ci ALI_SPDIF_OUT_CH_ENABLE)) { 5658c2ecf20Sopenharmony_ci idx = ALI_SPDIF_OUT_CHANNEL; 5668c2ecf20Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 5678c2ecf20Sopenharmony_ci if (result >= 0) 5688c2ecf20Sopenharmony_ci return result; 5698c2ecf20Sopenharmony_ci else 5708c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 5718c2ecf20Sopenharmony_ci "ali_find_free_channel: S/PDIF out channel is in busy now.\n"); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci for (idx = 0; idx < ALI_CHANNELS; idx++) { 5758c2ecf20Sopenharmony_ci result = snd_ali_alloc_pcm_channel(codec, idx); 5768c2ecf20Sopenharmony_ci if (result >= 0) 5778c2ecf20Sopenharmony_ci return result; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_find_free_channel: no free channels.\n"); 5808c2ecf20Sopenharmony_ci return -1; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci unsigned int idx = channel & 0x0000001f; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "free_channel_pcm channel=%d\n", channel); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (channel < 0 || channel >= ALI_CHANNELS) 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (!(codec->synth.chmap & (1 << idx))) { 5938c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 5948c2ecf20Sopenharmony_ci "ali_free_channel_pcm: channel %d is not in use.\n", 5958c2ecf20Sopenharmony_ci channel); 5968c2ecf20Sopenharmony_ci return; 5978c2ecf20Sopenharmony_ci } else { 5988c2ecf20Sopenharmony_ci codec->synth.chmap &= ~(1 << idx); 5998c2ecf20Sopenharmony_ci codec->synth.chcnt--; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci unsigned int mask = 1 << (channel & 0x1f); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "stop_voice: channel=%d\n", channel); 6088c2ecf20Sopenharmony_ci outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* 6128c2ecf20Sopenharmony_ci * S/PDIF Part 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic void snd_ali_delay(struct snd_ali *codec,int interval) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci unsigned long begintimer,currenttimer; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci begintimer = inl(ALI_REG(codec, ALI_STIMER)); 6208c2ecf20Sopenharmony_ci currenttimer = inl(ALI_REG(codec, ALI_STIMER)); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci while (currenttimer < begintimer + interval) { 6238c2ecf20Sopenharmony_ci if (snd_ali_stimer_ready(codec) < 0) 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci currenttimer = inl(ALI_REG(codec, ALI_STIMER)); 6268c2ecf20Sopenharmony_ci cpu_relax(); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic void snd_ali_detect_spdif_rate(struct snd_ali *codec) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci u16 wval; 6338c2ecf20Sopenharmony_ci u16 count = 0; 6348c2ecf20Sopenharmony_ci u8 bval, R1 = 0, R2; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 6378c2ecf20Sopenharmony_ci bval |= 0x1F; 6388c2ecf20Sopenharmony_ci outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci while ((R1 < 0x0b || R1 > 0x0e) && R1 != 0x12 && count <= 50000) { 6418c2ecf20Sopenharmony_ci count ++; 6428c2ecf20Sopenharmony_ci snd_ali_delay(codec, 6); 6438c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); 6448c2ecf20Sopenharmony_ci R1 = bval & 0x1F; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (count > 50000) { 6488c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n"); 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci for (count = 0; count <= 50000; count++) { 6538c2ecf20Sopenharmony_ci snd_ali_delay(codec, 6); 6548c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); 6558c2ecf20Sopenharmony_ci R2 = bval & 0x1F; 6568c2ecf20Sopenharmony_ci if (R2 != R1) 6578c2ecf20Sopenharmony_ci R1 = R2; 6588c2ecf20Sopenharmony_ci else 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (count > 50000) { 6638c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n"); 6648c2ecf20Sopenharmony_ci return; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (R2 >= 0x0b && R2 <= 0x0e) { 6688c2ecf20Sopenharmony_ci wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 6698c2ecf20Sopenharmony_ci wval &= 0xe0f0; 6708c2ecf20Sopenharmony_ci wval |= (0x09 << 8) | 0x05; 6718c2ecf20Sopenharmony_ci outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)) & 0xf0; 6748c2ecf20Sopenharmony_ci outb(bval | 0x02, ALI_REG(codec, ALI_SPDIF_CS + 3)); 6758c2ecf20Sopenharmony_ci } else if (R2 == 0x12) { 6768c2ecf20Sopenharmony_ci wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 6778c2ecf20Sopenharmony_ci wval &= 0xe0f0; 6788c2ecf20Sopenharmony_ci wval |= (0x0e << 8) | 0x08; 6798c2ecf20Sopenharmony_ci outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)) & 0xf0; 6828c2ecf20Sopenharmony_ci outb(bval | 0x03, ALI_REG(codec, ALI_SPDIF_CS + 3)); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic unsigned int snd_ali_get_spdif_in_rate(struct snd_ali *codec) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci u32 dwRate; 6898c2ecf20Sopenharmony_ci u8 bval; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 6928c2ecf20Sopenharmony_ci bval &= 0x7f; 6938c2ecf20Sopenharmony_ci bval |= 0x40; 6948c2ecf20Sopenharmony_ci outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL)); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci snd_ali_detect_spdif_rate(codec); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)); 6998c2ecf20Sopenharmony_ci bval &= 0x0f; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci switch (bval) { 7028c2ecf20Sopenharmony_ci case 0: dwRate = 44100; break; 7038c2ecf20Sopenharmony_ci case 1: dwRate = 48000; break; 7048c2ecf20Sopenharmony_ci case 2: dwRate = 32000; break; 7058c2ecf20Sopenharmony_ci default: dwRate = 0; break; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return dwRate; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_in(struct snd_ali *codec) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci unsigned int dwVal; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7168c2ecf20Sopenharmony_ci dwVal |= ALI_SPDIF_IN_SUPPORT; 7178c2ecf20Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 7208c2ecf20Sopenharmony_ci dwVal |= 0x02; 7218c2ecf20Sopenharmony_ci outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_in(struct snd_ali *codec) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci unsigned int dwVal; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7318c2ecf20Sopenharmony_ci dwVal &= ~ALI_SPDIF_IN_SUPPORT; 7328c2ecf20Sopenharmony_ci outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic void snd_ali_set_spdif_out_rate(struct snd_ali *codec, unsigned int rate) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci unsigned char bVal; 7418c2ecf20Sopenharmony_ci unsigned int dwRate; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci switch (rate) { 7448c2ecf20Sopenharmony_ci case 32000: dwRate = 0x300; break; 7458c2ecf20Sopenharmony_ci case 48000: dwRate = 0x200; break; 7468c2ecf20Sopenharmony_ci default: dwRate = 0; break; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 7508c2ecf20Sopenharmony_ci bVal &= (unsigned char)(~(1<<6)); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci bVal |= 0x80; /* select right */ 7538c2ecf20Sopenharmony_ci outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 7548c2ecf20Sopenharmony_ci outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci bVal &= ~0x80; /* select left */ 7578c2ecf20Sopenharmony_ci outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); 7588c2ecf20Sopenharmony_ci outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_out(struct snd_ali *codec) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci unsigned short wVal; 7648c2ecf20Sopenharmony_ci unsigned char bVal; 7658c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci pci_dev = codec->pci_m1533; 7688c2ecf20Sopenharmony_ci if (pci_dev == NULL) 7698c2ecf20Sopenharmony_ci return; 7708c2ecf20Sopenharmony_ci pci_read_config_byte(pci_dev, 0x61, &bVal); 7718c2ecf20Sopenharmony_ci bVal |= 0x40; 7728c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, 0x61, bVal); 7738c2ecf20Sopenharmony_ci pci_read_config_byte(pci_dev, 0x7d, &bVal); 7748c2ecf20Sopenharmony_ci bVal |= 0x01; 7758c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, 0x7d, bVal); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci pci_read_config_byte(pci_dev, 0x7e, &bVal); 7788c2ecf20Sopenharmony_ci bVal &= (~0x20); 7798c2ecf20Sopenharmony_ci bVal |= 0x10; 7808c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, 0x7e, bVal); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SCTRL)); 7838c2ecf20Sopenharmony_ci outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); 7868c2ecf20Sopenharmony_ci outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL)); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7898c2ecf20Sopenharmony_ci wVal |= ALI_SPDIF_OUT_SEL_PCM; 7908c2ecf20Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7918c2ecf20Sopenharmony_ci snd_ali_disable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void snd_ali_enable_spdif_chnout(struct snd_ali *codec) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci unsigned short wVal; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 7998c2ecf20Sopenharmony_ci wVal &= ~ALI_SPDIF_OUT_SEL_PCM; 8008c2ecf20Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 8018c2ecf20Sopenharmony_ci/* 8028c2ecf20Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); 8038c2ecf20Sopenharmony_ci if (flag & ALI_SPDIF_OUT_NON_PCM) 8048c2ecf20Sopenharmony_ci wVal |= 0x0002; 8058c2ecf20Sopenharmony_ci else 8068c2ecf20Sopenharmony_ci wVal &= (~0x0002); 8078c2ecf20Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); 8088c2ecf20Sopenharmony_ci*/ 8098c2ecf20Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_chnout(struct snd_ali *codec) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci unsigned short wVal; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); 8178c2ecf20Sopenharmony_ci wVal |= ALI_SPDIF_OUT_SEL_PCM; 8188c2ecf20Sopenharmony_ci outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void snd_ali_disable_spdif_out(struct snd_ali *codec) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci unsigned char bVal; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci bVal = inb(ALI_REG(codec, ALI_SCTRL)); 8288c2ecf20Sopenharmony_ci outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci snd_ali_disable_spdif_chnout(codec); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void snd_ali_update_ptr(struct snd_ali *codec, int channel) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice; 8368c2ecf20Sopenharmony_ci struct snd_ali_channel_control *pchregs; 8378c2ecf20Sopenharmony_ci unsigned int old, mask; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci pchregs = &(codec->chregs); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* check if interrupt occurred for channel */ 8428c2ecf20Sopenharmony_ci old = pchregs->data.aint; 8438c2ecf20Sopenharmony_ci mask = 1U << (channel & 0x1f); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (!(old & mask)) 8468c2ecf20Sopenharmony_ci return; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci pvoice = &codec->synth.voices[channel]; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci udelay(100); 8518c2ecf20Sopenharmony_ci spin_lock(&codec->reg_lock); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (pvoice->pcm && pvoice->substream) { 8548c2ecf20Sopenharmony_ci /* pcm interrupt */ 8558c2ecf20Sopenharmony_ci if (pvoice->running) { 8568c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, 8578c2ecf20Sopenharmony_ci "update_ptr: cso=%4.4x cspf=%d.\n", 8588c2ecf20Sopenharmony_ci inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)), 8598c2ecf20Sopenharmony_ci (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask); 8608c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 8618c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(pvoice->substream); 8628c2ecf20Sopenharmony_ci spin_lock(&codec->reg_lock); 8638c2ecf20Sopenharmony_ci } else { 8648c2ecf20Sopenharmony_ci snd_ali_stop_voice(codec, channel); 8658c2ecf20Sopenharmony_ci snd_ali_disable_voice_irq(codec, channel); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci } else if (codec->synth.voices[channel].synth) { 8688c2ecf20Sopenharmony_ci /* synth interrupt */ 8698c2ecf20Sopenharmony_ci } else if (codec->synth.voices[channel].midi) { 8708c2ecf20Sopenharmony_ci /* midi interrupt */ 8718c2ecf20Sopenharmony_ci } else { 8728c2ecf20Sopenharmony_ci /* unknown interrupt */ 8738c2ecf20Sopenharmony_ci snd_ali_stop_voice(codec, channel); 8748c2ecf20Sopenharmony_ci snd_ali_disable_voice_irq(codec, channel); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 8778c2ecf20Sopenharmony_ci outl(mask,ALI_REG(codec,pchregs->regs.aint)); 8788c2ecf20Sopenharmony_ci pchregs->data.aint = old & (~mask); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic irqreturn_t snd_ali_card_interrupt(int irq, void *dev_id) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct snd_ali *codec = dev_id; 8848c2ecf20Sopenharmony_ci int channel; 8858c2ecf20Sopenharmony_ci unsigned int audio_int; 8868c2ecf20Sopenharmony_ci struct snd_ali_channel_control *pchregs; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (codec == NULL || !codec->hw_initialized) 8898c2ecf20Sopenharmony_ci return IRQ_NONE; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci audio_int = inl(ALI_REG(codec, ALI_MISCINT)); 8928c2ecf20Sopenharmony_ci if (!audio_int) 8938c2ecf20Sopenharmony_ci return IRQ_NONE; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci pchregs = &(codec->chregs); 8968c2ecf20Sopenharmony_ci if (audio_int & ADDRESS_IRQ) { 8978c2ecf20Sopenharmony_ci /* get interrupt status for all channels */ 8988c2ecf20Sopenharmony_ci pchregs->data.aint = inl(ALI_REG(codec, pchregs->regs.aint)); 8998c2ecf20Sopenharmony_ci for (channel = 0; channel < ALI_CHANNELS; channel++) 9008c2ecf20Sopenharmony_ci snd_ali_update_ptr(codec, channel); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), 9038c2ecf20Sopenharmony_ci ALI_REG(codec, ALI_MISCINT)); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec, 9108c2ecf20Sopenharmony_ci int type, int rec, int channel) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice; 9138c2ecf20Sopenharmony_ci int idx; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci spin_lock_irq(&codec->voice_alloc); 9188c2ecf20Sopenharmony_ci if (type == SNDRV_ALI_VOICE_TYPE_PCM) { 9198c2ecf20Sopenharmony_ci idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) : 9208c2ecf20Sopenharmony_ci snd_ali_find_free_channel(codec,rec); 9218c2ecf20Sopenharmony_ci if (idx < 0) { 9228c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_alloc_voice: err.\n"); 9238c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 9248c2ecf20Sopenharmony_ci return NULL; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci pvoice = &(codec->synth.voices[idx]); 9278c2ecf20Sopenharmony_ci pvoice->codec = codec; 9288c2ecf20Sopenharmony_ci pvoice->use = 1; 9298c2ecf20Sopenharmony_ci pvoice->pcm = 1; 9308c2ecf20Sopenharmony_ci pvoice->mode = rec; 9318c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 9328c2ecf20Sopenharmony_ci return pvoice; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 9358c2ecf20Sopenharmony_ci return NULL; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic void snd_ali_free_voice(struct snd_ali * codec, 9408c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci void (*private_free)(void *); 9438c2ecf20Sopenharmony_ci void *private_data; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "free_voice: channel=%d\n", pvoice->number); 9468c2ecf20Sopenharmony_ci if (!pvoice->use) 9478c2ecf20Sopenharmony_ci return; 9488c2ecf20Sopenharmony_ci snd_ali_clear_voices(codec, pvoice->number, pvoice->number); 9498c2ecf20Sopenharmony_ci spin_lock_irq(&codec->voice_alloc); 9508c2ecf20Sopenharmony_ci private_free = pvoice->private_free; 9518c2ecf20Sopenharmony_ci private_data = pvoice->private_data; 9528c2ecf20Sopenharmony_ci pvoice->private_free = NULL; 9538c2ecf20Sopenharmony_ci pvoice->private_data = NULL; 9548c2ecf20Sopenharmony_ci if (pvoice->pcm) 9558c2ecf20Sopenharmony_ci snd_ali_free_channel_pcm(codec, pvoice->number); 9568c2ecf20Sopenharmony_ci pvoice->use = pvoice->pcm = pvoice->synth = 0; 9578c2ecf20Sopenharmony_ci pvoice->substream = NULL; 9588c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->voice_alloc); 9598c2ecf20Sopenharmony_ci if (private_free) 9608c2ecf20Sopenharmony_ci private_free(private_data); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic void snd_ali_clear_voices(struct snd_ali *codec, 9658c2ecf20Sopenharmony_ci unsigned int v_min, 9668c2ecf20Sopenharmony_ci unsigned int v_max) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci unsigned int i; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci for (i = v_min; i <= v_max; i++) { 9718c2ecf20Sopenharmony_ci snd_ali_stop_voice(codec, i); 9728c2ecf20Sopenharmony_ci snd_ali_disable_voice_irq(codec, i); 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic void snd_ali_write_voice_regs(struct snd_ali *codec, 9778c2ecf20Sopenharmony_ci unsigned int Channel, 9788c2ecf20Sopenharmony_ci unsigned int LBA, 9798c2ecf20Sopenharmony_ci unsigned int CSO, 9808c2ecf20Sopenharmony_ci unsigned int ESO, 9818c2ecf20Sopenharmony_ci unsigned int DELTA, 9828c2ecf20Sopenharmony_ci unsigned int ALPHA_FMS, 9838c2ecf20Sopenharmony_ci unsigned int GVSEL, 9848c2ecf20Sopenharmony_ci unsigned int PAN, 9858c2ecf20Sopenharmony_ci unsigned int VOL, 9868c2ecf20Sopenharmony_ci unsigned int CTRL, 9878c2ecf20Sopenharmony_ci unsigned int EC) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci unsigned int ctlcmds[4]; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci outb((unsigned char)(Channel & 0x001f), ALI_REG(codec, ALI_GC_CIR)); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); 9948c2ecf20Sopenharmony_ci ctlcmds[1] = LBA; 9958c2ecf20Sopenharmony_ci ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); 9968c2ecf20Sopenharmony_ci ctlcmds[3] = (GVSEL << 31) | 9978c2ecf20Sopenharmony_ci ((PAN & 0x0000007f) << 24) | 9988c2ecf20Sopenharmony_ci ((VOL & 0x000000ff) << 16) | 9998c2ecf20Sopenharmony_ci ((CTRL & 0x0000000f) << 12) | 10008c2ecf20Sopenharmony_ci (EC & 0x00000fff); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci outb(Channel, ALI_REG(codec, ALI_GC_CIR)); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci outl(ctlcmds[0], ALI_REG(codec, ALI_CSO_ALPHA_FMS)); 10058c2ecf20Sopenharmony_ci outl(ctlcmds[1], ALI_REG(codec, ALI_LBA)); 10068c2ecf20Sopenharmony_ci outl(ctlcmds[2], ALI_REG(codec, ALI_ESO_DELTA)); 10078c2ecf20Sopenharmony_ci outl(ctlcmds[3], ALI_REG(codec, ALI_GVSEL_PAN_VOC_CTRL_EC)); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ 10108c2ecf20Sopenharmony_ci outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic unsigned int snd_ali_convert_rate(unsigned int rate, int rec) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci unsigned int delta; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (rate < 4000) 10188c2ecf20Sopenharmony_ci rate = 4000; 10198c2ecf20Sopenharmony_ci if (rate > 48000) 10208c2ecf20Sopenharmony_ci rate = 48000; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (rec) { 10238c2ecf20Sopenharmony_ci if (rate == 44100) 10248c2ecf20Sopenharmony_ci delta = 0x116a; 10258c2ecf20Sopenharmony_ci else if (rate == 8000) 10268c2ecf20Sopenharmony_ci delta = 0x6000; 10278c2ecf20Sopenharmony_ci else if (rate == 48000) 10288c2ecf20Sopenharmony_ci delta = 0x1000; 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci delta = ((48000 << 12) / rate) & 0x0000ffff; 10318c2ecf20Sopenharmony_ci } else { 10328c2ecf20Sopenharmony_ci if (rate == 44100) 10338c2ecf20Sopenharmony_ci delta = 0xeb3; 10348c2ecf20Sopenharmony_ci else if (rate == 8000) 10358c2ecf20Sopenharmony_ci delta = 0x2ab; 10368c2ecf20Sopenharmony_ci else if (rate == 48000) 10378c2ecf20Sopenharmony_ci delta = 0x1000; 10388c2ecf20Sopenharmony_ci else 10398c2ecf20Sopenharmony_ci delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return delta; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic unsigned int snd_ali_control_mode(struct snd_pcm_substream *substream) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci unsigned int CTRL; 10488c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* set ctrl mode 10518c2ecf20Sopenharmony_ci CTRL default: 8-bit (unsigned) mono, loop mode enabled 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_ci CTRL = 0x00000001; 10548c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 10558c2ecf20Sopenharmony_ci CTRL |= 0x00000008; /* 16-bit data */ 10568c2ecf20Sopenharmony_ci if (!snd_pcm_format_unsigned(runtime->format)) 10578c2ecf20Sopenharmony_ci CTRL |= 0x00000002; /* signed data */ 10588c2ecf20Sopenharmony_ci if (runtime->channels > 1) 10598c2ecf20Sopenharmony_ci CTRL |= 0x00000004; /* stereo data */ 10608c2ecf20Sopenharmony_ci return CTRL; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci/* 10648c2ecf20Sopenharmony_ci * PCM part 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic int snd_ali_trigger(struct snd_pcm_substream *substream, 10688c2ecf20Sopenharmony_ci int cmd) 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 10728c2ecf20Sopenharmony_ci struct snd_pcm_substream *s; 10738c2ecf20Sopenharmony_ci unsigned int what, whati; 10748c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice, *evoice; 10758c2ecf20Sopenharmony_ci unsigned int val; 10768c2ecf20Sopenharmony_ci int do_start; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci switch (cmd) { 10798c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 10808c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 10818c2ecf20Sopenharmony_ci do_start = 1; 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 10848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 10858c2ecf20Sopenharmony_ci do_start = 0; 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci default: 10888c2ecf20Sopenharmony_ci return -EINVAL; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci what = whati = 0; 10928c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 10938c2ecf20Sopenharmony_ci if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) { 10948c2ecf20Sopenharmony_ci pvoice = s->runtime->private_data; 10958c2ecf20Sopenharmony_ci evoice = pvoice->extra; 10968c2ecf20Sopenharmony_ci what |= 1 << (pvoice->number & 0x1f); 10978c2ecf20Sopenharmony_ci if (evoice == NULL) 10988c2ecf20Sopenharmony_ci whati |= 1 << (pvoice->number & 0x1f); 10998c2ecf20Sopenharmony_ci else { 11008c2ecf20Sopenharmony_ci whati |= 1 << (evoice->number & 0x1f); 11018c2ecf20Sopenharmony_ci what |= 1 << (evoice->number & 0x1f); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci if (do_start) { 11048c2ecf20Sopenharmony_ci pvoice->running = 1; 11058c2ecf20Sopenharmony_ci if (evoice != NULL) 11068c2ecf20Sopenharmony_ci evoice->running = 1; 11078c2ecf20Sopenharmony_ci } else { 11088c2ecf20Sopenharmony_ci pvoice->running = 0; 11098c2ecf20Sopenharmony_ci if (evoice != NULL) 11108c2ecf20Sopenharmony_ci evoice->running = 0; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci snd_pcm_trigger_done(s, substream); 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci spin_lock(&codec->reg_lock); 11168c2ecf20Sopenharmony_ci if (!do_start) 11178c2ecf20Sopenharmony_ci outl(what, ALI_REG(codec, ALI_STOP)); 11188c2ecf20Sopenharmony_ci val = inl(ALI_REG(codec, ALI_AINTEN)); 11198c2ecf20Sopenharmony_ci if (do_start) 11208c2ecf20Sopenharmony_ci val |= whati; 11218c2ecf20Sopenharmony_ci else 11228c2ecf20Sopenharmony_ci val &= ~whati; 11238c2ecf20Sopenharmony_ci outl(val, ALI_REG(codec, ALI_AINTEN)); 11248c2ecf20Sopenharmony_ci if (do_start) 11258c2ecf20Sopenharmony_ci outl(what, ALI_REG(codec, ALI_START)); 11268c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati); 11278c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return 0; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int snd_ali_playback_hw_params(struct snd_pcm_substream *substream, 11338c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 11368c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11378c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 11388c2ecf20Sopenharmony_ci struct snd_ali_voice *evoice = pvoice->extra; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* voice management */ 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (params_buffer_size(hw_params) / 2 != 11438c2ecf20Sopenharmony_ci params_period_size(hw_params)) { 11448c2ecf20Sopenharmony_ci if (!evoice) { 11458c2ecf20Sopenharmony_ci evoice = snd_ali_alloc_voice(codec, 11468c2ecf20Sopenharmony_ci SNDRV_ALI_VOICE_TYPE_PCM, 11478c2ecf20Sopenharmony_ci 0, -1); 11488c2ecf20Sopenharmony_ci if (!evoice) 11498c2ecf20Sopenharmony_ci return -ENOMEM; 11508c2ecf20Sopenharmony_ci pvoice->extra = evoice; 11518c2ecf20Sopenharmony_ci evoice->substream = substream; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci } else { 11548c2ecf20Sopenharmony_ci if (evoice) { 11558c2ecf20Sopenharmony_ci snd_ali_free_voice(codec, evoice); 11568c2ecf20Sopenharmony_ci pvoice->extra = evoice = NULL; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int snd_ali_playback_hw_free(struct snd_pcm_substream *substream) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 11668c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11678c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 11688c2ecf20Sopenharmony_ci struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (evoice) { 11718c2ecf20Sopenharmony_ci snd_ali_free_voice(codec, evoice); 11728c2ecf20Sopenharmony_ci pvoice->extra = NULL; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci return 0; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int snd_ali_playback_prepare(struct snd_pcm_substream *substream) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 11808c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11818c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 11828c2ecf20Sopenharmony_ci struct snd_ali_voice *evoice = pvoice->extra; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci unsigned int LBA; 11858c2ecf20Sopenharmony_ci unsigned int Delta; 11868c2ecf20Sopenharmony_ci unsigned int ESO; 11878c2ecf20Sopenharmony_ci unsigned int CTRL; 11888c2ecf20Sopenharmony_ci unsigned int GVSEL; 11898c2ecf20Sopenharmony_ci unsigned int PAN; 11908c2ecf20Sopenharmony_ci unsigned int VOL; 11918c2ecf20Sopenharmony_ci unsigned int EC; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare ...\n"); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* set Delta (rate) value */ 11988c2ecf20Sopenharmony_ci Delta = snd_ali_convert_rate(runtime->rate, 0); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (pvoice->number == ALI_SPDIF_IN_CHANNEL || 12018c2ecf20Sopenharmony_ci pvoice->number == ALI_PCM_IN_CHANNEL) 12028c2ecf20Sopenharmony_ci snd_ali_disable_special_channel(codec, pvoice->number); 12038c2ecf20Sopenharmony_ci else if (codec->spdif_support && 12048c2ecf20Sopenharmony_ci (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & 12058c2ecf20Sopenharmony_ci ALI_SPDIF_OUT_CH_ENABLE) 12068c2ecf20Sopenharmony_ci && pvoice->number == ALI_SPDIF_OUT_CHANNEL) { 12078c2ecf20Sopenharmony_ci snd_ali_set_spdif_out_rate(codec, runtime->rate); 12088c2ecf20Sopenharmony_ci Delta = 0x1000; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci /* set Loop Back Address */ 12128c2ecf20Sopenharmony_ci LBA = runtime->dma_addr; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* set interrupt count size */ 12158c2ecf20Sopenharmony_ci pvoice->count = runtime->period_size; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* set target ESO for channel */ 12188c2ecf20Sopenharmony_ci pvoice->eso = runtime->buffer_size; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare: eso=%xh count=%xh\n", 12218c2ecf20Sopenharmony_ci pvoice->eso, pvoice->count); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* set ESO to capture first MIDLP interrupt */ 12248c2ecf20Sopenharmony_ci ESO = pvoice->eso -1; 12258c2ecf20Sopenharmony_ci /* set ctrl mode */ 12268c2ecf20Sopenharmony_ci CTRL = snd_ali_control_mode(substream); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci GVSEL = 1; 12298c2ecf20Sopenharmony_ci PAN = 0; 12308c2ecf20Sopenharmony_ci VOL = 0; 12318c2ecf20Sopenharmony_ci EC = 0; 12328c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "playback_prepare:\n"); 12338c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, 12348c2ecf20Sopenharmony_ci "ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n", 12358c2ecf20Sopenharmony_ci pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); 12368c2ecf20Sopenharmony_ci snd_ali_write_voice_regs(codec, 12378c2ecf20Sopenharmony_ci pvoice->number, 12388c2ecf20Sopenharmony_ci LBA, 12398c2ecf20Sopenharmony_ci 0, /* cso */ 12408c2ecf20Sopenharmony_ci ESO, 12418c2ecf20Sopenharmony_ci Delta, 12428c2ecf20Sopenharmony_ci 0, /* alpha */ 12438c2ecf20Sopenharmony_ci GVSEL, 12448c2ecf20Sopenharmony_ci PAN, 12458c2ecf20Sopenharmony_ci VOL, 12468c2ecf20Sopenharmony_ci CTRL, 12478c2ecf20Sopenharmony_ci EC); 12488c2ecf20Sopenharmony_ci if (evoice) { 12498c2ecf20Sopenharmony_ci evoice->count = pvoice->count; 12508c2ecf20Sopenharmony_ci evoice->eso = pvoice->count << 1; 12518c2ecf20Sopenharmony_ci ESO = evoice->eso - 1; 12528c2ecf20Sopenharmony_ci snd_ali_write_voice_regs(codec, 12538c2ecf20Sopenharmony_ci evoice->number, 12548c2ecf20Sopenharmony_ci LBA, 12558c2ecf20Sopenharmony_ci 0, /* cso */ 12568c2ecf20Sopenharmony_ci ESO, 12578c2ecf20Sopenharmony_ci Delta, 12588c2ecf20Sopenharmony_ci 0, /* alpha */ 12598c2ecf20Sopenharmony_ci GVSEL, 12608c2ecf20Sopenharmony_ci 0x7f, 12618c2ecf20Sopenharmony_ci 0x3ff, 12628c2ecf20Sopenharmony_ci CTRL, 12638c2ecf20Sopenharmony_ci EC); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic int snd_ali_prepare(struct snd_pcm_substream *substream) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 12738c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 12748c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 12758c2ecf20Sopenharmony_ci unsigned int LBA; 12768c2ecf20Sopenharmony_ci unsigned int Delta; 12778c2ecf20Sopenharmony_ci unsigned int ESO; 12788c2ecf20Sopenharmony_ci unsigned int CTRL; 12798c2ecf20Sopenharmony_ci unsigned int GVSEL; 12808c2ecf20Sopenharmony_ci unsigned int PAN; 12818c2ecf20Sopenharmony_ci unsigned int VOL; 12828c2ecf20Sopenharmony_ci unsigned int EC; 12838c2ecf20Sopenharmony_ci u8 bValue; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "ali_prepare...\n"); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci snd_ali_enable_special_channel(codec,pvoice->number); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL || 12928c2ecf20Sopenharmony_ci pvoice->number == ALI_MODEM_OUT_CHANNEL) ? 12938c2ecf20Sopenharmony_ci 0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* Prepare capture intr channel */ 12968c2ecf20Sopenharmony_ci if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci unsigned int rate; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 13018c2ecf20Sopenharmony_ci if (codec->revision != ALI_5451_V02) 13028c2ecf20Sopenharmony_ci return -1; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci rate = snd_ali_get_spdif_in_rate(codec); 13058c2ecf20Sopenharmony_ci if (rate == 0) { 13068c2ecf20Sopenharmony_ci dev_warn(codec->card->dev, 13078c2ecf20Sopenharmony_ci "ali_capture_prepare: spdif rate detect err!\n"); 13088c2ecf20Sopenharmony_ci rate = 48000; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 13118c2ecf20Sopenharmony_ci bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); 13128c2ecf20Sopenharmony_ci if (bValue & 0x10) { 13138c2ecf20Sopenharmony_ci outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); 13148c2ecf20Sopenharmony_ci dev_warn(codec->card->dev, 13158c2ecf20Sopenharmony_ci "clear SPDIF parity error flag.\n"); 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if (rate != 48000) 13198c2ecf20Sopenharmony_ci Delta = ((rate << 12) / runtime->rate) & 0x00ffff; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* set target ESO for channel */ 13238c2ecf20Sopenharmony_ci pvoice->eso = runtime->buffer_size; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* set interrupt count size */ 13268c2ecf20Sopenharmony_ci pvoice->count = runtime->period_size; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* set Loop Back Address */ 13298c2ecf20Sopenharmony_ci LBA = runtime->dma_addr; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* set ESO to capture first MIDLP interrupt */ 13328c2ecf20Sopenharmony_ci ESO = pvoice->eso - 1; 13338c2ecf20Sopenharmony_ci CTRL = snd_ali_control_mode(substream); 13348c2ecf20Sopenharmony_ci GVSEL = 0; 13358c2ecf20Sopenharmony_ci PAN = 0x00; 13368c2ecf20Sopenharmony_ci VOL = 0x00; 13378c2ecf20Sopenharmony_ci EC = 0; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci snd_ali_write_voice_regs( codec, 13408c2ecf20Sopenharmony_ci pvoice->number, 13418c2ecf20Sopenharmony_ci LBA, 13428c2ecf20Sopenharmony_ci 0, /* cso */ 13438c2ecf20Sopenharmony_ci ESO, 13448c2ecf20Sopenharmony_ci Delta, 13458c2ecf20Sopenharmony_ci 0, /* alpha */ 13468c2ecf20Sopenharmony_ci GVSEL, 13478c2ecf20Sopenharmony_ci PAN, 13488c2ecf20Sopenharmony_ci VOL, 13498c2ecf20Sopenharmony_ci CTRL, 13508c2ecf20Sopenharmony_ci EC); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return 0; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 13598c2ecf20Sopenharmony_cisnd_ali_playback_pointer(struct snd_pcm_substream *substream) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 13628c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 13638c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 13648c2ecf20Sopenharmony_ci unsigned int cso; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci spin_lock(&codec->reg_lock); 13678c2ecf20Sopenharmony_ci if (!pvoice->running) { 13688c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); 13728c2ecf20Sopenharmony_ci cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); 13738c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 13748c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci cso %= runtime->buffer_size; 13778c2ecf20Sopenharmony_ci return cso; 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 13848c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 13858c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 13868c2ecf20Sopenharmony_ci unsigned int cso; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci spin_lock(&codec->reg_lock); 13898c2ecf20Sopenharmony_ci if (!pvoice->running) { 13908c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 13918c2ecf20Sopenharmony_ci return 0; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); 13948c2ecf20Sopenharmony_ci cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); 13958c2ecf20Sopenharmony_ci spin_unlock(&codec->reg_lock); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci cso %= runtime->buffer_size; 13988c2ecf20Sopenharmony_ci return cso; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_playback = 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 14048c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 14058c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 14068c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 14078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 14088c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 14098c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 14108c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 14118c2ecf20Sopenharmony_ci .rate_min = 4000, 14128c2ecf20Sopenharmony_ci .rate_max = 48000, 14138c2ecf20Sopenharmony_ci .channels_min = 1, 14148c2ecf20Sopenharmony_ci .channels_max = 2, 14158c2ecf20Sopenharmony_ci .buffer_bytes_max = (256*1024), 14168c2ecf20Sopenharmony_ci .period_bytes_min = 64, 14178c2ecf20Sopenharmony_ci .period_bytes_max = (256*1024), 14188c2ecf20Sopenharmony_ci .periods_min = 1, 14198c2ecf20Sopenharmony_ci .periods_max = 1024, 14208c2ecf20Sopenharmony_ci .fifo_size = 0, 14218c2ecf20Sopenharmony_ci}; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci/* 14248c2ecf20Sopenharmony_ci * Capture support device description 14258c2ecf20Sopenharmony_ci */ 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_capture = 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 14308c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 14318c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 14328c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 14338c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 14348c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 14358c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), 14368c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 14378c2ecf20Sopenharmony_ci .rate_min = 4000, 14388c2ecf20Sopenharmony_ci .rate_max = 48000, 14398c2ecf20Sopenharmony_ci .channels_min = 1, 14408c2ecf20Sopenharmony_ci .channels_max = 2, 14418c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 14428c2ecf20Sopenharmony_ci .period_bytes_min = 64, 14438c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 14448c2ecf20Sopenharmony_ci .periods_min = 1, 14458c2ecf20Sopenharmony_ci .periods_max = 1024, 14468c2ecf20Sopenharmony_ci .fifo_size = 0, 14478c2ecf20Sopenharmony_ci}; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime) 14508c2ecf20Sopenharmony_ci{ 14518c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = runtime->private_data; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (pvoice) 14548c2ecf20Sopenharmony_ci snd_ali_free_voice(pvoice->codec, pvoice); 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int snd_ali_open(struct snd_pcm_substream *substream, int rec, 14588c2ecf20Sopenharmony_ci int channel, const struct snd_pcm_hardware *phw) 14598c2ecf20Sopenharmony_ci{ 14608c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 14618c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 14628c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec, 14658c2ecf20Sopenharmony_ci channel); 14668c2ecf20Sopenharmony_ci if (!pvoice) 14678c2ecf20Sopenharmony_ci return -EAGAIN; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci pvoice->substream = substream; 14708c2ecf20Sopenharmony_ci runtime->private_data = pvoice; 14718c2ecf20Sopenharmony_ci runtime->private_free = snd_ali_pcm_free_substream; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci runtime->hw = *phw; 14748c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 14758c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 14768c2ecf20Sopenharmony_ci 0, 64*1024); 14778c2ecf20Sopenharmony_ci return 0; 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic int snd_ali_playback_open(struct snd_pcm_substream *substream) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci return snd_ali_open(substream, 0, -1, &snd_ali_playback); 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic int snd_ali_capture_open(struct snd_pcm_substream *substream) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci return snd_ali_open(substream, 1, -1, &snd_ali_capture); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic int snd_ali_playback_close(struct snd_pcm_substream *substream) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci return 0; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic int snd_ali_close(struct snd_pcm_substream *substream) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct snd_ali *codec = snd_pcm_substream_chip(substream); 14988c2ecf20Sopenharmony_ci struct snd_ali_voice *pvoice = substream->runtime->private_data; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci snd_ali_disable_special_channel(codec,pvoice->number); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_playback_ops = { 15068c2ecf20Sopenharmony_ci .open = snd_ali_playback_open, 15078c2ecf20Sopenharmony_ci .close = snd_ali_playback_close, 15088c2ecf20Sopenharmony_ci .hw_params = snd_ali_playback_hw_params, 15098c2ecf20Sopenharmony_ci .hw_free = snd_ali_playback_hw_free, 15108c2ecf20Sopenharmony_ci .prepare = snd_ali_playback_prepare, 15118c2ecf20Sopenharmony_ci .trigger = snd_ali_trigger, 15128c2ecf20Sopenharmony_ci .pointer = snd_ali_playback_pointer, 15138c2ecf20Sopenharmony_ci}; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_capture_ops = { 15168c2ecf20Sopenharmony_ci .open = snd_ali_capture_open, 15178c2ecf20Sopenharmony_ci .close = snd_ali_close, 15188c2ecf20Sopenharmony_ci .prepare = snd_ali_prepare, 15198c2ecf20Sopenharmony_ci .trigger = snd_ali_trigger, 15208c2ecf20Sopenharmony_ci .pointer = snd_ali_pointer, 15218c2ecf20Sopenharmony_ci}; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci/* 15248c2ecf20Sopenharmony_ci * Modem PCM 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic int snd_ali_modem_hw_params(struct snd_pcm_substream *substream, 15288c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci struct snd_ali *chip = snd_pcm_substream_chip(substream); 15318c2ecf20Sopenharmony_ci unsigned int modem_num = chip->num_of_codecs - 1; 15328c2ecf20Sopenharmony_ci snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE, 15338c2ecf20Sopenharmony_ci params_rate(hw_params)); 15348c2ecf20Sopenharmony_ci snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0); 15358c2ecf20Sopenharmony_ci return 0; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ali_modem = 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 15418c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 15428c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 15438c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 15448c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 15458c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 15468c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000 | 15478c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_16000), 15488c2ecf20Sopenharmony_ci .rate_min = 8000, 15498c2ecf20Sopenharmony_ci .rate_max = 16000, 15508c2ecf20Sopenharmony_ci .channels_min = 1, 15518c2ecf20Sopenharmony_ci .channels_max = 1, 15528c2ecf20Sopenharmony_ci .buffer_bytes_max = (256*1024), 15538c2ecf20Sopenharmony_ci .period_bytes_min = 64, 15548c2ecf20Sopenharmony_ci .period_bytes_max = (256*1024), 15558c2ecf20Sopenharmony_ci .periods_min = 1, 15568c2ecf20Sopenharmony_ci .periods_max = 1024, 15578c2ecf20Sopenharmony_ci .fifo_size = 0, 15588c2ecf20Sopenharmony_ci}; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec, 15618c2ecf20Sopenharmony_ci int channel) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci static const unsigned int rates[] = {8000, 9600, 12000, 16000}; 15648c2ecf20Sopenharmony_ci static const struct snd_pcm_hw_constraint_list hw_constraint_rates = { 15658c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(rates), 15668c2ecf20Sopenharmony_ci .list = rates, 15678c2ecf20Sopenharmony_ci .mask = 0, 15688c2ecf20Sopenharmony_ci }; 15698c2ecf20Sopenharmony_ci int err = snd_ali_open(substream, rec, channel, &snd_ali_modem); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (err) 15728c2ecf20Sopenharmony_ci return err; 15738c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_list(substream->runtime, 0, 15748c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_cistatic int snd_ali_modem_playback_open(struct snd_pcm_substream *substream) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL); 15808c2ecf20Sopenharmony_ci} 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_cistatic int snd_ali_modem_capture_open(struct snd_pcm_substream *substream) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL); 15858c2ecf20Sopenharmony_ci} 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_playback_ops = { 15888c2ecf20Sopenharmony_ci .open = snd_ali_modem_playback_open, 15898c2ecf20Sopenharmony_ci .close = snd_ali_close, 15908c2ecf20Sopenharmony_ci .hw_params = snd_ali_modem_hw_params, 15918c2ecf20Sopenharmony_ci .prepare = snd_ali_prepare, 15928c2ecf20Sopenharmony_ci .trigger = snd_ali_trigger, 15938c2ecf20Sopenharmony_ci .pointer = snd_ali_pointer, 15948c2ecf20Sopenharmony_ci}; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ali_modem_capture_ops = { 15978c2ecf20Sopenharmony_ci .open = snd_ali_modem_capture_open, 15988c2ecf20Sopenharmony_ci .close = snd_ali_close, 15998c2ecf20Sopenharmony_ci .hw_params = snd_ali_modem_hw_params, 16008c2ecf20Sopenharmony_ci .prepare = snd_ali_prepare, 16018c2ecf20Sopenharmony_ci .trigger = snd_ali_trigger, 16028c2ecf20Sopenharmony_ci .pointer = snd_ali_pointer, 16038c2ecf20Sopenharmony_ci}; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cistruct ali_pcm_description { 16078c2ecf20Sopenharmony_ci char *name; 16088c2ecf20Sopenharmony_ci unsigned int playback_num; 16098c2ecf20Sopenharmony_ci unsigned int capture_num; 16108c2ecf20Sopenharmony_ci const struct snd_pcm_ops *playback_ops; 16118c2ecf20Sopenharmony_ci const struct snd_pcm_ops *capture_ops; 16128c2ecf20Sopenharmony_ci unsigned short class; 16138c2ecf20Sopenharmony_ci}; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic void snd_ali_pcm_free(struct snd_pcm *pcm) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci struct snd_ali *codec = pcm->private_data; 16198c2ecf20Sopenharmony_ci codec->pcm[pcm->device] = NULL; 16208c2ecf20Sopenharmony_ci} 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int snd_ali_pcm(struct snd_ali *codec, int device, 16248c2ecf20Sopenharmony_ci struct ali_pcm_description *desc) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 16278c2ecf20Sopenharmony_ci int err; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci err = snd_pcm_new(codec->card, desc->name, device, 16308c2ecf20Sopenharmony_ci desc->playback_num, desc->capture_num, &pcm); 16318c2ecf20Sopenharmony_ci if (err < 0) { 16328c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 16338c2ecf20Sopenharmony_ci "snd_ali_pcm: err called snd_pcm_new.\n"); 16348c2ecf20Sopenharmony_ci return err; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci pcm->private_data = codec; 16378c2ecf20Sopenharmony_ci pcm->private_free = snd_ali_pcm_free; 16388c2ecf20Sopenharmony_ci if (desc->playback_ops) 16398c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 16408c2ecf20Sopenharmony_ci desc->playback_ops); 16418c2ecf20Sopenharmony_ci if (desc->capture_ops) 16428c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 16438c2ecf20Sopenharmony_ci desc->capture_ops); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 16468c2ecf20Sopenharmony_ci &codec->pci->dev, 64*1024, 128*1024); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci pcm->info_flags = 0; 16498c2ecf20Sopenharmony_ci pcm->dev_class = desc->class; 16508c2ecf20Sopenharmony_ci pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; 16518c2ecf20Sopenharmony_ci strcpy(pcm->name, desc->name); 16528c2ecf20Sopenharmony_ci codec->pcm[0] = pcm; 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci} 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cistatic struct ali_pcm_description ali_pcms[] = { 16578c2ecf20Sopenharmony_ci { .name = "ALI 5451", 16588c2ecf20Sopenharmony_ci .playback_num = ALI_CHANNELS, 16598c2ecf20Sopenharmony_ci .capture_num = 1, 16608c2ecf20Sopenharmony_ci .playback_ops = &snd_ali_playback_ops, 16618c2ecf20Sopenharmony_ci .capture_ops = &snd_ali_capture_ops 16628c2ecf20Sopenharmony_ci }, 16638c2ecf20Sopenharmony_ci { .name = "ALI 5451 modem", 16648c2ecf20Sopenharmony_ci .playback_num = 1, 16658c2ecf20Sopenharmony_ci .capture_num = 1, 16668c2ecf20Sopenharmony_ci .playback_ops = &snd_ali_modem_playback_ops, 16678c2ecf20Sopenharmony_ci .capture_ops = &snd_ali_modem_capture_ops, 16688c2ecf20Sopenharmony_ci .class = SNDRV_PCM_CLASS_MODEM 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci}; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic int snd_ali_build_pcms(struct snd_ali *codec) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci int i, err; 16758c2ecf20Sopenharmony_ci for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) { 16768c2ecf20Sopenharmony_ci err = snd_ali_pcm(codec, i, &ali_pcms[i]); 16778c2ecf20Sopenharmony_ci if (err < 0) 16788c2ecf20Sopenharmony_ci return err; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci return 0; 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci#define ALI5451_SPDIF(xname, xindex, value) \ 16858c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ 16868c2ecf20Sopenharmony_ci.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ 16878c2ecf20Sopenharmony_ci.put = snd_ali5451_spdif_put, .private_value = value} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci#define snd_ali5451_spdif_info snd_ctl_boolean_mono_info 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, 16928c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci struct snd_ali *codec = kcontrol->private_data; 16958c2ecf20Sopenharmony_ci unsigned int spdif_enable; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 17008c2ecf20Sopenharmony_ci switch (kcontrol->private_value) { 17018c2ecf20Sopenharmony_ci case 0: 17028c2ecf20Sopenharmony_ci spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0; 17038c2ecf20Sopenharmony_ci break; 17048c2ecf20Sopenharmony_ci case 1: 17058c2ecf20Sopenharmony_ci spdif_enable = ((codec->spdif_mask & 0x02) && 17068c2ecf20Sopenharmony_ci (codec->spdif_mask & 0x04)) ? 1 : 0; 17078c2ecf20Sopenharmony_ci break; 17088c2ecf20Sopenharmony_ci case 2: 17098c2ecf20Sopenharmony_ci spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0; 17108c2ecf20Sopenharmony_ci break; 17118c2ecf20Sopenharmony_ci default: 17128c2ecf20Sopenharmony_ci break; 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = spdif_enable; 17158c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 17168c2ecf20Sopenharmony_ci return 0; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_cistatic int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, 17208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci struct snd_ali *codec = kcontrol->private_data; 17238c2ecf20Sopenharmony_ci unsigned int change = 0, spdif_enable = 0; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci spin_lock_irq(&codec->reg_lock); 17288c2ecf20Sopenharmony_ci switch (kcontrol->private_value) { 17298c2ecf20Sopenharmony_ci case 0: 17308c2ecf20Sopenharmony_ci change = (codec->spdif_mask & 0x02) ? 1 : 0; 17318c2ecf20Sopenharmony_ci change = change ^ spdif_enable; 17328c2ecf20Sopenharmony_ci if (change) { 17338c2ecf20Sopenharmony_ci if (spdif_enable) { 17348c2ecf20Sopenharmony_ci codec->spdif_mask |= 0x02; 17358c2ecf20Sopenharmony_ci snd_ali_enable_spdif_out(codec); 17368c2ecf20Sopenharmony_ci } else { 17378c2ecf20Sopenharmony_ci codec->spdif_mask &= ~(0x02); 17388c2ecf20Sopenharmony_ci codec->spdif_mask &= ~(0x04); 17398c2ecf20Sopenharmony_ci snd_ali_disable_spdif_out(codec); 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci break; 17438c2ecf20Sopenharmony_ci case 1: 17448c2ecf20Sopenharmony_ci change = (codec->spdif_mask & 0x04) ? 1 : 0; 17458c2ecf20Sopenharmony_ci change = change ^ spdif_enable; 17468c2ecf20Sopenharmony_ci if (change && (codec->spdif_mask & 0x02)) { 17478c2ecf20Sopenharmony_ci if (spdif_enable) { 17488c2ecf20Sopenharmony_ci codec->spdif_mask |= 0x04; 17498c2ecf20Sopenharmony_ci snd_ali_enable_spdif_chnout(codec); 17508c2ecf20Sopenharmony_ci } else { 17518c2ecf20Sopenharmony_ci codec->spdif_mask &= ~(0x04); 17528c2ecf20Sopenharmony_ci snd_ali_disable_spdif_chnout(codec); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci break; 17568c2ecf20Sopenharmony_ci case 2: 17578c2ecf20Sopenharmony_ci change = (codec->spdif_mask & 0x01) ? 1 : 0; 17588c2ecf20Sopenharmony_ci change = change ^ spdif_enable; 17598c2ecf20Sopenharmony_ci if (change) { 17608c2ecf20Sopenharmony_ci if (spdif_enable) { 17618c2ecf20Sopenharmony_ci codec->spdif_mask |= 0x01; 17628c2ecf20Sopenharmony_ci snd_ali_enable_spdif_in(codec); 17638c2ecf20Sopenharmony_ci } else { 17648c2ecf20Sopenharmony_ci codec->spdif_mask &= ~(0x01); 17658c2ecf20Sopenharmony_ci snd_ali_disable_spdif_in(codec); 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci break; 17698c2ecf20Sopenharmony_ci default: 17708c2ecf20Sopenharmony_ci break; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci spin_unlock_irq(&codec->reg_lock); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci return change; 17758c2ecf20Sopenharmony_ci} 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = { 17788c2ecf20Sopenharmony_ci /* spdif aplayback switch */ 17798c2ecf20Sopenharmony_ci /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */ 17808c2ecf20Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0), 17818c2ecf20Sopenharmony_ci /* spdif out to spdif channel */ 17828c2ecf20Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1), 17838c2ecf20Sopenharmony_ci /* spdif in from spdif channel */ 17848c2ecf20Sopenharmony_ci ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) 17858c2ecf20Sopenharmony_ci}; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic int snd_ali_mixer(struct snd_ali *codec) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 17908c2ecf20Sopenharmony_ci unsigned int idx; 17918c2ecf20Sopenharmony_ci int i, err; 17928c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 17938c2ecf20Sopenharmony_ci .write = snd_ali_codec_write, 17948c2ecf20Sopenharmony_ci .read = snd_ali_codec_read, 17958c2ecf20Sopenharmony_ci }; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus); 17988c2ecf20Sopenharmony_ci if (err < 0) 17998c2ecf20Sopenharmony_ci return err; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 18028c2ecf20Sopenharmony_ci ac97.private_data = codec; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci for (i = 0; i < codec->num_of_codecs; i++) { 18058c2ecf20Sopenharmony_ci ac97.num = i; 18068c2ecf20Sopenharmony_ci err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]); 18078c2ecf20Sopenharmony_ci if (err < 0) { 18088c2ecf20Sopenharmony_ci dev_err(codec->card->dev, 18098c2ecf20Sopenharmony_ci "ali mixer %d creating error.\n", i); 18108c2ecf20Sopenharmony_ci if (i == 0) 18118c2ecf20Sopenharmony_ci return err; 18128c2ecf20Sopenharmony_ci codec->num_of_codecs = 1; 18138c2ecf20Sopenharmony_ci break; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci if (codec->spdif_support) { 18188c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) { 18198c2ecf20Sopenharmony_ci err = snd_ctl_add(codec->card, 18208c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); 18218c2ecf20Sopenharmony_ci if (err < 0) 18228c2ecf20Sopenharmony_ci return err; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci return 0; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 18298c2ecf20Sopenharmony_cistatic int ali_suspend(struct device *dev) 18308c2ecf20Sopenharmony_ci{ 18318c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 18328c2ecf20Sopenharmony_ci struct snd_ali *chip = card->private_data; 18338c2ecf20Sopenharmony_ci struct snd_ali_image *im; 18348c2ecf20Sopenharmony_ci int i, j; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci im = chip->image; 18378c2ecf20Sopenharmony_ci if (!im) 18388c2ecf20Sopenharmony_ci return 0; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 18418c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_of_codecs; i++) 18428c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97[i]); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); 18478c2ecf20Sopenharmony_ci /* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */ 18488c2ecf20Sopenharmony_ci im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci /* disable all IRQ bits */ 18518c2ecf20Sopenharmony_ci outl(0, ALI_REG(chip, ALI_MISCINT)); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci for (i = 0; i < ALI_GLOBAL_REGS; i++) { 18548c2ecf20Sopenharmony_ci if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) 18558c2ecf20Sopenharmony_ci continue; 18568c2ecf20Sopenharmony_ci im->regs[i] = inl(ALI_REG(chip, i*4)); 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) { 18608c2ecf20Sopenharmony_ci outb(i, ALI_REG(chip, ALI_GC_CIR)); 18618c2ecf20Sopenharmony_ci for (j = 0; j < ALI_CHANNEL_REGS; j++) 18628c2ecf20Sopenharmony_ci im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci /* stop all HW channel */ 18668c2ecf20Sopenharmony_ci outl(0xffffffff, ALI_REG(chip, ALI_STOP)); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 18698c2ecf20Sopenharmony_ci return 0; 18708c2ecf20Sopenharmony_ci} 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_cistatic int ali_resume(struct device *dev) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 18758c2ecf20Sopenharmony_ci struct snd_ali *chip = card->private_data; 18768c2ecf20Sopenharmony_ci struct snd_ali_image *im; 18778c2ecf20Sopenharmony_ci int i, j; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci im = chip->image; 18808c2ecf20Sopenharmony_ci if (!im) 18818c2ecf20Sopenharmony_ci return 0; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) { 18868c2ecf20Sopenharmony_ci outb(i, ALI_REG(chip, ALI_GC_CIR)); 18878c2ecf20Sopenharmony_ci for (j = 0; j < ALI_CHANNEL_REGS; j++) 18888c2ecf20Sopenharmony_ci outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci for (i = 0; i < ALI_GLOBAL_REGS; i++) { 18928c2ecf20Sopenharmony_ci if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || 18938c2ecf20Sopenharmony_ci (i*4 == ALI_START)) 18948c2ecf20Sopenharmony_ci continue; 18958c2ecf20Sopenharmony_ci outl(im->regs[i], ALI_REG(chip, i*4)); 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci /* start HW channel */ 18998c2ecf20Sopenharmony_ci outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); 19008c2ecf20Sopenharmony_ci /* restore IRQ enable bits */ 19018c2ecf20Sopenharmony_ci outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci for (i = 0 ; i < chip->num_of_codecs; i++) 19068c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97[i]); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 19098c2ecf20Sopenharmony_ci return 0; 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); 19138c2ecf20Sopenharmony_ci#define ALI_PM_OPS &ali_pm 19148c2ecf20Sopenharmony_ci#else 19158c2ecf20Sopenharmony_ci#define ALI_PM_OPS NULL 19168c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_cistatic int snd_ali_free(struct snd_ali * codec) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci if (codec->hw_initialized) 19218c2ecf20Sopenharmony_ci snd_ali_disable_address_interrupt(codec); 19228c2ecf20Sopenharmony_ci if (codec->irq >= 0) 19238c2ecf20Sopenharmony_ci free_irq(codec->irq, codec); 19248c2ecf20Sopenharmony_ci if (codec->port) 19258c2ecf20Sopenharmony_ci pci_release_regions(codec->pci); 19268c2ecf20Sopenharmony_ci pci_disable_device(codec->pci); 19278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 19288c2ecf20Sopenharmony_ci kfree(codec->image); 19298c2ecf20Sopenharmony_ci#endif 19308c2ecf20Sopenharmony_ci pci_dev_put(codec->pci_m1533); 19318c2ecf20Sopenharmony_ci pci_dev_put(codec->pci_m7101); 19328c2ecf20Sopenharmony_ci kfree(codec); 19338c2ecf20Sopenharmony_ci return 0; 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_cistatic int snd_ali_chip_init(struct snd_ali *codec) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci unsigned int legacy; 19398c2ecf20Sopenharmony_ci unsigned char temp; 19408c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "chip initializing ...\n"); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (snd_ali_reset_5451(codec)) { 19458c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "ali_chip_init: reset 5451 error.\n"); 19468c2ecf20Sopenharmony_ci return -1; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci if (codec->revision == ALI_5451_V02) { 19508c2ecf20Sopenharmony_ci pci_dev = codec->pci_m1533; 19518c2ecf20Sopenharmony_ci pci_read_config_byte(pci_dev, 0x59, &temp); 19528c2ecf20Sopenharmony_ci temp |= 0x80; 19538c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, 0x59, temp); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci pci_dev = codec->pci_m7101; 19568c2ecf20Sopenharmony_ci pci_read_config_byte(pci_dev, 0xb8, &temp); 19578c2ecf20Sopenharmony_ci temp |= 0x20; 19588c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, 0xB8, temp); 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci pci_read_config_dword(codec->pci, 0x44, &legacy); 19628c2ecf20Sopenharmony_ci legacy &= 0xff00ff00; 19638c2ecf20Sopenharmony_ci legacy |= 0x000800aa; 19648c2ecf20Sopenharmony_ci pci_write_config_dword(codec->pci, 0x44, legacy); 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); 19678c2ecf20Sopenharmony_ci outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); 19688c2ecf20Sopenharmony_ci outl(0xffffffff, ALI_REG(codec, ALI_AINT)); 19698c2ecf20Sopenharmony_ci outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); 19708c2ecf20Sopenharmony_ci outb(0x10, ALI_REG(codec, ALI_MPUR2)); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); 19738c2ecf20Sopenharmony_ci codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, 19748c2ecf20Sopenharmony_ci AC97_EXTENDED_STATUS); 19758c2ecf20Sopenharmony_ci if (codec->spdif_support) { 19768c2ecf20Sopenharmony_ci snd_ali_enable_spdif_out(codec); 19778c2ecf20Sopenharmony_ci codec->spdif_mask = 0x00000002; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci codec->num_of_codecs = 1; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* secondary codec - modem */ 19838c2ecf20Sopenharmony_ci if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) { 19848c2ecf20Sopenharmony_ci codec->num_of_codecs++; 19858c2ecf20Sopenharmony_ci outl(inl(ALI_REG(codec, ALI_SCTRL)) | 19868c2ecf20Sopenharmony_ci (ALI_SCTRL_LINE_IN2 | ALI_SCTRL_GPIO_IN2 | 19878c2ecf20Sopenharmony_ci ALI_SCTRL_LINE_OUT_EN), 19888c2ecf20Sopenharmony_ci ALI_REG(codec, ALI_SCTRL)); 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "chip initialize succeed.\n"); 19928c2ecf20Sopenharmony_ci return 0; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci} 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci/* proc for register dump */ 19978c2ecf20Sopenharmony_cistatic void snd_ali_proc_read(struct snd_info_entry *entry, 19988c2ecf20Sopenharmony_ci struct snd_info_buffer *buf) 19998c2ecf20Sopenharmony_ci{ 20008c2ecf20Sopenharmony_ci struct snd_ali *codec = entry->private_data; 20018c2ecf20Sopenharmony_ci int i; 20028c2ecf20Sopenharmony_ci for (i = 0; i < 256 ; i+= 4) 20038c2ecf20Sopenharmony_ci snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i))); 20048c2ecf20Sopenharmony_ci} 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_cistatic void snd_ali_proc_init(struct snd_ali *codec) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read); 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic int snd_ali_resources(struct snd_ali *codec) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci int err; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "resources allocation ...\n"); 20168c2ecf20Sopenharmony_ci err = pci_request_regions(codec->pci, "ALI 5451"); 20178c2ecf20Sopenharmony_ci if (err < 0) 20188c2ecf20Sopenharmony_ci return err; 20198c2ecf20Sopenharmony_ci codec->port = pci_resource_start(codec->pci, 0); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci if (request_irq(codec->pci->irq, snd_ali_card_interrupt, 20228c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, codec)) { 20238c2ecf20Sopenharmony_ci dev_err(codec->card->dev, "Unable to request irq.\n"); 20248c2ecf20Sopenharmony_ci return -EBUSY; 20258c2ecf20Sopenharmony_ci } 20268c2ecf20Sopenharmony_ci codec->irq = codec->pci->irq; 20278c2ecf20Sopenharmony_ci codec->card->sync_irq = codec->irq; 20288c2ecf20Sopenharmony_ci dev_dbg(codec->card->dev, "resources allocated.\n"); 20298c2ecf20Sopenharmony_ci return 0; 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_cistatic int snd_ali_dev_free(struct snd_device *device) 20328c2ecf20Sopenharmony_ci{ 20338c2ecf20Sopenharmony_ci struct snd_ali *codec = device->device_data; 20348c2ecf20Sopenharmony_ci snd_ali_free(codec); 20358c2ecf20Sopenharmony_ci return 0; 20368c2ecf20Sopenharmony_ci} 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_cistatic int snd_ali_create(struct snd_card *card, 20398c2ecf20Sopenharmony_ci struct pci_dev *pci, 20408c2ecf20Sopenharmony_ci int pcm_streams, 20418c2ecf20Sopenharmony_ci int spdif_support, 20428c2ecf20Sopenharmony_ci struct snd_ali **r_ali) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct snd_ali *codec; 20458c2ecf20Sopenharmony_ci int i, err; 20468c2ecf20Sopenharmony_ci unsigned short cmdw; 20478c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 20488c2ecf20Sopenharmony_ci .dev_free = snd_ali_dev_free, 20498c2ecf20Sopenharmony_ci }; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci *r_ali = NULL; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci dev_dbg(card->dev, "creating ...\n"); 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci /* enable PCI device */ 20568c2ecf20Sopenharmony_ci err = pci_enable_device(pci); 20578c2ecf20Sopenharmony_ci if (err < 0) 20588c2ecf20Sopenharmony_ci return err; 20598c2ecf20Sopenharmony_ci /* check, if we can restrict PCI DMA transfers to 31 bits */ 20608c2ecf20Sopenharmony_ci if (dma_set_mask(&pci->dev, DMA_BIT_MASK(31)) < 0 || 20618c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(31)) < 0) { 20628c2ecf20Sopenharmony_ci dev_err(card->dev, 20638c2ecf20Sopenharmony_ci "architecture does not support 31bit PCI busmaster DMA\n"); 20648c2ecf20Sopenharmony_ci pci_disable_device(pci); 20658c2ecf20Sopenharmony_ci return -ENXIO; 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci codec = kzalloc(sizeof(*codec), GFP_KERNEL); 20698c2ecf20Sopenharmony_ci if (!codec) { 20708c2ecf20Sopenharmony_ci pci_disable_device(pci); 20718c2ecf20Sopenharmony_ci return -ENOMEM; 20728c2ecf20Sopenharmony_ci } 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci spin_lock_init(&codec->reg_lock); 20758c2ecf20Sopenharmony_ci spin_lock_init(&codec->voice_alloc); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci codec->card = card; 20788c2ecf20Sopenharmony_ci codec->pci = pci; 20798c2ecf20Sopenharmony_ci codec->irq = -1; 20808c2ecf20Sopenharmony_ci codec->revision = pci->revision; 20818c2ecf20Sopenharmony_ci codec->spdif_support = spdif_support; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci if (pcm_streams < 1) 20848c2ecf20Sopenharmony_ci pcm_streams = 1; 20858c2ecf20Sopenharmony_ci if (pcm_streams > 32) 20868c2ecf20Sopenharmony_ci pcm_streams = 32; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci pci_set_master(pci); 20898c2ecf20Sopenharmony_ci pci_read_config_word(pci, PCI_COMMAND, &cmdw); 20908c2ecf20Sopenharmony_ci if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { 20918c2ecf20Sopenharmony_ci cmdw |= PCI_COMMAND_IO; 20928c2ecf20Sopenharmony_ci pci_write_config_word(pci, PCI_COMMAND, cmdw); 20938c2ecf20Sopenharmony_ci } 20948c2ecf20Sopenharmony_ci pci_set_master(pci); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci if (snd_ali_resources(codec)) { 20978c2ecf20Sopenharmony_ci snd_ali_free(codec); 20988c2ecf20Sopenharmony_ci return -EBUSY; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci codec->synth.chmap = 0; 21028c2ecf20Sopenharmony_ci codec->synth.chcnt = 0; 21038c2ecf20Sopenharmony_ci codec->spdif_mask = 0; 21048c2ecf20Sopenharmony_ci codec->synth.synthcount = 0; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci if (codec->revision == ALI_5451_V02) 21078c2ecf20Sopenharmony_ci codec->chregs.regs.ac97read = ALI_AC97_WRITE; 21088c2ecf20Sopenharmony_ci else 21098c2ecf20Sopenharmony_ci codec->chregs.regs.ac97read = ALI_AC97_READ; 21108c2ecf20Sopenharmony_ci codec->chregs.regs.ac97write = ALI_AC97_WRITE; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci codec->chregs.regs.start = ALI_START; 21138c2ecf20Sopenharmony_ci codec->chregs.regs.stop = ALI_STOP; 21148c2ecf20Sopenharmony_ci codec->chregs.regs.aint = ALI_AINT; 21158c2ecf20Sopenharmony_ci codec->chregs.regs.ainten = ALI_AINTEN; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci codec->chregs.data.start = 0x00; 21188c2ecf20Sopenharmony_ci codec->chregs.data.stop = 0x00; 21198c2ecf20Sopenharmony_ci codec->chregs.data.aint = 0x00; 21208c2ecf20Sopenharmony_ci codec->chregs.data.ainten = 0x00; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci /* M1533: southbridge */ 21238c2ecf20Sopenharmony_ci codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL); 21248c2ecf20Sopenharmony_ci if (!codec->pci_m1533) { 21258c2ecf20Sopenharmony_ci dev_err(card->dev, "cannot find ALi 1533 chip.\n"); 21268c2ecf20Sopenharmony_ci snd_ali_free(codec); 21278c2ecf20Sopenharmony_ci return -ENODEV; 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci /* M7101: power management */ 21308c2ecf20Sopenharmony_ci codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL); 21318c2ecf20Sopenharmony_ci if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) { 21328c2ecf20Sopenharmony_ci dev_err(card->dev, "cannot find ALi 7101 chip.\n"); 21338c2ecf20Sopenharmony_ci snd_ali_free(codec); 21348c2ecf20Sopenharmony_ci return -ENODEV; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci dev_dbg(card->dev, "snd_device_new is called.\n"); 21388c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops); 21398c2ecf20Sopenharmony_ci if (err < 0) { 21408c2ecf20Sopenharmony_ci snd_ali_free(codec); 21418c2ecf20Sopenharmony_ci return err; 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci /* initialise synth voices*/ 21458c2ecf20Sopenharmony_ci for (i = 0; i < ALI_CHANNELS; i++) 21468c2ecf20Sopenharmony_ci codec->synth.voices[i].number = i; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci err = snd_ali_chip_init(codec); 21498c2ecf20Sopenharmony_ci if (err < 0) { 21508c2ecf20Sopenharmony_ci dev_err(card->dev, "ali create: chip init error.\n"); 21518c2ecf20Sopenharmony_ci return err; 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 21558c2ecf20Sopenharmony_ci codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL); 21568c2ecf20Sopenharmony_ci if (!codec->image) 21578c2ecf20Sopenharmony_ci dev_warn(card->dev, "can't allocate apm buffer\n"); 21588c2ecf20Sopenharmony_ci#endif 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci snd_ali_enable_address_interrupt(codec); 21618c2ecf20Sopenharmony_ci codec->hw_initialized = 1; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci *r_ali = codec; 21648c2ecf20Sopenharmony_ci dev_dbg(card->dev, "created.\n"); 21658c2ecf20Sopenharmony_ci return 0; 21668c2ecf20Sopenharmony_ci} 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_cistatic int snd_ali_probe(struct pci_dev *pci, 21698c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci struct snd_card *card; 21728c2ecf20Sopenharmony_ci struct snd_ali *codec; 21738c2ecf20Sopenharmony_ci int err; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "probe ...\n"); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card); 21788c2ecf20Sopenharmony_ci if (err < 0) 21798c2ecf20Sopenharmony_ci return err; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci err = snd_ali_create(card, pci, pcm_channels, spdif, &codec); 21828c2ecf20Sopenharmony_ci if (err < 0) 21838c2ecf20Sopenharmony_ci goto error; 21848c2ecf20Sopenharmony_ci card->private_data = codec; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "mixer building ...\n"); 21878c2ecf20Sopenharmony_ci err = snd_ali_mixer(codec); 21888c2ecf20Sopenharmony_ci if (err < 0) 21898c2ecf20Sopenharmony_ci goto error; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "pcm building ...\n"); 21928c2ecf20Sopenharmony_ci err = snd_ali_build_pcms(codec); 21938c2ecf20Sopenharmony_ci if (err < 0) 21948c2ecf20Sopenharmony_ci goto error; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci snd_ali_proc_init(codec); 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci strcpy(card->driver, "ALI5451"); 21998c2ecf20Sopenharmony_ci strcpy(card->shortname, "ALI 5451"); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i", 22028c2ecf20Sopenharmony_ci card->shortname, codec->port, codec->irq); 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "register card.\n"); 22058c2ecf20Sopenharmony_ci err = snd_card_register(card); 22068c2ecf20Sopenharmony_ci if (err < 0) 22078c2ecf20Sopenharmony_ci goto error; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 22108c2ecf20Sopenharmony_ci return 0; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci error: 22138c2ecf20Sopenharmony_ci snd_card_free(card); 22148c2ecf20Sopenharmony_ci return err; 22158c2ecf20Sopenharmony_ci} 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_cistatic void snd_ali_remove(struct pci_dev *pci) 22188c2ecf20Sopenharmony_ci{ 22198c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic struct pci_driver ali5451_driver = { 22238c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 22248c2ecf20Sopenharmony_ci .id_table = snd_ali_ids, 22258c2ecf20Sopenharmony_ci .probe = snd_ali_probe, 22268c2ecf20Sopenharmony_ci .remove = snd_ali_remove, 22278c2ecf20Sopenharmony_ci .driver = { 22288c2ecf20Sopenharmony_ci .pm = ALI_PM_OPS, 22298c2ecf20Sopenharmony_ci }, 22308c2ecf20Sopenharmony_ci}; 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_cimodule_pci_driver(ali5451_driver); 2233