linux/sound/pci/es1938.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
 *  Copyright (c) by Jaromir Koutek <[email protected]>,
 *                   Jaroslav Kysela <[email protected]>,
 *                   Thomas Sailer <[email protected]>,
 *                   Abramo Bagnara <[email protected]>,
 *                   Markus Gruber <[email protected]>
 * 
 * Rewritten from sonicvibes.c source.
 *
 *  TODO:
 *    Rewrite better spinlocks
 */

/*
  NOTES:
  - Capture data is written unaligned starting from dma_base + 1 so I need to
    disable mmap and to add a copy callback.
  - After several cycle of the following:
    while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done
    a "playback write error (DMA or IRQ trouble?)" may happen.
    This is due to playback interrupts not generated.
    I suspect a timing issue.
  - Sometimes the interrupt handler is invoked wrongly during playback.
    This generates some harmless "Unexpected hw_pointer: wrong interrupt
    acknowledge".
    I've seen that using small period sizes.
    Reproducible with:
    mpg123 test.mp3 &
    hdparm -t -T /dev/hda
*/


#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/opl3.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
#include <sound/tlv.h>

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

#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK
#endif

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 */

module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();

#define SLIO_REG(chip, x)

#define SLDM_REG(chip, x)

#define SLSB_REG(chip, x)

#define SL_PCI_LEGACYCONTROL
#define SL_PCI_CONFIG
#define SL_PCI_DDMACONTROL

#define ESSIO_REG_AUDIO2DMAADDR
#define ESSIO_REG_AUDIO2DMACOUNT
#define ESSIO_REG_AUDIO2MODE
#define ESSIO_REG_IRQCONTROL

#define ESSDM_REG_DMAADDR
#define ESSDM_REG_DMACOUNT
#define ESSDM_REG_DMACOMMAND
#define ESSDM_REG_DMASTATUS
#define ESSDM_REG_DMAMODE
#define ESSDM_REG_DMACLEAR
#define ESSDM_REG_DMAMASK

#define ESSSB_REG_FMLOWADDR
#define ESSSB_REG_FMHIGHADDR
#define ESSSB_REG_MIXERADDR
#define ESSSB_REG_MIXERDATA

#define ESSSB_IREG_AUDIO1
#define ESSSB_IREG_MICMIX
#define ESSSB_IREG_RECSRC
#define ESSSB_IREG_MASTER
#define ESSSB_IREG_FM
#define ESSSB_IREG_AUXACD
#define ESSSB_IREG_AUXB
#define ESSSB_IREG_PCSPEAKER
#define ESSSB_IREG_LINE
#define ESSSB_IREG_SPATCONTROL
#define ESSSB_IREG_SPATLEVEL
#define ESSSB_IREG_MASTER_LEFT
#define ESSSB_IREG_MASTER_RIGHT
#define ESSSB_IREG_MPU401CONTROL
#define ESSSB_IREG_MICMIXRECORD
#define ESSSB_IREG_AUDIO2RECORD
#define ESSSB_IREG_AUXACDRECORD
#define ESSSB_IREG_FMRECORD
#define ESSSB_IREG_AUXBRECORD
#define ESSSB_IREG_MONO
#define ESSSB_IREG_LINERECORD
#define ESSSB_IREG_MONORECORD
#define ESSSB_IREG_AUDIO2SAMPLE
#define ESSSB_IREG_AUDIO2MODE
#define ESSSB_IREG_AUDIO2FILTER
#define ESSSB_IREG_AUDIO2TCOUNTL
#define ESSSB_IREG_AUDIO2TCOUNTH
#define ESSSB_IREG_AUDIO2CONTROL1
#define ESSSB_IREG_AUDIO2CONTROL2
#define ESSSB_IREG_AUDIO2

#define ESSSB_REG_RESET

#define ESSSB_REG_READDATA
#define ESSSB_REG_WRITEDATA
#define ESSSB_REG_READSTATUS

#define ESSSB_REG_STATUS

#define ESS_CMD_EXTSAMPLERATE
#define ESS_CMD_FILTERDIV
#define ESS_CMD_DMACNTRELOADL
#define ESS_CMD_DMACNTRELOADH
#define ESS_CMD_ANALOGCONTROL
#define ESS_CMD_IRQCONTROL
#define ESS_CMD_DRQCONTROL
#define ESS_CMD_RECLEVEL
#define ESS_CMD_SETFORMAT
#define ESS_CMD_SETFORMAT2
#define ESS_CMD_DMACONTROL
#define ESS_CMD_DMATYPE
#define ESS_CMD_OFFSETLEFT	
#define ESS_CMD_OFFSETRIGHT
#define ESS_CMD_READREG
#define ESS_CMD_ENABLEEXT
#define ESS_CMD_PAUSEDMA
#define ESS_CMD_ENABLEAUDIO1
#define ESS_CMD_STOPAUDIO1
#define ESS_CMD_AUDIO1STATUS
#define ESS_CMD_CONTDMA
#define ESS_CMD_TESTIRQ

#define ESS_RECSRC_MIC
#define ESS_RECSRC_AUXACD
#define ESS_RECSRC_AUXB
#define ESS_RECSRC_LINE
#define ESS_RECSRC_NONE

#define DAC1
#define ADC1
#define DAC2

/*

 */

#define SAVED_REG_SIZE

struct es1938 {};

static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);

static const struct pci_device_id snd_es1938_ids[] =;

MODULE_DEVICE_TABLE(pci, snd_es1938_ids);

#define RESET_LOOP_TIMEOUT
#define WRITE_LOOP_TIMEOUT
#define GET_LOOP_TIMEOUT

/* -----------------------------------------------------------------
 * Write to a mixer register
 * -----------------------------------------------------------------*/
static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{}

/* -----------------------------------------------------------------
 * Read from a mixer register
 * -----------------------------------------------------------------*/
static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
{}

/* -----------------------------------------------------------------
 * Write to some bits of a mixer register (return old value)
 * -----------------------------------------------------------------*/
static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
				 unsigned char mask, unsigned char val)
{}

/* -----------------------------------------------------------------
 * Write command to Controller Registers
 * -----------------------------------------------------------------*/
static void snd_es1938_write_cmd(struct es1938 *chip, unsigned char cmd)
{}

/* -----------------------------------------------------------------
 * Read the Read Data Buffer
 * -----------------------------------------------------------------*/
static int snd_es1938_get_byte(struct es1938 *chip)
{}

/* -----------------------------------------------------------------
 * Write value cmd register
 * -----------------------------------------------------------------*/
static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{}

/* -----------------------------------------------------------------
 * Read data from cmd register and return it
 * -----------------------------------------------------------------*/
static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
{}

/* -----------------------------------------------------------------
 * Write data to cmd register and return old value
 * -----------------------------------------------------------------*/
static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char mask,
			   unsigned char val)
{}

/* --------------------------------------------------------------------
 * Reset the chip
 * --------------------------------------------------------------------*/
static void snd_es1938_reset(struct es1938 *chip)
{}

/* --------------------------------------------------------------------
 * Reset the FIFOs
 * --------------------------------------------------------------------*/
static void snd_es1938_reset_fifo(struct es1938 *chip)
{}

static const struct snd_ratnum clocks[2] =;

static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks =;


static void snd_es1938_rate_set(struct es1938 *chip, 
				struct snd_pcm_substream *substream,
				int mode)
{}

/* --------------------------------------------------------------------
 * Configure Solo1 builtin DMA Controller
 * --------------------------------------------------------------------*/

static void snd_es1938_playback1_setdma(struct es1938 *chip)
{}

static void snd_es1938_playback2_setdma(struct es1938 *chip)
{}

static void snd_es1938_capture_setdma(struct es1938 *chip)
{}

/* ----------------------------------------------------------------------
 *
 *                           *** PCM part ***
 */

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

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

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

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

/* --------------------------------------------------------------------
 * First channel for Extended Mode Audio 1 ADC Operation
 * --------------------------------------------------------------------*/
static int snd_es1938_capture_prepare(struct snd_pcm_substream *substream)
{}


/* ------------------------------------------------------------------------------
 * Second Audio channel DAC Operation
 * ------------------------------------------------------------------------------*/
static int snd_es1938_playback1_prepare(struct snd_pcm_substream *substream)
{}

static int snd_es1938_playback2_prepare(struct snd_pcm_substream *substream)
{}

static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
{}

/* during the incrementing of dma counters the DMA register reads sometimes
   returns garbage. To ensure a valid hw pointer, the following checks which
   should be very unlikely to fail are used:
   - is the current DMA address in the valid DMA range ?
   - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
   One can argue this could differ by one byte depending on which register is
   updated first, so the implementation below allows for that.
*/
static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
{}

static snd_pcm_uframes_t snd_es1938_playback1_pointer(struct snd_pcm_substream *substream)
{}

static snd_pcm_uframes_t snd_es1938_playback2_pointer(struct snd_pcm_substream *substream)
{}

static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *substream)
{}

static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
				   int channel, unsigned long pos,
				   struct iov_iter *dst, unsigned long count)
{}

/* ----------------------------------------------------------------------
 * Audio1 Capture (ADC)
 * ----------------------------------------------------------------------*/
static const struct snd_pcm_hardware snd_es1938_capture =;

/* -----------------------------------------------------------------------
 * Audio2 Playback (DAC)
 * -----------------------------------------------------------------------*/
static const struct snd_pcm_hardware snd_es1938_playback =;

static int snd_es1938_capture_open(struct snd_pcm_substream *substream)
{}

static int snd_es1938_playback_open(struct snd_pcm_substream *substream)
{}

static int snd_es1938_capture_close(struct snd_pcm_substream *substream)
{}

static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
{}

static const struct snd_pcm_ops snd_es1938_playback_ops =;

static const struct snd_pcm_ops snd_es1938_capture_ops =;

static int snd_es1938_new_pcm(struct es1938 *chip, int device)
{}

/* -------------------------------------------------------------------
 * 
 *                       *** Mixer part ***
 */

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

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

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

#define snd_es1938_info_spatializer_enable

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

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

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

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

#define snd_es1938_info_hw_switch

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

static void snd_es1938_hwv_free(struct snd_kcontrol *kcontrol)
{}

static int snd_es1938_reg_bits(struct es1938 *chip, unsigned char reg,
			       unsigned char mask, unsigned char val)
{}

static int snd_es1938_reg_read(struct es1938 *chip, unsigned char reg)
{}

#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv)
#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert)

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

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

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

#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv)
#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert)

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

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

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

static const DECLARE_TLV_DB_RANGE(db_scale_master,
	0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
	54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
);

static const DECLARE_TLV_DB_RANGE(db_scale_audio1,
	0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
	8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
);

static const DECLARE_TLV_DB_RANGE(db_scale_audio2,
	0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
	8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
);

static const DECLARE_TLV_DB_RANGE(db_scale_mic,
	0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
	8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
);

static const DECLARE_TLV_DB_RANGE(db_scale_line,
	0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
	8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
);

static const DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);

static const struct snd_kcontrol_new snd_es1938_controls[] =;


/* ---------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------- */

/*
 * initialize the chip - used by resume callback, too
 */
static void snd_es1938_chip_init(struct es1938 *chip)
{}

/*
 * PM support
 */

static const unsigned char saved_regs[SAVED_REG_SIZE+1] =;


static int es1938_suspend(struct device *dev)
{}

static int es1938_resume(struct device *dev)
{}

static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);

#ifdef SUPPORT_JOYSTICK
static int snd_es1938_create_gameport(struct es1938 *chip)
{}

static void snd_es1938_free_gameport(struct es1938 *chip)
{}
#else
static inline int snd_es1938_create_gameport(struct es1938 *chip) { return -ENOSYS; }
static inline void snd_es1938_free_gameport(struct es1938 *chip) { }
#endif /* SUPPORT_JOYSTICK */

static void snd_es1938_free(struct snd_card *card)
{}

static int snd_es1938_create(struct snd_card *card,
			     struct pci_dev *pci)
{}

/* --------------------------------------------------------------------
 * Interrupt handler
 * -------------------------------------------------------------------- */
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
{}

#define ES1938_DMA_SIZE

static int snd_es1938_mixer(struct es1938 *chip)
{}
       

static int __snd_es1938_probe(struct pci_dev *pci,
			      const struct pci_device_id *pci_id)
{}

static int snd_es1938_probe(struct pci_dev *pci,
			    const struct pci_device_id *pci_id)
{}

static struct pci_driver es1938_driver =;

module_pci_driver();