cregit-Linux how code gets into the kernel

Release 4.13 sound/pci/es1968.c

Directory: sound/pci
/*
 *  Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
 *  Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
 *                   Takashi Iwai <tiwai@suse.de>
 *                  
 *  Most of the driver code comes from Zach Brown(zab@redhat.com)
 *      Alan Cox OSS Driver
 *  Rewritted from card-es1938.c source.
 *
 *  TODO:
 *   Perhaps Synth
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *
 *  Notes from Zach Brown about the driver code
 *
 *  Hardware Description
 *
 *      A working Maestro setup contains the Maestro chip wired to a 
 *      codec or 2.  In the Maestro we have the APUs, the ASSP, and the
 *      Wavecache.  The APUs can be though of as virtual audio routing
 *      channels.  They can take data from a number of sources and perform
 *      basic encodings of the data.  The wavecache is a storehouse for
 *      PCM data.  Typically it deals with PCI and interracts with the
 *      APUs.  The ASSP is a wacky DSP like device that ESS is loth
 *      to release docs on.  Thankfully it isn't required on the Maestro
 *      until you start doing insane things like FM emulation and surround
 *      encoding.  The codecs are almost always AC-97 compliant codecs, 
 *      but it appears that early Maestros may have had PT101 (an ESS
 *      part?) wired to them.  The only real difference in the Maestro
 *      families is external goop like docking capability, memory for
 *      the ASSP, and initialization differences.
 *
 *  Driver Operation
 *
 *      We only drive the APU/Wavecache as typical DACs and drive the
 *      mixers in the codecs.  There are 64 APUs.  We assign 6 to each
 *      /dev/dsp? device.  2 channels for output, and 4 channels for
 *      input.
 *
 *      Each APU can do a number of things, but we only really use
 *      3 basic functions.  For playback we use them to convert PCM
 *      data fetched over PCI by the wavecahche into analog data that
 *      is handed to the codec.  One APU for mono, and a pair for stereo.
 *      When in stereo, the combination of smarts in the APU and Wavecache
 *      decide which wavecache gets the left or right channel.
 *
 *      For record we still use the old overly mono system.  For each in
 *      coming channel the data comes in from the codec, through a 'input'
 *      APU, through another rate converter APU, and then into memory via
 *      the wavecache and PCI.  If its stereo, we mash it back into LRLR in
 *      software.  The pass between the 2 APUs is supposedly what requires us
 *      to have a 512 byte buffer sitting around in wavecache/memory.
 *
 *      The wavecache makes our life even more fun.  First off, it can
 *      only address the first 28 bits of PCI address space, making it
 *      useless on quite a few architectures.  Secondly, its insane.
 *      It claims to fetch from 4 regions of PCI space, each 4 meg in length.
 *      But that doesn't really work.  You can only use 1 region.  So all our
 *      allocations have to be in 4meg of each other.  Booo.  Hiss.
 *      So we have a module parameter, dsps_order, that is the order of
 *      the number of dsps to provide.  All their buffer space is allocated
 *      on open time.  The sonicvibes OSS routines we inherited really want
 *      power of 2 buffers, so we have all those next to each other, then
 *      512 byte regions for the recording wavecaches.  This ends up
 *      wasting quite a bit of memory.  The only fixes I can see would be 
 *      getting a kernel allocator that could work in zones, or figuring out
 *      just how to coerce the WP into doing what we want.
 *
 *      The indirection of the various registers means we have to spinlock
 *      nearly all register accesses.  We have the main register indirection
 *      like the wave cache, maestro registers, etc.  Then we have beasts
 *      like the APU interface that is indirect registers gotten at through
 *      the main maestro indirection.  Ouch.  We spinlock around the actual
 *      ports on a per card basis.  This means spinlock activity at each IO
 *      operation, but the only IO operation clusters are in non critical 
 *      paths and it makes the code far easier to follow.  Interrupts are
 *      blocked while holding the locks because the int handler has to
 *      get at some of them :(.  The mixer interface doesn't, however.
 *      We also have an OSS state lock that is thrown around in a few
 *      places.
 */

#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/input.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/mpu401.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>

#ifdef CONFIG_SND_ES1968_RADIO
#include <media/drv-intf/tea575x.h>
#endif


#define CARD_NAME "ESS Maestro1/2"

#define DRIVER_NAME "ES1968"

MODULE_DESCRIPTION("ESS Maestro");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e},"
		"{ESS,Maestro 2},"
		"{ESS,Maestro 1},"
		"{TerraTec,DMX}}");

#if IS_REACHABLE(CONFIG_GAMEPORT)

#define SUPPORT_JOYSTICK 1
#endif


static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	
/* Index 1-MAX */

static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	
/* ID for this card */

static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	
/* Enable this card */

static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };

static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };

static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };

static int clock[SNDRV_CARDS];

static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};

static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
#ifdef SUPPORT_JOYSTICK

static bool joystick[SNDRV_CARDS];
#endif

static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
module_param_array(total_bufsize, int, NULL, 0444);
MODULE_PARM_DESC(total_bufsize, "Total buffer size in kB.");
module_param_array(pcm_substreams_p, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard.");
module_param_array(pcm_substreams_c, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard.");
module_param_array(clock, int, NULL, 0444);
MODULE_PARM_DESC(clock, "Clock on " CARD_NAME " soundcard.  (0 = auto-detect)");
module_param_array(use_pm, int, NULL, 0444);
MODULE_PARM_DESC(use_pm, "Toggle power-management.  (0 = off, 1 = on, 2 = auto)");
module_param_array(enable_mpu, int, NULL, 0444);
MODULE_PARM_DESC(enable_mpu, "Enable MPU401.  (0 = off, 1 = on, 2 = auto)");
#ifdef SUPPORT_JOYSTICK
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable joystick.");
#endif
module_param_array(radio_nr, int, NULL, 0444);
MODULE_PARM_DESC(radio_nr, "Radio device numbers");




#define NR_APUS			64

#define NR_APU_REGS		16

/* NEC Versas ? */

#define NEC_VERSA_SUBID1	0x80581033

#define NEC_VERSA_SUBID2	0x803c1033

/* Mode Flags */

#define ESS_FMT_STEREO     	0x01

#define ESS_FMT_16BIT      	0x02


#define DAC_RUNNING		1

#define ADC_RUNNING		2

/* Values for the ESM_LEGACY_AUDIO_CONTROL */


#define ESS_DISABLE_AUDIO	0x8000

#define ESS_ENABLE_SERIAL_IRQ	0x4000

#define IO_ADRESS_ALIAS		0x0020

#define MPU401_IRQ_ENABLE	0x0010

#define MPU401_IO_ENABLE	0x0008

#define GAME_IO_ENABLE		0x0004

#define FM_IO_ENABLE		0x0002

#define SB_IO_ENABLE		0x0001

/* Values for the ESM_CONFIG_A */


#define PIC_SNOOP1		0x4000

#define PIC_SNOOP2		0x2000

#define SAFEGUARD		0x0800

#define DMA_CLEAR		0x0700

#define DMA_DDMA		0x0000

#define DMA_TDMA		0x0100

#define DMA_PCPCI		0x0200

#define POST_WRITE		0x0080

#define PCI_TIMING		0x0040

#define SWAP_LR			0x0020

#define SUBTR_DECODE		0x0002

/* Values for the ESM_CONFIG_B */


#define SPDIF_CONFB		0x0100

#define HWV_CONFB		0x0080

#define DEBOUNCE		0x0040

#define GPIO_CONFB		0x0020

#define CHI_CONFB		0x0010

#define IDMA_CONFB		0x0008	
/*undoc */

#define MIDI_FIX		0x0004	
/*undoc */

#define IRQ_TO_ISA		0x0001	
/*undoc */

/* Values for Ring Bus Control B */

#define	RINGB_2CODEC_ID_MASK	0x0003

#define RINGB_DIS_VALIDATION	0x0008

#define RINGB_EN_SPDIF		0x0010

#define	RINGB_EN_2CODEC		0x0020

#define RINGB_SING_BIT_DUAL	0x0040

/* ****Port Addresses**** */

/*   Write & Read */

#define ESM_INDEX		0x02

#define ESM_DATA		0x00

/*   AC97 + RingBus */

#define ESM_AC97_INDEX		0x30

#define	ESM_AC97_DATA		0x32

#define ESM_RING_BUS_DEST	0x34

#define ESM_RING_BUS_CONTR_A	0x36

#define ESM_RING_BUS_CONTR_B	0x38

#define ESM_RING_BUS_SDO	0x3A

/*   WaveCache*/

#define WC_INDEX		0x10

#define WC_DATA			0x12

#define WC_CONTROL		0x14

/*   ASSP*/

#define ASSP_INDEX		0x80

#define ASSP_MEMORY		0x82

#define ASSP_DATA		0x84

#define ASSP_CONTROL_A		0xA2

#define ASSP_CONTROL_B		0xA4

#define ASSP_CONTROL_C		0xA6

#define ASSP_HOSTW_INDEX	0xA8

#define ASSP_HOSTW_DATA		0xAA

#define ASSP_HOSTW_IRQ		0xAC
/* Midi */

#define ESM_MPU401_PORT		0x98
/* Others */

#define ESM_PORT_HOST_IRQ	0x18


#define IDR0_DATA_PORT		0x00

#define IDR1_CRAM_POINTER	0x01

#define IDR2_CRAM_DATA		0x02

#define IDR3_WAVE_DATA		0x03

#define IDR4_WAVE_PTR_LOW	0x04

#define IDR5_WAVE_PTR_HI	0x05

#define IDR6_TIMER_CTRL		0x06

#define IDR7_WAVE_ROMRAM	0x07


#define WRITEABLE_MAP		0xEFFFFF

#define READABLE_MAP		0x64003F

/* PCI Register */


#define ESM_LEGACY_AUDIO_CONTROL 0x40

#define ESM_ACPI_COMMAND	0x54

#define ESM_CONFIG_A		0x50

#define ESM_CONFIG_B		0x52

#define ESM_DDMA		0x60

/* Bob Bits */

#define ESM_BOB_ENABLE		0x0001

#define ESM_BOB_START		0x0001

/* Host IRQ Control Bits */

#define ESM_RESET_MAESTRO	0x8000

#define ESM_RESET_DIRECTSOUND   0x4000

#define ESM_HIRQ_ClkRun		0x0100

#define ESM_HIRQ_HW_VOLUME	0x0040

#define ESM_HIRQ_HARPO		0x0030	
/* What's that? */

#define ESM_HIRQ_ASSP		0x0010

#define	ESM_HIRQ_DSIE		0x0004

#define ESM_HIRQ_MPU401		0x0002

#define ESM_HIRQ_SB		0x0001

/* Host IRQ Status Bits */

#define ESM_MPU401_IRQ		0x02

#define ESM_SB_IRQ		0x01

#define ESM_SOUND_IRQ		0x04

#define	ESM_ASSP_IRQ		0x10

#define ESM_HWVOL_IRQ		0x40


#define ESS_SYSCLK		50000000

#define ESM_BOB_FREQ 		200

#define ESM_BOB_FREQ_MAX	800


#define ESM_FREQ_ESM1  		(49152000L / 1024L)	
/* default rate 48000 */

#define ESM_FREQ_ESM2  		(50000000L / 1024L)

/* APU Modes: reg 0x00, bit 4-7 */

#define ESM_APU_MODE_SHIFT	4

#define ESM_APU_MODE_MASK	(0xf << 4)

#define	ESM_APU_OFF		0x00

#define	ESM_APU_16BITLINEAR	0x01	
/* 16-Bit Linear Sample Player */

#define	ESM_APU_16BITSTEREO	0x02	
/* 16-Bit Stereo Sample Player */

#define	ESM_APU_8BITLINEAR	0x03	
/* 8-Bit Linear Sample Player */

#define	ESM_APU_8BITSTEREO	0x04	
/* 8-Bit Stereo Sample Player */

#define	ESM_APU_8BITDIFF	0x05	
/* 8-Bit Differential Sample Playrer */

#define	ESM_APU_DIGITALDELAY	0x06	
/* Digital Delay Line */

#define	ESM_APU_DUALTAP		0x07	
/* Dual Tap Reader */

#define	ESM_APU_CORRELATOR	0x08	
/* Correlator */

#define	ESM_APU_INPUTMIXER	0x09	
/* Input Mixer */

#define	ESM_APU_WAVETABLE	0x0A	
/* Wave Table Mode */

#define	ESM_APU_SRCONVERTOR	0x0B	
/* Sample Rate Convertor */

#define	ESM_APU_16BITPINGPONG	0x0C	
/* 16-Bit Ping-Pong Sample Player */

#define	ESM_APU_RESERVED1	0x0D	
/* Reserved 1 */

#define	ESM_APU_RESERVED2	0x0E	
/* Reserved 2 */

#define	ESM_APU_RESERVED3	0x0F	
/* Reserved 3 */

/* reg 0x00 */

#define ESM_APU_FILTER_Q_SHIFT		0

#define ESM_APU_FILTER_Q_MASK		(3 << 0)
/* APU Filtey Q Control */

#define ESM_APU_FILTER_LESSQ	0x00

#define ESM_APU_FILTER_MOREQ	0x03


#define ESM_APU_FILTER_TYPE_SHIFT	2

#define ESM_APU_FILTER_TYPE_MASK	(3 << 2)

#define ESM_APU_ENV_TYPE_SHIFT		8

#define ESM_APU_ENV_TYPE_MASK		(3 << 8)

#define ESM_APU_ENV_STATE_SHIFT		10

#define ESM_APU_ENV_STATE_MASK		(3 << 10)

#define ESM_APU_END_CURVE		(1 << 12)

#define ESM_APU_INT_ON_LOOP		(1 << 13)

#define ESM_APU_DMA_ENABLE		(1 << 14)

/* reg 0x02 */

#define ESM_APU_SUBMIX_GROUP_SHIRT	0

#define ESM_APU_SUBMIX_GROUP_MASK	(7 << 0)

#define ESM_APU_SUBMIX_MODE		(1 << 3)

#define ESM_APU_6dB			(1 << 4)

#define ESM_APU_DUAL_EFFECT		(1 << 5)

#define ESM_APU_EFFECT_CHANNELS_SHIFT	6

#define ESM_APU_EFFECT_CHANNELS_MASK	(3 << 6)

/* reg 0x03 */

#define ESM_APU_STEP_SIZE_MASK		0x0fff

/* reg 0x04 */

#define ESM_APU_PHASE_SHIFT		0

#define ESM_APU_PHASE_MASK		(0xff << 0)

#define ESM_APU_WAVE64K_PAGE_SHIFT	8	
/* most 8bit of wave start offset */

#define ESM_APU_WAVE64K_PAGE_MASK	(0xff << 8)

/* reg 0x05 - wave start offset */
/* reg 0x06 - wave end offset */
/* reg 0x07 - wave loop length */

/* reg 0x08 */

#define ESM_APU_EFFECT_GAIN_SHIFT	0

#define ESM_APU_EFFECT_GAIN_MASK	(0xff << 0)

#define ESM_APU_TREMOLO_DEPTH_SHIFT	8

#define ESM_APU_TREMOLO_DEPTH_MASK	(0xf << 8)

#define ESM_APU_TREMOLO_RATE_SHIFT	12

#define ESM_APU_TREMOLO_RATE_MASK	(0xf << 12)

/* reg 0x09 */
/* bit 0-7 amplitude dest? */

#define ESM_APU_AMPLITUDE_NOW_SHIFT	8

#define ESM_APU_AMPLITUDE_NOW_MASK	(0xff << 8)

/* reg 0x0a */

#define ESM_APU_POLAR_PAN_SHIFT		0

#define ESM_APU_POLAR_PAN_MASK		(0x3f << 0)
/* Polar Pan Control */

#define	ESM_APU_PAN_CENTER_CIRCLE		0x00

#define	ESM_APU_PAN_MIDDLE_RADIUS		0x01

#define	ESM_APU_PAN_OUTSIDE_RADIUS		0x02


#define ESM_APU_FILTER_TUNING_SHIFT	8

#define ESM_APU_FILTER_TUNING_MASK	(0xff << 8)

/* reg 0x0b */

#define ESM_APU_DATA_SRC_A_SHIFT	0

#define ESM_APU_DATA_SRC_A_MASK		(0x7f << 0)

#define ESM_APU_INV_POL_A		(1 << 7)

#define ESM_APU_DATA_SRC_B_SHIFT	8

#define ESM_APU_DATA_SRC_B_MASK		(0x7f << 8)

#define ESM_APU_INV_POL_B		(1 << 15)


#define ESM_APU_VIBRATO_RATE_SHIFT	0

#define ESM_APU_VIBRATO_RATE_MASK	(0xf << 0)

#define ESM_APU_VIBRATO_DEPTH_SHIFT	4

#define ESM_APU_VIBRATO_DEPTH_MASK	(0xf << 4)

#define ESM_APU_VIBRATO_PHASE_SHIFT	8

#define ESM_APU_VIBRATO_PHASE_MASK	(0xff << 8)

/* reg 0x0c */

#define ESM_APU_RADIUS_SELECT		(1 << 6)

/* APU Filter Control */

#define	ESM_APU_FILTER_2POLE_LOPASS	0x00

#define	ESM_APU_FILTER_2POLE_BANDPASS	0x01

#define	ESM_APU_FILTER_2POLE_HIPASS	0x02

#define	ESM_APU_FILTER_1POLE_LOPASS	0x03

#define	ESM_APU_FILTER_1POLE_HIPASS	0x04

#define	ESM_APU_FILTER_OFF		0x05

/* APU ATFP Type */

#define	ESM_APU_ATFP_AMPLITUDE			0x00

#define	ESM_APU_ATFP_TREMELO			0x01

#define	ESM_APU_ATFP_FILTER			0x02

#define	ESM_APU_ATFP_PAN			0x03

/* APU ATFP Flags */

#define	ESM_APU_ATFP_FLG_OFF			0x00

#define	ESM_APU_ATFP_FLG_WAIT			0x01

#define	ESM_APU_ATFP_FLG_DONE			0x02

#define	ESM_APU_ATFP_FLG_INPROCESS		0x03


/* capture mixing buffer size */

#define ESM_MEM_ALIGN		0x1000

#define ESM_MIXBUF_SIZE		0x400


#define ESM_MODE_PLAY		0

#define ESM_MODE_CAPTURE	1


/* APU use in the driver */

enum snd_enum_apu_type {
	
ESM_APU_PCM_PLAY,
	
ESM_APU_PCM_CAPTURE,
	
ESM_APU_PCM_RATECONV,
	
ESM_APU_FREE
};

/* chip type */
enum {
	


TYPE_MAESTRO, TYPE_MAESTRO2, TYPE_MAESTRO2E
};

/* DMA Hack! */

struct esm_memory {
	
struct snd_dma_buffer buf;
	
int empty;	/* status */
	
struct list_head list;
};

/* Playback Channel */

struct esschan {
	
int running;

	
u8 apu[4];
	
u8 apu_mode[4];

	/* playback/capture pcm buffer */
	
struct esm_memory *memory;
	/* capture mixer buffer */
	
struct esm_memory *mixbuf;

	
unsigned int hwptr;	/* current hw pointer in bytes */
	
unsigned int count;	/* sample counter in bytes */
	
unsigned int dma_size;	/* total buffer size in bytes */
	
unsigned int frag_size;	/* period size in bytes */
	
unsigned int wav_shift;
	
u16 base[4];		/* offset for ptr */

	/* stereo/16bit flag */
	
unsigned char fmt;
	
int mode;	/* playback / capture */

	
int bob_freq;	/* required timer frequency */

	
struct snd_pcm_substream *substream;

	/* linked list */
	
struct list_head list;

#ifdef CONFIG_PM_SLEEP
	
u16 wc_map[4];
#endif
};


struct es1968 {
	/* Module Config */
	
int total_bufsize;			/* in bytes */

	

int playback_streams, capture_streams;

	
unsigned int clock;		/* clock */
	/* for clock measurement */
	
unsigned int in_measurement: 1;
	
unsigned int measure_apu;
	
unsigned int measure_lastpos;
	
unsigned int measure_count;

	/* buffer */
	
struct snd_dma_buffer dma;

	/* Resources... */
	
int irq;
	
unsigned long io_port;
	
int type;
	
struct pci_dev *pci;
	
struct snd_card *card;
	
struct snd_pcm *pcm;
	
int do_pm;		/* power-management enabled */

	/* DMA memory block */
	
struct list_head buf_list;

	/* ALSA Stuff */
	
struct snd_ac97 *ac97;
	
struct snd_rawmidi *rmidi;

	
spinlock_t reg_lock;
	
unsigned int in_suspend;

	/* Maestro Stuff */
	
u16 maestro_map[32];
	
int bobclient;		/* active timer instancs */
	
int bob_freq;		/* timer frequency */
	
struct mutex memory_mutex;	/* memory lock */

	/* APU states */
	
unsigned char apu[NR_APUS];

	/* active substreams */
	
struct list_head substream_list;
	
spinlock_t substream_lock;

#ifdef CONFIG_PM_SLEEP
	
u16 apu_map[NR_APUS][NR_APU_REGS];
#endif

#ifdef SUPPORT_JOYSTICK
	
struct gameport *gameport;
#endif

#ifdef CONFIG_SND_ES1968_INPUT
	
struct input_dev *input_dev;
	
char phys[64];			/* physical device path */
#else
	
struct snd_kcontrol *master_switch; /* for h/w volume control */
	
struct snd_kcontrol *master_volume;
#endif
	
struct work_struct hwvol_work;

#ifdef CONFIG_SND_ES1968_RADIO
	
struct v4l2_device v4l2_dev;
	
struct snd_tea575x tea;
	
unsigned int tea575x_tuner;
#endif
};

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


static const struct pci_device_id snd_es1968_ids[] = {
	/* Maestro 1 */
        { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
	/* Maestro 2 */
	{ 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2 },
	/* Maestro 2E */
        { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2E },
	{ 0, }
};

MODULE_DEVICE_TABLE(pci, snd_es1968_ids);

/* *********************
   * Low Level Funcs!  *
   *********************/

/* no spinlock */

static void __maestro_write(struct es1968 *chip, u16 reg, u16 data) { outw(reg, chip->io_port + ESM_INDEX); outw(data, chip->io_port + ESM_DATA); chip->maestro_map[reg] = data; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4695.83%150.00%
Takashi Iwai24.17%150.00%
Total48100.00%2100.00%


static inline void maestro_write(struct es1968 *chip, u16 reg, u16 data) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); __maestro_write(chip, reg, data); spin_unlock_irqrestore(&chip->reg_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4894.12%133.33%
Takashi Iwai23.92%133.33%
Jesper Juhl11.96%133.33%
Total51100.00%3100.00%

/* no spinlock */
static u16 __maestro_read(struct es1968 *chip, u16 reg) { if (READABLE_MAP & (1 << reg)) { outw(reg, chip->io_port + ESM_INDEX); chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA); } return chip->maestro_map[reg]; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5996.72%150.00%
Takashi Iwai23.28%150.00%
Total61100.00%2100.00%


static inline u16 maestro_read(struct es1968 *chip, u16 reg) { unsigned long flags; u16 result; spin_lock_irqsave(&chip->reg_lock, flags); result = __maestro_read(chip, reg); spin_unlock_irqrestore(&chip->reg_lock, flags); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5194.44%133.33%
Takashi Iwai23.70%133.33%
Jesper Juhl11.85%133.33%
Total54100.00%3100.00%

/* Wait for the codec bus to be free */
static int snd_es1968_ac97_wait(struct es1968 *chip) { int timeout = 100000; while (timeout-- > 0) { if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) return 0; cond_resched(); } dev_dbg(chip->card->dev, "ac97 timeout\n"); return 1; /* timeout */ }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5283.87%250.00%
Takashi Iwai1016.13%250.00%
Total62100.00%4100.00%


static int snd_es1968_ac97_wait_poll(struct es1968 *chip) { int timeout = 100000; while (timeout-- > 0) { if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) return 0; } dev_dbg(chip->card->dev, "ac97 timeout\n"); return 1; /* timeout */ }

Contributors

PersonTokensPropCommitsCommitProp
Arjan van de Ven5186.44%150.00%
Takashi Iwai813.56%150.00%
Total59100.00%2100.00%


static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct es1968 *chip = ac97->private_data; snd_es1968_ac97_wait(chip); /* Write the bus */ outw(val, chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ outb(reg, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5493.10%375.00%
Takashi Iwai46.90%125.00%
Total58100.00%4100.00%


static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { u16 data = 0; struct es1968 *chip = ac97->private_data; snd_es1968_ac97_wait(chip); outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ if (!snd_es1968_ac97_wait_poll(chip)) { data = inw(chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ } return data; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6993.24%360.00%
Takashi Iwai45.41%120.00%
Arjan van de Ven11.35%120.00%
Total74100.00%5100.00%

/* no spinlock */
static void apu_index_set(struct es1968 *chip, u16 index) { int i; __maestro_write(chip, IDR1_CRAM_POINTER, index); for (i = 0; i < 1000; i++) if (__maestro_read(chip, IDR1_CRAM_POINTER) == index) return; dev_dbg(chip->card->dev, "APU register select failed. (Timeout)\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5283.87%133.33%
Takashi Iwai1016.13%266.67%
Total62100.00%3100.00%

/* no spinlock */
static void apu_data_set(struct es1968 *chip, u16 data) { int i; for (i = 0; i < 1000; i++) { if (__maestro_read(chip, IDR0_DATA_PORT) == data) return; __maestro_write(chip, IDR0_DATA_PORT, data); } dev_dbg(chip->card->dev, "APU register set probably failed (Timeout)!\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5484.38%133.33%
Takashi Iwai1015.62%266.67%
Total64100.00%3100.00%

/* no spinlock */
static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data) { if (snd_BUG_ON(channel >= NR_APUS)) return; #ifdef CONFIG_PM_SLEEP chip->apu_map[channel][reg] = data; #endif reg |= (channel << 4); apu_index_set(chip, reg); apu_data_set(chip, data); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5884.06%240.00%
Takashi Iwai1115.94%360.00%
Total69100.00%5100.00%


static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); __apu_set_register(chip, channel, reg, data); spin_unlock_irqrestore(&chip->reg_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5396.36%150.00%
Takashi Iwai23.64%150.00%
Total55100.00%2100.00%


static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg) { if (snd_BUG_ON(channel >= NR_APUS)) return 0; reg |= (channel << 4); apu_index_set(chip, reg); return __maestro_read(chip, IDR0_DATA_PORT); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4076.92%133.33%
Takashi Iwai1223.08%266.67%
Total52100.00%3100.00%


static u16 apu_get_register(struct es1968 *chip, u16 channel, u8 reg) { unsigned long flags; u16 v; spin_lock_irqsave(&chip->reg_lock, flags); v = __apu_get_register(chip, channel, reg); spin_unlock_irqrestore(&chip->reg_lock, flags); return v; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5696.55%150.00%
Takashi Iwai23.45%150.00%
Total58100.00%2100.00%

#if 0 /* ASSP is not supported */ static void assp_set_register(struct es1968 *chip, u32 reg, u32 value) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); outl(reg, chip->io_port + ASSP_INDEX); outl(value, chip->io_port + ASSP_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); } static u32 assp_get_register(struct es1968 *chip, u32 reg) { unsigned long flags; u32 value; spin_lock_irqsave(&chip->reg_lock, flags); outl(reg, chip->io_port + ASSP_INDEX); value = inl(chip->io_port + ASSP_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); return value; } #endif
static void wave_set_register(struct es1968 *chip, u16 reg, u16 value) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); outw(reg, chip->io_port + WC_INDEX); outw(value, chip->io_port + WC_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6196.83%150.00%
Takashi Iwai23.17%150.00%
Total63100.00%2100.00%


static u16 wave_get_register(struct es1968 *chip, u16 reg) { unsigned long flags; u16 value; spin_lock_irqsave(&chip->reg_lock, flags); outw(reg, chip->io_port + WC_INDEX); value = inw(chip->io_port + WC_DATA); spin_unlock_irqrestore(&chip->reg_lock, flags); return value; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6496.97%150.00%
Takashi Iwai23.03%150.00%
Total66100.00%2100.00%

/* ******************* * Bob the Timer! * *******************/
static void snd_es1968_bob_stop(struct es1968 *chip) { u16 reg; reg = __maestro_read(chip, 0x11); reg &= ~ESM_BOB_ENABLE; __maestro_write(chip, 0x11, reg); reg = __maestro_read(chip, 0x17); reg &= ~ESM_BOB_START; __maestro_write(chip, 0x17, reg); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5896.67%150.00%
Takashi Iwai23.33%150.00%
Total60100.00%2100.00%


static void snd_es1968_bob_start(struct es1968 *chip) { int prescale; int divide; /* compute ideal interrupt frequency for buffer size & play rate */ /* first, find best prescaler value to match freq */ for (prescale = 5; prescale < 12; prescale++) if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9))) break; /* next, back off prescaler whilst getting divider into optimum range */ divide = 1; while ((prescale > 5) && (divide < 32)) { prescale--; divide <<= 1; } divide >>= 1; /* now fine-tune the divider for best match */ for (; divide < 31; divide++) if (chip->bob_freq > ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break; /* divide = 0 is illegal, but don't let prescale = 4! */ if (divide == 0) { divide++; if (prescale > 5) prescale--; } else if (divide > 1) divide--; __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */ /* Now set IDR 11/17 */ __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1); __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19798.99%150.00%
Takashi Iwai21.01%150.00%
Total199100.00%2100.00%

/* call with substream spinlock */
static void snd_es1968_bob_inc(struct es1968 *chip, int freq) { chip->bobclient++; if (chip->bobclient == 1) { chip->bob_freq = freq; snd_es1968_bob_start(chip); } else if (chip->bob_freq < freq) { snd_es1968_bob_stop(chip); chip->bob_freq = freq; snd_es1968_bob_start(chip); } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6597.01%266.67%
Takashi Iwai22.99%133.33%
Total67100.00%3100.00%

/* call with substream spinlock */
static void snd_es1968_bob_dec(struct es1968 *chip) { chip->bobclient--; if (chip->bobclient <= 0) snd_es1968_bob_stop(chip); else if (chip->bob_freq > ESM_BOB_FREQ) { /* check reduction of timer frequency */ int max_freq = ESM_BOB_FREQ; struct esschan *es; list_for_each_entry(es, &chip->substream_list, list) { if (max_freq < es->bob_freq) max_freq = es->bob_freq; } if (max_freq != chip->bob_freq) { snd_es1968_bob_stop(chip); chip->bob_freq = max_freq; snd_es1968_bob_start(chip); } } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9190.10%250.00%
Matthias Kaehlcke65.94%125.00%
Takashi Iwai43.96%125.00%
Total101100.00%4100.00%


static int snd_es1968_calc_bob_rate(struct es1968 *chip, struct esschan *es, struct snd_pcm_runtime *runtime) { /* we acquire 4 interrupts per period for precise control.. */ int freq = runtime->rate * 4; if (es->fmt & ESS_FMT_STEREO) freq <<= 1; if (es->fmt & ESS_FMT_16BIT) freq <<= 1; freq /= es->frag_size; if (freq < ESM_BOB_FREQ) freq = ESM_BOB_FREQ; else if (freq > ESM_BOB_FREQ_MAX) freq = ESM_BOB_FREQ_MAX; return freq; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7992.94%150.00%
Takashi Iwai67.06%150.00%
Total85100.00%2100.00%

/************* * PCM Part * *************/
static u32 snd_es1968_compute_rate(struct es1968 *chip, u32 freq) { u32 rate = (freq << 16) / chip->clock; #if 0 /* XXX: do we need this? */ if (rate > 0x10000) rate = 0x10000; #endif return rate; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3594.59%150.00%
Takashi Iwai25.41%150.00%
Total37100.00%2100.00%

/* get current pointer */
static inline unsigned int snd_es1968_get_dma_ptr(struct es1968 *chip, struct esschan *es) { unsigned int offset; offset = apu_get_register(chip, es->apu[0], 5); offset -= es->base[0]; return (offset & 0xFFFE); /* hardware is in words */ }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5090.91%133.33%
Takashi Iwai47.27%133.33%
Jesper Juhl11.82%133.33%
Total55100.00%3100.00%


static void snd_es1968_apu_set_freq(struct es1968 *chip, int apu, int freq) { apu_set_register(chip, apu, 2, (apu_get_register(chip, apu, 2) & 0x00FF) | ((freq & 0xff) << 8) | 0x10); apu_set_register(chip, apu, 3, freq >> 8); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela6296.88%150.00%
Takashi Iwai23.12%150.00%
Total64100.00%2100.00%

/* spin lock held */
static inline void snd_es1968_trigger_apu(struct es1968 *esm, int apu, int mode) { /* set the APU mode */ __apu_set_register(esm, apu, 0, (__apu_get_register(esm, apu, 0) & 0xff0f) | (mode << 4)); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4493.62%250.00%
Takashi Iwai24.26%125.00%
Jesper Juhl12.13%125.00%
Total47100.00%4100.00%


static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es) { spin_lock(&chip->reg_lock); __apu_set_register(chip, es->apu[0], 5, es->base[0]); snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]); if (es->mode == ESM_MODE_CAPTURE) { __apu_set_register(chip, es->apu[2], 5, es->base[2]); snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]); } if (es->fmt & ESS_FMT_STEREO) { __apu_set_register(chip, es->apu[1], 5, es->base[1]); snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]); if (es->mode == ESM_MODE_CAPTURE) { __apu_set_register(chip, es->apu[3], 5, es->base[3]); snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]); } } spin_unlock(&chip->reg_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela21898.20%266.67%
Takashi Iwai41.80%133.33%
Total222100.00%3100.00%


static void snd_es1968_pcm_stop(struct es1968 *chip, struct esschan *es) { spin_lock(&chip->reg_lock); snd_es1968_trigger_apu(chip, es->apu[0], 0); snd_es1968_trigger_apu(chip, es->apu[1], 0); if (es->mode == ESM_MODE_CAPTURE) { snd_es1968_trigger_apu(chip, es->apu[2], 0); snd_es1968_trigger_apu(chip, es->apu[3], 0); } spin_unlock(&chip->reg_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9495.92%266.67%
Takashi Iwai44.08%133.33%
Total98100.00%3100.00%

/* set the wavecache control reg */
static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es, int channel, u32 addr, int capture) { u32 tmpval = (addr - 0x10) & 0xFFF8; if (! capture) { if (!(es->fmt & ESS_FMT_16BIT)) tmpval |= 4; /* 8bit */ if (es->fmt & ESS_FMT_STEREO) tmpval |= 2; /* stereo */ } /* set the wavecache control reg */ wave_set_register(chip, es->apu[channel] << 3, tmpval); #ifdef CONFIG_PM_SLEEP es->wc_map[channel] = tmpval; #endif }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela9895.15%250.00%
Takashi Iwai54.85%250.00%
Total103100.00%4100.00%


static void snd_es1968_playback_setup(struct es1968 *chip, struct esschan *es, struct snd_pcm_runtime *runtime) { u32 pa; int high_apu = 0; int channel, apu; int i, size; unsigned long flags; u32 freq; size = es->dma_size >> es->wav_shift; if (es->fmt & ESS_FMT_STEREO) high_apu++; for (channel = 0; channel <= high_apu; channel++) { apu = es->apu[channel]; snd_es1968_program_wavecache(chip, es, channel, es->memory->buf.addr, 0); /* Offset to PCMBAR */ pa = es->memory->buf.addr; pa -= chip->dma.addr; pa >>= 1; /* words */ pa |= 0x00400000; /* System RAM (Bit 22) */ if (es->fmt & ESS_FMT_STEREO) { /* Enable stereo */ if (channel) pa |= 0x00800000; /* (Bit 23) */ if (es->fmt & ESS_FMT_16BIT) pa >>= 1; } /* base offset of dma calcs when reading the pointer on this left one */ es->base[channel] = pa & 0xFFFF; for (i = 0; i < 16; i++) apu_set_register(chip, apu, i, 0x0000); /* Load the buffer into the wave engine */ apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); apu_set_register(chip, apu, 5, pa & 0xFFFF); apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF); /* setting loop == sample len */ apu_set_register(chip, apu, 7, size); /* clear effects/env.. */ apu_set_register(chip, apu, 8, 0x0000); /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ apu_set_register(chip, apu, 9, 0xD000); /* clear routing stuff */ apu_set_register(chip, apu, 11, 0x0000); /* dma on, no envelopes, filter to all 1s) */ apu_set_register(chip, apu, 0, 0x400F); if (es->fmt & ESS_FMT_16BIT) es->apu_mode[channel] = ESM_APU_16BITLINEAR; else es->apu_mode[channel] = ESM_APU_8BITLINEAR; if (es->fmt & ESS_FMT_STEREO) { /* set panning: left or right */ /* Check: different panning. On my Canyon 3D Chipset the Channels are swapped. I don't know, about the output to the SPDif Link. Perhaps you have to change this and not the APU Regs 4-5. */ apu_set_register(chip, apu, 10, 0x8F00 | (channel ? 0 : 0x10)); es->apu_mode[channel] += 1; /* stereo */ } else apu_set_register(chip, apu, 10, 0x8F08); } spin_lock_irqsave(&chip->reg_lock, flags); /* clear WP interrupts */ outw(1, chip->io_port + 0x04); /* enable WP ints */ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); spin_unlock_irqrestore(&chip->reg_lock, flags); freq = runtime->rate; /* set frequency */ if (freq > 48000) freq = 48000; if (freq < 4000) freq = 4000; /* hmmm.. */ if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO)) freq >>= 1; freq = snd_es1968_compute_rate(chip, freq); /* Load the frequency, turn on 6dB */ snd_es1968_apu_set_freq(chip, es->apu[0], freq); snd_es1968_apu_set_freq(chip, es->apu[1], freq); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela53598.71%675.00%
Takashi Iwai61.11%112.50%
Steven Cole10.18%112.50%
Total542100.00%8100.00%


static void init_capture_apu(struct es1968 *chip, struct esschan *es, int channel, unsigned int pa, unsigned int bsize, int mode, int route) { int i, apu = es->apu[channel]; es->apu_mode[channel] = mode; /* set the wavecache control reg */ snd_es1968_program_wavecache(chip, es, channel, pa, 1); /* Offset to PCMBAR */ pa -= chip->dma.addr; pa >>= 1; /* words */ /* base offset of dma calcs when reading the pointer on this left one */ es->base[channel] = pa & 0xFFFF; pa |= 0x00400000; /* bit 22 -> System RAM */ /* Begin loading the APU */ for (i = 0; i < 16; i++) apu_set_register(chip, apu, i, 0x0000); /* need to enable subgroups.. and we should probably have different groups for different /dev/dsps.. */ apu_set_register(chip, apu, 2, 0x8); /* Load the buffer into the wave engine */ apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); apu_set_register(chip, apu, 5, pa & 0xFFFF); apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF); apu_set_register(chip, apu, 7, bsize); /* clear effects/env.. */ apu_set_register(chip, apu, 8, 0x00F0); /* amplitude now? sure. why not. */ apu_set_register(chip, apu, 9, 0x0000); /* set filter tune, radius, polar pan */ apu_set_register(chip, apu, 10, 0x8F08); /* route input */ apu_set_register(chip, apu, 11, route); /* dma on, no envelopes, filter to all 1s) */ apu_set_register(chip, apu, 0, 0x400F); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela25598.46%480.00%
Takashi Iwai41.54%120.00%
Total259100.00%5100.00%


static void snd_es1968_capture_setup(struct es1968 *chip, struct esschan *es, struct snd_pcm_runtime *runtime) { int size; u32 freq; unsigned long flags; size = es->dma_size >> es->wav_shift; /* APU assignments: 0 = mono/left SRC 1 = right SRC 2 = mono/left Input Mixer 3 = right Input Mixer */ /* data seems to flow from the codec, through an apu into the 'mixbuf' bit of page, then through the SRC apu and out to the real 'buffer'. ok. sure. */ /* input mixer (left/mono) */ /* parallel in crap, see maestro reg 0xC [8-11] */ init_capture_apu(chip, es, 2, es->mixbuf->buf.addr, ESM_MIXBUF_SIZE/4, /* in words */ ESM_APU_INPUTMIXER, 0x14); /* SRC (left/mono); get input from inputing apu */ init_capture_apu(chip, es, 0, es->memory->buf.addr, size, ESM_APU_SRCONVERTOR, es->apu[2]); if (es->fmt & ESS_FMT_STEREO) { /* input mixer (right) */ init_capture_apu(chip, es, 3, es->mixbuf->buf.addr + ESM_MIXBUF_SIZE/2, ESM_MIXBUF_SIZE/4, /* in words */ ESM_APU_INPUTMIXER, 0x15); /* SRC (right) */ init_capture_apu(chip, es, 1, es->memory->buf.addr + size*2, size, ESM_APU_SRCONVERTOR, es->apu[3]); } freq = runtime->rate; /* Sample Rate conversion APUs don't like 0x10000 for their rate */ if (freq > 47999) freq = 47999; if (freq < 4000) freq = 4000; freq = snd_es1968_compute_rate(chip, freq); /* Load the frequency, turn on 6dB */ snd_es1968_apu_set_freq(chip, es->apu[0], freq); snd_es1968_apu_set_freq(chip, es->apu[1], freq); /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ freq = 0x10000; snd_es1968_apu_set_freq(chip, es->apu[2], freq); snd_es1968_apu_set_freq(chip, es->apu[3], freq); spin_lock_irqsave(&chip->reg_lock, flags); /* clear WP interrupts */ outw(1, chip->io_port + 0x04); /* enable WP ints */ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); spin_unlock_irqrestore(&chip->reg_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela31998.15%375.00%
Takashi Iwai61.85%125.00%
Total325100.00%4100.00%

/******************* * ALSA Interface * *******************/
static int snd_es1968_pcm_prepare(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *es = runtime->private_data; es->dma_size = snd_pcm_lib_buffer_bytes(substream); es->frag_size = snd_pcm_lib_period_bytes(substream); es->wav_shift = 1; /* maestro handles always 16bit */ es->fmt = 0; if (snd_pcm_format_width(runtime->format) == 16) es->fmt |= ESS_FMT_16BIT; if (runtime->channels > 1) { es->fmt |= ESS_FMT_STEREO; if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */ es->wav_shift++; } es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime); switch (es->mode) { case ESM_MODE_PLAY: snd_es1968_playback_setup(chip, es, runtime); break; case ESM_MODE_CAPTURE: snd_es1968_capture_setup(chip, es, runtime); break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela15995.21%266.67%
Takashi Iwai84.79%133.33%
Total167100.00%3100.00%


static int snd_es1968_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es = substream->runtime->private_data; spin_lock(&chip->substream_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: if (es->running) break; snd_es1968_bob_inc(chip, es->bob_freq); es->count = 0; es->hwptr = 0; snd_es1968_pcm_start(chip, es); es->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: if (! es->running) break; snd_es1968_pcm_stop(chip, es); es->running = 0; snd_es1968_bob_dec(chip); break; } spin_unlock(&chip->substream_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela13595.74%480.00%
Takashi Iwai64.26%120.00%
Total141100.00%5100.00%


static snd_pcm_uframes_t snd_es1968_pcm_pointer(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es = substream->runtime->private_data; unsigned int ptr; ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; return bytes_to_frames(substream->runtime, ptr % es->dma_size); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5790.48%266.67%
Takashi Iwai69.52%133.33%
Total63100.00%3100.00%

static struct snd_pcm_hardware snd_es1968_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | /*SNDRV_PCM_INFO_PAUSE |*/ SNDRV_PCM_INFO_RESUME), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 4000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 65536, .period_bytes_min = 256, .period_bytes_max = 65536, .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; static struct snd_pcm_hardware snd_es1968_capture = { .info = (SNDRV_PCM_INFO_NONINTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER | /*SNDRV_PCM_INFO_PAUSE |*/ SNDRV_PCM_INFO_RESUME), .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 4000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 65536, .period_bytes_min = 256, .period_bytes_max = 65536, .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; /* ************************* * DMA memory management * *************************/ /* Because the Maestro can only take addresses relative to the PCM base address register :( */
static int calc_available_memory_size(struct es1968 *chip) { int max_size = 0; struct esm_memory *buf; mutex_lock(&chip->memory_mutex); list_for_each_entry(buf, &chip->buf_list, list) { if (buf->empty && buf->buf.bytes > max_size) max_size = buf->buf.bytes; } mutex_unlock(&chip->memory_mutex); if (max_size >= 128*1024) max_size = 127*1024; return max_size; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7384.88%350.00%
Matthias Kaehlcke910.47%116.67%
Takashi Iwai22.33%116.67%
Ingo Molnar22.33%116.67%
Total86100.00%6100.00%

/* allocate a new memory chunk with the specified size */
static struct esm_memory *snd_es1968_new_memory(struct es1968 *chip, int size) { struct esm_memory *buf; size = ALIGN(size, ESM_MEM_ALIGN); mutex_lock(&chip->memory_mutex); list_for_each_entry(buf, &chip->buf_list, list) { if (buf->empty && buf->buf.bytes >= size) goto __found; } mutex_unlock(&chip->memory_mutex); return NULL; __found: if (buf->buf.bytes > size) { struct esm_memory *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); if (chunk == NULL) { mutex_unlock(&chip->memory_mutex); return NULL; } chunk->buf = buf->buf; chunk->buf.bytes -= size; chunk->buf.area += size; chunk->buf.addr += size; chunk->empty = 1; buf->buf.bytes = size; list_add(&chunk->list, &buf->list); } buf->empty = 0; mutex_unlock(&chip->memory_mutex); return buf; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18190.05%450.00%
Takashi Iwai83.98%112.50%
Ingo Molnar41.99%112.50%
Clemens Ladisch41.99%112.50%
Matthias Kaehlcke41.99%112.50%
Total201100.00%8100.00%

/* free a memory chunk */
static void snd_es1968_free_memory(struct es1968 *chip, struct esm_memory *buf) { struct esm_memory *chunk; mutex_lock(&chip->memory_mutex); buf->empty = 1; if (buf->list.prev != &chip->buf_list) { chunk = list_entry(buf->list.prev, struct esm_memory, list); if (chunk->empty) { chunk->buf.bytes += buf->buf.bytes; list_del(&buf->list); kfree(buf); buf = chunk; } } if (buf->list.next != &chip->buf_list) { chunk = list_entry(buf->list.next, struct esm_memory, list); if (chunk->empty) { buf->buf.bytes += chunk->buf.bytes; list_del(&chunk->list); kfree(chunk); } } mutex_unlock(&chip->memory_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela16393.14%250.00%
Takashi Iwai105.71%125.00%
Ingo Molnar21.14%125.00%
Total175100.00%4100.00%


static void snd_es1968_free_dmabuf(struct es1968 *chip) { struct list_head *p; if (! chip->dma.area) return; snd_dma_free_pages(&chip->dma); while ((p = chip->buf_list.next) != &chip->buf_list) { struct esm_memory *chunk = list_entry(p, struct esm_memory, list); list_del(p); kfree(chunk); } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7191.03%360.00%
Takashi Iwai78.97%240.00%
Total78100.00%5100.00%


static int snd_es1968_init_dmabuf(struct es1968 *chip) { int err; struct esm_memory *chunk; chip->dma.dev.type = SNDRV_DMA_TYPE_DEV; chip->dma.dev.dev = snd_dma_pci_data(chip->pci); err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), chip->total_bufsize, &chip->dma); if (err < 0 || ! chip->dma.area) { dev_err(chip->card->dev, "can't allocate dma pages for size %d\n", chip->total_bufsize); return -ENOMEM; } if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) { snd_dma_free_pages(&chip->dma); dev_err(chip->card->dev, "DMA buffer beyond 256MB.\n"); return -ENOMEM; } INIT_LIST_HEAD(&chip->buf_list); /* allocate an empty chunk */ chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); if (chunk == NULL) { snd_es1968_free_dmabuf(chip); return -ENOMEM; } memset(chip->dma.area, 0, ESM_MEM_ALIGN); chunk->buf = chip->dma; chunk->buf.area += ESM_MEM_ALIGN; chunk->buf.addr += ESM_MEM_ALIGN; chunk->buf.bytes -= ESM_MEM_ALIGN; chunk->empty = 1; list_add(&chunk->list, &chip->buf_list); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela24192.34%777.78%
Takashi Iwai207.66%222.22%
Total261100.00%9100.00%

/* setup the dma_areas */ /* buffer is extracted from the pre-allocated memory chunk */
static int snd_es1968_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *chan = runtime->private_data; int size = params_buffer_bytes(hw_params); if (chan->memory) { if (chan->memory->buf.bytes >= size) { runtime->dma_bytes = size; return 0; } snd_es1968_free_memory(chip, chan->memory); } chan->memory = snd_es1968_new_memory(chip, size); if (chan->memory == NULL) { dev_dbg(chip->card->dev, "cannot allocate dma buffer: size = %d\n", size); return -ENOMEM; } snd_pcm_set_runtime_buffer(substream, &chan->memory->buf); return 1; /* area was changed */ }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela12384.25%360.00%
Takashi Iwai2315.75%240.00%
Total146100.00%5100.00%

/* remove dma areas if allocated */
static int snd_es1968_hw_free(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *chan; if (runtime->private_data == NULL) return 0; chan = runtime->private_data; if (chan->memory) { snd_es1968_free_memory(chip, chan->memory); chan->memory = NULL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela7089.74%266.67%
Takashi Iwai810.26%133.33%
Total78100.00%3100.00%

/* * allocate APU pair */
static int snd_es1968_alloc_apu_pair(struct es1968 *chip, int type) { int apu; for (apu = 0; apu < NR_APUS; apu += 2) { if (chip->apu[apu] == ESM_APU_FREE && chip->apu[apu + 1] == ESM_APU_FREE) { chip->apu[apu] = chip->apu[apu + 1] = type; return apu; } } return -EBUSY; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8097.56%150.00%
Takashi Iwai22.44%150.00%
Total82100.00%2100.00%

/* * release APU pair */
static void snd_es1968_free_apu_pair(struct es1968 *chip, int apu) { chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela3093.75%150.00%
Takashi Iwai26.25%150.00%
Total32100.00%2100.00%

/****************** * PCM open/close * ******************/
static int snd_es1968_playback_open(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct esschan *es; int apu1; /* search 2 APUs */ apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); if (apu1 < 0) return apu1; es = kzalloc(sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); return -ENOMEM; } es->apu[0] = apu1; es->apu[1] = apu1 + 1; es->apu_mode[0] = 0; es->apu_mode[1] = 0; es->running = 0; es->substream = substream; es->mode = ESM_MODE_PLAY; runtime->private_data = es; runtime->hw = snd_es1968_playback; runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = calc_available_memory_size(chip); spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); spin_unlock_irq(&chip->substream_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19695.61%466.67%
Takashi Iwai94.39%233.33%
Total205100.00%6100.00%


static int snd_es1968_capture_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; int apu1, apu2; apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); if (apu1 < 0) return apu1; apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV); if (apu2 < 0) { snd_es1968_free_apu_pair(chip, apu1); return apu2; } es = kzalloc(sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); return -ENOMEM; } es->apu[0] = apu1; es->apu[1] = apu1 + 1; es->apu[2] = apu2; es->apu[3] = apu2 + 1; es->apu_mode[0] = 0; es->apu_mode[1] = 0; es->apu_mode[2] = 0; es->apu_mode[3] = 0; es->running = 0; es->substream = substream; es->mode = ESM_MODE_CAPTURE; /* get mixbuffer */ if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); kfree(es); return -ENOMEM; } memset(es->mixbuf->buf.area, 0, ESM_MIXBUF_SIZE); runtime->private_data = es; runtime->hw = snd_es1968_capture; runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */ snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES); spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); spin_unlock_irq(&chip->substream_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela33195.11%666.67%
Takashi Iwai92.59%222.22%
René Herman82.30%111.11%
Total348100.00%9100.00%


static int snd_es1968_playback_close(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; if (substream->runtime->private_data == NULL) return 0; es = substream->runtime->private_data; spin_lock_irq(&chip->substream_lock); list_del(&es->list); spin_unlock_irq(&chip->substream_lock); snd_es1968_free_apu_pair(chip, es->apu[0]); kfree(es); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela8593.41%480.00%
Takashi Iwai66.59%120.00%
Total91100.00%5100.00%


static int snd_es1968_capture_close(struct snd_pcm_substream *substream) { struct es1968 *chip = snd_pcm_substream_chip(substream); struct esschan *es; if (substream->runtime->private_data == NULL) return 0; es = substream->runtime->private_data; spin_lock_irq(&chip->substream_lock); list_del(&es->list); spin_unlock_irq(&chip->substream_lock); snd_es1968_free_memory(chip, es->mixbuf); snd_es1968_free_apu_pair(chip, es->apu[0]); snd_es1968_free_apu_pair(chip, es->apu[2]); kfree(es); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela10694.64%480.00%
Takashi Iwai65.36%120.00%
Total112100.00%5100.00%

static const struct snd_pcm_ops snd_es1968_playback_ops = { .open = snd_es1968_playback_open, .close = snd_es1968_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es1968_hw_params, .hw_free = snd_es1968_hw_free, .prepare = snd_es1968_pcm_prepare, .trigger = snd_es1968_pcm_trigger, .pointer = snd_es1968_pcm_pointer, }; static const struct snd_pcm_ops snd_es1968_capture_ops = { .open = snd_es1968_capture_open, .close = snd_es1968_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_es1968_hw_params, .hw_free = snd_es1968_hw_free, .prepare = snd_es1968_pcm_prepare, .trigger = snd_es1968_pcm_trigger, .pointer = snd_es1968_pcm_pointer, }; /* * measure clock */ #define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */
static void es1968_measure_clock(struct es1968 *chip) { int i, apu; unsigned int pa, offset, t; struct esm_memory *memory; ktime_t start_time, stop_time; ktime_t diff; if (chip->clock == 0) chip->clock = 48000; /* default clock value */ /* search 2 APUs (although one apu is enough) */ if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) { dev_err(chip->card->dev, "Hmm, cannot find empty APU pair!?\n"); return; } if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) { dev_warn(chip->card->dev, "cannot allocate dma buffer - using default clock %d\n", chip->clock); snd_es1968_free_apu_pair(chip, apu); return; } memset(memory->buf.area, 0, CLOCK_MEASURE_BUFSIZE); wave_set_register(chip, apu << 3, (memory->buf.addr - 0x10) & 0xfff8); pa = (unsigned int)((memory->buf.addr - chip->dma.addr) >> 1); pa |= 0x00400000; /* System RAM (Bit 22) */ /* initialize apu */ for (i = 0; i < 16; i++) apu_set_register(chip, apu, i, 0x0000); apu_set_register(chip, apu, 0, 0x400f); apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8); apu_set_register(chip, apu, 5, pa & 0xffff); apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff); apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2); apu_set_register(chip, apu, 8, 0x0000); apu_set_register(chip, apu, 9, 0xD000); apu_set_register(chip, apu, 10, 0x8F08); apu_set_register(chip, apu, 11, 0x0000); spin_lock_irq(&chip->reg_lock); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */ spin_unlock_irq(&chip->reg_lock); snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ chip->in_measurement = 1; chip->measure_apu = apu; spin_lock_irq(&chip->reg_lock); snd_es1968_bob_inc(chip, ESM_BOB_FREQ); __apu_set_register(chip, apu, 5, pa & 0xffff); snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); start_time = ktime_get(); spin_unlock_irq(&chip->reg_lock); msleep(50); spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); stop_time = ktime_get(); snd_es1968_trigger_apu(chip, apu, 0); /* stop */ snd_es1968_bob_dec(chip); chip->in_measurement = 0; spin_unlock_irq(&chip->reg_lock); /* check the current position */ offset -= (pa & 0xffff); offset &= 0xfffe; offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2); diff = ktime_sub(stop_time, start_time); t = ktime_to_us(diff); if (t == 0) { dev_err(chip->card->dev, "?? calculation error..\n"); } else { offset *= 1000; offset = (offset / t) * 1000 + ((offset % t) * 1000) / t; if (offset < 47500 || offset > 48500) { if (offset >= 40000 && offset <= 50000) chip->clock = (chip->clock * offset) / 48000; } dev_info(chip->card->dev, "clocking to %d\n", chip->clock); } snd_es1968_free_memory(chip, memory); snd_es1968_free_apu_pair(chip, apu); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela62091.72%861.54%
Takashi Iwai334.88%215.38%
Tina Ruchandani202.96%17.69%
Nishanth Aravamudan20.30%17.69%
Steven Cole10.15%17.69%
Total676100.00%13100.00%

/* */
static void snd_es1968_pcm_free(struct snd_pcm *pcm) { struct es1968 *esm = pcm->private_data; snd_es1968_free_dmabuf(esm); esm->pcm = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2787.10%266.67%
Takashi Iwai412.90%133.33%
Total31100.00%3100.00%


static int snd_es1968_pcm(struct es1968 *chip, int device) { struct snd_pcm *pcm; int err; /* get DMA buffer */ if ((err = snd_es1968_init_dmabuf(chip)) < 0) return err; /* set PCMBAR */ wave_set_register(chip, 0x01FC, chip->dma.addr >> 12); wave_set_register(chip, 0x01FD, chip->dma.addr >> 12); wave_set_register(chip, 0x01FE, chip->dma.addr >> 12); wave_set_register(chip, 0x01FF, chip->dma.addr >> 12); if ((err = snd_pcm_new(chip->card, "ESS Maestro", device, chip->playback_streams, chip->capture_streams, &pcm)) < 0) return err; pcm->private_data = chip; pcm->private_free = snd_es1968_pcm_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops); pcm->info_flags = 0; strcpy(pcm->name, "ESS Maestro"); chip->pcm = pcm; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18597.88%266.67%
Takashi Iwai42.12%133.33%
Total189100.00%3100.00%

/* * suppress jitter on some maestros when playing stereo */
static void snd_es1968_suppress_jitter(struct es1968 *chip, struct esschan *es) { unsigned int cp1; unsigned int cp2; unsigned int diff; cp1 = __apu_get_register(chip, 0, 5); cp2 = __apu_get_register(chip, 1, 5); diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); if (diff > 1) __maestro_write(chip, IDR0_DATA_PORT, cp1); }

Contributors

PersonTokensPropCommitsCommitProp
Andreas Mueller81100.00%1100.00%
Total81100.00%1100.00%

/* * update pointer */
static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es) { unsigned int hwptr; unsigned int diff; struct snd_pcm_substream *subs = es->substream; if (subs == NULL || !es->running) return; hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; hwptr %= es->dma_size; diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size; es->hwptr = hwptr; es->count += diff; if (es->count > es->frag_size) { spin_unlock(&chip->substream_lock); snd_pcm_period_elapsed(subs); spin_lock(&chip->substream_lock); es->count %= es->frag_size; } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela12995.56%266.67%
Takashi Iwai64.44%133.33%
Total135100.00%3100.00%

/* The hardware volume works by incrementing / decrementing 2 counters (without wrap around) in response to volume button presses and then generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 of a byte wide register. The meaning of bits 0 and 4 is unknown. */
static void es1968_update_hw_volume(struct work_struct *work) { struct es1968 *chip = container_of(work, struct es1968, hwvol_work); int x, val; /* Figure out which volume control button was pushed, based on differences from the default register values. */ x = inb(chip->io_port + 0x1c) & 0xee; /* Reset the volume control registers. */ outb(0x88, chip->io_port + 0x1c); outb(0x88, chip->io_port + 0x1d); outb(0x88, chip->io_port + 0x1e); outb(0x88, chip->io_port + 0x1f); if (chip->in_suspend) return; #ifndef CONFIG_SND_ES1968_INPUT if (! chip->master_switch || ! chip->master_volume) return; val = snd_ac97_read(chip->ac97, AC97_MASTER); switch (x) { case 0x88: /* mute */ val ^= 0x8000; break; case 0xaa: /* volume up */ if ((val & 0x7f) > 0) val--; if ((val & 0x7f00) > 0) val -= 0x0100; break; case 0x66: /* volume down */ if ((val & 0x7f) < 0x1f) val++; if ((val & 0x7f00) < 0x1f00) val += 0x0100; break; } if (snd_ac97_update(chip->ac97, AC97_MASTER, val)) snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); #else if (!chip->input_dev) return; val = 0; switch (x) { case 0x88: /* The counters have not changed, yet we've received a HV interrupt. According to tests run by various people this happens when pressing the mute button. */ val = KEY_MUTE; break; case 0xaa: /* counters increased by 1 -> volume up */ val = KEY_VOLUMEUP; break; case 0x66: /* counters decreased by 1 -> volume down */ val = KEY_VOLUMEDOWN; break; } if (val) { input_report_key(chip->input_dev, val, 1); input_sync(chip->input_dev); input_report_key(chip->input_dev, val, 0); input_sync(chip->input_dev); } #endif }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18456.97%450.00%
Hans de Goede9429.10%112.50%
Takashi Iwai278.36%225.00%
Ville Syrjälä185.57%112.50%
Total323100.00%8100.00%

/* * interrupt handler */
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) { struct es1968 *chip = dev_id; u32 event; if (!(event = inb(chip->io_port + 0x1A))) return IRQ_NONE; outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); if (event & ESM_HWVOL_IRQ) schedule_work(&chip->hwvol_work); /* else ack 'em all, i imagine */ outb(0xFF, chip->io_port + 0x1A); if ((event & ESM_MPU401_IRQ) && chip->rmidi) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); } if (event & ESM_SOUND_IRQ) { struct esschan *es; spin_lock(&chip->substream_lock); list_for_each_entry(es, &chip->substream_list, list) { if (es->running) { snd_es1968_update_pcm(chip, es); if (es->fmt & ESS_FMT_STEREO) snd_es1968_suppress_jitter(chip, es); } } spin_unlock(&chip->substream_lock); if (chip->in_measurement) { unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5); if (curp < chip->measure_lastpos) chip->measure_count++; chip->measure_lastpos = curp; } } return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19385.02%444.44%
Andreas Mueller177.49%111.11%
David S. Miller73.08%111.11%
Matthias Kaehlcke62.64%111.11%
Takashi Iwai41.76%222.22%
Total227100.00%9100.00%

/* * Mixer stuff */
static int snd_es1968_mixer(struct es1968 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; #ifndef CONFIG_SND_ES1968_INPUT struct snd_ctl_elem_id elem_id; #endif int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, .read = snd_es1968_ac97_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; pbus->no_vra = 1; /* ES1968 doesn't need VRA */ memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0) return err; #ifndef CONFIG_SND_ES1968_INPUT /* attach master switch / volumes for h/w volume control */ memset(&elem_id, 0, sizeof(elem_id)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(elem_id.name, "Master Playback Switch"); chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); memset(&elem_id, 0, sizeof(elem_id)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(elem_id.name, "Master Playback Volume"); chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela18985.91%457.14%
Harvey Harrison115.00%114.29%
Takashi Iwai104.55%114.29%
Hans de Goede104.55%114.29%
Total220100.00%7100.00%

/* * reset ac97 codec */
static void snd_es1968_ac97_reset(struct es1968 *chip) { unsigned long ioaddr = chip->io_port; unsigned short save_ringbus_a; unsigned short save_68; unsigned short w; unsigned int vend; /* save configuration */ save_ringbus_a = inw(ioaddr + 0x36); //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */ /* set command/status address i/o to 1st codec */ outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); /* disable ac link */ outw(0x0000, ioaddr + 0x36); save_68 = inw(ioaddr + 0x68); pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */ pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); if (w & 1) save_68 |= 0x10; outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */ outw(0x0001, ioaddr + 0x68); /* gpio write */ outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ udelay(20); outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ msleep(20); outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a); outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c); /* now the second codec */ /* disable ac link */ outw(0x0000, ioaddr + 0x36); outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */ save_68 = inw(ioaddr + 0x68); outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */ outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ udelay(20); outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ msleep(500); //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); #if 0 /* the loop here needs to be much better if we want it.. */ dev_info(chip->card->dev, "trying software reset\n"); /* try and do a software reset */ outb(0x80 | 0x7c, ioaddr + 0x30); for (w = 0;; w++) { if ((inw(ioaddr + 0x30) & 1) == 0) { if (inb(ioaddr + 0x32) != 0) break; outb(0x80 | 0x7d, ioaddr + 0x30); if (((inw(ioaddr + 0x30) & 1) == 0) && (inb(ioaddr + 0x32) != 0)) break; outb(0x80 | 0x7f, ioaddr + 0x30); if (((inw(ioaddr + 0x30) & 1) == 0) && (inb(ioaddr + 0x32) != 0)) break; } if (w > 10000) { outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ msleep(500); /* oh my.. */ outb(inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); udelay(1); outw(0x80, ioaddr + 0x30); for (w = 0; w < 10000; w++) { if ((inw(ioaddr + 0x30) & 1) == 0) break; } } } #endif if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { /* turn on external amp? */ outw(0xf9ff, ioaddr + 0x64); outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68); outw(0x0209, ioaddr + 0x60); } /* restore.. */ outw(save_ringbus_a, ioaddr + 0x36); /* Turn on the 978 docking chip. First frob the "master output enable" bit, then set most of the playback volume control registers to max. */ outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); outb(0xff, ioaddr+0xc3); outb(0xff, ioaddr+0xc4); outb(0xff, ioaddr+0xc6); outb(0xff, ioaddr+0xc8); outb(0x3f, ioaddr+0xcf); outb(0x3f, ioaddr+0xd0); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela49798.81%125.00%
Nishanth Aravamudan30.60%125.00%
Takashi Iwai30.60%250.00%
Total503100.00%4100.00%


static void snd_es1968_reset(struct es1968 *chip) { /* Reset */ outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND, chip->io_port + ESM_PORT_HOST_IRQ); udelay(10); outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ); udelay(10); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4495.65%150.00%
Takashi Iwai24.35%150.00%
Total46100.00%2100.00%

/* * initialize maestro chip */
static void snd_es1968_chip_init(struct es1968 *chip) { struct pci_dev *pci = chip->pci; int i; unsigned long iobase = chip->io_port; u16 w; u32 n; /* We used to muck around with pci config space that * we had no business messing with. We don't know enough * about the machine to know which DMA mode is appropriate, * etc. We were guessing wrong on some machines and making * them unhappy. We now trust in the BIOS to do things right, * which almost certainly means a new host of problems will * arise with broken BIOS implementations. screw 'em. * We're already intolerant of machines that don't assign * IRQs. */ /* Config Reg A */ pci_read_config_word(pci, ESM_CONFIG_A, &w); w &= ~DMA_CLEAR; /* Clear DMA bits */ w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */ w &= ~SAFEGUARD; /* Safeguard off */ w |= POST_WRITE; /* Posted write */ w |= PCI_TIMING; /* PCI timing on */ /* XXX huh? claims to be reserved.. */ w &= ~SWAP_LR; /* swap left/right seems to only have effect on SB Emulation */ w &= ~SUBTR_DECODE; /* Subtractive decode off */ pci_write_config_word(pci, ESM_CONFIG_A, w); /* Config Reg B */ pci_read_config_word(pci, ESM_CONFIG_B, &w); w &= ~(1 << 15); /* Turn off internal clock multiplier */ /* XXX how do we know which to use? */ w &= ~(1 << 14); /* External clock */ w &= ~SPDIF_CONFB; /* disable S/PDIF output */ w |= HWV_CONFB; /* HWV on */ w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */ w &= ~GPIO_CONFB; /* GPIO 4:5 */ w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ w &= ~IDMA_CONFB; /* IDMA off (undocumented) */ w &= ~MIDI_FIX; /* MIDI fix off (undoc) */ w &= ~(1 << 1); /* reserved, always write 0 */ w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */ pci_write_config_word(pci, ESM_CONFIG_B, w); /* DDMA off */ pci_read_config_word(pci, ESM_DDMA, &w); w &= ~(1 << 0); pci_write_config_word(pci, ESM_DDMA, w); /* * Legacy mode */ pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w); w |= ESS_DISABLE_AUDIO; /* Disable Legacy Audio */ w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */ w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w); /* Set up 978 docking control chip. */ pci_read_config_word(pci, 0x58, &w); w|=1<<2; /* Enable 978. */ w|=1<<3; /* Turn on 978 hardware volume control. */ w&=~(1<<11); /* Turn on 978 mixer volume control. */ pci_write_config_word(pci, 0x58, w); /* Sound Reset */ snd_es1968_reset(chip); /* * Ring Bus Setup */ /* setup usual 0x34 stuff.. 0x36 may be chip specific */ outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */ udelay(20); outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */ udelay(20); /* * Reset the CODEC */ snd_es1968_ac97_reset(chip); /* Ring Bus Control B */ n = inl(iobase + ESM_RING_BUS_CONTR_B); n &= ~RINGB_EN_SPDIF; /* SPDIF off */ //w |= RINGB_EN_2CODEC; /* enable 2nd codec */ outl(n, iobase + ESM_RING_BUS_CONTR_B); /* Set hardware volume control registers to midpoints. We can tell which button was pushed based on how they change. */ outb(0x88, iobase+0x1c); outb(0x88, iobase+0x1d); outb(0x88, iobase+0x1e); outb(0x88, iobase+0x1f); /* it appears some maestros (dell 7500) only work if these are set, regardless of whether we use the assp or not. */ outb(0, iobase + ASSP_CONTROL_B); outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */ outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */ /* * set up wavecache */ for (i = 0; i < 16; i++) { /* Write 0 into the buffer area 0x1E0->1EF */ outw(0x01E0 + i, iobase + WC_INDEX); outw(0x0000, iobase + WC_DATA); /* The 1.10 test program seem to write 0 into the buffer area * 0x1D0-0x1DF too.*/ outw(0x01D0 + i, iobase + WC_INDEX); outw(0x0000, iobase + WC_DATA); } wave_set_register(chip, IDR7_WAVE_ROMRAM, (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00)); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200); wave_set_register(chip, IDR7_WAVE_ROMRAM, wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400); maestro_write(chip, IDR2_CRAM_DATA, 0x0000); /* Now back to the DirectSound stuff */ /* audio serial configuration.. ? */ maestro_write(chip, 0x08, 0xB004); maestro_write(chip, 0x09, 0x001B); maestro_write(chip, 0x0A, 0x8000); maestro_write(chip, 0x0B, 0x3F37); maestro_write(chip, 0x0C, 0x0098); /* parallel in, has something to do with recording :) */ maestro_write(chip, 0x0C, (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000); /* parallel out */ maestro_write(chip, 0x0C, (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500); maestro_write(chip, 0x0D, 0x7632); /* Wave cache control on - test off, sg off, enable, enable extra chans 1Mb */ w = inw(iobase + WC_CONTROL); w &= ~0xFA00; /* Seems to be reserved? I don't know */ w |= 0xA000; /* reserved... I don't know */ w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable Seems to crash the Computer if enabled... */ w |= 0x0100; /* Wave Cache Operation Enabled */ w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */ w &= ~0x0060; /* Clear Wavtable Size */ w |= 0x0020; /* Wavetable Size : 1MB */ /* Bit 4 is reserved */ w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */ /* Bit 1 is reserved */ w &= ~0x0001; /* Test Mode off */ outw(w, iobase + WC_CONTROL); /* Now clear the APU control ram */ for (i = 0; i < NR_APUS; i++) { for (w = 0; w < NR_APU_REGS; w++) apu_set_register(chip, i, w, 0); } }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela78799.12%125.00%
René Herman40.50%125.00%
Takashi Iwai20.25%125.00%
Adam Buchbinder10.13%125.00%
Total794100.00%4100.00%

/* Enable IRQ's */
static void snd_es1968_start_irq(struct es1968 *chip) { unsigned short w; w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME; if (chip->rmidi) w |= ESM_HIRQ_MPU401; outb(w, chip->io_port + 0x1A); outw(w, chip->io_port + ESM_PORT_HOST_IRQ); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela4075.47%250.00%
Ville Syrjälä1120.75%125.00%
Takashi Iwai23.77%125.00%
Total53100.00%4100.00%

#ifdef CONFIG_PM_SLEEP /* * PM support */
static int es1968_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; if (! chip->do_pm) return 0; chip->in_suspend = 1; cancel_work_sync(&chip->hwvol_work); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_es1968_bob_stop(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela5262.65%660.00%
Takashi Iwai3137.35%440.00%
Total83100.00%10100.00%


static int es1968_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; struct esschan *es; if (! chip->do_pm) return 0; snd_es1968_chip_init(chip); /* need to restore the base pointers.. */ if (chip->dma.addr) { /* set PCMBAR */ wave_set_register(chip, 0x01FC, chip->dma.addr >> 12); } snd_es1968_start_irq(chip); /* restore ac97 state */ snd_ac97_resume(chip->ac97); list_for_each_entry(es, &chip->substream_list, list) { switch (es->mode) { case ESM_MODE_PLAY: snd_es1968_playback_setup(chip, es, es->substream->runtime); break; case ESM_MODE_CAPTURE: snd_es1968_capture_setup(chip, es, es->substream->runtime); break; } } /* start timer again */ if (chip->bobclient) snd_es1968_bob_start(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); chip->in_suspend = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela14182.94%763.64%
Takashi Iwai2313.53%327.27%
Matthias Kaehlcke63.53%19.09%
Total170100.00%11100.00%

static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); #define ES1968_PM_OPS &es1968_pm #else #define ES1968_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ #ifdef SUPPORT_JOYSTICK #define JOYSTICK_ADDR 0x200
static int snd_es1968_create_gameport(struct es1968 *chip, int dev) { struct gameport *gp; struct resource *r; u16 val; if (!joystick[dev]) return -ENODEV; r = request_region(JOYSTICK_ADDR, 8, "ES1968 gameport"); if (!r) return -EBUSY; chip->gameport = gp = gameport_allocate_port(); if (!gp) { dev_err(chip->card->dev, "cannot allocate memory for gameport\n"); release_and_free_resource(r); return -ENOMEM; } pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val); pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val | 0x04); gameport_set_name(gp, "ES1968 Gameport"); gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); gameport_set_dev_parent(gp, &chip->pci->dev); gp->io = JOYSTICK_ADDR; gameport_set_port_data(gp, r); gameport_register_port(gp); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov15287.36%116.67%
Jaroslav Kysela116.32%233.33%
Takashi Iwai116.32%350.00%
Total174100.00%6100.00%


static void snd_es1968_free_gameport(struct es1968 *chip) { if (chip->gameport) { struct resource *r = gameport_get_port_data(chip->gameport); gameport_unregister_port(chip->gameport); chip->gameport = NULL; release_and_free_resource(r); } }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4387.76%125.00%
Jaroslav Kysela36.12%125.00%
Takashi Iwai36.12%250.00%
Total49100.00%4100.00%

#else
static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov1789.47%150.00%
Takashi Iwai210.53%150.00%
Total19100.00%2100.00%


static inline void snd_es1968_free_gameport(struct es1968 *chip) { }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov981.82%150.00%
Takashi Iwai218.18%150.00%
Total11100.00%2100.00%

#endif #ifdef CONFIG_SND_ES1968_INPUT
static int snd_es1968_input_register(struct es1968 *chip) { struct input_dev *input_dev; int err; input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", pci_name(chip->pci)); input_dev->name = chip->card->driver; input_dev->phys = chip->phys; input_dev->id.bustype = BUS_PCI; input_dev->id.vendor = chip->pci->vendor; input_dev->id.product = chip->pci->device; input_dev->dev.parent = &chip->pci->dev; __set_bit(EV_KEY, input_dev->evbit); __set_bit(KEY_MUTE, input_dev->keybit); __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); __set_bit(KEY_VOLUMEUP, input_dev->keybit); err = input_register_device(input_dev); if (err) { input_free_device(input_dev); return err; } chip->input_dev = input_dev; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede185100.00%1100.00%
Total185100.00%1100.00%

#endif /* CONFIG_SND_ES1968_INPUT */ #ifdef CONFIG_SND_ES1968_RADIO #define GPIO_DATA 0x60 #define IO_MASK 4 /* mask register offset from GPIO_DATA bits 1=unmask write to given bit */ #define IO_DIR 8 /* direction register offset from GPIO_DATA bits 0/1=read/write direction */ /* GPIO to TEA575x maps */ struct snd_es1968_tea575x_gpio { u8 data, clk, wren, most; char *name; }; static struct snd_es1968_tea575x_gpio snd_es1968_tea575x_gpios[] = { { .data = 6, .clk = 7, .wren = 8, .most = 9, .name = "SF64-PCE2" }, { .data = 7, .clk = 8, .wren = 6, .most = 10, .name = "M56VAP" }, }; #define get_tea575x_gpio(chip) \ (&snd_es1968_tea575x_gpios[(chip)->tea575x_tuner])
static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) { struct es1968 *chip = tea->private_data; struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip); u16 val = 0; val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0; val |= (pins & TEA575X_CLK) ? (1 << gpio.clk) : 0; val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0; outw(val, chip->io_port + GPIO_DATA); }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary9390.29%342.86%
Jaroslav Kysela65.83%228.57%
Hans de Goede32.91%114.29%
Takashi Iwai10.97%114.29%
Total103100.00%7100.00%


static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea) { struct es1968 *chip = tea->private_data; struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip); u16 val = inw(chip->io_port + GPIO_DATA); u8 ret = 0; if (val & (1 << gpio.data)) ret |= TEA575X_DATA; if (val & (1 << gpio.most)) ret |= TEA575X_MOST; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary6579.27%375.00%
Dan Carpenter1720.73%125.00%
Total82100.00%4100.00%


static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output) { struct es1968 *chip = tea->private_data; unsigned long io = chip->io_port + GPIO_DATA; u16 odir = inw(io + IO_DIR); struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip); if (output) { outw(~((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren)), io + IO_MASK); outw(odir | (1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren), io + IO_DIR); } else { outw(~((1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) | (1 << gpio.most)), io + IO_MASK); outw((odir & ~((1 << gpio.data) | (1 << gpio.most))) | (1 << gpio.clk) | (1 << gpio.wren), io + IO_DIR); } }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary217100.00%3100.00%
Total217100.00%3100.00%

static const struct snd_tea575x_ops snd_es1968_tea_ops = { .set_pins = snd_es1968_tea575x_set_pins, .get_pins = snd_es1968_tea575x_get_pins, .set_direction = snd_es1968_tea575x_set_direction, }; #endif
static int snd_es1968_free(struct es1968 *chip) { cancel_work_sync(&chip->hwvol_work); #ifdef CONFIG_SND_ES1968_INPUT if (chip->input_dev) input_unregister_device(chip->input_dev); #endif if (chip->io_port) { if (chip->irq >= 0) synchronize_irq(chip->irq); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */ } #ifdef CONFIG_SND_ES1968_RADIO snd_tea575x_exit(&chip->tea); v4l2_device_unregister(&chip->v4l2_dev); #endif if (chip->irq >= 0) free_irq(chip->irq, chip); snd_es1968_free_gameport(chip); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ondrej Zary13389.26%133.33%
Hans Verkuil85.37%133.33%
Takashi Iwai85.37%133.33%
Total149100.00%3100.00%


static int snd_es1968_dev_free(struct snd_device *device) { struct es1968 *chip = device->device_data; return snd_es1968_free(chip); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela2284.62%266.67%
Takashi Iwai415.38%133.33%
Total26100.00%3100.00%

struct ess_device_list { unsigned short type; /* chip type */ unsigned short vendor; /* subsystem vendor id */ }; static struct ess_device_list pm_whitelist[] = { { TYPE_MAESTRO2E, 0x0e11 }, /* Compaq Armada */ { TYPE_MAESTRO2E, 0x1028 }, { TYPE_MAESTRO2E, 0x103c }, { TYPE_MAESTRO2E, 0x1179 }, { TYPE_MAESTRO2E, 0x14c0 }, /* HP omnibook 4150 */ { TYPE_MAESTRO2E, 0x1558 }, { TYPE_MAESTRO2E, 0x125d }, /* a PCI card, e.g. Terratec DMX */ { TYPE_MAESTRO2, 0x125d }, /* a PCI card, e.g. SF64-PCE2 */ }; static struct ess_device_list mpu_blacklist[] = { { TYPE_MAESTRO2, 0x125d }, };
static int snd_es1968_create(struct snd_card *card, struct pci_dev *pci, int total_bufsize, int play_streams, int capt_streams, int chip_type, int do_pm, int radio_nr, struct es1968 **chip_ret) { static struct snd_device_ops ops = { .dev_free = snd_es1968_dev_free, }; struct es1968 *chip; int i, err; *chip_ret = NULL; /* enable PCI device */ if ((err = pci_enable_device(pci)) < 0) return err; /* check, if we can restrict PCI DMA transfers to 28 bits */ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 || dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "architecture does not support 28bit PCI busmaster DMA\n"); pci_disable_device(pci); return -ENXIO; } chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (! chip) { pci_disable_device(pci); return -ENOMEM; } /* Set Vars */ chip->type = chip_type; spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->substream_lock); INIT_LIST_HEAD(&chip->buf_list); INIT_LIST_HEAD(&chip->substream_list); mutex_init(&chip->memory_mutex); INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume); chip->card = card; chip->pci = pci; chip->irq = -1; chip->total_bufsize = total_bufsize; /* in bytes */ chip->playback_streams = play_streams; chip->capture_streams = capt_streams; if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) { kfree(chip); pci_disable_device(pci); return err; } chip->io_port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); snd_es1968_free(chip); return -EBUSY; } chip->irq = pci->irq; /* Clear Maestro_map */ for (i = 0; i < 32; i++) chip->maestro_map[i] = 0; /* Clear Apu Map */ for (i = 0; i < NR_APUS; i++) chip->apu[i] = ESM_APU_FREE; /* just to be sure */ pci_set_master(pci); if (do_pm > 1) { /* disable power-management if not on the whitelist */ unsigned short vend; pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); for (i = 0; i < (int)ARRAY_SIZE(pm_whitelist); i++) { if (chip->type == pm_whitelist[i].type && vend == pm_whitelist[i].vendor) { do_pm = 1; break; } } if (do_pm > 1) { /* not matched; disabling pm */ dev_info(card->dev, "not attempting power management.\n"); do_pm = 0; } } chip->do_pm = do_pm; snd_es1968_chip_init(chip); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_es1968_free(chip); return err; } #ifdef CONFIG_SND_ES1968_RADIO /* don't play with GPIOs on laptops */ if (chip->pci->subsystem_vendor != 0x125d) goto no_radio; err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); if (err < 0) { snd_es1968_free(chip); return err; } chip->tea.v4l2_dev = &chip->v4l2_dev; chip->tea.private_data = chip; chip->tea.radio_nr = radio_nr; chip->tea.ops = &snd_es1968_tea_ops; sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); for (i = 0; i < ARRAY_SIZE(snd_es1968_tea575x_gpios); i++) { chip->tea575x_tuner = i; if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) { dev_info(card->dev, "detected TEA575x radio type %s\n", get_tea575x_gpio(chip)->name); strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name, sizeof(chip->tea.card)); break; } } no_radio: #endif *chip_ret = chip; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela49967.98%1346.43%
Ondrej Zary12717.30%414.29%
Hans Verkuil537.22%13.57%
Takashi Iwai344.63%517.86%
Yang Hongyang81.09%13.57%
Quentin Lambert81.09%13.57%
Hans de Goede40.54%27.14%
Thomas Gleixner10.14%13.57%
Total734100.00%28100.00%

/* */
static int snd_es1968_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; struct snd_card *card; struct es1968 *chip; unsigned int i; int err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) return err; if (total_bufsize[dev] < 128) total_bufsize[dev] = 128; if (total_bufsize[dev] > 4096) total_bufsize[dev] = 4096; if ((err = snd_es1968_create(card, pci, total_bufsize[dev] * 1024, /* in bytes */ pcm_substreams_p[dev], pcm_substreams_c[dev], pci_id->driver_data, use_pm[dev], radio_nr[dev], &chip)) < 0) { snd_card_free(card); return err; } card->private_data = chip; switch (chip->type) { case TYPE_MAESTRO2E: strcpy(card->driver, "ES1978"); strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); break; case TYPE_MAESTRO2: strcpy(card->driver, "ES1968"); strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); break; case TYPE_MAESTRO: strcpy(card->driver, "ESM1"); strcpy(card->shortname, "ESS Maestro 1"); break; } if ((err = snd_es1968_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } if ((err = snd_es1968_mixer(chip)) < 0) { snd_card_free(card); return err; } if (enable_mpu[dev] == 2) { /* check the black list */ unsigned short vend; pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); for (i = 0; i < ARRAY_SIZE(mpu_blacklist); i++) { if (chip->type == mpu_blacklist[i].type && vend == mpu_blacklist[i].vendor) { enable_mpu[dev] = 0; break; } } } if (enable_mpu[dev]) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, chip->io_port + ESM_MPU401_PORT, MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK, -1, &chip->rmidi)) < 0) { dev_warn(card->dev, "skipping MPU-401 MIDI support..\n"); } } snd_es1968_create_gameport(chip, dev); #ifdef CONFIG_SND_ES1968_INPUT err = snd_es1968_input_register(chip); if (err) dev_warn(card->dev, "Input device registration failed with error %i", err); #endif snd_es1968_start_irq(chip); chip->clock = clock[dev]; if (! chip->clock) es1968_measure_clock(chip); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->io_port, chip->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); dev++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela50187.74%947.37%
Takashi Iwai376.48%631.58%
Hans de Goede213.68%15.26%
Hans Verkuil50.88%15.26%
Clemens Ladisch40.70%15.26%
Dmitry Torokhov30.53%15.26%
Total571100.00%19100.00%


static void snd_es1968_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); }

Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela19100.00%2100.00%
Total19100.00%2100.00%

static struct pci_driver es1968_driver = { .name = KBUILD_MODNAME, .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .remove = snd_es1968_remove, .driver = { .pm = ES1968_PM_OPS, }, }; module_pci_driver(es1968_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
Jaroslav Kysela1124882.05%4241.58%
Ondrej Zary8015.84%54.95%
Takashi Iwai5984.36%1817.82%
Hans de Goede3572.60%21.98%
Dmitry Torokhov2361.72%10.99%
Hans Verkuil1100.80%10.99%
Andreas Mueller990.72%10.99%
Arjan van de Ven520.38%10.99%
Matthias Kaehlcke310.23%10.99%
Ville Syrjälä290.21%21.98%
Tina Ruchandani200.15%10.99%
Dan Carpenter170.12%10.99%
René Herman140.10%21.98%
Ingo Molnar120.09%10.99%
Rusty Russell120.09%21.98%
Harvey Harrison110.08%10.99%
Yang Hongyang80.06%10.99%
Quentin Lambert80.06%10.99%
David S. Miller80.06%10.99%
Clemens Ladisch80.06%21.98%
Benoit Taine60.04%10.99%
Nishanth Aravamudan50.04%10.99%
Jesper Juhl40.03%10.99%
Tobias Klauser30.02%10.99%
Julia Lawall30.02%21.98%
Steven Cole30.02%21.98%
Thomas Gleixner10.01%10.99%
Paul Gortmaker10.01%10.99%
Fabian Frederick10.01%10.99%
Uwe Kleine-König10.01%10.99%
Adam Buchbinder10.01%10.99%
Mauro Carvalho Chehab10.01%10.99%
Bill Pemberton0.00%00.00%
Total13709100.00%101100.00%
Directory: sound/pci
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.