linux/sound/isa/es18xx.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Driver for generic ESS AudioDrive ES18xx soundcards
 *  Copyright (c) by Christian Fischbach <[email protected]>
 *  Copyright (c) by Abramo Bagnara <[email protected]>
 */
/* GENERAL NOTES:
 *
 * BUGS:
 * - There are pops (we can't delay in trigger function, cause midlevel 
 *   often need to trigger down and then up very quickly).
 *   Any ideas?
 * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it.
 */

/*
 * ES1868  NOTES:
 * - The chip has one half duplex pcm (with very limited full duplex support).
 *
 * - Duplex stereophonic sound is impossible.
 * - Record and playback must share the same frequency rate.
 *
 * - The driver use dma2 for playback and dma1 for capture.
 */

/*
 * ES1869 NOTES:
 *
 * - there are a first full duplex pcm and a second playback only pcm
 *   (incompatible with first pcm capture)
 * 
 * - there is support for the capture volume and ESS Spatializer 3D effect.
 *
 * - contrarily to some pages in DS_1869.PDF the rates can be set
 *   independently.
 *
 * - Zoom Video is implemented by sharing the FM DAC, thus the user can
 *   have either FM playback or Video playback but not both simultaneously.
 *   The Video Playback Switch mixer control toggles this choice.
 *
 * BUGS:
 *
 * - There is a major trouble I noted:
 *
 *   using both channel for playback stereo 16 bit samples at 44100 Hz
 *   the second pcm (Audio1) DMA slows down irregularly and sound is garbled.
 *   
 *   The same happens using Audio1 for captureing.
 *
 *   The Windows driver does not suffer of this (although it use Audio1
 *   only for captureing). I'm unable to discover why.
 *
 */

/*
 * ES1879 NOTES:
 * - When Zoom Video is enabled (reg 0x71 bit 6 toggled on) the PCM playback
 *   seems to be effected (speaker_test plays a lower frequency). Can't find
 *   anything in the datasheet to account for this, so a Video Playback Switch
 *   control has been included to allow ZV to be enabled only when necessary.
 *   Then again on at least one test system the 0x71 bit 6 enable bit is not 
 *   needed for ZV, so maybe the datasheet is entirely wrong here.
 */
 
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/isapnp.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/dma.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>

#define PFX

struct snd_es18xx {};

#define AUDIO1_IRQ
#define AUDIO2_IRQ
#define HWV_IRQ
#define MPU_IRQ

#define ES18XX_PCM2
#define ES18XX_SPATIALIZER
#define ES18XX_RECMIX
#define ES18XX_DUPLEX_MONO
#define ES18XX_DUPLEX_SAME
#define ES18XX_NEW_RATE
#define ES18XX_AUXB
#define ES18XX_HWV
#define ES18XX_MONO
#define ES18XX_I2S
#define ES18XX_MUTEREC
#define ES18XX_CONTROL
#define ES18XX_GPO_2BIT

/* Power Management */
#define ES18XX_PM
#define ES18XX_PM_GPO0
#define ES18XX_PM_GPO1
#define ES18XX_PM_PDR
#define ES18XX_PM_ANA
#define ES18XX_PM_FM
#define ES18XX_PM_SUS

/* Lowlevel */

#define DAC1
#define ADC1
#define DAC2
#define MILLISECOND

static int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val)
{}

static int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
{}

#undef REG_DEBUG

static int snd_es18xx_write(struct snd_es18xx *chip,
			    unsigned char reg, unsigned char data)
{}

static int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg)
{}

/* Return old value */
static int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg,
			   unsigned char mask, unsigned char val)
{}

static inline void snd_es18xx_mixer_write(struct snd_es18xx *chip,
			    unsigned char reg, unsigned char data)
{}

static inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg)
{}

/* Return old value */
static inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char reg,
					unsigned char mask, unsigned char val)
{}

static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned char reg,
					    unsigned char mask)
{}


static int snd_es18xx_reset(struct snd_es18xx *chip)
{}

static int snd_es18xx_reset_fifo(struct snd_es18xx *chip)
{}

static const struct snd_ratnum new_clocks[2] =;

static const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks =;

static const struct snd_ratnum old_clocks[2] =;

static const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks  =;


static void snd_es18xx_rate_set(struct snd_es18xx *chip, 
				struct snd_pcm_substream *substream,
				int mode)
{}

static int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
					 struct snd_pcm_hw_params *hw_params)
{}

static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
					struct snd_pcm_substream *substream)
{}

static int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
					struct snd_pcm_substream *substream,
					int cmd)
{}

static int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
					struct snd_pcm_hw_params *hw_params)
{}

static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
{}

static int snd_es18xx_capture_trigger(struct snd_pcm_substream *substream,
				      int cmd)
{}

static int snd_es18xx_playback2_prepare(struct snd_es18xx *chip,
					struct snd_pcm_substream *substream)
{}

static int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
					struct snd_pcm_substream *substream,
					int cmd)
{}

static int snd_es18xx_playback_prepare(struct snd_pcm_substream *substream)
{}

static int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream,
				       int cmd)
{}

static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
{}

static snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
{}

static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
{}

static const struct snd_pcm_hardware snd_es18xx_playback =;

static const struct snd_pcm_hardware snd_es18xx_capture =;

static int snd_es18xx_playback_open(struct snd_pcm_substream *substream)
{}

static int snd_es18xx_capture_open(struct snd_pcm_substream *substream)
{}

static int snd_es18xx_playback_close(struct snd_pcm_substream *substream)
{}

static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
{}

/*
 *  MIXER part
 */

/* Record source mux routines:
 * Depending on the chipset this mux switches between 4, 5, or 8 possible inputs.
 * bit table for the 4/5 source mux:
 * reg 1C:
 *  b2 b1 b0   muxSource
 *   x  0  x   microphone
 *   0  1  x   CD
 *   1  1  0   line
 *   1  1  1   mixer
 * if it's "mixer" and it's a 5 source mux chipset then reg 7A bit 3 determines
 * either the play mixer or the capture mixer.
 *
 * "map4Source" translates from source number to reg bit pattern
 * "invMap4Source" translates from reg bit pattern to source number
 */

static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{}

static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

#define snd_es18xx_info_spatializer_enable

static int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static int snd_es18xx_put_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static int snd_es18xx_info_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{}

static int snd_es18xx_get_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

#define snd_es18xx_info_hw_switch

static int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static void snd_es18xx_hwv_free(struct snd_kcontrol *kcontrol)
{}

static int snd_es18xx_reg_bits(struct snd_es18xx *chip, unsigned char reg,
			       unsigned char mask, unsigned char val)
{}

static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg)
{}

#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags)

#define ES18XX_FL_INVERT
#define ES18XX_FL_PMPORT

static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{}

static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert)

static int snd_es18xx_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{}

static int snd_es18xx_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

static int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{}

/* Mixer controls
 * These arrays contain setup data for mixer controls.
 * 
 * The controls that are universal to all chipsets are fully initialized
 * here.
 */
static const struct snd_kcontrol_new snd_es18xx_base_controls[] =;

static const struct snd_kcontrol_new snd_es18xx_recmix_controls[] =;

/*
 * The chipset specific mixer controls
 */
static const struct snd_kcontrol_new snd_es18xx_opt_speaker =;

static const struct snd_kcontrol_new snd_es18xx_opt_1869[] =;

static const struct snd_kcontrol_new snd_es18xx_opt_1878 =;

static const struct snd_kcontrol_new snd_es18xx_opt_1879[] =;

static const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] =;

static const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] =;

static const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] =;

static const struct snd_kcontrol_new snd_es18xx_micpre1_control =;

static const struct snd_kcontrol_new snd_es18xx_micpre2_control =;

static const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] =;

static const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] =;

static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
{}

static void snd_es18xx_config_write(struct snd_es18xx *chip,
				    unsigned char reg, unsigned char data)
{}

static int snd_es18xx_initialize(struct snd_es18xx *chip,
				 unsigned long mpu_port,
				 unsigned long fm_port)
{}

static int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip)
{}

static int snd_es18xx_probe(struct snd_card *card,
			    struct snd_es18xx *chip,
			    unsigned long mpu_port,
			    unsigned long fm_port)
{}

static const struct snd_pcm_ops snd_es18xx_playback_ops =;

static const struct snd_pcm_ops snd_es18xx_capture_ops =;

static int snd_es18xx_pcm(struct snd_card *card, int device)
{}

/* Power Management support functions */
#ifdef CONFIG_PM
static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
{}

static int snd_es18xx_resume(struct snd_card *card)
{}
#endif /* CONFIG_PM */

static int snd_es18xx_new_device(struct snd_card *card,
				 unsigned long port,
				 unsigned long mpu_port,
				 unsigned long fm_port,
				 int irq, int dma1, int dma2)
{}

static int snd_es18xx_mixer(struct snd_card *card)
{}
       

/* Card level */

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();

static int index[SNDRV_CARDS] =;	/* Index 0-MAX */
static char *id[SNDRV_CARDS] =;	/* ID for this card */
static bool enable[SNDRV_CARDS] =; /* Enable this card */
#ifdef CONFIG_PNP
static bool isapnp[SNDRV_CARDS] =;
#endif
static long port[SNDRV_CARDS] =;	/* 0x220,0x240,0x260,0x280 */
#ifndef CONFIG_PNP
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
#else
static long mpu_port[SNDRV_CARDS] =;
#endif
static long fm_port[SNDRV_CARDS] =;
static int irq[SNDRV_CARDS] =;	/* 5,7,9,10 */
static int dma1[SNDRV_CARDS] =;	/* 0,1,3 */
static int dma2[SNDRV_CARDS] =;	/* 0,1,3 */

module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();
#ifdef CONFIG_PNP
module_param_array();
MODULE_PARM_DESC();
#endif
module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC();
module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC();
module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC();
module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC();
module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC();
module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC();

#ifdef CONFIG_PNP
static int isa_registered;
static int pnp_registered;
static int pnpc_registered;

static const struct pnp_device_id snd_audiodrive_pnpbiosids[] =;

MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);

/* PnP main device initialization */
static int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
{}

static int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
			      struct pnp_dev *pdev)
{}

static const struct pnp_card_device_id snd_audiodrive_pnpids[] =;

MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);

static int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
			       struct pnp_card_link *card,
			       const struct pnp_card_device_id *id)
{}
#endif /* CONFIG_PNP */

#ifdef CONFIG_PNP
#define is_isapnp_selected(dev)
#else
#define is_isapnp_selected
#endif

static int snd_es18xx_card_new(struct device *pdev, int dev,
			       struct snd_card **cardp)
{}

static int snd_audiodrive_probe(struct snd_card *card, int dev)
{}

static int snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
{}

static int snd_es18xx_isa_probe1(int dev, struct device *devptr)
{}

static int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
{}

#ifdef CONFIG_PM
static int snd_es18xx_isa_suspend(struct device *dev, unsigned int n,
				  pm_message_t state)
{}

static int snd_es18xx_isa_resume(struct device *dev, unsigned int n)
{}
#endif

#define DEV_NAME

static struct isa_driver snd_es18xx_isa_driver =;


#ifdef CONFIG_PNP
static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
				     const struct pnp_device_id *id)
{}

#ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{}
static int snd_audiodrive_pnp_resume(struct pnp_dev *pdev)
{}
#endif

static struct pnp_driver es18xx_pnp_driver =;

static int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
				      const struct pnp_card_device_id *pid)
{}

#ifdef CONFIG_PM
static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{}

static int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard)
{}

#endif

static struct pnp_card_driver es18xx_pnpc_driver =;
#endif /* CONFIG_PNP */

static int __init alsa_card_es18xx_init(void)
{}

static void __exit alsa_card_es18xx_exit(void)
{}

module_init()
module_exit()