162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for generic ESS AudioDrive ES18xx soundcards 462306a36Sopenharmony_ci * Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de> 562306a36Sopenharmony_ci * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci/* GENERAL NOTES: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * BUGS: 1062306a36Sopenharmony_ci * - There are pops (we can't delay in trigger function, cause midlevel 1162306a36Sopenharmony_ci * often need to trigger down and then up very quickly). 1262306a36Sopenharmony_ci * Any ideas? 1362306a36Sopenharmony_ci * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * ES1868 NOTES: 1862306a36Sopenharmony_ci * - The chip has one half duplex pcm (with very limited full duplex support). 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Duplex stereophonic sound is impossible. 2162306a36Sopenharmony_ci * - Record and playback must share the same frequency rate. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * - The driver use dma2 for playback and dma1 for capture. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * ES1869 NOTES: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * - there are a first full duplex pcm and a second playback only pcm 3062306a36Sopenharmony_ci * (incompatible with first pcm capture) 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * - there is support for the capture volume and ESS Spatializer 3D effect. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * - contrarily to some pages in DS_1869.PDF the rates can be set 3562306a36Sopenharmony_ci * independently. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * - Zoom Video is implemented by sharing the FM DAC, thus the user can 3862306a36Sopenharmony_ci * have either FM playback or Video playback but not both simultaneously. 3962306a36Sopenharmony_ci * The Video Playback Switch mixer control toggles this choice. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * BUGS: 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * - There is a major trouble I noted: 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * using both channel for playback stereo 16 bit samples at 44100 Hz 4662306a36Sopenharmony_ci * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * The same happens using Audio1 for captureing. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * The Windows driver does not suffer of this (although it use Audio1 5162306a36Sopenharmony_ci * only for captureing). I'm unable to discover why. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * ES1879 NOTES: 5762306a36Sopenharmony_ci * - When Zoom Video is enabled (reg 0x71 bit 6 toggled on) the PCM playback 5862306a36Sopenharmony_ci * seems to be effected (speaker_test plays a lower frequency). Can't find 5962306a36Sopenharmony_ci * anything in the datasheet to account for this, so a Video Playback Switch 6062306a36Sopenharmony_ci * control has been included to allow ZV to be enabled only when necessary. 6162306a36Sopenharmony_ci * Then again on at least one test system the 0x71 bit 6 enable bit is not 6262306a36Sopenharmony_ci * needed for ZV, so maybe the datasheet is entirely wrong here. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#include <linux/init.h> 6662306a36Sopenharmony_ci#include <linux/err.h> 6762306a36Sopenharmony_ci#include <linux/isa.h> 6862306a36Sopenharmony_ci#include <linux/pnp.h> 6962306a36Sopenharmony_ci#include <linux/isapnp.h> 7062306a36Sopenharmony_ci#include <linux/module.h> 7162306a36Sopenharmony_ci#include <linux/delay.h> 7262306a36Sopenharmony_ci#include <linux/io.h> 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#include <asm/dma.h> 7562306a36Sopenharmony_ci#include <sound/core.h> 7662306a36Sopenharmony_ci#include <sound/control.h> 7762306a36Sopenharmony_ci#include <sound/pcm.h> 7862306a36Sopenharmony_ci#include <sound/pcm_params.h> 7962306a36Sopenharmony_ci#include <sound/mpu401.h> 8062306a36Sopenharmony_ci#include <sound/opl3.h> 8162306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 8262306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 8362306a36Sopenharmony_ci#include <sound/initval.h> 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define PFX "es18xx: " 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct snd_es18xx { 8862306a36Sopenharmony_ci unsigned long port; /* port of ESS chip */ 8962306a36Sopenharmony_ci unsigned long ctrl_port; /* Control port of ESS chip */ 9062306a36Sopenharmony_ci int irq; /* IRQ number of ESS chip */ 9162306a36Sopenharmony_ci int dma1; /* DMA1 */ 9262306a36Sopenharmony_ci int dma2; /* DMA2 */ 9362306a36Sopenharmony_ci unsigned short version; /* version of ESS chip */ 9462306a36Sopenharmony_ci int caps; /* Chip capabilities */ 9562306a36Sopenharmony_ci unsigned short audio2_vol; /* volume level of audio2 */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci unsigned short active; /* active channel mask */ 9862306a36Sopenharmony_ci unsigned int dma1_shift; 9962306a36Sopenharmony_ci unsigned int dma2_shift; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci struct snd_pcm *pcm; 10262306a36Sopenharmony_ci struct snd_pcm_substream *playback_a_substream; 10362306a36Sopenharmony_ci struct snd_pcm_substream *capture_a_substream; 10462306a36Sopenharmony_ci struct snd_pcm_substream *playback_b_substream; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci struct snd_kcontrol *hw_volume; 10962306a36Sopenharmony_ci struct snd_kcontrol *hw_switch; 11062306a36Sopenharmony_ci struct snd_kcontrol *master_volume; 11162306a36Sopenharmony_ci struct snd_kcontrol *master_switch; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spinlock_t reg_lock; 11462306a36Sopenharmony_ci spinlock_t mixer_lock; 11562306a36Sopenharmony_ci#ifdef CONFIG_PM 11662306a36Sopenharmony_ci unsigned char pm_reg; 11762306a36Sopenharmony_ci#endif 11862306a36Sopenharmony_ci#ifdef CONFIG_PNP 11962306a36Sopenharmony_ci struct pnp_dev *dev; 12062306a36Sopenharmony_ci struct pnp_dev *devc; 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define AUDIO1_IRQ 0x01 12562306a36Sopenharmony_ci#define AUDIO2_IRQ 0x02 12662306a36Sopenharmony_ci#define HWV_IRQ 0x04 12762306a36Sopenharmony_ci#define MPU_IRQ 0x08 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define ES18XX_PCM2 0x0001 /* Has two useable PCM */ 13062306a36Sopenharmony_ci#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ 13162306a36Sopenharmony_ci#define ES18XX_RECMIX 0x0004 /* Has record mixer */ 13262306a36Sopenharmony_ci#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ 13362306a36Sopenharmony_ci#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ 13462306a36Sopenharmony_ci#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ 13562306a36Sopenharmony_ci#define ES18XX_AUXB 0x0040 /* AuxB mixer control */ 13662306a36Sopenharmony_ci#define ES18XX_HWV 0x0080 /* Has separate hardware volume mixer controls*/ 13762306a36Sopenharmony_ci#define ES18XX_MONO 0x0100 /* Mono_in mixer control */ 13862306a36Sopenharmony_ci#define ES18XX_I2S 0x0200 /* I2S mixer control */ 13962306a36Sopenharmony_ci#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ 14062306a36Sopenharmony_ci#define ES18XX_CONTROL 0x0800 /* Has control ports */ 14162306a36Sopenharmony_ci#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Power Management */ 14462306a36Sopenharmony_ci#define ES18XX_PM 0x07 14562306a36Sopenharmony_ci#define ES18XX_PM_GPO0 0x01 14662306a36Sopenharmony_ci#define ES18XX_PM_GPO1 0x02 14762306a36Sopenharmony_ci#define ES18XX_PM_PDR 0x04 14862306a36Sopenharmony_ci#define ES18XX_PM_ANA 0x08 14962306a36Sopenharmony_ci#define ES18XX_PM_FM 0x020 15062306a36Sopenharmony_ci#define ES18XX_PM_SUS 0x080 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* Lowlevel */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define DAC1 0x01 15562306a36Sopenharmony_ci#define ADC1 0x02 15662306a36Sopenharmony_ci#define DAC2 0x04 15762306a36Sopenharmony_ci#define MILLISECOND 10000 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int i; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci for(i = MILLISECOND; i; i--) 16462306a36Sopenharmony_ci if ((inb(chip->port + 0x0C) & 0x80) == 0) { 16562306a36Sopenharmony_ci outb(val, chip->port + 0x0C); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci snd_printk(KERN_ERR "dsp_command: timeout (0x%x)\n", val); 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for(i = MILLISECOND/10; i; i--) 17762306a36Sopenharmony_ci if (inb(chip->port + 0x0C) & 0x40) 17862306a36Sopenharmony_ci return inb(chip->port + 0x0A); 17962306a36Sopenharmony_ci snd_printk(KERN_ERR "dsp_get_byte failed: 0x%lx = 0x%x!!!\n", 18062306a36Sopenharmony_ci chip->port + 0x0A, inb(chip->port + 0x0A)); 18162306a36Sopenharmony_ci return -ENODEV; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#undef REG_DEBUG 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int snd_es18xx_write(struct snd_es18xx *chip, 18762306a36Sopenharmony_ci unsigned char reg, unsigned char data) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned long flags; 19062306a36Sopenharmony_ci int ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 19362306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 19462306a36Sopenharmony_ci if (ret < 0) 19562306a36Sopenharmony_ci goto end; 19662306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, data); 19762306a36Sopenharmony_ci end: 19862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 19962306a36Sopenharmony_ci#ifdef REG_DEBUG 20062306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, data); 20162306a36Sopenharmony_ci#endif 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci unsigned long flags; 20862306a36Sopenharmony_ci int ret, data; 20962306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 21062306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, 0xC0); 21162306a36Sopenharmony_ci if (ret < 0) 21262306a36Sopenharmony_ci goto end; 21362306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 21462306a36Sopenharmony_ci if (ret < 0) 21562306a36Sopenharmony_ci goto end; 21662306a36Sopenharmony_ci data = snd_es18xx_dsp_get_byte(chip); 21762306a36Sopenharmony_ci ret = data; 21862306a36Sopenharmony_ci#ifdef REG_DEBUG 21962306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x now is %02x (%d)\n", reg, data, ret); 22062306a36Sopenharmony_ci#endif 22162306a36Sopenharmony_ci end: 22262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* Return old value */ 22762306a36Sopenharmony_cistatic int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg, 22862306a36Sopenharmony_ci unsigned char mask, unsigned char val) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci unsigned char old, new, oval; 23262306a36Sopenharmony_ci unsigned long flags; 23362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 23462306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, 0xC0); 23562306a36Sopenharmony_ci if (ret < 0) 23662306a36Sopenharmony_ci goto end; 23762306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 23862306a36Sopenharmony_ci if (ret < 0) 23962306a36Sopenharmony_ci goto end; 24062306a36Sopenharmony_ci ret = snd_es18xx_dsp_get_byte(chip); 24162306a36Sopenharmony_ci if (ret < 0) { 24262306a36Sopenharmony_ci goto end; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci old = ret; 24562306a36Sopenharmony_ci oval = old & mask; 24662306a36Sopenharmony_ci if (val != oval) { 24762306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, reg); 24862306a36Sopenharmony_ci if (ret < 0) 24962306a36Sopenharmony_ci goto end; 25062306a36Sopenharmony_ci new = (old & ~mask) | (val & mask); 25162306a36Sopenharmony_ci ret = snd_es18xx_dsp_command(chip, new); 25262306a36Sopenharmony_ci if (ret < 0) 25362306a36Sopenharmony_ci goto end; 25462306a36Sopenharmony_ci#ifdef REG_DEBUG 25562306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x (%d)\n", 25662306a36Sopenharmony_ci reg, old, new, ret); 25762306a36Sopenharmony_ci#endif 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci ret = oval; 26062306a36Sopenharmony_ci end: 26162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic inline void snd_es18xx_mixer_write(struct snd_es18xx *chip, 26662306a36Sopenharmony_ci unsigned char reg, unsigned char data) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci unsigned long flags; 26962306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 27062306a36Sopenharmony_ci outb(reg, chip->port + 0x04); 27162306a36Sopenharmony_ci outb(data, chip->port + 0x05); 27262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 27362306a36Sopenharmony_ci#ifdef REG_DEBUG 27462306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, data); 27562306a36Sopenharmony_ci#endif 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci unsigned long flags; 28162306a36Sopenharmony_ci int data; 28262306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 28362306a36Sopenharmony_ci outb(reg, chip->port + 0x04); 28462306a36Sopenharmony_ci data = inb(chip->port + 0x05); 28562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 28662306a36Sopenharmony_ci#ifdef REG_DEBUG 28762306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data); 28862306a36Sopenharmony_ci#endif 28962306a36Sopenharmony_ci return data; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* Return old value */ 29362306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char reg, 29462306a36Sopenharmony_ci unsigned char mask, unsigned char val) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned char old, new, oval; 29762306a36Sopenharmony_ci unsigned long flags; 29862306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 29962306a36Sopenharmony_ci outb(reg, chip->port + 0x04); 30062306a36Sopenharmony_ci old = inb(chip->port + 0x05); 30162306a36Sopenharmony_ci oval = old & mask; 30262306a36Sopenharmony_ci if (val != oval) { 30362306a36Sopenharmony_ci new = (old & ~mask) | (val & mask); 30462306a36Sopenharmony_ci outb(new, chip->port + 0x05); 30562306a36Sopenharmony_ci#ifdef REG_DEBUG 30662306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n", 30762306a36Sopenharmony_ci reg, old, new); 30862306a36Sopenharmony_ci#endif 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 31162306a36Sopenharmony_ci return oval; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned char reg, 31562306a36Sopenharmony_ci unsigned char mask) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int old, expected, new; 31862306a36Sopenharmony_ci unsigned long flags; 31962306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 32062306a36Sopenharmony_ci outb(reg, chip->port + 0x04); 32162306a36Sopenharmony_ci old = inb(chip->port + 0x05); 32262306a36Sopenharmony_ci expected = old ^ mask; 32362306a36Sopenharmony_ci outb(expected, chip->port + 0x05); 32462306a36Sopenharmony_ci new = inb(chip->port + 0x05); 32562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 32662306a36Sopenharmony_ci#ifdef REG_DEBUG 32762306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x, now is %02x\n", 32862306a36Sopenharmony_ci reg, old, expected, new); 32962306a36Sopenharmony_ci#endif 33062306a36Sopenharmony_ci return expected == new; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int snd_es18xx_reset(struct snd_es18xx *chip) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci int i; 33762306a36Sopenharmony_ci outb(0x03, chip->port + 0x06); 33862306a36Sopenharmony_ci inb(chip->port + 0x06); 33962306a36Sopenharmony_ci outb(0x00, chip->port + 0x06); 34062306a36Sopenharmony_ci for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); 34162306a36Sopenharmony_ci if (inb(chip->port + 0x0A) != 0xAA) 34262306a36Sopenharmony_ci return -1; 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int snd_es18xx_reset_fifo(struct snd_es18xx *chip) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci outb(0x02, chip->port + 0x06); 34962306a36Sopenharmony_ci inb(chip->port + 0x06); 35062306a36Sopenharmony_ci outb(0x00, chip->port + 0x06); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct snd_ratnum new_clocks[2] = { 35562306a36Sopenharmony_ci { 35662306a36Sopenharmony_ci .num = 793800, 35762306a36Sopenharmony_ci .den_min = 1, 35862306a36Sopenharmony_ci .den_max = 128, 35962306a36Sopenharmony_ci .den_step = 1, 36062306a36Sopenharmony_ci }, 36162306a36Sopenharmony_ci { 36262306a36Sopenharmony_ci .num = 768000, 36362306a36Sopenharmony_ci .den_min = 1, 36462306a36Sopenharmony_ci .den_max = 128, 36562306a36Sopenharmony_ci .den_step = 1, 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = { 37062306a36Sopenharmony_ci .nrats = 2, 37162306a36Sopenharmony_ci .rats = new_clocks, 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic const struct snd_ratnum old_clocks[2] = { 37562306a36Sopenharmony_ci { 37662306a36Sopenharmony_ci .num = 795444, 37762306a36Sopenharmony_ci .den_min = 1, 37862306a36Sopenharmony_ci .den_max = 128, 37962306a36Sopenharmony_ci .den_step = 1, 38062306a36Sopenharmony_ci }, 38162306a36Sopenharmony_ci { 38262306a36Sopenharmony_ci .num = 397722, 38362306a36Sopenharmony_ci .den_min = 1, 38462306a36Sopenharmony_ci .den_max = 128, 38562306a36Sopenharmony_ci .den_step = 1, 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = { 39062306a36Sopenharmony_ci .nrats = 2, 39162306a36Sopenharmony_ci .rats = old_clocks, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void snd_es18xx_rate_set(struct snd_es18xx *chip, 39662306a36Sopenharmony_ci struct snd_pcm_substream *substream, 39762306a36Sopenharmony_ci int mode) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci unsigned int bits, div0; 40062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 40162306a36Sopenharmony_ci if (chip->caps & ES18XX_NEW_RATE) { 40262306a36Sopenharmony_ci if (runtime->rate_num == new_clocks[0].num) 40362306a36Sopenharmony_ci bits = 128 - runtime->rate_den; 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci bits = 256 - runtime->rate_den; 40662306a36Sopenharmony_ci } else { 40762306a36Sopenharmony_ci if (runtime->rate_num == old_clocks[0].num) 40862306a36Sopenharmony_ci bits = 256 - runtime->rate_den; 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci bits = 128 - runtime->rate_den; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* set filter register */ 41462306a36Sopenharmony_ci div0 = 256 - 7160000*20/(8*82*runtime->rate); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { 41762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x70, bits); 41862306a36Sopenharmony_ci /* 41962306a36Sopenharmony_ci * Comment from kernel oss driver: 42062306a36Sopenharmony_ci * FKS: fascinating: 0x72 doesn't seem to work. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA2, div0); 42362306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x72, div0); 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA1, bits); 42662306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA2, div0); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream, 43162306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 43462306a36Sopenharmony_ci int shift; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci shift = 0; 43762306a36Sopenharmony_ci if (params_channels(hw_params) == 2) 43862306a36Sopenharmony_ci shift++; 43962306a36Sopenharmony_ci if (snd_pcm_format_width(params_format(hw_params)) == 16) 44062306a36Sopenharmony_ci shift++; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 44362306a36Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 44462306a36Sopenharmony_ci (chip->capture_a_substream) && 44562306a36Sopenharmony_ci params_channels(hw_params) != 1) { 44662306a36Sopenharmony_ci _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); 44762306a36Sopenharmony_ci return -EBUSY; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci chip->dma2_shift = shift; 45062306a36Sopenharmony_ci } else { 45162306a36Sopenharmony_ci chip->dma1_shift = shift; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int snd_es18xx_playback1_prepare(struct snd_es18xx *chip, 45762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 46062306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 46162306a36Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci snd_es18xx_rate_set(chip, substream, DAC2); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Transfer Count Reload */ 46662306a36Sopenharmony_ci count = 0x10000 - count; 46762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x74, count & 0xff); 46862306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x76, count >> 8); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Set format */ 47162306a36Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7A, 0x07, 47262306a36Sopenharmony_ci ((runtime->channels == 1) ? 0x00 : 0x02) | 47362306a36Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | 47462306a36Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Set DMA controller */ 47762306a36Sopenharmony_ci snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int snd_es18xx_playback1_trigger(struct snd_es18xx *chip, 48362306a36Sopenharmony_ci struct snd_pcm_substream *substream, 48462306a36Sopenharmony_ci int cmd) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci switch (cmd) { 48762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 48862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 48962306a36Sopenharmony_ci if (chip->active & DAC2) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci chip->active |= DAC2; 49262306a36Sopenharmony_ci /* Start DMA */ 49362306a36Sopenharmony_ci if (chip->dma2 >= 4) 49462306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0xb3); 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0x93); 49762306a36Sopenharmony_ci#ifdef AVOID_POPS 49862306a36Sopenharmony_ci /* Avoid pops */ 49962306a36Sopenharmony_ci mdelay(100); 50062306a36Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 50162306a36Sopenharmony_ci /* Restore Audio 2 volume */ 50262306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci /* Enable PCM output */ 50562306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 50662306a36Sopenharmony_ci#endif 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 50962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 51062306a36Sopenharmony_ci if (!(chip->active & DAC2)) 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci chip->active &= ~DAC2; 51362306a36Sopenharmony_ci /* Stop DMA */ 51462306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x78, 0x00); 51562306a36Sopenharmony_ci#ifdef AVOID_POPS 51662306a36Sopenharmony_ci mdelay(25); 51762306a36Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 51862306a36Sopenharmony_ci /* Set Audio 2 volume to 0 */ 51962306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7C, 0); 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci /* Disable PCM output */ 52262306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD3); 52362306a36Sopenharmony_ci#endif 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci default: 52662306a36Sopenharmony_ci return -EINVAL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream, 53362306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 53662306a36Sopenharmony_ci int shift; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci shift = 0; 53962306a36Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 54062306a36Sopenharmony_ci chip->playback_a_substream && 54162306a36Sopenharmony_ci params_channels(hw_params) != 1) { 54262306a36Sopenharmony_ci _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); 54362306a36Sopenharmony_ci return -EBUSY; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci if (params_channels(hw_params) == 2) 54662306a36Sopenharmony_ci shift++; 54762306a36Sopenharmony_ci if (snd_pcm_format_width(params_format(hw_params)) == 16) 54862306a36Sopenharmony_ci shift++; 54962306a36Sopenharmony_ci chip->dma1_shift = shift; 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 55662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 55762306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 55862306a36Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci snd_es18xx_reset_fifo(chip); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Set stereo/mono */ 56362306a36Sopenharmony_ci snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci snd_es18xx_rate_set(chip, substream, ADC1); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Transfer Count Reload */ 56862306a36Sopenharmony_ci count = 0x10000 - count; 56962306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA4, count & 0xff); 57062306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA5, count >> 8); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci#ifdef AVOID_POPS 57362306a36Sopenharmony_ci mdelay(100); 57462306a36Sopenharmony_ci#endif 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Set format */ 57762306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 57862306a36Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); 57962306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x90 | 58062306a36Sopenharmony_ci ((runtime->channels == 1) ? 0x40 : 0x08) | 58162306a36Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | 58262306a36Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Set DMA controller */ 58562306a36Sopenharmony_ci snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int snd_es18xx_capture_trigger(struct snd_pcm_substream *substream, 59162306a36Sopenharmony_ci int cmd) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci switch (cmd) { 59662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 59762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 59862306a36Sopenharmony_ci if (chip->active & ADC1) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci chip->active |= ADC1; 60162306a36Sopenharmony_ci /* Start DMA */ 60262306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x0f); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 60562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 60662306a36Sopenharmony_ci if (!(chip->active & ADC1)) 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci chip->active &= ~ADC1; 60962306a36Sopenharmony_ci /* Stop DMA */ 61062306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x00); 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci default: 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int snd_es18xx_playback2_prepare(struct snd_es18xx *chip, 62062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 62362306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 62462306a36Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci snd_es18xx_reset_fifo(chip); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Set stereo/mono */ 62962306a36Sopenharmony_ci snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci snd_es18xx_rate_set(chip, substream, DAC1); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Transfer Count Reload */ 63462306a36Sopenharmony_ci count = 0x10000 - count; 63562306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA4, count & 0xff); 63662306a36Sopenharmony_ci snd_es18xx_write(chip, 0xA5, count >> 8); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Set format */ 63962306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB6, 64062306a36Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); 64162306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 64262306a36Sopenharmony_ci snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); 64362306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x90 | 64462306a36Sopenharmony_ci (runtime->channels == 1 ? 0x40 : 0x08) | 64562306a36Sopenharmony_ci (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | 64662306a36Sopenharmony_ci (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Set DMA controller */ 64962306a36Sopenharmony_ci snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int snd_es18xx_playback2_trigger(struct snd_es18xx *chip, 65562306a36Sopenharmony_ci struct snd_pcm_substream *substream, 65662306a36Sopenharmony_ci int cmd) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci switch (cmd) { 65962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 66062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 66162306a36Sopenharmony_ci if (chip->active & DAC1) 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci chip->active |= DAC1; 66462306a36Sopenharmony_ci /* Start DMA */ 66562306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x05); 66662306a36Sopenharmony_ci#ifdef AVOID_POPS 66762306a36Sopenharmony_ci /* Avoid pops */ 66862306a36Sopenharmony_ci mdelay(100); 66962306a36Sopenharmony_ci /* Enable Audio 1 */ 67062306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 67162306a36Sopenharmony_ci#endif 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 67462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 67562306a36Sopenharmony_ci if (!(chip->active & DAC1)) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci chip->active &= ~DAC1; 67862306a36Sopenharmony_ci /* Stop DMA */ 67962306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB8, 0x00); 68062306a36Sopenharmony_ci#ifdef AVOID_POPS 68162306a36Sopenharmony_ci /* Avoid pops */ 68262306a36Sopenharmony_ci mdelay(25); 68362306a36Sopenharmony_ci /* Disable Audio 1 */ 68462306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD3); 68562306a36Sopenharmony_ci#endif 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci default: 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return 0; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int snd_es18xx_playback_prepare(struct snd_pcm_substream *substream) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 69762306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 69862306a36Sopenharmony_ci return snd_es18xx_playback1_prepare(chip, substream); 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci return snd_es18xx_playback2_prepare(chip, substream); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream, 70462306a36Sopenharmony_ci int cmd) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 70762306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 70862306a36Sopenharmony_ci return snd_es18xx_playback1_trigger(chip, substream, cmd); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci return snd_es18xx_playback2_trigger(chip, substream, cmd); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct snd_card *card = dev_id; 71662306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 71762306a36Sopenharmony_ci unsigned char status; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (chip->caps & ES18XX_CONTROL) { 72062306a36Sopenharmony_ci /* Read Interrupt status */ 72162306a36Sopenharmony_ci status = inb(chip->ctrl_port + 6); 72262306a36Sopenharmony_ci } else { 72362306a36Sopenharmony_ci /* Read Interrupt status */ 72462306a36Sopenharmony_ci status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci#if 0 72762306a36Sopenharmony_ci else { 72862306a36Sopenharmony_ci status = 0; 72962306a36Sopenharmony_ci if (inb(chip->port + 0x0C) & 0x01) 73062306a36Sopenharmony_ci status |= AUDIO1_IRQ; 73162306a36Sopenharmony_ci if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) 73262306a36Sopenharmony_ci status |= AUDIO2_IRQ; 73362306a36Sopenharmony_ci if ((chip->caps & ES18XX_HWV) && 73462306a36Sopenharmony_ci snd_es18xx_mixer_read(chip, 0x64) & 0x10) 73562306a36Sopenharmony_ci status |= HWV_IRQ; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci#endif 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Audio 1 & Audio 2 */ 74062306a36Sopenharmony_ci if (status & AUDIO2_IRQ) { 74162306a36Sopenharmony_ci if (chip->active & DAC2) 74262306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_a_substream); 74362306a36Sopenharmony_ci /* ack interrupt */ 74462306a36Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci if (status & AUDIO1_IRQ) { 74762306a36Sopenharmony_ci /* ok.. capture is active */ 74862306a36Sopenharmony_ci if (chip->active & ADC1) 74962306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_a_substream); 75062306a36Sopenharmony_ci /* ok.. playback2 is active */ 75162306a36Sopenharmony_ci else if (chip->active & DAC1) 75262306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_b_substream); 75362306a36Sopenharmony_ci /* ack interrupt */ 75462306a36Sopenharmony_ci inb(chip->port + 0x0E); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* MPU */ 75862306a36Sopenharmony_ci if ((status & MPU_IRQ) && chip->rmidi) 75962306a36Sopenharmony_ci snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Hardware volume */ 76262306a36Sopenharmony_ci if (status & HWV_IRQ) { 76362306a36Sopenharmony_ci int split = 0; 76462306a36Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 76562306a36Sopenharmony_ci split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; 76662306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 76762306a36Sopenharmony_ci &chip->hw_switch->id); 76862306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 76962306a36Sopenharmony_ci &chip->hw_volume->id); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci if (!split) { 77262306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 77362306a36Sopenharmony_ci &chip->master_switch->id); 77462306a36Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, 77562306a36Sopenharmony_ci &chip->master_volume->id); 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci /* ack interrupt */ 77862306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x66, 0x00); 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci return IRQ_HANDLED; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 78662306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 78762306a36Sopenharmony_ci int pos; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 79062306a36Sopenharmony_ci if (!(chip->active & DAC2)) 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci pos = snd_dma_pointer(chip->dma2, size); 79362306a36Sopenharmony_ci return pos >> chip->dma2_shift; 79462306a36Sopenharmony_ci } else { 79562306a36Sopenharmony_ci if (!(chip->active & DAC1)) 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci pos = snd_dma_pointer(chip->dma1, size); 79862306a36Sopenharmony_ci return pos >> chip->dma1_shift; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 80562306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 80662306a36Sopenharmony_ci int pos; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (!(chip->active & ADC1)) 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci pos = snd_dma_pointer(chip->dma1, size); 81162306a36Sopenharmony_ci return pos >> chip->dma1_shift; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_playback = 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 81762306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 81862306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 81962306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 82062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), 82162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 82262306a36Sopenharmony_ci .rate_min = 4000, 82362306a36Sopenharmony_ci .rate_max = 48000, 82462306a36Sopenharmony_ci .channels_min = 1, 82562306a36Sopenharmony_ci .channels_max = 2, 82662306a36Sopenharmony_ci .buffer_bytes_max = 65536, 82762306a36Sopenharmony_ci .period_bytes_min = 64, 82862306a36Sopenharmony_ci .period_bytes_max = 65536, 82962306a36Sopenharmony_ci .periods_min = 1, 83062306a36Sopenharmony_ci .periods_max = 1024, 83162306a36Sopenharmony_ci .fifo_size = 0, 83262306a36Sopenharmony_ci}; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_capture = 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 83762306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 83862306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 83962306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 84062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), 84162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 84262306a36Sopenharmony_ci .rate_min = 4000, 84362306a36Sopenharmony_ci .rate_max = 48000, 84462306a36Sopenharmony_ci .channels_min = 1, 84562306a36Sopenharmony_ci .channels_max = 2, 84662306a36Sopenharmony_ci .buffer_bytes_max = 65536, 84762306a36Sopenharmony_ci .period_bytes_min = 64, 84862306a36Sopenharmony_ci .period_bytes_max = 65536, 84962306a36Sopenharmony_ci .periods_min = 1, 85062306a36Sopenharmony_ci .periods_max = 1024, 85162306a36Sopenharmony_ci .fifo_size = 0, 85262306a36Sopenharmony_ci}; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int snd_es18xx_playback_open(struct snd_pcm_substream *substream) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 85762306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) { 86062306a36Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 86162306a36Sopenharmony_ci chip->capture_a_substream && 86262306a36Sopenharmony_ci chip->capture_a_substream->runtime->channels != 1) 86362306a36Sopenharmony_ci return -EAGAIN; 86462306a36Sopenharmony_ci chip->playback_a_substream = substream; 86562306a36Sopenharmony_ci } else if (substream->number <= 1) { 86662306a36Sopenharmony_ci if (chip->capture_a_substream) 86762306a36Sopenharmony_ci return -EAGAIN; 86862306a36Sopenharmony_ci chip->playback_b_substream = substream; 86962306a36Sopenharmony_ci } else { 87062306a36Sopenharmony_ci snd_BUG(); 87162306a36Sopenharmony_ci return -EINVAL; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci substream->runtime->hw = snd_es18xx_playback; 87462306a36Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 87562306a36Sopenharmony_ci (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int snd_es18xx_capture_open(struct snd_pcm_substream *substream) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 88262306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (chip->playback_b_substream) 88562306a36Sopenharmony_ci return -EAGAIN; 88662306a36Sopenharmony_ci if ((chip->caps & ES18XX_DUPLEX_MONO) && 88762306a36Sopenharmony_ci chip->playback_a_substream && 88862306a36Sopenharmony_ci chip->playback_a_substream->runtime->channels != 1) 88962306a36Sopenharmony_ci return -EAGAIN; 89062306a36Sopenharmony_ci chip->capture_a_substream = substream; 89162306a36Sopenharmony_ci substream->runtime->hw = snd_es18xx_capture; 89262306a36Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 89362306a36Sopenharmony_ci (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic int snd_es18xx_playback_close(struct snd_pcm_substream *substream) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) 90262306a36Sopenharmony_ci chip->playback_a_substream = NULL; 90362306a36Sopenharmony_ci else 90462306a36Sopenharmony_ci chip->playback_b_substream = NULL; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci return 0; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int snd_es18xx_capture_close(struct snd_pcm_substream *substream) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct snd_es18xx *chip = snd_pcm_substream_chip(substream); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci chip->capture_a_substream = NULL; 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci/* 91862306a36Sopenharmony_ci * MIXER part 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* Record source mux routines: 92262306a36Sopenharmony_ci * Depending on the chipset this mux switches between 4, 5, or 8 possible inputs. 92362306a36Sopenharmony_ci * bit table for the 4/5 source mux: 92462306a36Sopenharmony_ci * reg 1C: 92562306a36Sopenharmony_ci * b2 b1 b0 muxSource 92662306a36Sopenharmony_ci * x 0 x microphone 92762306a36Sopenharmony_ci * 0 1 x CD 92862306a36Sopenharmony_ci * 1 1 0 line 92962306a36Sopenharmony_ci * 1 1 1 mixer 93062306a36Sopenharmony_ci * if it's "mixer" and it's a 5 source mux chipset then reg 7A bit 3 determines 93162306a36Sopenharmony_ci * either the play mixer or the capture mixer. 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * "map4Source" translates from source number to reg bit pattern 93462306a36Sopenharmony_ci * "invMap4Source" translates from reg bit pattern to source number 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci static const char * const texts5Source[5] = { 94062306a36Sopenharmony_ci "Mic", "CD", "Line", "Master", "Mix" 94162306a36Sopenharmony_ci }; 94262306a36Sopenharmony_ci static const char * const texts8Source[8] = { 94362306a36Sopenharmony_ci "Mic", "Mic Master", "CD", "AOUT", 94462306a36Sopenharmony_ci "Mic1", "Mix", "Line", "Master" 94562306a36Sopenharmony_ci }; 94662306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci switch (chip->version) { 94962306a36Sopenharmony_ci case 0x1868: 95062306a36Sopenharmony_ci case 0x1878: 95162306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 4, texts5Source); 95262306a36Sopenharmony_ci case 0x1887: 95362306a36Sopenharmony_ci case 0x1888: 95462306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 5, texts5Source); 95562306a36Sopenharmony_ci case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */ 95662306a36Sopenharmony_ci case 0x1879: 95762306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 8, texts8Source); 95862306a36Sopenharmony_ci default: 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci static const unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3}; 96662306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 96762306a36Sopenharmony_ci int muxSource = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; 96862306a36Sopenharmony_ci if (!(chip->version == 0x1869 || chip->version == 0x1879)) { 96962306a36Sopenharmony_ci muxSource = invMap4Source[muxSource]; 97062306a36Sopenharmony_ci if (muxSource==3 && 97162306a36Sopenharmony_ci (chip->version == 0x1887 || chip->version == 0x1888) && 97262306a36Sopenharmony_ci (snd_es18xx_mixer_read(chip, 0x7a) & 0x08) 97362306a36Sopenharmony_ci ) 97462306a36Sopenharmony_ci muxSource = 4; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = muxSource; 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci static const unsigned char map4Source[4] = {0, 2, 6, 7}; 98362306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 98462306a36Sopenharmony_ci unsigned char val = ucontrol->value.enumerated.item[0]; 98562306a36Sopenharmony_ci unsigned char retVal = 0; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci switch (chip->version) { 98862306a36Sopenharmony_ci /* 5 source chips */ 98962306a36Sopenharmony_ci case 0x1887: 99062306a36Sopenharmony_ci case 0x1888: 99162306a36Sopenharmony_ci if (val > 4) 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci if (val == 4) { 99462306a36Sopenharmony_ci retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x08) != 0x08; 99562306a36Sopenharmony_ci val = 3; 99662306a36Sopenharmony_ci } else 99762306a36Sopenharmony_ci retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x00) != 0x00; 99862306a36Sopenharmony_ci fallthrough; 99962306a36Sopenharmony_ci /* 4 source chips */ 100062306a36Sopenharmony_ci case 0x1868: 100162306a36Sopenharmony_ci case 0x1878: 100262306a36Sopenharmony_ci if (val > 3) 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci val = map4Source[val]; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci /* 8 source chips */ 100762306a36Sopenharmony_ci case 0x1869: 100862306a36Sopenharmony_ci case 0x1879: 100962306a36Sopenharmony_ci if (val > 7) 101062306a36Sopenharmony_ci return -EINVAL; 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci default: 101362306a36Sopenharmony_ci return -EINVAL; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci return (snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val) || retVal; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci#define snd_es18xx_info_spatializer_enable snd_ctl_boolean_mono_info 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 102362306a36Sopenharmony_ci unsigned char val = snd_es18xx_mixer_read(chip, 0x50); 102462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !!(val & 8); 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int snd_es18xx_put_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 103162306a36Sopenharmony_ci unsigned char oval, nval; 103262306a36Sopenharmony_ci int change; 103362306a36Sopenharmony_ci nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; 103462306a36Sopenharmony_ci oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; 103562306a36Sopenharmony_ci change = nval != oval; 103662306a36Sopenharmony_ci if (change) { 103762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); 103862306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x50, nval); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci return change; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int snd_es18xx_info_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 104662306a36Sopenharmony_ci uinfo->count = 2; 104762306a36Sopenharmony_ci uinfo->value.integer.min = 0; 104862306a36Sopenharmony_ci uinfo->value.integer.max = 63; 104962306a36Sopenharmony_ci return 0; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic int snd_es18xx_get_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 105562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; 105662306a36Sopenharmony_ci ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; 105762306a36Sopenharmony_ci return 0; 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci#define snd_es18xx_info_hw_switch snd_ctl_boolean_stereo_info 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 106562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); 106662306a36Sopenharmony_ci ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); 106762306a36Sopenharmony_ci return 0; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_cistatic void snd_es18xx_hwv_free(struct snd_kcontrol *kcontrol) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 107362306a36Sopenharmony_ci chip->master_volume = NULL; 107462306a36Sopenharmony_ci chip->master_switch = NULL; 107562306a36Sopenharmony_ci chip->hw_volume = NULL; 107662306a36Sopenharmony_ci chip->hw_switch = NULL; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic int snd_es18xx_reg_bits(struct snd_es18xx *chip, unsigned char reg, 108062306a36Sopenharmony_ci unsigned char mask, unsigned char val) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci if (reg < 0xa0) 108362306a36Sopenharmony_ci return snd_es18xx_mixer_bits(chip, reg, mask, val); 108462306a36Sopenharmony_ci else 108562306a36Sopenharmony_ci return snd_es18xx_bits(chip, reg, mask, val); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci if (reg < 0xa0) 109162306a36Sopenharmony_ci return snd_es18xx_mixer_read(chip, reg); 109262306a36Sopenharmony_ci else 109362306a36Sopenharmony_ci return snd_es18xx_read(chip, reg); 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \ 109762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 109862306a36Sopenharmony_ci .info = snd_es18xx_info_single, \ 109962306a36Sopenharmony_ci .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ 110062306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci#define ES18XX_FL_INVERT (1 << 0) 110362306a36Sopenharmony_ci#define ES18XX_FL_PMPORT (1 << 1) 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 111062306a36Sopenharmony_ci uinfo->count = 1; 111162306a36Sopenharmony_ci uinfo->value.integer.min = 0; 111262306a36Sopenharmony_ci uinfo->value.integer.max = mask; 111362306a36Sopenharmony_ci return 0; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 111962306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 112062306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 112162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 112262306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; 112362306a36Sopenharmony_ci int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; 112462306a36Sopenharmony_ci int val; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (pm_port) 112762306a36Sopenharmony_ci val = inb(chip->port + ES18XX_PM); 112862306a36Sopenharmony_ci else 112962306a36Sopenharmony_ci val = snd_es18xx_reg_read(chip, reg); 113062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (val >> shift) & mask; 113162306a36Sopenharmony_ci if (invert) 113262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 113362306a36Sopenharmony_ci return 0; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 113962306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 114062306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 114162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 114262306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; 114362306a36Sopenharmony_ci int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; 114462306a36Sopenharmony_ci unsigned char val; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 114762306a36Sopenharmony_ci if (invert) 114862306a36Sopenharmony_ci val = mask - val; 114962306a36Sopenharmony_ci mask <<= shift; 115062306a36Sopenharmony_ci val <<= shift; 115162306a36Sopenharmony_ci if (pm_port) { 115262306a36Sopenharmony_ci unsigned char cur = inb(chip->port + ES18XX_PM); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if ((cur & mask) == val) 115562306a36Sopenharmony_ci return 0; 115662306a36Sopenharmony_ci outb((cur & ~mask) | val, chip->port + ES18XX_PM); 115762306a36Sopenharmony_ci return 1; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return snd_es18xx_reg_bits(chip, reg, mask, val) != val; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 116462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 116562306a36Sopenharmony_ci .info = snd_es18xx_info_double, \ 116662306a36Sopenharmony_ci .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \ 116762306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int snd_es18xx_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 117462306a36Sopenharmony_ci uinfo->count = 2; 117562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 117662306a36Sopenharmony_ci uinfo->value.integer.max = mask; 117762306a36Sopenharmony_ci return 0; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic int snd_es18xx_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 118362306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 118462306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 118562306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 118662306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 118762306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 118862306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 118962306a36Sopenharmony_ci unsigned char left, right; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci left = snd_es18xx_reg_read(chip, left_reg); 119262306a36Sopenharmony_ci if (left_reg != right_reg) 119362306a36Sopenharmony_ci right = snd_es18xx_reg_read(chip, right_reg); 119462306a36Sopenharmony_ci else 119562306a36Sopenharmony_ci right = left; 119662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (left >> shift_left) & mask; 119762306a36Sopenharmony_ci ucontrol->value.integer.value[1] = (right >> shift_right) & mask; 119862306a36Sopenharmony_ci if (invert) { 119962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 120062306a36Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci return 0; 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); 120862306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 120962306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 121062306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 121162306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 121262306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 121362306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 121462306a36Sopenharmony_ci int change; 121562306a36Sopenharmony_ci unsigned char val1, val2, mask1, mask2; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 121862306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 121962306a36Sopenharmony_ci if (invert) { 122062306a36Sopenharmony_ci val1 = mask - val1; 122162306a36Sopenharmony_ci val2 = mask - val2; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci val1 <<= shift_left; 122462306a36Sopenharmony_ci val2 <<= shift_right; 122562306a36Sopenharmony_ci mask1 = mask << shift_left; 122662306a36Sopenharmony_ci mask2 = mask << shift_right; 122762306a36Sopenharmony_ci if (left_reg != right_reg) { 122862306a36Sopenharmony_ci change = 0; 122962306a36Sopenharmony_ci if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) 123062306a36Sopenharmony_ci change = 1; 123162306a36Sopenharmony_ci if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) 123262306a36Sopenharmony_ci change = 1; 123362306a36Sopenharmony_ci } else { 123462306a36Sopenharmony_ci change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, 123562306a36Sopenharmony_ci val1 | val2) != (val1 | val2)); 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci return change; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci/* Mixer controls 124162306a36Sopenharmony_ci * These arrays contain setup data for mixer controls. 124262306a36Sopenharmony_ci * 124362306a36Sopenharmony_ci * The controls that are universal to all chipsets are fully initialized 124462306a36Sopenharmony_ci * here. 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_base_controls[] = { 124762306a36Sopenharmony_ciES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), 124862306a36Sopenharmony_ciES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), 124962306a36Sopenharmony_ciES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), 125062306a36Sopenharmony_ciES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), 125162306a36Sopenharmony_ciES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), 125262306a36Sopenharmony_ciES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), 125362306a36Sopenharmony_ciES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), 125462306a36Sopenharmony_ciES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), 125562306a36Sopenharmony_ciES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 125862306a36Sopenharmony_ci .name = "Capture Source", 125962306a36Sopenharmony_ci .info = snd_es18xx_info_mux, 126062306a36Sopenharmony_ci .get = snd_es18xx_get_mux, 126162306a36Sopenharmony_ci .put = snd_es18xx_put_mux, 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci}; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_recmix_controls[] = { 126662306a36Sopenharmony_ciES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), 126762306a36Sopenharmony_ciES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), 126862306a36Sopenharmony_ciES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), 126962306a36Sopenharmony_ciES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), 127062306a36Sopenharmony_ciES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), 127162306a36Sopenharmony_ciES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) 127262306a36Sopenharmony_ci}; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci/* 127562306a36Sopenharmony_ci * The chipset specific mixer controls 127662306a36Sopenharmony_ci */ 127762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_speaker = 127862306a36Sopenharmony_ci ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1869[] = { 128162306a36Sopenharmony_ciES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT), 128262306a36Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0), 128362306a36Sopenharmony_ciES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), 128462306a36Sopenharmony_ciES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) 128562306a36Sopenharmony_ci}; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1878 = 128862306a36Sopenharmony_ci ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1879[] = { 129162306a36Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0), 129262306a36Sopenharmony_ciES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), 129362306a36Sopenharmony_ciES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) 129462306a36Sopenharmony_ci}; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = { 129762306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), 129862306a36Sopenharmony_ci}; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = { 130162306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), 130262306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) 130362306a36Sopenharmony_ci}; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = { 130662306a36Sopenharmony_ciES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 130962306a36Sopenharmony_ci .name = "3D Control - Switch", 131062306a36Sopenharmony_ci .info = snd_es18xx_info_spatializer_enable, 131162306a36Sopenharmony_ci .get = snd_es18xx_get_spatializer_enable, 131262306a36Sopenharmony_ci .put = snd_es18xx_put_spatializer_enable, 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci}; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre1_control = 131762306a36Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre2_control = 132062306a36Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = { 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 132562306a36Sopenharmony_ci .name = "Hardware Master Playback Volume", 132662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 132762306a36Sopenharmony_ci .info = snd_es18xx_info_hw_volume, 132862306a36Sopenharmony_ci .get = snd_es18xx_get_hw_volume, 132962306a36Sopenharmony_ci}, 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 133262306a36Sopenharmony_ci .name = "Hardware Master Playback Switch", 133362306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 133462306a36Sopenharmony_ci .info = snd_es18xx_info_hw_switch, 133562306a36Sopenharmony_ci .get = snd_es18xx_get_hw_switch, 133662306a36Sopenharmony_ci}, 133762306a36Sopenharmony_ciES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), 133862306a36Sopenharmony_ci}; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = { 134162306a36Sopenharmony_ciES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT), 134262306a36Sopenharmony_ciES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT), 134362306a36Sopenharmony_ci}; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci outb(reg, chip->ctrl_port); 134862306a36Sopenharmony_ci return inb(chip->ctrl_port + 1); 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic void snd_es18xx_config_write(struct snd_es18xx *chip, 135262306a36Sopenharmony_ci unsigned char reg, unsigned char data) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci /* No need for spinlocks, this function is used only in 135562306a36Sopenharmony_ci otherwise protected init code */ 135662306a36Sopenharmony_ci outb(reg, chip->ctrl_port); 135762306a36Sopenharmony_ci outb(data, chip->ctrl_port + 1); 135862306a36Sopenharmony_ci#ifdef REG_DEBUG 135962306a36Sopenharmony_ci snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data); 136062306a36Sopenharmony_ci#endif 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int snd_es18xx_initialize(struct snd_es18xx *chip, 136462306a36Sopenharmony_ci unsigned long mpu_port, 136562306a36Sopenharmony_ci unsigned long fm_port) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci int mask = 0; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci /* enable extended mode */ 137062306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xC6); 137162306a36Sopenharmony_ci /* Reset mixer registers */ 137262306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x00, 0x00); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* Audio 1 DMA demand mode (4 bytes/request) */ 137562306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB9, 2); 137662306a36Sopenharmony_ci if (chip->caps & ES18XX_CONTROL) { 137762306a36Sopenharmony_ci /* Hardware volume IRQ */ 137862306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x27, chip->irq); 137962306a36Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 138062306a36Sopenharmony_ci /* FM I/O */ 138162306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x62, fm_port >> 8); 138262306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x63, fm_port & 0xff); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) { 138562306a36Sopenharmony_ci /* MPU-401 I/O */ 138662306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x64, mpu_port >> 8); 138762306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff); 138862306a36Sopenharmony_ci /* MPU-401 IRQ */ 138962306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x28, chip->irq); 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci /* Audio1 IRQ */ 139262306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x70, chip->irq); 139362306a36Sopenharmony_ci /* Audio2 IRQ */ 139462306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x72, chip->irq); 139562306a36Sopenharmony_ci /* Audio1 DMA */ 139662306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x74, chip->dma1); 139762306a36Sopenharmony_ci /* Audio2 DMA */ 139862306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x75, chip->dma2); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* Enable Audio 1 IRQ */ 140162306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB1, 0x50); 140262306a36Sopenharmony_ci /* Enable Audio 2 IRQ */ 140362306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7A, 0x40); 140462306a36Sopenharmony_ci /* Enable Audio 1 DMA */ 140562306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB2, 0x50); 140662306a36Sopenharmony_ci /* Enable MPU and hardware volume interrupt */ 140762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x64, 0x42); 140862306a36Sopenharmony_ci /* Enable ESS wavetable input */ 140962306a36Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci else { 141262306a36Sopenharmony_ci int irqmask, dma1mask, dma2mask; 141362306a36Sopenharmony_ci switch (chip->irq) { 141462306a36Sopenharmony_ci case 2: 141562306a36Sopenharmony_ci case 9: 141662306a36Sopenharmony_ci irqmask = 0; 141762306a36Sopenharmony_ci break; 141862306a36Sopenharmony_ci case 5: 141962306a36Sopenharmony_ci irqmask = 1; 142062306a36Sopenharmony_ci break; 142162306a36Sopenharmony_ci case 7: 142262306a36Sopenharmony_ci irqmask = 2; 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci case 10: 142562306a36Sopenharmony_ci irqmask = 3; 142662306a36Sopenharmony_ci break; 142762306a36Sopenharmony_ci default: 142862306a36Sopenharmony_ci snd_printk(KERN_ERR "invalid irq %d\n", chip->irq); 142962306a36Sopenharmony_ci return -ENODEV; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci switch (chip->dma1) { 143262306a36Sopenharmony_ci case 0: 143362306a36Sopenharmony_ci dma1mask = 1; 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci case 1: 143662306a36Sopenharmony_ci dma1mask = 2; 143762306a36Sopenharmony_ci break; 143862306a36Sopenharmony_ci case 3: 143962306a36Sopenharmony_ci dma1mask = 3; 144062306a36Sopenharmony_ci break; 144162306a36Sopenharmony_ci default: 144262306a36Sopenharmony_ci snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1); 144362306a36Sopenharmony_ci return -ENODEV; 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci switch (chip->dma2) { 144662306a36Sopenharmony_ci case 0: 144762306a36Sopenharmony_ci dma2mask = 0; 144862306a36Sopenharmony_ci break; 144962306a36Sopenharmony_ci case 1: 145062306a36Sopenharmony_ci dma2mask = 1; 145162306a36Sopenharmony_ci break; 145262306a36Sopenharmony_ci case 3: 145362306a36Sopenharmony_ci dma2mask = 2; 145462306a36Sopenharmony_ci break; 145562306a36Sopenharmony_ci case 5: 145662306a36Sopenharmony_ci dma2mask = 3; 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci default: 145962306a36Sopenharmony_ci snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2); 146062306a36Sopenharmony_ci return -ENODEV; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* Enable and set Audio 1 IRQ */ 146462306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); 146562306a36Sopenharmony_ci /* Enable and set Audio 1 DMA */ 146662306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); 146762306a36Sopenharmony_ci /* Set Audio 2 DMA */ 146862306a36Sopenharmony_ci snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); 146962306a36Sopenharmony_ci /* Enable Audio 2 IRQ and DMA 147062306a36Sopenharmony_ci Set capture mixer input */ 147162306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7A, 0x68); 147262306a36Sopenharmony_ci /* Enable and set hardware volume interrupt */ 147362306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x64, 0x06); 147462306a36Sopenharmony_ci if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) { 147562306a36Sopenharmony_ci /* MPU401 share irq with audio 147662306a36Sopenharmony_ci Joystick enabled 147762306a36Sopenharmony_ci FM enabled */ 147862306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x40, 147962306a36Sopenharmony_ci 0x43 | (mpu_port & 0xf0) >> 1); 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci if (chip->caps & ES18XX_NEW_RATE) { 148462306a36Sopenharmony_ci /* Change behaviour of register A1 148562306a36Sopenharmony_ci 4x oversampling 148662306a36Sopenharmony_ci 2nd channel DAC asynchronous */ 148762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x71, 0x32); 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci if (!(chip->caps & ES18XX_PCM2)) { 149062306a36Sopenharmony_ci /* Enable DMA FIFO */ 149162306a36Sopenharmony_ci snd_es18xx_write(chip, 0xB7, 0x80); 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci if (chip->caps & ES18XX_SPATIALIZER) { 149462306a36Sopenharmony_ci /* Set spatializer parameters to recommended values */ 149562306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x54, 0x8f); 149662306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x56, 0x95); 149762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x58, 0x94); 149862306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x5a, 0x80); 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci /* Flip the "enable I2S" bits for those chipsets that need it */ 150162306a36Sopenharmony_ci switch (chip->version) { 150262306a36Sopenharmony_ci case 0x1879: 150362306a36Sopenharmony_ci //Leaving I2S enabled on the 1879 screws up the PCM playback (rate effected somehow) 150462306a36Sopenharmony_ci //so a Switch control has been added to toggle this 0x71 bit on/off: 150562306a36Sopenharmony_ci //snd_es18xx_mixer_bits(chip, 0x71, 0x40, 0x40); 150662306a36Sopenharmony_ci /* Note: we fall through on purpose here. */ 150762306a36Sopenharmony_ci case 0x1878: 150862306a36Sopenharmony_ci snd_es18xx_config_write(chip, 0x29, snd_es18xx_config_read(chip, 0x29) | 0x40); 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci /* Mute input source */ 151262306a36Sopenharmony_ci if (chip->caps & ES18XX_MUTEREC) 151362306a36Sopenharmony_ci mask = 0x10; 151462306a36Sopenharmony_ci if (chip->caps & ES18XX_RECMIX) 151562306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); 151662306a36Sopenharmony_ci else { 151762306a36Sopenharmony_ci snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); 151862306a36Sopenharmony_ci snd_es18xx_write(chip, 0xb4, 0x00); 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci#ifndef AVOID_POPS 152162306a36Sopenharmony_ci /* Enable PCM output */ 152262306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xD1); 152362306a36Sopenharmony_ci#endif 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci int hi,lo; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* reset */ 153362306a36Sopenharmony_ci if (snd_es18xx_reset(chip) < 0) { 153462306a36Sopenharmony_ci snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port); 153562306a36Sopenharmony_ci return -ENODEV; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci snd_es18xx_dsp_command(chip, 0xe7); 153962306a36Sopenharmony_ci hi = snd_es18xx_dsp_get_byte(chip); 154062306a36Sopenharmony_ci if (hi < 0) { 154162306a36Sopenharmony_ci return hi; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci lo = snd_es18xx_dsp_get_byte(chip); 154462306a36Sopenharmony_ci if ((lo & 0xf0) != 0x80) { 154562306a36Sopenharmony_ci return -ENODEV; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci if (hi == 0x48) { 154862306a36Sopenharmony_ci chip->version = 0x488; 154962306a36Sopenharmony_ci return 0; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci if (hi != 0x68) { 155262306a36Sopenharmony_ci return -ENODEV; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci if ((lo & 0x0f) < 8) { 155562306a36Sopenharmony_ci chip->version = 0x688; 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci outb(0x40, chip->port + 0x04); 156062306a36Sopenharmony_ci udelay(10); 156162306a36Sopenharmony_ci hi = inb(chip->port + 0x05); 156262306a36Sopenharmony_ci udelay(10); 156362306a36Sopenharmony_ci lo = inb(chip->port + 0x05); 156462306a36Sopenharmony_ci if (hi != lo) { 156562306a36Sopenharmony_ci chip->version = hi << 8 | lo; 156662306a36Sopenharmony_ci chip->ctrl_port = inb(chip->port + 0x05) << 8; 156762306a36Sopenharmony_ci udelay(10); 156862306a36Sopenharmony_ci chip->ctrl_port += inb(chip->port + 0x05); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (!devm_request_region(card->dev, chip->ctrl_port, 8, 157162306a36Sopenharmony_ci "ES18xx - CTRL")) { 157262306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port); 157362306a36Sopenharmony_ci return -EBUSY; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci return 0; 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci /* If has Hardware volume */ 158062306a36Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { 158162306a36Sopenharmony_ci /* If has Audio2 */ 158262306a36Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { 158362306a36Sopenharmony_ci /* If has volume count */ 158462306a36Sopenharmony_ci if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { 158562306a36Sopenharmony_ci chip->version = 0x1887; 158662306a36Sopenharmony_ci } else { 158762306a36Sopenharmony_ci chip->version = 0x1888; 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci } else { 159062306a36Sopenharmony_ci chip->version = 0x1788; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci else 159462306a36Sopenharmony_ci chip->version = 0x1688; 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic int snd_es18xx_probe(struct snd_card *card, 159962306a36Sopenharmony_ci struct snd_es18xx *chip, 160062306a36Sopenharmony_ci unsigned long mpu_port, 160162306a36Sopenharmony_ci unsigned long fm_port) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci if (snd_es18xx_identify(card, chip) < 0) { 160462306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); 160562306a36Sopenharmony_ci return -ENODEV; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci switch (chip->version) { 160962306a36Sopenharmony_ci case 0x1868: 161062306a36Sopenharmony_ci chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT; 161162306a36Sopenharmony_ci break; 161262306a36Sopenharmony_ci case 0x1869: 161362306a36Sopenharmony_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; 161462306a36Sopenharmony_ci break; 161562306a36Sopenharmony_ci case 0x1878: 161662306a36Sopenharmony_ci chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL; 161762306a36Sopenharmony_ci break; 161862306a36Sopenharmony_ci case 0x1879: 161962306a36Sopenharmony_ci chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; 162062306a36Sopenharmony_ci break; 162162306a36Sopenharmony_ci case 0x1887: 162262306a36Sopenharmony_ci case 0x1888: 162362306a36Sopenharmony_ci chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT; 162462306a36Sopenharmony_ci break; 162562306a36Sopenharmony_ci default: 162662306a36Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n", 162762306a36Sopenharmony_ci chip->port, chip->version); 162862306a36Sopenharmony_ci return -ENODEV; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (chip->dma1 == chip->dma2) 163462306a36Sopenharmony_ci chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return snd_es18xx_initialize(chip, mpu_port, fm_port); 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_playback_ops = { 164062306a36Sopenharmony_ci .open = snd_es18xx_playback_open, 164162306a36Sopenharmony_ci .close = snd_es18xx_playback_close, 164262306a36Sopenharmony_ci .hw_params = snd_es18xx_playback_hw_params, 164362306a36Sopenharmony_ci .prepare = snd_es18xx_playback_prepare, 164462306a36Sopenharmony_ci .trigger = snd_es18xx_playback_trigger, 164562306a36Sopenharmony_ci .pointer = snd_es18xx_playback_pointer, 164662306a36Sopenharmony_ci}; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_capture_ops = { 164962306a36Sopenharmony_ci .open = snd_es18xx_capture_open, 165062306a36Sopenharmony_ci .close = snd_es18xx_capture_close, 165162306a36Sopenharmony_ci .hw_params = snd_es18xx_capture_hw_params, 165262306a36Sopenharmony_ci .prepare = snd_es18xx_capture_prepare, 165362306a36Sopenharmony_ci .trigger = snd_es18xx_capture_trigger, 165462306a36Sopenharmony_ci .pointer = snd_es18xx_capture_pointer, 165562306a36Sopenharmony_ci}; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic int snd_es18xx_pcm(struct snd_card *card, int device) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 166062306a36Sopenharmony_ci struct snd_pcm *pcm; 166162306a36Sopenharmony_ci char str[16]; 166262306a36Sopenharmony_ci int err; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci sprintf(str, "ES%x", chip->version); 166562306a36Sopenharmony_ci if (chip->caps & ES18XX_PCM2) 166662306a36Sopenharmony_ci err = snd_pcm_new(card, str, device, 2, 1, &pcm); 166762306a36Sopenharmony_ci else 166862306a36Sopenharmony_ci err = snd_pcm_new(card, str, device, 1, 1, &pcm); 166962306a36Sopenharmony_ci if (err < 0) 167062306a36Sopenharmony_ci return err; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); 167362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci /* global setup */ 167662306a36Sopenharmony_ci pcm->private_data = chip; 167762306a36Sopenharmony_ci pcm->info_flags = 0; 167862306a36Sopenharmony_ci if (chip->caps & ES18XX_DUPLEX_SAME) 167962306a36Sopenharmony_ci pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; 168062306a36Sopenharmony_ci if (! (chip->caps & ES18XX_PCM2)) 168162306a36Sopenharmony_ci pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; 168262306a36Sopenharmony_ci sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); 168362306a36Sopenharmony_ci chip->pcm = pcm; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 168662306a36Sopenharmony_ci 64*1024, 168762306a36Sopenharmony_ci chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); 168862306a36Sopenharmony_ci return 0; 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci/* Power Management support functions */ 169262306a36Sopenharmony_ci#ifdef CONFIG_PM 169362306a36Sopenharmony_cistatic int snd_es18xx_suspend(struct snd_card *card, pm_message_t state) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* power down */ 170062306a36Sopenharmony_ci chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); 170162306a36Sopenharmony_ci chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); 170262306a36Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); 170362306a36Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci return 0; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic int snd_es18xx_resume(struct snd_card *card) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* restore PM register, we won't wake till (not 0x07) i/o activity though */ 171362306a36Sopenharmony_ci snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 171662306a36Sopenharmony_ci return 0; 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci#endif /* CONFIG_PM */ 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int snd_es18xx_new_device(struct snd_card *card, 172162306a36Sopenharmony_ci unsigned long port, 172262306a36Sopenharmony_ci unsigned long mpu_port, 172362306a36Sopenharmony_ci unsigned long fm_port, 172462306a36Sopenharmony_ci int irq, int dma1, int dma2) 172562306a36Sopenharmony_ci{ 172662306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 172962306a36Sopenharmony_ci spin_lock_init(&chip->mixer_lock); 173062306a36Sopenharmony_ci chip->port = port; 173162306a36Sopenharmony_ci chip->irq = -1; 173262306a36Sopenharmony_ci chip->dma1 = -1; 173362306a36Sopenharmony_ci chip->dma2 = -1; 173462306a36Sopenharmony_ci chip->audio2_vol = 0x00; 173562306a36Sopenharmony_ci chip->active = 0; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (!devm_request_region(card->dev, port, 16, "ES18xx")) { 173862306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); 173962306a36Sopenharmony_ci return -EBUSY; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx", 174362306a36Sopenharmony_ci (void *) card)) { 174462306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); 174562306a36Sopenharmony_ci return -EBUSY; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci chip->irq = irq; 174862306a36Sopenharmony_ci card->sync_irq = chip->irq; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) { 175162306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); 175262306a36Sopenharmony_ci return -EBUSY; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci chip->dma1 = dma1; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (dma2 != dma1 && 175762306a36Sopenharmony_ci snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) { 175862306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); 175962306a36Sopenharmony_ci return -EBUSY; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci chip->dma2 = dma2; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (snd_es18xx_probe(card, chip, mpu_port, fm_port) < 0) 176462306a36Sopenharmony_ci return -ENODEV; 176562306a36Sopenharmony_ci return 0; 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic int snd_es18xx_mixer(struct snd_card *card) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 177162306a36Sopenharmony_ci int err; 177262306a36Sopenharmony_ci unsigned int idx; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci strcpy(card->mixername, chip->pcm->name); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { 177762306a36Sopenharmony_ci struct snd_kcontrol *kctl; 177862306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); 177962306a36Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 178062306a36Sopenharmony_ci switch (idx) { 178162306a36Sopenharmony_ci case 0: 178262306a36Sopenharmony_ci chip->master_volume = kctl; 178362306a36Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 178462306a36Sopenharmony_ci break; 178562306a36Sopenharmony_ci case 1: 178662306a36Sopenharmony_ci chip->master_switch = kctl; 178762306a36Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 178862306a36Sopenharmony_ci break; 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 179262306a36Sopenharmony_ci if (err < 0) 179362306a36Sopenharmony_ci return err; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci if (chip->caps & ES18XX_PCM2) { 179662306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) { 179762306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip)); 179862306a36Sopenharmony_ci if (err < 0) 179962306a36Sopenharmony_ci return err; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci } else { 180262306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) { 180362306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip)); 180462306a36Sopenharmony_ci if (err < 0) 180562306a36Sopenharmony_ci return err; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (chip->caps & ES18XX_RECMIX) { 181062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) { 181162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip)); 181262306a36Sopenharmony_ci if (err < 0) 181362306a36Sopenharmony_ci return err; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci switch (chip->version) { 181762306a36Sopenharmony_ci default: 181862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip)); 181962306a36Sopenharmony_ci if (err < 0) 182062306a36Sopenharmony_ci return err; 182162306a36Sopenharmony_ci break; 182262306a36Sopenharmony_ci case 0x1869: 182362306a36Sopenharmony_ci case 0x1879: 182462306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip)); 182562306a36Sopenharmony_ci if (err < 0) 182662306a36Sopenharmony_ci return err; 182762306a36Sopenharmony_ci break; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci if (chip->caps & ES18XX_SPATIALIZER) { 183062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) { 183162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip)); 183262306a36Sopenharmony_ci if (err < 0) 183362306a36Sopenharmony_ci return err; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci if (chip->caps & ES18XX_HWV) { 183762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_hw_volume_controls); idx++) { 183862306a36Sopenharmony_ci struct snd_kcontrol *kctl; 183962306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); 184062306a36Sopenharmony_ci if (idx == 0) 184162306a36Sopenharmony_ci chip->hw_volume = kctl; 184262306a36Sopenharmony_ci else 184362306a36Sopenharmony_ci chip->hw_switch = kctl; 184462306a36Sopenharmony_ci kctl->private_free = snd_es18xx_hwv_free; 184562306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 184662306a36Sopenharmony_ci if (err < 0) 184762306a36Sopenharmony_ci return err; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci /* finish initializing other chipset specific controls 185262306a36Sopenharmony_ci */ 185362306a36Sopenharmony_ci if (chip->version != 0x1868) { 185462306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_speaker, 185562306a36Sopenharmony_ci chip)); 185662306a36Sopenharmony_ci if (err < 0) 185762306a36Sopenharmony_ci return err; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci if (chip->version == 0x1869) { 186062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1869); idx++) { 186162306a36Sopenharmony_ci err = snd_ctl_add(card, 186262306a36Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_1869[idx], 186362306a36Sopenharmony_ci chip)); 186462306a36Sopenharmony_ci if (err < 0) 186562306a36Sopenharmony_ci return err; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci } else if (chip->version == 0x1878) { 186862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_1878, 186962306a36Sopenharmony_ci chip)); 187062306a36Sopenharmony_ci if (err < 0) 187162306a36Sopenharmony_ci return err; 187262306a36Sopenharmony_ci } else if (chip->version == 0x1879) { 187362306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1879); idx++) { 187462306a36Sopenharmony_ci err = snd_ctl_add(card, 187562306a36Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_1879[idx], 187662306a36Sopenharmony_ci chip)); 187762306a36Sopenharmony_ci if (err < 0) 187862306a36Sopenharmony_ci return err; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci if (chip->caps & ES18XX_GPO_2BIT) { 188262306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) { 188362306a36Sopenharmony_ci err = snd_ctl_add(card, 188462306a36Sopenharmony_ci snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx], 188562306a36Sopenharmony_ci chip)); 188662306a36Sopenharmony_ci if (err < 0) 188762306a36Sopenharmony_ci return err; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci return 0; 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci/* Card level */ 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ciMODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>"); 189762306a36Sopenharmony_ciMODULE_DESCRIPTION("ESS ES18xx AudioDrive"); 189862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 190162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 190262306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ 190362306a36Sopenharmony_ci#ifdef CONFIG_PNP 190462306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; 190562306a36Sopenharmony_ci#endif 190662306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ 190762306a36Sopenharmony_ci#ifndef CONFIG_PNP 190862306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; 190962306a36Sopenharmony_ci#else 191062306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 191162306a36Sopenharmony_ci#endif 191262306a36Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 191362306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ 191462306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 191562306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 191862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ES18xx soundcard."); 191962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 192062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ES18xx soundcard."); 192162306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 192262306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ES18xx soundcard."); 192362306a36Sopenharmony_ci#ifdef CONFIG_PNP 192462306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444); 192562306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); 192662306a36Sopenharmony_ci#endif 192762306a36Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 192862306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for ES18xx driver."); 192962306a36Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444); 193062306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver."); 193162306a36Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444); 193262306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver."); 193362306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 193462306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for ES18xx driver."); 193562306a36Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 193662306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver."); 193762306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 193862306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver."); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci#ifdef CONFIG_PNP 194162306a36Sopenharmony_cistatic int isa_registered; 194262306a36Sopenharmony_cistatic int pnp_registered; 194362306a36Sopenharmony_cistatic int pnpc_registered; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_cistatic const struct pnp_device_id snd_audiodrive_pnpbiosids[] = { 194662306a36Sopenharmony_ci { .id = "ESS1869" }, 194762306a36Sopenharmony_ci { .id = "ESS1879" }, 194862306a36Sopenharmony_ci { .id = "" } /* end */ 194962306a36Sopenharmony_ci}; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci/* PnP main device initialization */ 195462306a36Sopenharmony_cistatic int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev) 195562306a36Sopenharmony_ci{ 195662306a36Sopenharmony_ci if (pnp_activate_dev(pdev) < 0) { 195762306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); 195862306a36Sopenharmony_ci return -EBUSY; 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci /* ok. hack using Vendor-Defined Card-Level registers */ 196162306a36Sopenharmony_ci /* skip csn and logdev initialization - already done in isapnp_configure */ 196262306a36Sopenharmony_ci if (pnp_device_is_isapnp(pdev)) { 196362306a36Sopenharmony_ci isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev)); 196462306a36Sopenharmony_ci isapnp_write_byte(0x27, pnp_irq(pdev, 0)); /* Hardware Volume IRQ Number */ 196562306a36Sopenharmony_ci if (mpu_port[dev] != SNDRV_AUTO_PORT) 196662306a36Sopenharmony_ci isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */ 196762306a36Sopenharmony_ci isapnp_write_byte(0x72, pnp_irq(pdev, 0)); /* second IRQ */ 196862306a36Sopenharmony_ci isapnp_cfg_end(); 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci port[dev] = pnp_port_start(pdev, 0); 197162306a36Sopenharmony_ci fm_port[dev] = pnp_port_start(pdev, 1); 197262306a36Sopenharmony_ci mpu_port[dev] = pnp_port_start(pdev, 2); 197362306a36Sopenharmony_ci dma1[dev] = pnp_dma(pdev, 0); 197462306a36Sopenharmony_ci dma2[dev] = pnp_dma(pdev, 1); 197562306a36Sopenharmony_ci irq[dev] = pnp_irq(pdev, 0); 197662306a36Sopenharmony_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]); 197762306a36Sopenharmony_ci snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); 197862306a36Sopenharmony_ci return 0; 197962306a36Sopenharmony_ci} 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_cistatic int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip, 198262306a36Sopenharmony_ci struct pnp_dev *pdev) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci chip->dev = pdev; 198562306a36Sopenharmony_ci if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0) 198662306a36Sopenharmony_ci return -EBUSY; 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_audiodrive_pnpids[] = { 199162306a36Sopenharmony_ci /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ 199262306a36Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } }, 199362306a36Sopenharmony_ci /* ESS 1868 (integrated on Maxisound Cards) */ 199462306a36Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS8601" }, { "ESS8600" } } }, 199562306a36Sopenharmony_ci /* ESS 1868 (integrated on Maxisound Cards) */ 199662306a36Sopenharmony_ci { .id = "ESS1868", .devs = { { "ESS8611" }, { "ESS8610" } } }, 199762306a36Sopenharmony_ci /* ESS ES1869 Plug and Play AudioDrive */ 199862306a36Sopenharmony_ci { .id = "ESS0003", .devs = { { "ESS1869" }, { "ESS0006" } } }, 199962306a36Sopenharmony_ci /* ESS 1869 */ 200062306a36Sopenharmony_ci { .id = "ESS1869", .devs = { { "ESS1869" }, { "ESS0006" } } }, 200162306a36Sopenharmony_ci /* ESS 1878 */ 200262306a36Sopenharmony_ci { .id = "ESS1878", .devs = { { "ESS1878" }, { "ESS0004" } } }, 200362306a36Sopenharmony_ci /* ESS 1879 */ 200462306a36Sopenharmony_ci { .id = "ESS1879", .devs = { { "ESS1879" }, { "ESS0009" } } }, 200562306a36Sopenharmony_ci /* --- */ 200662306a36Sopenharmony_ci { .id = "" } /* end */ 200762306a36Sopenharmony_ci}; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cistatic int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip, 201262306a36Sopenharmony_ci struct pnp_card_link *card, 201362306a36Sopenharmony_ci const struct pnp_card_device_id *id) 201462306a36Sopenharmony_ci{ 201562306a36Sopenharmony_ci chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL); 201662306a36Sopenharmony_ci if (chip->dev == NULL) 201762306a36Sopenharmony_ci return -EBUSY; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL); 202062306a36Sopenharmony_ci if (chip->devc == NULL) 202162306a36Sopenharmony_ci return -EBUSY; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci /* Control port initialization */ 202462306a36Sopenharmony_ci if (pnp_activate_dev(chip->devc) < 0) { 202562306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); 202662306a36Sopenharmony_ci return -EAGAIN; 202762306a36Sopenharmony_ci } 202862306a36Sopenharmony_ci snd_printdd("pnp: port=0x%llx\n", 202962306a36Sopenharmony_ci (unsigned long long)pnp_port_start(chip->devc, 0)); 203062306a36Sopenharmony_ci if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0) 203162306a36Sopenharmony_ci return -EBUSY; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci return 0; 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci#ifdef CONFIG_PNP 203862306a36Sopenharmony_ci#define is_isapnp_selected(dev) isapnp[dev] 203962306a36Sopenharmony_ci#else 204062306a36Sopenharmony_ci#define is_isapnp_selected(dev) 0 204162306a36Sopenharmony_ci#endif 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_cistatic int snd_es18xx_card_new(struct device *pdev, int dev, 204462306a36Sopenharmony_ci struct snd_card **cardp) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci return snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, 204762306a36Sopenharmony_ci sizeof(struct snd_es18xx), cardp); 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic int snd_audiodrive_probe(struct snd_card *card, int dev) 205162306a36Sopenharmony_ci{ 205262306a36Sopenharmony_ci struct snd_es18xx *chip = card->private_data; 205362306a36Sopenharmony_ci struct snd_opl3 *opl3; 205462306a36Sopenharmony_ci int err; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci err = snd_es18xx_new_device(card, 205762306a36Sopenharmony_ci port[dev], mpu_port[dev], fm_port[dev], 205862306a36Sopenharmony_ci irq[dev], dma1[dev], dma2[dev]); 205962306a36Sopenharmony_ci if (err < 0) 206062306a36Sopenharmony_ci return err; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci sprintf(card->driver, "ES%x", chip->version); 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); 206562306a36Sopenharmony_ci if (dma1[dev] != dma2[dev]) 206662306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", 206762306a36Sopenharmony_ci card->shortname, 206862306a36Sopenharmony_ci chip->port, 206962306a36Sopenharmony_ci irq[dev], dma1[dev], dma2[dev]); 207062306a36Sopenharmony_ci else 207162306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", 207262306a36Sopenharmony_ci card->shortname, 207362306a36Sopenharmony_ci chip->port, 207462306a36Sopenharmony_ci irq[dev], dma1[dev]); 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci err = snd_es18xx_pcm(card, 0); 207762306a36Sopenharmony_ci if (err < 0) 207862306a36Sopenharmony_ci return err; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci err = snd_es18xx_mixer(card); 208162306a36Sopenharmony_ci if (err < 0) 208262306a36Sopenharmony_ci return err; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { 208562306a36Sopenharmony_ci if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, 208662306a36Sopenharmony_ci OPL3_HW_OPL3, 0, &opl3) < 0) { 208762306a36Sopenharmony_ci snd_printk(KERN_WARNING PFX 208862306a36Sopenharmony_ci "opl3 not detected at 0x%lx\n", 208962306a36Sopenharmony_ci fm_port[dev]); 209062306a36Sopenharmony_ci } else { 209162306a36Sopenharmony_ci err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 209262306a36Sopenharmony_ci if (err < 0) 209362306a36Sopenharmony_ci return err; 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { 209862306a36Sopenharmony_ci err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, 209962306a36Sopenharmony_ci mpu_port[dev], MPU401_INFO_IRQ_HOOK, 210062306a36Sopenharmony_ci -1, &chip->rmidi); 210162306a36Sopenharmony_ci if (err < 0) 210262306a36Sopenharmony_ci return err; 210362306a36Sopenharmony_ci } 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci return snd_card_register(card); 210662306a36Sopenharmony_ci} 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_cistatic int snd_es18xx_isa_match(struct device *pdev, unsigned int dev) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci return enable[dev] && !is_isapnp_selected(dev); 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic int snd_es18xx_isa_probe1(int dev, struct device *devptr) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci struct snd_card *card; 211662306a36Sopenharmony_ci int err; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci err = snd_es18xx_card_new(devptr, dev, &card); 211962306a36Sopenharmony_ci if (err < 0) 212062306a36Sopenharmony_ci return err; 212162306a36Sopenharmony_ci err = snd_audiodrive_probe(card, dev); 212262306a36Sopenharmony_ci if (err < 0) 212362306a36Sopenharmony_ci return err; 212462306a36Sopenharmony_ci dev_set_drvdata(devptr, card); 212562306a36Sopenharmony_ci return 0; 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev) 212962306a36Sopenharmony_ci{ 213062306a36Sopenharmony_ci int err; 213162306a36Sopenharmony_ci static const int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; 213262306a36Sopenharmony_ci static const int possible_dmas[] = {1, 0, 3, 5, -1}; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (irq[dev] == SNDRV_AUTO_IRQ) { 213562306a36Sopenharmony_ci irq[dev] = snd_legacy_find_free_irq(possible_irqs); 213662306a36Sopenharmony_ci if (irq[dev] < 0) { 213762306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 213862306a36Sopenharmony_ci return -EBUSY; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci if (dma1[dev] == SNDRV_AUTO_DMA) { 214262306a36Sopenharmony_ci dma1[dev] = snd_legacy_find_free_dma(possible_dmas); 214362306a36Sopenharmony_ci if (dma1[dev] < 0) { 214462306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA1\n"); 214562306a36Sopenharmony_ci return -EBUSY; 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci if (dma2[dev] == SNDRV_AUTO_DMA) { 214962306a36Sopenharmony_ci dma2[dev] = snd_legacy_find_free_dma(possible_dmas); 215062306a36Sopenharmony_ci if (dma2[dev] < 0) { 215162306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA2\n"); 215262306a36Sopenharmony_ci return -EBUSY; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (port[dev] != SNDRV_AUTO_PORT) { 215762306a36Sopenharmony_ci return snd_es18xx_isa_probe1(dev, pdev); 215862306a36Sopenharmony_ci } else { 215962306a36Sopenharmony_ci static const unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280}; 216062306a36Sopenharmony_ci int i; 216162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(possible_ports); i++) { 216262306a36Sopenharmony_ci port[dev] = possible_ports[i]; 216362306a36Sopenharmony_ci err = snd_es18xx_isa_probe1(dev, pdev); 216462306a36Sopenharmony_ci if (! err) 216562306a36Sopenharmony_ci return 0; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci return err; 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci} 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci#ifdef CONFIG_PM 217262306a36Sopenharmony_cistatic int snd_es18xx_isa_suspend(struct device *dev, unsigned int n, 217362306a36Sopenharmony_ci pm_message_t state) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci return snd_es18xx_suspend(dev_get_drvdata(dev), state); 217662306a36Sopenharmony_ci} 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_cistatic int snd_es18xx_isa_resume(struct device *dev, unsigned int n) 217962306a36Sopenharmony_ci{ 218062306a36Sopenharmony_ci return snd_es18xx_resume(dev_get_drvdata(dev)); 218162306a36Sopenharmony_ci} 218262306a36Sopenharmony_ci#endif 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci#define DEV_NAME "es18xx" 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic struct isa_driver snd_es18xx_isa_driver = { 218762306a36Sopenharmony_ci .match = snd_es18xx_isa_match, 218862306a36Sopenharmony_ci .probe = snd_es18xx_isa_probe, 218962306a36Sopenharmony_ci#ifdef CONFIG_PM 219062306a36Sopenharmony_ci .suspend = snd_es18xx_isa_suspend, 219162306a36Sopenharmony_ci .resume = snd_es18xx_isa_resume, 219262306a36Sopenharmony_ci#endif 219362306a36Sopenharmony_ci .driver = { 219462306a36Sopenharmony_ci .name = DEV_NAME 219562306a36Sopenharmony_ci }, 219662306a36Sopenharmony_ci}; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci#ifdef CONFIG_PNP 220062306a36Sopenharmony_cistatic int snd_audiodrive_pnp_detect(struct pnp_dev *pdev, 220162306a36Sopenharmony_ci const struct pnp_device_id *id) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci static int dev; 220462306a36Sopenharmony_ci int err; 220562306a36Sopenharmony_ci struct snd_card *card; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci if (pnp_device_is_isapnp(pdev)) 220862306a36Sopenharmony_ci return -ENOENT; /* we have another procedure - card */ 220962306a36Sopenharmony_ci for (; dev < SNDRV_CARDS; dev++) { 221062306a36Sopenharmony_ci if (enable[dev] && isapnp[dev]) 221162306a36Sopenharmony_ci break; 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 221462306a36Sopenharmony_ci return -ENODEV; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci err = snd_es18xx_card_new(&pdev->dev, dev, &card); 221762306a36Sopenharmony_ci if (err < 0) 221862306a36Sopenharmony_ci return err; 221962306a36Sopenharmony_ci err = snd_audiodrive_pnp(dev, card->private_data, pdev); 222062306a36Sopenharmony_ci if (err < 0) 222162306a36Sopenharmony_ci return err; 222262306a36Sopenharmony_ci err = snd_audiodrive_probe(card, dev); 222362306a36Sopenharmony_ci if (err < 0) 222462306a36Sopenharmony_ci return err; 222562306a36Sopenharmony_ci pnp_set_drvdata(pdev, card); 222662306a36Sopenharmony_ci dev++; 222762306a36Sopenharmony_ci return 0; 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci#ifdef CONFIG_PM 223162306a36Sopenharmony_cistatic int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci return snd_es18xx_suspend(pnp_get_drvdata(pdev), state); 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_cistatic int snd_audiodrive_pnp_resume(struct pnp_dev *pdev) 223662306a36Sopenharmony_ci{ 223762306a36Sopenharmony_ci return snd_es18xx_resume(pnp_get_drvdata(pdev)); 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci#endif 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_cistatic struct pnp_driver es18xx_pnp_driver = { 224262306a36Sopenharmony_ci .name = "es18xx-pnpbios", 224362306a36Sopenharmony_ci .id_table = snd_audiodrive_pnpbiosids, 224462306a36Sopenharmony_ci .probe = snd_audiodrive_pnp_detect, 224562306a36Sopenharmony_ci#ifdef CONFIG_PM 224662306a36Sopenharmony_ci .suspend = snd_audiodrive_pnp_suspend, 224762306a36Sopenharmony_ci .resume = snd_audiodrive_pnp_resume, 224862306a36Sopenharmony_ci#endif 224962306a36Sopenharmony_ci}; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard, 225262306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci static int dev; 225562306a36Sopenharmony_ci struct snd_card *card; 225662306a36Sopenharmony_ci int res; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci for ( ; dev < SNDRV_CARDS; dev++) { 225962306a36Sopenharmony_ci if (enable[dev] && isapnp[dev]) 226062306a36Sopenharmony_ci break; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 226362306a36Sopenharmony_ci return -ENODEV; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci res = snd_es18xx_card_new(&pcard->card->dev, dev, &card); 226662306a36Sopenharmony_ci if (res < 0) 226762306a36Sopenharmony_ci return res; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid); 227062306a36Sopenharmony_ci if (res < 0) 227162306a36Sopenharmony_ci return res; 227262306a36Sopenharmony_ci res = snd_audiodrive_probe(card, dev); 227362306a36Sopenharmony_ci if (res < 0) 227462306a36Sopenharmony_ci return res; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 227762306a36Sopenharmony_ci dev++; 227862306a36Sopenharmony_ci return 0; 227962306a36Sopenharmony_ci} 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci#ifdef CONFIG_PM 228262306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state) 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state); 228562306a36Sopenharmony_ci} 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci return snd_es18xx_resume(pnp_get_card_drvdata(pcard)); 229062306a36Sopenharmony_ci} 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci#endif 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_cistatic struct pnp_card_driver es18xx_pnpc_driver = { 229562306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 229662306a36Sopenharmony_ci .name = "es18xx", 229762306a36Sopenharmony_ci .id_table = snd_audiodrive_pnpids, 229862306a36Sopenharmony_ci .probe = snd_audiodrive_pnpc_detect, 229962306a36Sopenharmony_ci#ifdef CONFIG_PM 230062306a36Sopenharmony_ci .suspend = snd_audiodrive_pnpc_suspend, 230162306a36Sopenharmony_ci .resume = snd_audiodrive_pnpc_resume, 230262306a36Sopenharmony_ci#endif 230362306a36Sopenharmony_ci}; 230462306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_cistatic int __init alsa_card_es18xx_init(void) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci int err; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci err = isa_register_driver(&snd_es18xx_isa_driver, SNDRV_CARDS); 231162306a36Sopenharmony_ci#ifdef CONFIG_PNP 231262306a36Sopenharmony_ci if (!err) 231362306a36Sopenharmony_ci isa_registered = 1; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci err = pnp_register_driver(&es18xx_pnp_driver); 231662306a36Sopenharmony_ci if (!err) 231762306a36Sopenharmony_ci pnp_registered = 1; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci err = pnp_register_card_driver(&es18xx_pnpc_driver); 232062306a36Sopenharmony_ci if (!err) 232162306a36Sopenharmony_ci pnpc_registered = 1; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci if (isa_registered || pnp_registered) 232462306a36Sopenharmony_ci err = 0; 232562306a36Sopenharmony_ci#endif 232662306a36Sopenharmony_ci return err; 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic void __exit alsa_card_es18xx_exit(void) 233062306a36Sopenharmony_ci{ 233162306a36Sopenharmony_ci#ifdef CONFIG_PNP 233262306a36Sopenharmony_ci if (pnpc_registered) 233362306a36Sopenharmony_ci pnp_unregister_card_driver(&es18xx_pnpc_driver); 233462306a36Sopenharmony_ci if (pnp_registered) 233562306a36Sopenharmony_ci pnp_unregister_driver(&es18xx_pnp_driver); 233662306a36Sopenharmony_ci if (isa_registered) 233762306a36Sopenharmony_ci#endif 233862306a36Sopenharmony_ci isa_unregister_driver(&snd_es18xx_isa_driver); 233962306a36Sopenharmony_ci} 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_cimodule_init(alsa_card_es18xx_init) 234262306a36Sopenharmony_cimodule_exit(alsa_card_es18xx_exit) 2343