18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for generic ESS AudioDrive ES18xx soundcards 48c2ecf20Sopenharmony_ci * Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de> 58c2ecf20Sopenharmony_ci * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci/* GENERAL NOTES: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * BUGS: 108c2ecf20Sopenharmony_ci * - There are pops (we can't delay in trigger function, cause midlevel 118c2ecf20Sopenharmony_ci * often need to trigger down and then up very quickly). 128c2ecf20Sopenharmony_ci * Any ideas? 138c2ecf20Sopenharmony_ci * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * ES1868 NOTES: 188c2ecf20Sopenharmony_ci * - The chip has one half duplex pcm (with very limited full duplex support). 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Duplex stereophonic sound is impossible. 218c2ecf20Sopenharmony_ci * - Record and playback must share the same frequency rate. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * - The driver use dma2 for playback and dma1 for capture. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * ES1869 NOTES: 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * - there are a first full duplex pcm and a second playback only pcm 308c2ecf20Sopenharmony_ci * (incompatible with first pcm capture) 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * - there is support for the capture volume and ESS Spatializer 3D effect. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * - contrarily to some pages in DS_1869.PDF the rates can be set 358c2ecf20Sopenharmony_ci * independently. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * - Zoom Video is implemented by sharing the FM DAC, thus the user can 388c2ecf20Sopenharmony_ci * have either FM playback or Video playback but not both simultaneously. 398c2ecf20Sopenharmony_ci * The Video Playback Switch mixer control toggles this choice. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * BUGS: 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * - There is a major trouble I noted: 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * using both channel for playback stereo 16 bit samples at 44100 Hz 468c2ecf20Sopenharmony_ci * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * The same happens using Audio1 for captureing. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * The Windows driver does not suffer of this (although it use Audio1 518c2ecf20Sopenharmony_ci * only for captureing). I'm unable to discover why. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * ES1879 NOTES: 578c2ecf20Sopenharmony_ci * - When Zoom Video is enabled (reg 0x71 bit 6 toggled on) the PCM playback 588c2ecf20Sopenharmony_ci * seems to be effected (speaker_test plays a lower frequency). Can't find 598c2ecf20Sopenharmony_ci * anything in the datasheet to account for this, so a Video Playback Switch 608c2ecf20Sopenharmony_ci * control has been included to allow ZV to be enabled only when necessary. 618c2ecf20Sopenharmony_ci * Then again on at least one test system the 0x71 bit 6 enable bit is not 628c2ecf20Sopenharmony_ci * needed for ZV, so maybe the datasheet is entirely wrong here. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#include <linux/init.h> 668c2ecf20Sopenharmony_ci#include <linux/err.h> 678c2ecf20Sopenharmony_ci#include <linux/isa.h> 688c2ecf20Sopenharmony_ci#include <linux/pnp.h> 698c2ecf20Sopenharmony_ci#include <linux/isapnp.h> 708c2ecf20Sopenharmony_ci#include <linux/module.h> 718c2ecf20Sopenharmony_ci#include <linux/delay.h> 728c2ecf20Sopenharmony_ci#include <linux/io.h> 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#include <asm/dma.h> 758c2ecf20Sopenharmony_ci#include <sound/core.h> 768c2ecf20Sopenharmony_ci#include <sound/control.h> 778c2ecf20Sopenharmony_ci#include <sound/pcm.h> 788c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 798c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 808c2ecf20Sopenharmony_ci#include <sound/opl3.h> 818c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 828c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 838c2ecf20Sopenharmony_ci#include <sound/initval.h> 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define PFX "es18xx: " 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct snd_es18xx { 888c2ecf20Sopenharmony_ci unsigned long port; /* port of ESS chip */ 898c2ecf20Sopenharmony_ci unsigned long ctrl_port; /* Control port of ESS chip */ 908c2ecf20Sopenharmony_ci struct resource *res_port; 918c2ecf20Sopenharmony_ci struct resource *res_mpu_port; 928c2ecf20Sopenharmony_ci struct resource *res_ctrl_port; 938c2ecf20Sopenharmony_ci int irq; /* IRQ number of ESS chip */ 948c2ecf20Sopenharmony_ci int dma1; /* DMA1 */ 958c2ecf20Sopenharmony_ci int dma2; /* DMA2 */ 968c2ecf20Sopenharmony_ci unsigned short version; /* version of ESS chip */ 978c2ecf20Sopenharmony_ci int caps; /* Chip capabilities */ 988c2ecf20Sopenharmony_ci unsigned short audio2_vol; /* volume level of audio2 */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci unsigned short active; /* active channel mask */ 1018c2ecf20Sopenharmony_ci unsigned int dma1_shift; 1028c2ecf20Sopenharmony_ci unsigned int dma2_shift; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 1058c2ecf20Sopenharmony_ci struct snd_pcm_substream *playback_a_substream; 1068c2ecf20Sopenharmony_ci struct snd_pcm_substream *capture_a_substream; 1078c2ecf20Sopenharmony_ci struct snd_pcm_substream *playback_b_substream; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci struct snd_kcontrol *hw_volume; 1128c2ecf20Sopenharmony_ci struct snd_kcontrol *hw_switch; 1138c2ecf20Sopenharmony_ci struct snd_kcontrol *master_volume; 1148c2ecf20Sopenharmony_ci struct snd_kcontrol *master_switch; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci spinlock_t reg_lock; 1178c2ecf20Sopenharmony_ci spinlock_t mixer_lock; 1188c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1198c2ecf20Sopenharmony_ci unsigned char pm_reg; 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 1228c2ecf20Sopenharmony_ci struct pnp_dev *dev; 1238c2ecf20Sopenharmony_ci struct pnp_dev *devc; 1248c2ecf20Sopenharmony_ci#endif 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define AUDIO1_IRQ 0x01 1288c2ecf20Sopenharmony_ci#define AUDIO2_IRQ 0x02 1298c2ecf20Sopenharmony_ci#define HWV_IRQ 0x04 1308c2ecf20Sopenharmony_ci#define MPU_IRQ 0x08 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define ES18XX_PCM2 0x0001 /* Has two useable PCM */ 1338c2ecf20Sopenharmony_ci#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ 1348c2ecf20Sopenharmony_ci#define ES18XX_RECMIX 0x0004 /* Has record mixer */ 1358c2ecf20Sopenharmony_ci#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ 1368c2ecf20Sopenharmony_ci#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ 1378c2ecf20Sopenharmony_ci#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ 1388c2ecf20Sopenharmony_ci#define ES18XX_AUXB 0x0040 /* AuxB mixer control */ 1398c2ecf20Sopenharmony_ci#define ES18XX_HWV 0x0080 /* Has separate hardware volume mixer controls*/ 1408c2ecf20Sopenharmony_ci#define ES18XX_MONO 0x0100 /* Mono_in mixer control */ 1418c2ecf20Sopenharmony_ci#define ES18XX_I2S 0x0200 /* I2S mixer control */ 1428c2ecf20Sopenharmony_ci#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ 1438c2ecf20Sopenharmony_ci#define ES18XX_CONTROL 0x0800 /* Has control ports */ 1448c2ecf20Sopenharmony_ci#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* Power Management */ 1478c2ecf20Sopenharmony_ci#define ES18XX_PM 0x07 1488c2ecf20Sopenharmony_ci#define ES18XX_PM_GPO0 0x01 1498c2ecf20Sopenharmony_ci#define ES18XX_PM_GPO1 0x02 1508c2ecf20Sopenharmony_ci#define ES18XX_PM_PDR 0x04 1518c2ecf20Sopenharmony_ci#define ES18XX_PM_ANA 0x08 1528c2ecf20Sopenharmony_ci#define ES18XX_PM_FM 0x020 1538c2ecf20Sopenharmony_ci#define ES18XX_PM_SUS 0x080 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Lowlevel */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define DAC1 0x01 1588c2ecf20Sopenharmony_ci#define ADC1 0x02 1598c2ecf20Sopenharmony_ci#define DAC2 0x04 1608c2ecf20Sopenharmony_ci#define MILLISECOND 10000 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for(i = MILLISECOND; i; i--) 1678c2ecf20Sopenharmony_ci if ((inb(chip->port + 0x0C) & 0x80) == 0) { 1688c2ecf20Sopenharmony_ci outb(val, chip->port + 0x0C); 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "dsp_command: timeout (0x%x)\n", val); 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for(i = MILLISECOND/10; i; i--) 1808c2ecf20Sopenharmony_ci if (inb(chip->port + 0x0C) & 0x40) 1818c2ecf20Sopenharmony_ci return inb(chip->port + 0x0A); 1828c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "dsp_get_byte failed: 0x%lx = 0x%x!!!\n", 1838c2ecf20Sopenharmony_ci chip->port + 0x0A, inb(chip->port + 0x0A)); 1848c2ecf20Sopenharmony_ci return -ENODEV; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#undef REG_DEBUG 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int snd_es18xx_write(struct snd_es18xx *chip, 1908c2ecf20Sopenharmony_ci unsigned char reg, unsigned char data) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci unsigned long flags; 1938c2ecf20Sopenharmony_ci int ret; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 1968c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 1978c2ecf20Sopenharmony_ci if (ret < 0) 1988c2ecf20Sopenharmony_ci goto end; 1998c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, data); 2008c2ecf20Sopenharmony_ci end: 2018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2028c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 2038c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, data); 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci unsigned long flags; 2118c2ecf20Sopenharmony_ci int ret, data; 2128c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2138c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, 0xC0); 2148c2ecf20Sopenharmony_ci if (ret < 0) 2158c2ecf20Sopenharmony_ci goto end; 2168c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 2178c2ecf20Sopenharmony_ci if (ret < 0) 2188c2ecf20Sopenharmony_ci goto end; 2198c2ecf20Sopenharmony_ci data = snd_es18xx_dsp_get_byte(chip); 2208c2ecf20Sopenharmony_ci ret = data; 2218c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 2228c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x now is %02x (%d)\n", reg, data, ret); 2238c2ecf20Sopenharmony_ci#endif 2248c2ecf20Sopenharmony_ci end: 2258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* Return old value */ 2308c2ecf20Sopenharmony_cistatic int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg, 2318c2ecf20Sopenharmony_ci unsigned char mask, unsigned char val) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci unsigned char old, new, oval; 2358c2ecf20Sopenharmony_ci unsigned long flags; 2368c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2378c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, 0xC0); 2388c2ecf20Sopenharmony_ci if (ret < 0) 2398c2ecf20Sopenharmony_ci goto end; 2408c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 2418c2ecf20Sopenharmony_ci if (ret < 0) 2428c2ecf20Sopenharmony_ci goto end; 2438c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_get_byte(chip); 2448c2ecf20Sopenharmony_ci if (ret < 0) { 2458c2ecf20Sopenharmony_ci goto end; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci old = ret; 2488c2ecf20Sopenharmony_ci oval = old & mask; 2498c2ecf20Sopenharmony_ci if (val != oval) { 2508c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 2518c2ecf20Sopenharmony_ci if (ret < 0) 2528c2ecf20Sopenharmony_ci goto end; 2538c2ecf20Sopenharmony_ci new = (old & ~mask) | (val & mask); 2548c2ecf20Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, new); 2558c2ecf20Sopenharmony_ci if (ret < 0) 2568c2ecf20Sopenharmony_ci goto end; 2578c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 2588c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x (%d)\n", 2598c2ecf20Sopenharmony_ci reg, old, new, ret); 2608c2ecf20Sopenharmony_ci#endif 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci ret = oval; 2638c2ecf20Sopenharmony_ci end: 2648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic inline void snd_es18xx_mixer_write(struct snd_es18xx *chip, 2698c2ecf20Sopenharmony_ci unsigned char reg, unsigned char data) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci unsigned long flags; 2728c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 2738c2ecf20Sopenharmony_ci outb(reg, chip->port + 0x04); 2748c2ecf20Sopenharmony_ci outb(data, chip->port + 0x05); 2758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 2768c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 2778c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, data); 2788c2ecf20Sopenharmony_ci#endif 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci unsigned long flags; 2848c2ecf20Sopenharmony_ci int data; 2858c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 2868c2ecf20Sopenharmony_ci outb(reg, chip->port + 0x04); 2878c2ecf20Sopenharmony_ci data = inb(chip->port + 0x05); 2888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 2898c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 2908c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data); 2918c2ecf20Sopenharmony_ci#endif 2928c2ecf20Sopenharmony_ci return data; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* Return old value */ 2968c2ecf20Sopenharmony_cistatic inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char reg, 2978c2ecf20Sopenharmony_ci unsigned char mask, unsigned char val) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci unsigned char old, new, oval; 3008c2ecf20Sopenharmony_ci unsigned long flags; 3018c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 3028c2ecf20Sopenharmony_ci outb(reg, chip->port + 0x04); 3038c2ecf20Sopenharmony_ci old = inb(chip->port + 0x05); 3048c2ecf20Sopenharmony_ci oval = old & mask; 3058c2ecf20Sopenharmony_ci if (val != oval) { 3068c2ecf20Sopenharmony_ci new = (old & ~mask) | (val & mask); 3078c2ecf20Sopenharmony_ci outb(new, chip->port + 0x05); 3088c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 3098c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n", 3108c2ecf20Sopenharmony_ci reg, old, new); 3118c2ecf20Sopenharmony_ci#endif 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 3148c2ecf20Sopenharmony_ci return oval; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned char reg, 3188c2ecf20Sopenharmony_ci unsigned char mask) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int old, expected, new; 3218c2ecf20Sopenharmony_ci unsigned long flags; 3228c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 3238c2ecf20Sopenharmony_ci outb(reg, chip->port + 0x04); 3248c2ecf20Sopenharmony_ci old = inb(chip->port + 0x05); 3258c2ecf20Sopenharmony_ci expected = old ^ mask; 3268c2ecf20Sopenharmony_ci outb(expected, chip->port + 0x05); 3278c2ecf20Sopenharmony_ci new = inb(chip->port + 0x05); 3288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 3298c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 3308c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x, now is %02x\n", 3318c2ecf20Sopenharmony_ci reg, old, expected, new); 3328c2ecf20Sopenharmony_ci#endif 3338c2ecf20Sopenharmony_ci return expected == new; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int snd_es18xx_reset(struct snd_es18xx *chip) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int i; 3408c2ecf20Sopenharmony_ci outb(0x03, chip->port + 0x06); 3418c2ecf20Sopenharmony_ci inb(chip->port + 0x06); 3428c2ecf20Sopenharmony_ci outb(0x00, chip->port + 0x06); 3438c2ecf20Sopenharmony_ci for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); 3448c2ecf20Sopenharmony_ci if (inb(chip->port + 0x0A) != 0xAA) 3458c2ecf20Sopenharmony_ci return -1; 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int snd_es18xx_reset_fifo(struct snd_es18xx *chip) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci outb(0x02, chip->port + 0x06); 3528c2ecf20Sopenharmony_ci inb(chip->port + 0x06); 3538c2ecf20Sopenharmony_ci outb(0x00, chip->port + 0x06); 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic const struct snd_ratnum new_clocks[2] = { 3588c2ecf20Sopenharmony_ci { 3598c2ecf20Sopenharmony_ci .num = 793800, 3608c2ecf20Sopenharmony_ci .den_min = 1, 3618c2ecf20Sopenharmony_ci .den_max = 128, 3628c2ecf20Sopenharmony_ci .den_step = 1, 3638c2ecf20Sopenharmony_ci }, 3648c2ecf20Sopenharmony_ci { 3658c2ecf20Sopenharmony_ci .num = 768000, 3668c2ecf20Sopenharmony_ci .den_min = 1, 3678c2ecf20Sopenharmony_ci .den_max = 128, 3688c2ecf20Sopenharmony_ci .den_step = 1, 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = { 3738c2ecf20Sopenharmony_ci .nrats = 2, 3748c2ecf20Sopenharmony_ci .rats = new_clocks, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const struct snd_ratnum old_clocks[2] = { 3788c2ecf20Sopenharmony_ci { 3798c2ecf20Sopenharmony_ci .num = 795444, 3808c2ecf20Sopenharmony_ci .den_min = 1, 3818c2ecf20Sopenharmony_ci .den_max = 128, 3828c2ecf20Sopenharmony_ci .den_step = 1, 3838c2ecf20Sopenharmony_ci }, 3848c2ecf20Sopenharmony_ci { 3858c2ecf20Sopenharmony_ci .num = 397722, 3868c2ecf20Sopenharmony_ci .den_min = 1, 3878c2ecf20Sopenharmony_ci .den_max = 128, 3888c2ecf20Sopenharmony_ci .den_step = 1, 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = { 3938c2ecf20Sopenharmony_ci .nrats = 2, 3948c2ecf20Sopenharmony_ci .rats = old_clocks, 3958c2ecf20Sopenharmony_ci}; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void snd_es18xx_rate_set(struct snd_es18xx *chip, 3998c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 4008c2ecf20Sopenharmony_ci int mode) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci unsigned int bits, div0; 4038c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4048c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_NEW_RATE) { 4058c2ecf20Sopenharmony_ci if (runtime->rate_num == new_clocks[0].num) 4068c2ecf20Sopenharmony_ci bits = 128 - runtime->rate_den; 4078c2ecf20Sopenharmony_ci else 4088c2ecf20Sopenharmony_ci bits = 256 - runtime->rate_den; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci if (runtime->rate_num == old_clocks[0].num) 4118c2ecf20Sopenharmony_ci bits = 256 - runtime->rate_den; 4128c2ecf20Sopenharmony_ci else 4138c2ecf20Sopenharmony_ci bits = 128 - runtime->rate_den; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* set filter register */ 4178c2ecf20Sopenharmony_ci div0 = 256 - 7160000*20/(8*82*runtime->rate); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { 4208c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x70, bits); 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * Comment from kernel oss driver: 4238c2ecf20Sopenharmony_ci * FKS: fascinating: 0x72 doesn't seem to work. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA2, div0); 4268c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x72, div0); 4278c2ecf20Sopenharmony_ci } else { 4288c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA1, bits); 4298c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA2, div0); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream, 4348c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 4378c2ecf20Sopenharmony_ci int shift; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci shift = 0; 4408c2ecf20Sopenharmony_ci if (params_channels(hw_params) == 2) 4418c2ecf20Sopenharmony_ci shift++; 4428c2ecf20Sopenharmony_ci if (snd_pcm_format_width(params_format(hw_params)) == 16) 4438c2ecf20Sopenharmony_ci shift++; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 4468c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 4478c2ecf20Sopenharmony_ci (chip->capture_a_substream) && 4488c2ecf20Sopenharmony_ci params_channels(hw_params) != 1) { 4498c2ecf20Sopenharmony_ci _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); 4508c2ecf20Sopenharmony_ci return -EBUSY; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci chip->dma2_shift = shift; 4538c2ecf20Sopenharmony_ci } else { 4548c2ecf20Sopenharmony_ci chip->dma1_shift = shift; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int snd_es18xx_playback1_prepare(struct snd_es18xx *chip, 4608c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4638c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 4648c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci snd_es18xx_rate_set(chip, substream, DAC2); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Transfer Count Reload */ 4698c2ecf20Sopenharmony_ci count = 0x10000 - count; 4708c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x74, count & 0xff); 4718c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x76, count >> 8); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Set format */ 4748c2ecf20Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7A, 0x07, 4758c2ecf20Sopenharmony_ci ((runtime->channels == 1) ? 0x00 : 0x02) | 4768c2ecf20Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | 4778c2ecf20Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Set DMA controller */ 4808c2ecf20Sopenharmony_ci snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int snd_es18xx_playback1_trigger(struct snd_es18xx *chip, 4868c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 4878c2ecf20Sopenharmony_ci int cmd) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci switch (cmd) { 4908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 4928c2ecf20Sopenharmony_ci if (chip->active & DAC2) 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci chip->active |= DAC2; 4958c2ecf20Sopenharmony_ci /* Start DMA */ 4968c2ecf20Sopenharmony_ci if (chip->dma2 >= 4) 4978c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0xb3); 4988c2ecf20Sopenharmony_ci else 4998c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0x93); 5008c2ecf20Sopenharmony_ci#ifdef AVOID_POPS 5018c2ecf20Sopenharmony_ci /* Avoid pops */ 5028c2ecf20Sopenharmony_ci mdelay(100); 5038c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 5048c2ecf20Sopenharmony_ci /* Restore Audio 2 volume */ 5058c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci /* Enable PCM output */ 5088c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 5098c2ecf20Sopenharmony_ci#endif 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5128c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 5138c2ecf20Sopenharmony_ci if (!(chip->active & DAC2)) 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci chip->active &= ~DAC2; 5168c2ecf20Sopenharmony_ci /* Stop DMA */ 5178c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0x00); 5188c2ecf20Sopenharmony_ci#ifdef AVOID_POPS 5198c2ecf20Sopenharmony_ci mdelay(25); 5208c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 5218c2ecf20Sopenharmony_ci /* Set Audio 2 volume to 0 */ 5228c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7C, 0); 5238c2ecf20Sopenharmony_ci else 5248c2ecf20Sopenharmony_ci /* Disable PCM output */ 5258c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD3); 5268c2ecf20Sopenharmony_ci#endif 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci default: 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream, 5368c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 5398c2ecf20Sopenharmony_ci int shift; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci shift = 0; 5428c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 5438c2ecf20Sopenharmony_ci chip->playback_a_substream && 5448c2ecf20Sopenharmony_ci params_channels(hw_params) != 1) { 5458c2ecf20Sopenharmony_ci _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); 5468c2ecf20Sopenharmony_ci return -EBUSY; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci if (params_channels(hw_params) == 2) 5498c2ecf20Sopenharmony_ci shift++; 5508c2ecf20Sopenharmony_ci if (snd_pcm_format_width(params_format(hw_params)) == 16) 5518c2ecf20Sopenharmony_ci shift++; 5528c2ecf20Sopenharmony_ci chip->dma1_shift = shift; 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 5598c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5608c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 5618c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci snd_es18xx_reset_fifo(chip); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Set stereo/mono */ 5668c2ecf20Sopenharmony_ci snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci snd_es18xx_rate_set(chip, substream, ADC1); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* Transfer Count Reload */ 5718c2ecf20Sopenharmony_ci count = 0x10000 - count; 5728c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA4, count & 0xff); 5738c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA5, count >> 8); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci#ifdef AVOID_POPS 5768c2ecf20Sopenharmony_ci mdelay(100); 5778c2ecf20Sopenharmony_ci#endif 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Set format */ 5808c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 5818c2ecf20Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); 5828c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x90 | 5838c2ecf20Sopenharmony_ci ((runtime->channels == 1) ? 0x40 : 0x08) | 5848c2ecf20Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | 5858c2ecf20Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Set DMA controller */ 5888c2ecf20Sopenharmony_ci snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int snd_es18xx_capture_trigger(struct snd_pcm_substream *substream, 5948c2ecf20Sopenharmony_ci int cmd) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci switch (cmd) { 5998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6008c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 6018c2ecf20Sopenharmony_ci if (chip->active & ADC1) 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci chip->active |= ADC1; 6048c2ecf20Sopenharmony_ci /* Start DMA */ 6058c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x0f); 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6088c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 6098c2ecf20Sopenharmony_ci if (!(chip->active & ADC1)) 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci chip->active &= ~ADC1; 6128c2ecf20Sopenharmony_ci /* Stop DMA */ 6138c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x00); 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci default: 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int snd_es18xx_playback2_prepare(struct snd_es18xx *chip, 6238c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6268c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 6278c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci snd_es18xx_reset_fifo(chip); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* Set stereo/mono */ 6328c2ecf20Sopenharmony_ci snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci snd_es18xx_rate_set(chip, substream, DAC1); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Transfer Count Reload */ 6378c2ecf20Sopenharmony_ci count = 0x10000 - count; 6388c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA4, count & 0xff); 6398c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xA5, count >> 8); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Set format */ 6428c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB6, 6438c2ecf20Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); 6448c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 6458c2ecf20Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); 6468c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x90 | 6478c2ecf20Sopenharmony_ci (runtime->channels == 1 ? 0x40 : 0x08) | 6488c2ecf20Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | 6498c2ecf20Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* Set DMA controller */ 6528c2ecf20Sopenharmony_ci snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int snd_es18xx_playback2_trigger(struct snd_es18xx *chip, 6588c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 6598c2ecf20Sopenharmony_ci int cmd) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci switch (cmd) { 6628c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6638c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 6648c2ecf20Sopenharmony_ci if (chip->active & DAC1) 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci chip->active |= DAC1; 6678c2ecf20Sopenharmony_ci /* Start DMA */ 6688c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x05); 6698c2ecf20Sopenharmony_ci#ifdef AVOID_POPS 6708c2ecf20Sopenharmony_ci /* Avoid pops */ 6718c2ecf20Sopenharmony_ci mdelay(100); 6728c2ecf20Sopenharmony_ci /* Enable Audio 1 */ 6738c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 6748c2ecf20Sopenharmony_ci#endif 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6778c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 6788c2ecf20Sopenharmony_ci if (!(chip->active & DAC1)) 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci chip->active &= ~DAC1; 6818c2ecf20Sopenharmony_ci /* Stop DMA */ 6828c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x00); 6838c2ecf20Sopenharmony_ci#ifdef AVOID_POPS 6848c2ecf20Sopenharmony_ci /* Avoid pops */ 6858c2ecf20Sopenharmony_ci mdelay(25); 6868c2ecf20Sopenharmony_ci /* Disable Audio 1 */ 6878c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD3); 6888c2ecf20Sopenharmony_ci#endif 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci default: 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int snd_es18xx_playback_prepare(struct snd_pcm_substream *substream) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 7008c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 7018c2ecf20Sopenharmony_ci return snd_es18xx_playback1_prepare(chip, substream); 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci return snd_es18xx_playback2_prepare(chip, substream); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream, 7078c2ecf20Sopenharmony_ci int cmd) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 7108c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 7118c2ecf20Sopenharmony_ci return snd_es18xx_playback1_trigger(chip, substream, cmd); 7128c2ecf20Sopenharmony_ci else 7138c2ecf20Sopenharmony_ci return snd_es18xx_playback2_trigger(chip, substream, cmd); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct snd_card *card = dev_id; 7198c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 7208c2ecf20Sopenharmony_ci unsigned char status; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_CONTROL) { 7238c2ecf20Sopenharmony_ci /* Read Interrupt status */ 7248c2ecf20Sopenharmony_ci status = inb(chip->ctrl_port + 6); 7258c2ecf20Sopenharmony_ci } else { 7268c2ecf20Sopenharmony_ci /* Read Interrupt status */ 7278c2ecf20Sopenharmony_ci status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci#if 0 7308c2ecf20Sopenharmony_ci else { 7318c2ecf20Sopenharmony_ci status = 0; 7328c2ecf20Sopenharmony_ci if (inb(chip->port + 0x0C) & 0x01) 7338c2ecf20Sopenharmony_ci status |= AUDIO1_IRQ; 7348c2ecf20Sopenharmony_ci if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) 7358c2ecf20Sopenharmony_ci status |= AUDIO2_IRQ; 7368c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_HWV) && 7378c2ecf20Sopenharmony_ci snd_es18xx_mixer_read(chip, 0x64) & 0x10) 7388c2ecf20Sopenharmony_ci status |= HWV_IRQ; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci#endif 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* Audio 1 & Audio 2 */ 7438c2ecf20Sopenharmony_ci if (status & AUDIO2_IRQ) { 7448c2ecf20Sopenharmony_ci if (chip->active & DAC2) 7458c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_a_substream); 7468c2ecf20Sopenharmony_ci /* ack interrupt */ 7478c2ecf20Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci if (status & AUDIO1_IRQ) { 7508c2ecf20Sopenharmony_ci /* ok.. capture is active */ 7518c2ecf20Sopenharmony_ci if (chip->active & ADC1) 7528c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_a_substream); 7538c2ecf20Sopenharmony_ci /* ok.. playback2 is active */ 7548c2ecf20Sopenharmony_ci else if (chip->active & DAC1) 7558c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_b_substream); 7568c2ecf20Sopenharmony_ci /* ack interrupt */ 7578c2ecf20Sopenharmony_ci inb(chip->port + 0x0E); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* MPU */ 7618c2ecf20Sopenharmony_ci if ((status & MPU_IRQ) && chip->rmidi) 7628c2ecf20Sopenharmony_ci snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Hardware volume */ 7658c2ecf20Sopenharmony_ci if (status & HWV_IRQ) { 7668c2ecf20Sopenharmony_ci int split = 0; 7678c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 7688c2ecf20Sopenharmony_ci split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; 7698c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 7708c2ecf20Sopenharmony_ci &chip->hw_switch->id); 7718c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 7728c2ecf20Sopenharmony_ci &chip->hw_volume->id); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci if (!split) { 7758c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 7768c2ecf20Sopenharmony_ci &chip->master_switch->id); 7778c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 7788c2ecf20Sopenharmony_ci &chip->master_volume->id); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci /* ack interrupt */ 7818c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x66, 0x00); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 7898c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 7908c2ecf20Sopenharmony_ci int pos; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 7938c2ecf20Sopenharmony_ci if (!(chip->active & DAC2)) 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci pos = snd_dma_pointer(chip->dma2, size); 7968c2ecf20Sopenharmony_ci return pos >> chip->dma2_shift; 7978c2ecf20Sopenharmony_ci } else { 7988c2ecf20Sopenharmony_ci if (!(chip->active & DAC1)) 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci pos = snd_dma_pointer(chip->dma1, size); 8018c2ecf20Sopenharmony_ci return pos >> chip->dma1_shift; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 8088c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 8098c2ecf20Sopenharmony_ci int pos; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (!(chip->active & ADC1)) 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci pos = snd_dma_pointer(chip->dma1, size); 8148c2ecf20Sopenharmony_ci return pos >> chip->dma1_shift; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_playback = 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 8208c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 8218c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 8228c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 8238c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), 8248c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 8258c2ecf20Sopenharmony_ci .rate_min = 4000, 8268c2ecf20Sopenharmony_ci .rate_max = 48000, 8278c2ecf20Sopenharmony_ci .channels_min = 1, 8288c2ecf20Sopenharmony_ci .channels_max = 2, 8298c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 8308c2ecf20Sopenharmony_ci .period_bytes_min = 64, 8318c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 8328c2ecf20Sopenharmony_ci .periods_min = 1, 8338c2ecf20Sopenharmony_ci .periods_max = 1024, 8348c2ecf20Sopenharmony_ci .fifo_size = 0, 8358c2ecf20Sopenharmony_ci}; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_capture = 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 8408c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 8418c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 8428c2ecf20Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 8438c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), 8448c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 8458c2ecf20Sopenharmony_ci .rate_min = 4000, 8468c2ecf20Sopenharmony_ci .rate_max = 48000, 8478c2ecf20Sopenharmony_ci .channels_min = 1, 8488c2ecf20Sopenharmony_ci .channels_max = 2, 8498c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 8508c2ecf20Sopenharmony_ci .period_bytes_min = 64, 8518c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 8528c2ecf20Sopenharmony_ci .periods_min = 1, 8538c2ecf20Sopenharmony_ci .periods_max = 1024, 8548c2ecf20Sopenharmony_ci .fifo_size = 0, 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int snd_es18xx_playback_open(struct snd_pcm_substream *substream) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8608c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 8638c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 8648c2ecf20Sopenharmony_ci chip->capture_a_substream && 8658c2ecf20Sopenharmony_ci chip->capture_a_substream->runtime->channels != 1) 8668c2ecf20Sopenharmony_ci return -EAGAIN; 8678c2ecf20Sopenharmony_ci chip->playback_a_substream = substream; 8688c2ecf20Sopenharmony_ci } else if (substream->number <= 1) { 8698c2ecf20Sopenharmony_ci if (chip->capture_a_substream) 8708c2ecf20Sopenharmony_ci return -EAGAIN; 8718c2ecf20Sopenharmony_ci chip->playback_b_substream = substream; 8728c2ecf20Sopenharmony_ci } else { 8738c2ecf20Sopenharmony_ci snd_BUG(); 8748c2ecf20Sopenharmony_ci return -EINVAL; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci substream->runtime->hw = snd_es18xx_playback; 8778c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 8788c2ecf20Sopenharmony_ci (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int snd_es18xx_capture_open(struct snd_pcm_substream *substream) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8858c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (chip->playback_b_substream) 8888c2ecf20Sopenharmony_ci return -EAGAIN; 8898c2ecf20Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 8908c2ecf20Sopenharmony_ci chip->playback_a_substream && 8918c2ecf20Sopenharmony_ci chip->playback_a_substream->runtime->channels != 1) 8928c2ecf20Sopenharmony_ci return -EAGAIN; 8938c2ecf20Sopenharmony_ci chip->capture_a_substream = substream; 8948c2ecf20Sopenharmony_ci substream->runtime->hw = snd_es18xx_capture; 8958c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 8968c2ecf20Sopenharmony_ci (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); 8978c2ecf20Sopenharmony_ci return 0; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic int snd_es18xx_playback_close(struct snd_pcm_substream *substream) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 9058c2ecf20Sopenharmony_ci chip->playback_a_substream = NULL; 9068c2ecf20Sopenharmony_ci else 9078c2ecf20Sopenharmony_ci chip->playback_b_substream = NULL; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic int snd_es18xx_capture_close(struct snd_pcm_substream *substream) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci chip->capture_a_substream = NULL; 9178c2ecf20Sopenharmony_ci return 0; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* 9218c2ecf20Sopenharmony_ci * MIXER part 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci/* Record source mux routines: 9258c2ecf20Sopenharmony_ci * Depending on the chipset this mux switches between 4, 5, or 8 possible inputs. 9268c2ecf20Sopenharmony_ci * bit table for the 4/5 source mux: 9278c2ecf20Sopenharmony_ci * reg 1C: 9288c2ecf20Sopenharmony_ci * b2 b1 b0 muxSource 9298c2ecf20Sopenharmony_ci * x 0 x microphone 9308c2ecf20Sopenharmony_ci * 0 1 x CD 9318c2ecf20Sopenharmony_ci * 1 1 0 line 9328c2ecf20Sopenharmony_ci * 1 1 1 mixer 9338c2ecf20Sopenharmony_ci * if it's "mixer" and it's a 5 source mux chipset then reg 7A bit 3 determines 9348c2ecf20Sopenharmony_ci * either the play mixer or the capture mixer. 9358c2ecf20Sopenharmony_ci * 9368c2ecf20Sopenharmony_ci * "map4Source" translates from source number to reg bit pattern 9378c2ecf20Sopenharmony_ci * "invMap4Source" translates from reg bit pattern to source number 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci static const char * const texts5Source[5] = { 9438c2ecf20Sopenharmony_ci "Mic", "CD", "Line", "Master", "Mix" 9448c2ecf20Sopenharmony_ci }; 9458c2ecf20Sopenharmony_ci static const char * const texts8Source[8] = { 9468c2ecf20Sopenharmony_ci "Mic", "Mic Master", "CD", "AOUT", 9478c2ecf20Sopenharmony_ci "Mic1", "Mix", "Line", "Master" 9488c2ecf20Sopenharmony_ci }; 9498c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci switch (chip->version) { 9528c2ecf20Sopenharmony_ci case 0x1868: 9538c2ecf20Sopenharmony_ci case 0x1878: 9548c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 4, texts5Source); 9558c2ecf20Sopenharmony_ci case 0x1887: 9568c2ecf20Sopenharmony_ci case 0x1888: 9578c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 5, texts5Source); 9588c2ecf20Sopenharmony_ci case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */ 9598c2ecf20Sopenharmony_ci case 0x1879: 9608c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 8, texts8Source); 9618c2ecf20Sopenharmony_ci default: 9628c2ecf20Sopenharmony_ci return -EINVAL; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci static const unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3}; 9698c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 9708c2ecf20Sopenharmony_ci int muxSource = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; 9718c2ecf20Sopenharmony_ci if (!(chip->version == 0x1869 || chip->version == 0x1879)) { 9728c2ecf20Sopenharmony_ci muxSource = invMap4Source[muxSource]; 9738c2ecf20Sopenharmony_ci if (muxSource==3 && 9748c2ecf20Sopenharmony_ci (chip->version == 0x1887 || chip->version == 0x1888) && 9758c2ecf20Sopenharmony_ci (snd_es18xx_mixer_read(chip, 0x7a) & 0x08) 9768c2ecf20Sopenharmony_ci ) 9778c2ecf20Sopenharmony_ci muxSource = 4; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = muxSource; 9808c2ecf20Sopenharmony_ci return 0; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci static const unsigned char map4Source[4] = {0, 2, 6, 7}; 9868c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 9878c2ecf20Sopenharmony_ci unsigned char val = ucontrol->value.enumerated.item[0]; 9888c2ecf20Sopenharmony_ci unsigned char retVal = 0; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci switch (chip->version) { 9918c2ecf20Sopenharmony_ci /* 5 source chips */ 9928c2ecf20Sopenharmony_ci case 0x1887: 9938c2ecf20Sopenharmony_ci case 0x1888: 9948c2ecf20Sopenharmony_ci if (val > 4) 9958c2ecf20Sopenharmony_ci return -EINVAL; 9968c2ecf20Sopenharmony_ci if (val == 4) { 9978c2ecf20Sopenharmony_ci retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x08) != 0x08; 9988c2ecf20Sopenharmony_ci val = 3; 9998c2ecf20Sopenharmony_ci } else 10008c2ecf20Sopenharmony_ci retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x00) != 0x00; 10018c2ecf20Sopenharmony_ci fallthrough; 10028c2ecf20Sopenharmony_ci /* 4 source chips */ 10038c2ecf20Sopenharmony_ci case 0x1868: 10048c2ecf20Sopenharmony_ci case 0x1878: 10058c2ecf20Sopenharmony_ci if (val > 3) 10068c2ecf20Sopenharmony_ci return -EINVAL; 10078c2ecf20Sopenharmony_ci val = map4Source[val]; 10088c2ecf20Sopenharmony_ci break; 10098c2ecf20Sopenharmony_ci /* 8 source chips */ 10108c2ecf20Sopenharmony_ci case 0x1869: 10118c2ecf20Sopenharmony_ci case 0x1879: 10128c2ecf20Sopenharmony_ci if (val > 7) 10138c2ecf20Sopenharmony_ci return -EINVAL; 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci default: 10168c2ecf20Sopenharmony_ci return -EINVAL; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci return (snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val) || retVal; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci#define snd_es18xx_info_spatializer_enable snd_ctl_boolean_mono_info 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 10268c2ecf20Sopenharmony_ci unsigned char val = snd_es18xx_mixer_read(chip, 0x50); 10278c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = !!(val & 8); 10288c2ecf20Sopenharmony_ci return 0; 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic int snd_es18xx_put_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 10348c2ecf20Sopenharmony_ci unsigned char oval, nval; 10358c2ecf20Sopenharmony_ci int change; 10368c2ecf20Sopenharmony_ci nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; 10378c2ecf20Sopenharmony_ci oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; 10388c2ecf20Sopenharmony_ci change = nval != oval; 10398c2ecf20Sopenharmony_ci if (change) { 10408c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); 10418c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x50, nval); 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci return change; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int snd_es18xx_info_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 10498c2ecf20Sopenharmony_ci uinfo->count = 2; 10508c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 10518c2ecf20Sopenharmony_ci uinfo->value.integer.max = 63; 10528c2ecf20Sopenharmony_ci return 0; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic int snd_es18xx_get_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 10588c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; 10598c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci#define snd_es18xx_info_hw_switch snd_ctl_boolean_stereo_info 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 10688c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); 10698c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); 10708c2ecf20Sopenharmony_ci return 0; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void snd_es18xx_hwv_free(struct snd_kcontrol *kcontrol) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 10768c2ecf20Sopenharmony_ci chip->master_volume = NULL; 10778c2ecf20Sopenharmony_ci chip->master_switch = NULL; 10788c2ecf20Sopenharmony_ci chip->hw_volume = NULL; 10798c2ecf20Sopenharmony_ci chip->hw_switch = NULL; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic int snd_es18xx_reg_bits(struct snd_es18xx *chip, unsigned char reg, 10838c2ecf20Sopenharmony_ci unsigned char mask, unsigned char val) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci if (reg < 0xa0) 10868c2ecf20Sopenharmony_ci return snd_es18xx_mixer_bits(chip, reg, mask, val); 10878c2ecf20Sopenharmony_ci else 10888c2ecf20Sopenharmony_ci return snd_es18xx_bits(chip, reg, mask, val); 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci if (reg < 0xa0) 10948c2ecf20Sopenharmony_ci return snd_es18xx_mixer_read(chip, reg); 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci return snd_es18xx_read(chip, reg); 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \ 11008c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 11018c2ecf20Sopenharmony_ci .info = snd_es18xx_info_single, \ 11028c2ecf20Sopenharmony_ci .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ 11038c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci#define ES18XX_FL_INVERT (1 << 0) 11068c2ecf20Sopenharmony_ci#define ES18XX_FL_PMPORT (1 << 1) 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 11138c2ecf20Sopenharmony_ci uinfo->count = 1; 11148c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 11158c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 11228c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 11238c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 11248c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 11258c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; 11268c2ecf20Sopenharmony_ci int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; 11278c2ecf20Sopenharmony_ci int val; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (pm_port) 11308c2ecf20Sopenharmony_ci val = inb(chip->port + ES18XX_PM); 11318c2ecf20Sopenharmony_ci else 11328c2ecf20Sopenharmony_ci val = snd_es18xx_reg_read(chip, reg); 11338c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (val >> shift) & mask; 11348c2ecf20Sopenharmony_ci if (invert) 11358c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 11428c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 11438c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 11448c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 11458c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; 11468c2ecf20Sopenharmony_ci int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; 11478c2ecf20Sopenharmony_ci unsigned char val; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 11508c2ecf20Sopenharmony_ci if (invert) 11518c2ecf20Sopenharmony_ci val = mask - val; 11528c2ecf20Sopenharmony_ci mask <<= shift; 11538c2ecf20Sopenharmony_ci val <<= shift; 11548c2ecf20Sopenharmony_ci if (pm_port) { 11558c2ecf20Sopenharmony_ci unsigned char cur = inb(chip->port + ES18XX_PM); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if ((cur & mask) == val) 11588c2ecf20Sopenharmony_ci return 0; 11598c2ecf20Sopenharmony_ci outb((cur & ~mask) | val, chip->port + ES18XX_PM); 11608c2ecf20Sopenharmony_ci return 1; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return snd_es18xx_reg_bits(chip, reg, mask, val) != val; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 11678c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 11688c2ecf20Sopenharmony_ci .info = snd_es18xx_info_double, \ 11698c2ecf20Sopenharmony_ci .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \ 11708c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic int snd_es18xx_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 11778c2ecf20Sopenharmony_ci uinfo->count = 2; 11788c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 11798c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic int snd_es18xx_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 11868c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 11878c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 11888c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 11898c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 11908c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 11918c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 11928c2ecf20Sopenharmony_ci unsigned char left, right; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci left = snd_es18xx_reg_read(chip, left_reg); 11958c2ecf20Sopenharmony_ci if (left_reg != right_reg) 11968c2ecf20Sopenharmony_ci right = snd_es18xx_reg_read(chip, right_reg); 11978c2ecf20Sopenharmony_ci else 11988c2ecf20Sopenharmony_ci right = left; 11998c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (left >> shift_left) & mask; 12008c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (right >> shift_right) & mask; 12018c2ecf20Sopenharmony_ci if (invert) { 12028c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 12038c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 12118c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 12128c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 12138c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 12148c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 12158c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 12168c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 12178c2ecf20Sopenharmony_ci int change; 12188c2ecf20Sopenharmony_ci unsigned char val1, val2, mask1, mask2; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 12218c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 12228c2ecf20Sopenharmony_ci if (invert) { 12238c2ecf20Sopenharmony_ci val1 = mask - val1; 12248c2ecf20Sopenharmony_ci val2 = mask - val2; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci val1 <<= shift_left; 12278c2ecf20Sopenharmony_ci val2 <<= shift_right; 12288c2ecf20Sopenharmony_ci mask1 = mask << shift_left; 12298c2ecf20Sopenharmony_ci mask2 = mask << shift_right; 12308c2ecf20Sopenharmony_ci if (left_reg != right_reg) { 12318c2ecf20Sopenharmony_ci change = 0; 12328c2ecf20Sopenharmony_ci if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) 12338c2ecf20Sopenharmony_ci change = 1; 12348c2ecf20Sopenharmony_ci if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) 12358c2ecf20Sopenharmony_ci change = 1; 12368c2ecf20Sopenharmony_ci } else { 12378c2ecf20Sopenharmony_ci change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, 12388c2ecf20Sopenharmony_ci val1 | val2) != (val1 | val2)); 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci return change; 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci/* Mixer controls 12448c2ecf20Sopenharmony_ci * These arrays contain setup data for mixer controls. 12458c2ecf20Sopenharmony_ci * 12468c2ecf20Sopenharmony_ci * The controls that are universal to all chipsets are fully initialized 12478c2ecf20Sopenharmony_ci * here. 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_base_controls[] = { 12508c2ecf20Sopenharmony_ciES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), 12518c2ecf20Sopenharmony_ciES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), 12528c2ecf20Sopenharmony_ciES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), 12538c2ecf20Sopenharmony_ciES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), 12548c2ecf20Sopenharmony_ciES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), 12558c2ecf20Sopenharmony_ciES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), 12568c2ecf20Sopenharmony_ciES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), 12578c2ecf20Sopenharmony_ciES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), 12588c2ecf20Sopenharmony_ciES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12618c2ecf20Sopenharmony_ci .name = "Capture Source", 12628c2ecf20Sopenharmony_ci .info = snd_es18xx_info_mux, 12638c2ecf20Sopenharmony_ci .get = snd_es18xx_get_mux, 12648c2ecf20Sopenharmony_ci .put = snd_es18xx_put_mux, 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci}; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_recmix_controls[] = { 12698c2ecf20Sopenharmony_ciES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), 12708c2ecf20Sopenharmony_ciES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), 12718c2ecf20Sopenharmony_ciES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), 12728c2ecf20Sopenharmony_ciES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), 12738c2ecf20Sopenharmony_ciES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), 12748c2ecf20Sopenharmony_ciES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) 12758c2ecf20Sopenharmony_ci}; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci/* 12788c2ecf20Sopenharmony_ci * The chipset specific mixer controls 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_speaker = 12818c2ecf20Sopenharmony_ci ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1869[] = { 12848c2ecf20Sopenharmony_ciES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT), 12858c2ecf20Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0), 12868c2ecf20Sopenharmony_ciES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), 12878c2ecf20Sopenharmony_ciES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) 12888c2ecf20Sopenharmony_ci}; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1878 = 12918c2ecf20Sopenharmony_ci ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1879[] = { 12948c2ecf20Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0), 12958c2ecf20Sopenharmony_ciES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), 12968c2ecf20Sopenharmony_ciES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) 12978c2ecf20Sopenharmony_ci}; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = { 13008c2ecf20Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), 13018c2ecf20Sopenharmony_ci}; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = { 13048c2ecf20Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), 13058c2ecf20Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) 13068c2ecf20Sopenharmony_ci}; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = { 13098c2ecf20Sopenharmony_ciES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13128c2ecf20Sopenharmony_ci .name = "3D Control - Switch", 13138c2ecf20Sopenharmony_ci .info = snd_es18xx_info_spatializer_enable, 13148c2ecf20Sopenharmony_ci .get = snd_es18xx_get_spatializer_enable, 13158c2ecf20Sopenharmony_ci .put = snd_es18xx_put_spatializer_enable, 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci}; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre1_control = 13208c2ecf20Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre2_control = 13238c2ecf20Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = { 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13288c2ecf20Sopenharmony_ci .name = "Hardware Master Playback Volume", 13298c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 13308c2ecf20Sopenharmony_ci .info = snd_es18xx_info_hw_volume, 13318c2ecf20Sopenharmony_ci .get = snd_es18xx_get_hw_volume, 13328c2ecf20Sopenharmony_ci}, 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 13358c2ecf20Sopenharmony_ci .name = "Hardware Master Playback Switch", 13368c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 13378c2ecf20Sopenharmony_ci .info = snd_es18xx_info_hw_switch, 13388c2ecf20Sopenharmony_ci .get = snd_es18xx_get_hw_switch, 13398c2ecf20Sopenharmony_ci}, 13408c2ecf20Sopenharmony_ciES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), 13418c2ecf20Sopenharmony_ci}; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = { 13448c2ecf20Sopenharmony_ciES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT), 13458c2ecf20Sopenharmony_ciES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT), 13468c2ecf20Sopenharmony_ci}; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci int data; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci outb(reg, chip->ctrl_port); 13538c2ecf20Sopenharmony_ci data = inb(chip->ctrl_port + 1); 13548c2ecf20Sopenharmony_ci return data; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_cistatic void snd_es18xx_config_write(struct snd_es18xx *chip, 13588c2ecf20Sopenharmony_ci unsigned char reg, unsigned char data) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci /* No need for spinlocks, this function is used only in 13618c2ecf20Sopenharmony_ci otherwise protected init code */ 13628c2ecf20Sopenharmony_ci outb(reg, chip->ctrl_port); 13638c2ecf20Sopenharmony_ci outb(data, chip->ctrl_port + 1); 13648c2ecf20Sopenharmony_ci#ifdef REG_DEBUG 13658c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data); 13668c2ecf20Sopenharmony_ci#endif 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic int snd_es18xx_initialize(struct snd_es18xx *chip, 13708c2ecf20Sopenharmony_ci unsigned long mpu_port, 13718c2ecf20Sopenharmony_ci unsigned long fm_port) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci int mask = 0; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* enable extended mode */ 13768c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xC6); 13778c2ecf20Sopenharmony_ci /* Reset mixer registers */ 13788c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x00, 0x00); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* Audio 1 DMA demand mode (4 bytes/request) */ 13818c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB9, 2); 13828c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_CONTROL) { 13838c2ecf20Sopenharmony_ci /* Hardware volume IRQ */ 13848c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x27, chip->irq); 13858c2ecf20Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 13868c2ecf20Sopenharmony_ci /* FM I/O */ 13878c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x62, fm_port >> 8); 13888c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x63, fm_port & 0xff); 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) { 13918c2ecf20Sopenharmony_ci /* MPU-401 I/O */ 13928c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x64, mpu_port >> 8); 13938c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff); 13948c2ecf20Sopenharmony_ci /* MPU-401 IRQ */ 13958c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x28, chip->irq); 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci /* Audio1 IRQ */ 13988c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x70, chip->irq); 13998c2ecf20Sopenharmony_ci /* Audio2 IRQ */ 14008c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x72, chip->irq); 14018c2ecf20Sopenharmony_ci /* Audio1 DMA */ 14028c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x74, chip->dma1); 14038c2ecf20Sopenharmony_ci /* Audio2 DMA */ 14048c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x75, chip->dma2); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci /* Enable Audio 1 IRQ */ 14078c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB1, 0x50); 14088c2ecf20Sopenharmony_ci /* Enable Audio 2 IRQ */ 14098c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7A, 0x40); 14108c2ecf20Sopenharmony_ci /* Enable Audio 1 DMA */ 14118c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB2, 0x50); 14128c2ecf20Sopenharmony_ci /* Enable MPU and hardware volume interrupt */ 14138c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x64, 0x42); 14148c2ecf20Sopenharmony_ci /* Enable ESS wavetable input */ 14158c2ecf20Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10); 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci else { 14188c2ecf20Sopenharmony_ci int irqmask, dma1mask, dma2mask; 14198c2ecf20Sopenharmony_ci switch (chip->irq) { 14208c2ecf20Sopenharmony_ci case 2: 14218c2ecf20Sopenharmony_ci case 9: 14228c2ecf20Sopenharmony_ci irqmask = 0; 14238c2ecf20Sopenharmony_ci break; 14248c2ecf20Sopenharmony_ci case 5: 14258c2ecf20Sopenharmony_ci irqmask = 1; 14268c2ecf20Sopenharmony_ci break; 14278c2ecf20Sopenharmony_ci case 7: 14288c2ecf20Sopenharmony_ci irqmask = 2; 14298c2ecf20Sopenharmony_ci break; 14308c2ecf20Sopenharmony_ci case 10: 14318c2ecf20Sopenharmony_ci irqmask = 3; 14328c2ecf20Sopenharmony_ci break; 14338c2ecf20Sopenharmony_ci default: 14348c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "invalid irq %d\n", chip->irq); 14358c2ecf20Sopenharmony_ci return -ENODEV; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci switch (chip->dma1) { 14388c2ecf20Sopenharmony_ci case 0: 14398c2ecf20Sopenharmony_ci dma1mask = 1; 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci case 1: 14428c2ecf20Sopenharmony_ci dma1mask = 2; 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci case 3: 14458c2ecf20Sopenharmony_ci dma1mask = 3; 14468c2ecf20Sopenharmony_ci break; 14478c2ecf20Sopenharmony_ci default: 14488c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1); 14498c2ecf20Sopenharmony_ci return -ENODEV; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci switch (chip->dma2) { 14528c2ecf20Sopenharmony_ci case 0: 14538c2ecf20Sopenharmony_ci dma2mask = 0; 14548c2ecf20Sopenharmony_ci break; 14558c2ecf20Sopenharmony_ci case 1: 14568c2ecf20Sopenharmony_ci dma2mask = 1; 14578c2ecf20Sopenharmony_ci break; 14588c2ecf20Sopenharmony_ci case 3: 14598c2ecf20Sopenharmony_ci dma2mask = 2; 14608c2ecf20Sopenharmony_ci break; 14618c2ecf20Sopenharmony_ci case 5: 14628c2ecf20Sopenharmony_ci dma2mask = 3; 14638c2ecf20Sopenharmony_ci break; 14648c2ecf20Sopenharmony_ci default: 14658c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2); 14668c2ecf20Sopenharmony_ci return -ENODEV; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* Enable and set Audio 1 IRQ */ 14708c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); 14718c2ecf20Sopenharmony_ci /* Enable and set Audio 1 DMA */ 14728c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); 14738c2ecf20Sopenharmony_ci /* Set Audio 2 DMA */ 14748c2ecf20Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); 14758c2ecf20Sopenharmony_ci /* Enable Audio 2 IRQ and DMA 14768c2ecf20Sopenharmony_ci Set capture mixer input */ 14778c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7A, 0x68); 14788c2ecf20Sopenharmony_ci /* Enable and set hardware volume interrupt */ 14798c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x64, 0x06); 14808c2ecf20Sopenharmony_ci if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) { 14818c2ecf20Sopenharmony_ci /* MPU401 share irq with audio 14828c2ecf20Sopenharmony_ci Joystick enabled 14838c2ecf20Sopenharmony_ci FM enabled */ 14848c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x40, 14858c2ecf20Sopenharmony_ci 0x43 | (mpu_port & 0xf0) >> 1); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_NEW_RATE) { 14908c2ecf20Sopenharmony_ci /* Change behaviour of register A1 14918c2ecf20Sopenharmony_ci 4x oversampling 14928c2ecf20Sopenharmony_ci 2nd channel DAC asynchronous */ 14938c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x71, 0x32); 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci if (!(chip->caps & ES18XX_PCM2)) { 14968c2ecf20Sopenharmony_ci /* Enable DMA FIFO */ 14978c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x80); 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_SPATIALIZER) { 15008c2ecf20Sopenharmony_ci /* Set spatializer parameters to recommended values */ 15018c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x54, 0x8f); 15028c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x56, 0x95); 15038c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x58, 0x94); 15048c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x5a, 0x80); 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci /* Flip the "enable I2S" bits for those chipsets that need it */ 15078c2ecf20Sopenharmony_ci switch (chip->version) { 15088c2ecf20Sopenharmony_ci case 0x1879: 15098c2ecf20Sopenharmony_ci //Leaving I2S enabled on the 1879 screws up the PCM playback (rate effected somehow) 15108c2ecf20Sopenharmony_ci //so a Switch control has been added to toggle this 0x71 bit on/off: 15118c2ecf20Sopenharmony_ci //snd_es18xx_mixer_bits(chip, 0x71, 0x40, 0x40); 15128c2ecf20Sopenharmony_ci /* Note: we fall through on purpose here. */ 15138c2ecf20Sopenharmony_ci case 0x1878: 15148c2ecf20Sopenharmony_ci snd_es18xx_config_write(chip, 0x29, snd_es18xx_config_read(chip, 0x29) | 0x40); 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci /* Mute input source */ 15188c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_MUTEREC) 15198c2ecf20Sopenharmony_ci mask = 0x10; 15208c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_RECMIX) 15218c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); 15228c2ecf20Sopenharmony_ci else { 15238c2ecf20Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); 15248c2ecf20Sopenharmony_ci snd_es18xx_write(chip, 0xb4, 0x00); 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci#ifndef AVOID_POPS 15278c2ecf20Sopenharmony_ci /* Enable PCM output */ 15288c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 15298c2ecf20Sopenharmony_ci#endif 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci return 0; 15328c2ecf20Sopenharmony_ci} 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_cistatic int snd_es18xx_identify(struct snd_es18xx *chip) 15358c2ecf20Sopenharmony_ci{ 15368c2ecf20Sopenharmony_ci int hi,lo; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* reset */ 15398c2ecf20Sopenharmony_ci if (snd_es18xx_reset(chip) < 0) { 15408c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port); 15418c2ecf20Sopenharmony_ci return -ENODEV; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xe7); 15458c2ecf20Sopenharmony_ci hi = snd_es18xx_dsp_get_byte(chip); 15468c2ecf20Sopenharmony_ci if (hi < 0) { 15478c2ecf20Sopenharmony_ci return hi; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci lo = snd_es18xx_dsp_get_byte(chip); 15508c2ecf20Sopenharmony_ci if ((lo & 0xf0) != 0x80) { 15518c2ecf20Sopenharmony_ci return -ENODEV; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci if (hi == 0x48) { 15548c2ecf20Sopenharmony_ci chip->version = 0x488; 15558c2ecf20Sopenharmony_ci return 0; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci if (hi != 0x68) { 15588c2ecf20Sopenharmony_ci return -ENODEV; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci if ((lo & 0x0f) < 8) { 15618c2ecf20Sopenharmony_ci chip->version = 0x688; 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci outb(0x40, chip->port + 0x04); 15668c2ecf20Sopenharmony_ci udelay(10); 15678c2ecf20Sopenharmony_ci hi = inb(chip->port + 0x05); 15688c2ecf20Sopenharmony_ci udelay(10); 15698c2ecf20Sopenharmony_ci lo = inb(chip->port + 0x05); 15708c2ecf20Sopenharmony_ci if (hi != lo) { 15718c2ecf20Sopenharmony_ci chip->version = hi << 8 | lo; 15728c2ecf20Sopenharmony_ci chip->ctrl_port = inb(chip->port + 0x05) << 8; 15738c2ecf20Sopenharmony_ci udelay(10); 15748c2ecf20Sopenharmony_ci chip->ctrl_port += inb(chip->port + 0x05); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) { 15778c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port); 15788c2ecf20Sopenharmony_ci return -EBUSY; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci return 0; 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* If has Hardware volume */ 15858c2ecf20Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { 15868c2ecf20Sopenharmony_ci /* If has Audio2 */ 15878c2ecf20Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { 15888c2ecf20Sopenharmony_ci /* If has volume count */ 15898c2ecf20Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { 15908c2ecf20Sopenharmony_ci chip->version = 0x1887; 15918c2ecf20Sopenharmony_ci } else { 15928c2ecf20Sopenharmony_ci chip->version = 0x1888; 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci } else { 15958c2ecf20Sopenharmony_ci chip->version = 0x1788; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci else 15998c2ecf20Sopenharmony_ci chip->version = 0x1688; 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic int snd_es18xx_probe(struct snd_es18xx *chip, 16048c2ecf20Sopenharmony_ci unsigned long mpu_port, 16058c2ecf20Sopenharmony_ci unsigned long fm_port) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci if (snd_es18xx_identify(chip) < 0) { 16088c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); 16098c2ecf20Sopenharmony_ci return -ENODEV; 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci switch (chip->version) { 16138c2ecf20Sopenharmony_ci case 0x1868: 16148c2ecf20Sopenharmony_ci chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT; 16158c2ecf20Sopenharmony_ci break; 16168c2ecf20Sopenharmony_ci case 0x1869: 16178c2ecf20Sopenharmony_ci chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT; 16188c2ecf20Sopenharmony_ci break; 16198c2ecf20Sopenharmony_ci case 0x1878: 16208c2ecf20Sopenharmony_ci chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL; 16218c2ecf20Sopenharmony_ci break; 16228c2ecf20Sopenharmony_ci case 0x1879: 16238c2ecf20Sopenharmony_ci chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; 16248c2ecf20Sopenharmony_ci break; 16258c2ecf20Sopenharmony_ci case 0x1887: 16268c2ecf20Sopenharmony_ci case 0x1888: 16278c2ecf20Sopenharmony_ci chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT; 16288c2ecf20Sopenharmony_ci break; 16298c2ecf20Sopenharmony_ci default: 16308c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n", 16318c2ecf20Sopenharmony_ci chip->port, chip->version); 16328c2ecf20Sopenharmony_ci return -ENODEV; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (chip->dma1 == chip->dma2) 16388c2ecf20Sopenharmony_ci chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci return snd_es18xx_initialize(chip, mpu_port, fm_port); 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_playback_ops = { 16448c2ecf20Sopenharmony_ci .open = snd_es18xx_playback_open, 16458c2ecf20Sopenharmony_ci .close = snd_es18xx_playback_close, 16468c2ecf20Sopenharmony_ci .hw_params = snd_es18xx_playback_hw_params, 16478c2ecf20Sopenharmony_ci .prepare = snd_es18xx_playback_prepare, 16488c2ecf20Sopenharmony_ci .trigger = snd_es18xx_playback_trigger, 16498c2ecf20Sopenharmony_ci .pointer = snd_es18xx_playback_pointer, 16508c2ecf20Sopenharmony_ci}; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_capture_ops = { 16538c2ecf20Sopenharmony_ci .open = snd_es18xx_capture_open, 16548c2ecf20Sopenharmony_ci .close = snd_es18xx_capture_close, 16558c2ecf20Sopenharmony_ci .hw_params = snd_es18xx_capture_hw_params, 16568c2ecf20Sopenharmony_ci .prepare = snd_es18xx_capture_prepare, 16578c2ecf20Sopenharmony_ci .trigger = snd_es18xx_capture_trigger, 16588c2ecf20Sopenharmony_ci .pointer = snd_es18xx_capture_pointer, 16598c2ecf20Sopenharmony_ci}; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic int snd_es18xx_pcm(struct snd_card *card, int device) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 16648c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 16658c2ecf20Sopenharmony_ci char str[16]; 16668c2ecf20Sopenharmony_ci int err; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci sprintf(str, "ES%x", chip->version); 16698c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 16708c2ecf20Sopenharmony_ci err = snd_pcm_new(card, str, device, 2, 1, &pcm); 16718c2ecf20Sopenharmony_ci else 16728c2ecf20Sopenharmony_ci err = snd_pcm_new(card, str, device, 1, 1, &pcm); 16738c2ecf20Sopenharmony_ci if (err < 0) 16748c2ecf20Sopenharmony_ci return err; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); 16778c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci /* global setup */ 16808c2ecf20Sopenharmony_ci pcm->private_data = chip; 16818c2ecf20Sopenharmony_ci pcm->info_flags = 0; 16828c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_DUPLEX_SAME) 16838c2ecf20Sopenharmony_ci pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; 16848c2ecf20Sopenharmony_ci if (! (chip->caps & ES18XX_PCM2)) 16858c2ecf20Sopenharmony_ci pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; 16868c2ecf20Sopenharmony_ci sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); 16878c2ecf20Sopenharmony_ci chip->pcm = pcm; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 16908c2ecf20Sopenharmony_ci 64*1024, 16918c2ecf20Sopenharmony_ci chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); 16928c2ecf20Sopenharmony_ci return 0; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci/* Power Management support functions */ 16968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16978c2ecf20Sopenharmony_cistatic int snd_es18xx_suspend(struct snd_card *card, pm_message_t state) 16988c2ecf20Sopenharmony_ci{ 16998c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci /* power down */ 17048c2ecf20Sopenharmony_ci chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); 17058c2ecf20Sopenharmony_ci chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); 17068c2ecf20Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); 17078c2ecf20Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci return 0; 17108c2ecf20Sopenharmony_ci} 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_cistatic int snd_es18xx_resume(struct snd_card *card) 17138c2ecf20Sopenharmony_ci{ 17148c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci /* restore PM register, we won't wake till (not 0x07) i/o activity though */ 17178c2ecf20Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 17208c2ecf20Sopenharmony_ci return 0; 17218c2ecf20Sopenharmony_ci} 17228c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic int snd_es18xx_free(struct snd_card *card) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_port); 17298c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_ctrl_port); 17308c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mpu_port); 17318c2ecf20Sopenharmony_ci if (chip->irq >= 0) 17328c2ecf20Sopenharmony_ci free_irq(chip->irq, (void *) card); 17338c2ecf20Sopenharmony_ci if (chip->dma1 >= 0) { 17348c2ecf20Sopenharmony_ci disable_dma(chip->dma1); 17358c2ecf20Sopenharmony_ci free_dma(chip->dma1); 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci if (chip->dma2 >= 0 && chip->dma1 != chip->dma2) { 17388c2ecf20Sopenharmony_ci disable_dma(chip->dma2); 17398c2ecf20Sopenharmony_ci free_dma(chip->dma2); 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci return 0; 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic int snd_es18xx_dev_free(struct snd_device *device) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci return snd_es18xx_free(device->card); 17478c2ecf20Sopenharmony_ci} 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_cistatic int snd_es18xx_new_device(struct snd_card *card, 17508c2ecf20Sopenharmony_ci unsigned long port, 17518c2ecf20Sopenharmony_ci unsigned long mpu_port, 17528c2ecf20Sopenharmony_ci unsigned long fm_port, 17538c2ecf20Sopenharmony_ci int irq, int dma1, int dma2) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 17568c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 17578c2ecf20Sopenharmony_ci .dev_free = snd_es18xx_dev_free, 17588c2ecf20Sopenharmony_ci }; 17598c2ecf20Sopenharmony_ci int err; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 17628c2ecf20Sopenharmony_ci spin_lock_init(&chip->mixer_lock); 17638c2ecf20Sopenharmony_ci chip->port = port; 17648c2ecf20Sopenharmony_ci chip->irq = -1; 17658c2ecf20Sopenharmony_ci chip->dma1 = -1; 17668c2ecf20Sopenharmony_ci chip->dma2 = -1; 17678c2ecf20Sopenharmony_ci chip->audio2_vol = 0x00; 17688c2ecf20Sopenharmony_ci chip->active = 0; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci chip->res_port = request_region(port, 16, "ES18xx"); 17718c2ecf20Sopenharmony_ci if (chip->res_port == NULL) { 17728c2ecf20Sopenharmony_ci snd_es18xx_free(card); 17738c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); 17748c2ecf20Sopenharmony_ci return -EBUSY; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (request_irq(irq, snd_es18xx_interrupt, 0, "ES18xx", 17788c2ecf20Sopenharmony_ci (void *) card)) { 17798c2ecf20Sopenharmony_ci snd_es18xx_free(card); 17808c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); 17818c2ecf20Sopenharmony_ci return -EBUSY; 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci chip->irq = irq; 17848c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (request_dma(dma1, "ES18xx DMA 1")) { 17878c2ecf20Sopenharmony_ci snd_es18xx_free(card); 17888c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); 17898c2ecf20Sopenharmony_ci return -EBUSY; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci chip->dma1 = dma1; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) { 17948c2ecf20Sopenharmony_ci snd_es18xx_free(card); 17958c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); 17968c2ecf20Sopenharmony_ci return -EBUSY; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci chip->dma2 = dma2; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) { 18018c2ecf20Sopenharmony_ci snd_es18xx_free(card); 18028c2ecf20Sopenharmony_ci return -ENODEV; 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 18058c2ecf20Sopenharmony_ci if (err < 0) { 18068c2ecf20Sopenharmony_ci snd_es18xx_free(card); 18078c2ecf20Sopenharmony_ci return err; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic int snd_es18xx_mixer(struct snd_card *card) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 18158c2ecf20Sopenharmony_ci int err; 18168c2ecf20Sopenharmony_ci unsigned int idx; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci strcpy(card->mixername, chip->pcm->name); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { 18218c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 18228c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); 18238c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 18248c2ecf20Sopenharmony_ci switch (idx) { 18258c2ecf20Sopenharmony_ci case 0: 18268c2ecf20Sopenharmony_ci chip->master_volume = kctl; 18278c2ecf20Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 18288c2ecf20Sopenharmony_ci break; 18298c2ecf20Sopenharmony_ci case 1: 18308c2ecf20Sopenharmony_ci chip->master_switch = kctl; 18318c2ecf20Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 18328c2ecf20Sopenharmony_ci break; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl)) < 0) 18368c2ecf20Sopenharmony_ci return err; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_PCM2) { 18398c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) { 18408c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0) 18418c2ecf20Sopenharmony_ci return err; 18428c2ecf20Sopenharmony_ci } 18438c2ecf20Sopenharmony_ci } else { 18448c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) { 18458c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0) 18468c2ecf20Sopenharmony_ci return err; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_RECMIX) { 18518c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) { 18528c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0) 18538c2ecf20Sopenharmony_ci return err; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci switch (chip->version) { 18578c2ecf20Sopenharmony_ci default: 18588c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0) 18598c2ecf20Sopenharmony_ci return err; 18608c2ecf20Sopenharmony_ci break; 18618c2ecf20Sopenharmony_ci case 0x1869: 18628c2ecf20Sopenharmony_ci case 0x1879: 18638c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0) 18648c2ecf20Sopenharmony_ci return err; 18658c2ecf20Sopenharmony_ci break; 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_SPATIALIZER) { 18688c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) { 18698c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0) 18708c2ecf20Sopenharmony_ci return err; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 18748c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_hw_volume_controls); idx++) { 18758c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 18768c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); 18778c2ecf20Sopenharmony_ci if (idx == 0) 18788c2ecf20Sopenharmony_ci chip->hw_volume = kctl; 18798c2ecf20Sopenharmony_ci else 18808c2ecf20Sopenharmony_ci chip->hw_switch = kctl; 18818c2ecf20Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 18828c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl)) < 0) 18838c2ecf20Sopenharmony_ci return err; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci /* finish initializing other chipset specific controls 18888c2ecf20Sopenharmony_ci */ 18898c2ecf20Sopenharmony_ci if (chip->version != 0x1868) { 18908c2ecf20Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_speaker, 18918c2ecf20Sopenharmony_ci chip)); 18928c2ecf20Sopenharmony_ci if (err < 0) 18938c2ecf20Sopenharmony_ci return err; 18948c2ecf20Sopenharmony_ci } 18958c2ecf20Sopenharmony_ci if (chip->version == 0x1869) { 18968c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1869); idx++) { 18978c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 18988c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_1869[idx], 18998c2ecf20Sopenharmony_ci chip)); 19008c2ecf20Sopenharmony_ci if (err < 0) 19018c2ecf20Sopenharmony_ci return err; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci } else if (chip->version == 0x1878) { 19048c2ecf20Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_1878, 19058c2ecf20Sopenharmony_ci chip)); 19068c2ecf20Sopenharmony_ci if (err < 0) 19078c2ecf20Sopenharmony_ci return err; 19088c2ecf20Sopenharmony_ci } else if (chip->version == 0x1879) { 19098c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1879); idx++) { 19108c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 19118c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_1879[idx], 19128c2ecf20Sopenharmony_ci chip)); 19138c2ecf20Sopenharmony_ci if (err < 0) 19148c2ecf20Sopenharmony_ci return err; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci if (chip->caps & ES18XX_GPO_2BIT) { 19188c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) { 19198c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 19208c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx], 19218c2ecf20Sopenharmony_ci chip)); 19228c2ecf20Sopenharmony_ci if (err < 0) 19238c2ecf20Sopenharmony_ci return err; 19248c2ecf20Sopenharmony_ci } 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci/* Card level */ 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>"); 19338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ESS ES18xx AudioDrive"); 19348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 19358c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ESS,ES1868 PnP AudioDrive}," 19368c2ecf20Sopenharmony_ci "{ESS,ES1869 PnP AudioDrive}," 19378c2ecf20Sopenharmony_ci "{ESS,ES1878 PnP AudioDrive}," 19388c2ecf20Sopenharmony_ci "{ESS,ES1879 PnP AudioDrive}," 19398c2ecf20Sopenharmony_ci "{ESS,ES1887 PnP AudioDrive}," 19408c2ecf20Sopenharmony_ci "{ESS,ES1888 PnP AudioDrive}," 19418c2ecf20Sopenharmony_ci "{ESS,ES1887 AudioDrive}," 19428c2ecf20Sopenharmony_ci "{ESS,ES1888 AudioDrive}}"); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 19458c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 19468c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 19478c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 19488c2ecf20Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; 19498c2ecf20Sopenharmony_ci#endif 19508c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ 19518c2ecf20Sopenharmony_ci#ifndef CONFIG_PNP 19528c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; 19538c2ecf20Sopenharmony_ci#else 19548c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 19558c2ecf20Sopenharmony_ci#endif 19568c2ecf20Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 19578c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ 19588c2ecf20Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 19598c2ecf20Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 19628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ES18xx soundcard."); 19638c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 19648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ES18xx soundcard."); 19658c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 19668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ES18xx soundcard."); 19678c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 19688c2ecf20Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444); 19698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); 19708c2ecf20Sopenharmony_ci#endif 19718c2ecf20Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 19728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for ES18xx driver."); 19738c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444); 19748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver."); 19758c2ecf20Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444); 19768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver."); 19778c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 19788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for ES18xx driver."); 19798c2ecf20Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 19808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver."); 19818c2ecf20Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 19828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver."); 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 19858c2ecf20Sopenharmony_cistatic int isa_registered; 19868c2ecf20Sopenharmony_cistatic int pnp_registered; 19878c2ecf20Sopenharmony_cistatic int pnpc_registered; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_cistatic const struct pnp_device_id snd_audiodrive_pnpbiosids[] = { 19908c2ecf20Sopenharmony_ci { .id = "ESS1869" }, 19918c2ecf20Sopenharmony_ci { .id = "ESS1879" }, 19928c2ecf20Sopenharmony_ci { .id = "" } /* end */ 19938c2ecf20Sopenharmony_ci}; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci/* PnP main device initialization */ 19988c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev) 19998c2ecf20Sopenharmony_ci{ 20008c2ecf20Sopenharmony_ci if (pnp_activate_dev(pdev) < 0) { 20018c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); 20028c2ecf20Sopenharmony_ci return -EBUSY; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci /* ok. hack using Vendor-Defined Card-Level registers */ 20058c2ecf20Sopenharmony_ci /* skip csn and logdev initialization - already done in isapnp_configure */ 20068c2ecf20Sopenharmony_ci if (pnp_device_is_isapnp(pdev)) { 20078c2ecf20Sopenharmony_ci isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev)); 20088c2ecf20Sopenharmony_ci isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */ 20098c2ecf20Sopenharmony_ci if (mpu_port[dev] != SNDRV_AUTO_PORT) 20108c2ecf20Sopenharmony_ci isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */ 20118c2ecf20Sopenharmony_ci isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */ 20128c2ecf20Sopenharmony_ci isapnp_cfg_end(); 20138c2ecf20Sopenharmony_ci } 20148c2ecf20Sopenharmony_ci port[dev] = pnp_port_start(pdev, 0); 20158c2ecf20Sopenharmony_ci fm_port[dev] = pnp_port_start(pdev, 1); 20168c2ecf20Sopenharmony_ci mpu_port[dev] = pnp_port_start(pdev, 2); 20178c2ecf20Sopenharmony_ci dma1[dev] = pnp_dma(pdev, 0); 20188c2ecf20Sopenharmony_ci dma2[dev] = pnp_dma(pdev, 1); 20198c2ecf20Sopenharmony_ci irq[dev] = pnp_irq(pdev, 0); 20208c2ecf20Sopenharmony_ci snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]); 20218c2ecf20Sopenharmony_ci snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); 20228c2ecf20Sopenharmony_ci return 0; 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip, 20268c2ecf20Sopenharmony_ci struct pnp_dev *pdev) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci chip->dev = pdev; 20298c2ecf20Sopenharmony_ci if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0) 20308c2ecf20Sopenharmony_ci return -EBUSY; 20318c2ecf20Sopenharmony_ci return 0; 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id snd_audiodrive_pnpids[] = { 20358c2ecf20Sopenharmony_ci /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ 20368c2ecf20Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } }, 20378c2ecf20Sopenharmony_ci /* ESS 1868 (integrated on Maxisound Cards) */ 20388c2ecf20Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS8601" }, { "ESS8600" } } }, 20398c2ecf20Sopenharmony_ci /* ESS 1868 (integrated on Maxisound Cards) */ 20408c2ecf20Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS8611" }, { "ESS8610" } } }, 20418c2ecf20Sopenharmony_ci /* ESS ES1869 Plug and Play AudioDrive */ 20428c2ecf20Sopenharmony_ci { .id = "ESS0003", .devs = { { "ESS1869" }, { "ESS0006" } } }, 20438c2ecf20Sopenharmony_ci /* ESS 1869 */ 20448c2ecf20Sopenharmony_ci { .id = "ESS1869", .devs = { { "ESS1869" }, { "ESS0006" } } }, 20458c2ecf20Sopenharmony_ci /* ESS 1878 */ 20468c2ecf20Sopenharmony_ci { .id = "ESS1878", .devs = { { "ESS1878" }, { "ESS0004" } } }, 20478c2ecf20Sopenharmony_ci /* ESS 1879 */ 20488c2ecf20Sopenharmony_ci { .id = "ESS1879", .devs = { { "ESS1879" }, { "ESS0009" } } }, 20498c2ecf20Sopenharmony_ci /* --- */ 20508c2ecf20Sopenharmony_ci { .id = "" } /* end */ 20518c2ecf20Sopenharmony_ci}; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids); 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip, 20568c2ecf20Sopenharmony_ci struct pnp_card_link *card, 20578c2ecf20Sopenharmony_ci const struct pnp_card_device_id *id) 20588c2ecf20Sopenharmony_ci{ 20598c2ecf20Sopenharmony_ci chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 20608c2ecf20Sopenharmony_ci if (chip->dev == NULL) 20618c2ecf20Sopenharmony_ci return -EBUSY; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL); 20648c2ecf20Sopenharmony_ci if (chip->devc == NULL) 20658c2ecf20Sopenharmony_ci return -EBUSY; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci /* Control port initialization */ 20688c2ecf20Sopenharmony_ci if (pnp_activate_dev(chip->devc) < 0) { 20698c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); 20708c2ecf20Sopenharmony_ci return -EAGAIN; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci snd_printdd("pnp: port=0x%llx\n", 20738c2ecf20Sopenharmony_ci (unsigned long long)pnp_port_start(chip->devc, 0)); 20748c2ecf20Sopenharmony_ci if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0) 20758c2ecf20Sopenharmony_ci return -EBUSY; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci return 0; 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 20828c2ecf20Sopenharmony_ci#define is_isapnp_selected(dev) isapnp[dev] 20838c2ecf20Sopenharmony_ci#else 20848c2ecf20Sopenharmony_ci#define is_isapnp_selected(dev) 0 20858c2ecf20Sopenharmony_ci#endif 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_cistatic int snd_es18xx_card_new(struct device *pdev, int dev, 20888c2ecf20Sopenharmony_ci struct snd_card **cardp) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci return snd_card_new(pdev, index[dev], id[dev], THIS_MODULE, 20918c2ecf20Sopenharmony_ci sizeof(struct snd_es18xx), cardp); 20928c2ecf20Sopenharmony_ci} 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_cistatic int snd_audiodrive_probe(struct snd_card *card, int dev) 20958c2ecf20Sopenharmony_ci{ 20968c2ecf20Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 20978c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 20988c2ecf20Sopenharmony_ci int err; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci err = snd_es18xx_new_device(card, 21018c2ecf20Sopenharmony_ci port[dev], mpu_port[dev], fm_port[dev], 21028c2ecf20Sopenharmony_ci irq[dev], dma1[dev], dma2[dev]); 21038c2ecf20Sopenharmony_ci if (err < 0) 21048c2ecf20Sopenharmony_ci return err; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci sprintf(card->driver, "ES%x", chip->version); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); 21098c2ecf20Sopenharmony_ci if (dma1[dev] != dma2[dev]) 21108c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", 21118c2ecf20Sopenharmony_ci card->shortname, 21128c2ecf20Sopenharmony_ci chip->port, 21138c2ecf20Sopenharmony_ci irq[dev], dma1[dev], dma2[dev]); 21148c2ecf20Sopenharmony_ci else 21158c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", 21168c2ecf20Sopenharmony_ci card->shortname, 21178c2ecf20Sopenharmony_ci chip->port, 21188c2ecf20Sopenharmony_ci irq[dev], dma1[dev]); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci err = snd_es18xx_pcm(card, 0); 21218c2ecf20Sopenharmony_ci if (err < 0) 21228c2ecf20Sopenharmony_ci return err; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci err = snd_es18xx_mixer(card); 21258c2ecf20Sopenharmony_ci if (err < 0) 21268c2ecf20Sopenharmony_ci return err; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 21298c2ecf20Sopenharmony_ci if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, 21308c2ecf20Sopenharmony_ci OPL3_HW_OPL3, 0, &opl3) < 0) { 21318c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING PFX 21328c2ecf20Sopenharmony_ci "opl3 not detected at 0x%lx\n", 21338c2ecf20Sopenharmony_ci fm_port[dev]); 21348c2ecf20Sopenharmony_ci } else { 21358c2ecf20Sopenharmony_ci err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 21368c2ecf20Sopenharmony_ci if (err < 0) 21378c2ecf20Sopenharmony_ci return err; 21388c2ecf20Sopenharmony_ci } 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { 21428c2ecf20Sopenharmony_ci err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, 21438c2ecf20Sopenharmony_ci mpu_port[dev], MPU401_INFO_IRQ_HOOK, 21448c2ecf20Sopenharmony_ci -1, &chip->rmidi); 21458c2ecf20Sopenharmony_ci if (err < 0) 21468c2ecf20Sopenharmony_ci return err; 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci return snd_card_register(card); 21508c2ecf20Sopenharmony_ci} 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_match(struct device *pdev, unsigned int dev) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci return enable[dev] && !is_isapnp_selected(dev); 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_probe1(int dev, struct device *devptr) 21588c2ecf20Sopenharmony_ci{ 21598c2ecf20Sopenharmony_ci struct snd_card *card; 21608c2ecf20Sopenharmony_ci int err; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci err = snd_es18xx_card_new(devptr, dev, &card); 21638c2ecf20Sopenharmony_ci if (err < 0) 21648c2ecf20Sopenharmony_ci return err; 21658c2ecf20Sopenharmony_ci if ((err = snd_audiodrive_probe(card, dev)) < 0) { 21668c2ecf20Sopenharmony_ci snd_card_free(card); 21678c2ecf20Sopenharmony_ci return err; 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci dev_set_drvdata(devptr, card); 21708c2ecf20Sopenharmony_ci return 0; 21718c2ecf20Sopenharmony_ci} 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev) 21748c2ecf20Sopenharmony_ci{ 21758c2ecf20Sopenharmony_ci int err; 21768c2ecf20Sopenharmony_ci static const int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; 21778c2ecf20Sopenharmony_ci static const int possible_dmas[] = {1, 0, 3, 5, -1}; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci if (irq[dev] == SNDRV_AUTO_IRQ) { 21808c2ecf20Sopenharmony_ci if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) { 21818c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 21828c2ecf20Sopenharmony_ci return -EBUSY; 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci if (dma1[dev] == SNDRV_AUTO_DMA) { 21868c2ecf20Sopenharmony_ci if ((dma1[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) { 21878c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA1\n"); 21888c2ecf20Sopenharmony_ci return -EBUSY; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci if (dma2[dev] == SNDRV_AUTO_DMA) { 21928c2ecf20Sopenharmony_ci if ((dma2[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) { 21938c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA2\n"); 21948c2ecf20Sopenharmony_ci return -EBUSY; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci } 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci if (port[dev] != SNDRV_AUTO_PORT) { 21998c2ecf20Sopenharmony_ci return snd_es18xx_isa_probe1(dev, pdev); 22008c2ecf20Sopenharmony_ci } else { 22018c2ecf20Sopenharmony_ci static const unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280}; 22028c2ecf20Sopenharmony_ci int i; 22038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(possible_ports); i++) { 22048c2ecf20Sopenharmony_ci port[dev] = possible_ports[i]; 22058c2ecf20Sopenharmony_ci err = snd_es18xx_isa_probe1(dev, pdev); 22068c2ecf20Sopenharmony_ci if (! err) 22078c2ecf20Sopenharmony_ci return 0; 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci return err; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci} 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_remove(struct device *devptr, 22148c2ecf20Sopenharmony_ci unsigned int dev) 22158c2ecf20Sopenharmony_ci{ 22168c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(devptr)); 22178c2ecf20Sopenharmony_ci return 0; 22188c2ecf20Sopenharmony_ci} 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 22218c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_suspend(struct device *dev, unsigned int n, 22228c2ecf20Sopenharmony_ci pm_message_t state) 22238c2ecf20Sopenharmony_ci{ 22248c2ecf20Sopenharmony_ci return snd_es18xx_suspend(dev_get_drvdata(dev), state); 22258c2ecf20Sopenharmony_ci} 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_cistatic int snd_es18xx_isa_resume(struct device *dev, unsigned int n) 22288c2ecf20Sopenharmony_ci{ 22298c2ecf20Sopenharmony_ci return snd_es18xx_resume(dev_get_drvdata(dev)); 22308c2ecf20Sopenharmony_ci} 22318c2ecf20Sopenharmony_ci#endif 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci#define DEV_NAME "es18xx" 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_cistatic struct isa_driver snd_es18xx_isa_driver = { 22368c2ecf20Sopenharmony_ci .match = snd_es18xx_isa_match, 22378c2ecf20Sopenharmony_ci .probe = snd_es18xx_isa_probe, 22388c2ecf20Sopenharmony_ci .remove = snd_es18xx_isa_remove, 22398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 22408c2ecf20Sopenharmony_ci .suspend = snd_es18xx_isa_suspend, 22418c2ecf20Sopenharmony_ci .resume = snd_es18xx_isa_resume, 22428c2ecf20Sopenharmony_ci#endif 22438c2ecf20Sopenharmony_ci .driver = { 22448c2ecf20Sopenharmony_ci .name = DEV_NAME 22458c2ecf20Sopenharmony_ci }, 22468c2ecf20Sopenharmony_ci}; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 22508c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnp_detect(struct pnp_dev *pdev, 22518c2ecf20Sopenharmony_ci const struct pnp_device_id *id) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci static int dev; 22548c2ecf20Sopenharmony_ci int err; 22558c2ecf20Sopenharmony_ci struct snd_card *card; 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci if (pnp_device_is_isapnp(pdev)) 22588c2ecf20Sopenharmony_ci return -ENOENT; /* we have another procedure - card */ 22598c2ecf20Sopenharmony_ci for (; dev < SNDRV_CARDS; dev++) { 22608c2ecf20Sopenharmony_ci if (enable[dev] && isapnp[dev]) 22618c2ecf20Sopenharmony_ci break; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 22648c2ecf20Sopenharmony_ci return -ENODEV; 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci err = snd_es18xx_card_new(&pdev->dev, dev, &card); 22678c2ecf20Sopenharmony_ci if (err < 0) 22688c2ecf20Sopenharmony_ci return err; 22698c2ecf20Sopenharmony_ci if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) { 22708c2ecf20Sopenharmony_ci snd_card_free(card); 22718c2ecf20Sopenharmony_ci return err; 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci if ((err = snd_audiodrive_probe(card, dev)) < 0) { 22748c2ecf20Sopenharmony_ci snd_card_free(card); 22758c2ecf20Sopenharmony_ci return err; 22768c2ecf20Sopenharmony_ci } 22778c2ecf20Sopenharmony_ci pnp_set_drvdata(pdev, card); 22788c2ecf20Sopenharmony_ci dev++; 22798c2ecf20Sopenharmony_ci return 0; 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_cistatic void snd_audiodrive_pnp_remove(struct pnp_dev *pdev) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci snd_card_free(pnp_get_drvdata(pdev)); 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 22888c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci return snd_es18xx_suspend(pnp_get_drvdata(pdev), state); 22918c2ecf20Sopenharmony_ci} 22928c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnp_resume(struct pnp_dev *pdev) 22938c2ecf20Sopenharmony_ci{ 22948c2ecf20Sopenharmony_ci return snd_es18xx_resume(pnp_get_drvdata(pdev)); 22958c2ecf20Sopenharmony_ci} 22968c2ecf20Sopenharmony_ci#endif 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic struct pnp_driver es18xx_pnp_driver = { 22998c2ecf20Sopenharmony_ci .name = "es18xx-pnpbios", 23008c2ecf20Sopenharmony_ci .id_table = snd_audiodrive_pnpbiosids, 23018c2ecf20Sopenharmony_ci .probe = snd_audiodrive_pnp_detect, 23028c2ecf20Sopenharmony_ci .remove = snd_audiodrive_pnp_remove, 23038c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 23048c2ecf20Sopenharmony_ci .suspend = snd_audiodrive_pnp_suspend, 23058c2ecf20Sopenharmony_ci .resume = snd_audiodrive_pnp_resume, 23068c2ecf20Sopenharmony_ci#endif 23078c2ecf20Sopenharmony_ci}; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard, 23108c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 23118c2ecf20Sopenharmony_ci{ 23128c2ecf20Sopenharmony_ci static int dev; 23138c2ecf20Sopenharmony_ci struct snd_card *card; 23148c2ecf20Sopenharmony_ci int res; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci for ( ; dev < SNDRV_CARDS; dev++) { 23178c2ecf20Sopenharmony_ci if (enable[dev] && isapnp[dev]) 23188c2ecf20Sopenharmony_ci break; 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 23218c2ecf20Sopenharmony_ci return -ENODEV; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci res = snd_es18xx_card_new(&pcard->card->dev, dev, &card); 23248c2ecf20Sopenharmony_ci if (res < 0) 23258c2ecf20Sopenharmony_ci return res; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) { 23288c2ecf20Sopenharmony_ci snd_card_free(card); 23298c2ecf20Sopenharmony_ci return res; 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci if ((res = snd_audiodrive_probe(card, dev)) < 0) { 23328c2ecf20Sopenharmony_ci snd_card_free(card); 23338c2ecf20Sopenharmony_ci return res; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 23378c2ecf20Sopenharmony_ci dev++; 23388c2ecf20Sopenharmony_ci return 0; 23398c2ecf20Sopenharmony_ci} 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_cistatic void snd_audiodrive_pnpc_remove(struct pnp_card_link *pcard) 23428c2ecf20Sopenharmony_ci{ 23438c2ecf20Sopenharmony_ci snd_card_free(pnp_get_card_drvdata(pcard)); 23448c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, NULL); 23458c2ecf20Sopenharmony_ci} 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 23488c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) 23498c2ecf20Sopenharmony_ci{ 23508c2ecf20Sopenharmony_ci return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state); 23518c2ecf20Sopenharmony_ci} 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_cistatic int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard) 23548c2ecf20Sopenharmony_ci{ 23558c2ecf20Sopenharmony_ci return snd_es18xx_resume(pnp_get_card_drvdata(pcard)); 23568c2ecf20Sopenharmony_ci} 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci#endif 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_cistatic struct pnp_card_driver es18xx_pnpc_driver = { 23618c2ecf20Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 23628c2ecf20Sopenharmony_ci .name = "es18xx", 23638c2ecf20Sopenharmony_ci .id_table = snd_audiodrive_pnpids, 23648c2ecf20Sopenharmony_ci .probe = snd_audiodrive_pnpc_detect, 23658c2ecf20Sopenharmony_ci .remove = snd_audiodrive_pnpc_remove, 23668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 23678c2ecf20Sopenharmony_ci .suspend = snd_audiodrive_pnpc_suspend, 23688c2ecf20Sopenharmony_ci .resume = snd_audiodrive_pnpc_resume, 23698c2ecf20Sopenharmony_ci#endif 23708c2ecf20Sopenharmony_ci}; 23718c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_cistatic int __init alsa_card_es18xx_init(void) 23748c2ecf20Sopenharmony_ci{ 23758c2ecf20Sopenharmony_ci int err; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci err = isa_register_driver(&snd_es18xx_isa_driver, SNDRV_CARDS); 23788c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 23798c2ecf20Sopenharmony_ci if (!err) 23808c2ecf20Sopenharmony_ci isa_registered = 1; 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci err = pnp_register_driver(&es18xx_pnp_driver); 23838c2ecf20Sopenharmony_ci if (!err) 23848c2ecf20Sopenharmony_ci pnp_registered = 1; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci err = pnp_register_card_driver(&es18xx_pnpc_driver); 23878c2ecf20Sopenharmony_ci if (!err) 23888c2ecf20Sopenharmony_ci pnpc_registered = 1; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci if (isa_registered || pnp_registered) 23918c2ecf20Sopenharmony_ci err = 0; 23928c2ecf20Sopenharmony_ci#endif 23938c2ecf20Sopenharmony_ci return err; 23948c2ecf20Sopenharmony_ci} 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_cistatic void __exit alsa_card_es18xx_exit(void) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 23998c2ecf20Sopenharmony_ci if (pnpc_registered) 24008c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&es18xx_pnpc_driver); 24018c2ecf20Sopenharmony_ci if (pnp_registered) 24028c2ecf20Sopenharmony_ci pnp_unregister_driver(&es18xx_pnp_driver); 24038c2ecf20Sopenharmony_ci if (isa_registered) 24048c2ecf20Sopenharmony_ci#endif 24058c2ecf20Sopenharmony_ci isa_unregister_driver(&snd_es18xx_isa_driver); 24068c2ecf20Sopenharmony_ci} 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_cimodule_init(alsa_card_es18xx_init) 24098c2ecf20Sopenharmony_cimodule_exit(alsa_card_es18xx_exit) 2410