Commit a1a1c402 authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA update

   - CS46xx driver
     - DSP is started after initializing AC97 codecs
     - rewrite SPDIF output stuff
     - variable period size support on playback and capture
     - DAC volume mechanism  rewrite
     - IEC958 input volume mechanism rewrite
     - added "AC3 Mode Switch" in mixer
     - code cleanups
   - ENS1371 driver
     - added definitions for the ES1373 chip
     - added code to control IEC958 (S/PDIF) channel status register
parent a673e746
......@@ -196,80 +196,6 @@
#define BA1_OMNI_MEM 0x000E0000
/*
* The following define the offsets of the AC97 shadow registers, which appear
* as a virtual extension to the base address register zero memory range.
*/
#define AC97_REG_OFFSET_MASK 0x0000007EL
#define AC97_CODEC_NUMBER_MASK 0x00003000L
#define BA0_AC97_RESET 0x00001000L
#define BA0_AC97_MASTER_VOLUME 0x00001002L
#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L
#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L
#define BA0_AC97_MASTER_TONE 0x00001008L
#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL
#define BA0_AC97_PHONE_VOLUME 0x0000100CL
#define BA0_AC97_MIC_VOLUME 0x0000100EL
#define BA0_AC97_LINE_IN_VOLUME 0x00001010L
#define BA0_AC97_CD_VOLUME 0x00001012L
#define BA0_AC97_VIDEO_VOLUME 0x00001014L
#define BA0_AC97_AUX_VOLUME 0x00001016L
#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L
#define BA0_AC97_RECORD_SELECT 0x0000101AL
#define BA0_AC97_RECORD_GAIN 0x0000101CL
#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL
#define BA0_AC97_GENERAL_PURPOSE 0x00001020L
#define BA0_AC97_3D_CONTROL 0x00001022L
#define BA0_AC97_MODEM_RATE 0x00001024L
#define BA0_AC97_POWERDOWN 0x00001026L
#define BA0_AC97_EXT_AUDIO_ID 0x00001028L
#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL
#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL
#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL
#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L
#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L
#define BA0_AC97_MIC_ADC_RATE 0x00001034L
#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L
#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L
#define BA0_AC97_RESERVED_3A 0x0000103AL
#define BA0_AC97_EXT_MODEM_ID 0x0000103CL
#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL
#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L
#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L
#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L
#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L
#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L
#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL
#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL
#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL
#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L
#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L
#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L
#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L
#define BA0_AC97_RESERVED_58 0x00001058L
#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL
#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL
#define BA0_AC97_AC_MODE 0x0000105EL
#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L
#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L
#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L
#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L
#define BA0_AC97_SPDIF_CONTROL 0x00001068L
#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL
#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL
#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL
#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L
#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L
#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L
#define BA0_AC97_CAL_ADDRESS 0x00001076L
#define BA0_AC97_CAL_DATA 0x00001078L
#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL
#define BA0_AC97_VENDOR_ID1 0x0000107CL
#define BA0_AC97_VENDOR_ID2 0x0000107EL
/*
* The following defines are for the flags in the host interrupt status
* register.
......@@ -1790,6 +1716,7 @@ struct _snd_cs46xx {
struct pci_dev *pci;
snd_card_t *card;
snd_pcm_t *pcm;
snd_rawmidi_t *rmidi;
snd_rawmidi_substream_t *midi_input;
snd_rawmidi_substream_t *midi_output;
......@@ -1820,6 +1747,9 @@ struct _snd_cs46xx {
struct semaphore spos_mutex;
dsp_spos_instance_t * dsp_spos_instance;
snd_pcm_t *pcm_rear;
snd_pcm_t *pcm_iec958;
#else /* for compatibility */
cs46xx_pcm_t *playback_pcm;
unsigned int play_ctl;
......@@ -1832,8 +1762,11 @@ int snd_cs46xx_create(snd_card_t *card,
cs46xx_t **rcodec);
int snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t **rpcm);
int snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t **rpcm);
int snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t **rpcm);
int snd_cs46xx_mixer(cs46xx_t *chip);
int snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rmidi);
int snd_cs46xx_start_dsp(cs46xx_t *chip);
void snd_cs46xx_gameport(cs46xx_t *chip);
#ifdef CONFIG_PM
......
......@@ -54,6 +54,17 @@
#define DSP_MAX_PCM_CHANNELS 32
#define DSP_MAX_SRC_NR 6
#define DSP_PCM_MAIN_CHANNEL 1
#define DSP_PCM_REAR_CHANNEL 2
#define DSP_PCM_CENTER_CHANNEL 3
#define DSP_PCM_LFE_CHANNEL 4
#define DSP_IEC958_CHANNEL 5
#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1
#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2
#define DSP_SPDIF_STATUS_HW_ENABLED 4
#define DSP_SPDIF_STATUS_AC3_MODE 8
struct _dsp_module_desc_t;
typedef struct _symbol_entry_t {
......@@ -129,6 +140,8 @@ typedef struct _pcm_channel_descriptor_t {
u32 unlinked;
dsp_scb_descriptor_t * pcm_reader_scb;
dsp_scb_descriptor_t * src_scb;
dsp_scb_descriptor_t * mixer_scb;
int pcm_channel_id;
void * private_data;
} pcm_channel_descriptor_t;
......@@ -141,8 +154,14 @@ typedef struct _dsp_spos_instance_t {
segment_desc_t code;
/* PCM playback */
/* Main PCM playback mixer */
dsp_scb_descriptor_t * master_mix_scb;
u16 dac_volume_right;
u16 dac_volume_left;
/* Rear PCM playback mixer */
dsp_scb_descriptor_t * rear_mix_scb;
int npcm_channels;
int nsrc_scb;
pcm_channel_descriptor_t pcm_channels[DSP_MAX_PCM_CHANNELS];
......@@ -175,7 +194,8 @@ typedef struct _dsp_spos_instance_t {
/* SPDIF status */
int spdif_status_out;
int spdif_status_in;
u32 spdif_input_volume;
u16 spdif_input_volume_right;
u16 spdif_input_volume_left;
/* SPDIF input sample rate converter */
dsp_scb_descriptor_t * spdif_in_src;
......@@ -191,6 +211,12 @@ typedef struct _dsp_spos_instance_t {
/* reference snooper */
dsp_scb_descriptor_t * ref_snoop_scb;
/* SPDIF output PCM reference */
dsp_scb_descriptor_t * spdif_pcm_input_scb;
/* asynch TX task */
dsp_scb_descriptor_t * asynch_tx_scb;
/* record sources */
dsp_scb_descriptor_t * pcm_input;
dsp_scb_descriptor_t * adc_input;
......@@ -199,4 +225,3 @@ typedef struct _dsp_spos_instance_t {
} dsp_spos_instance_t;
#endif /* __DSP_SPOS_H__ */
......@@ -110,6 +110,16 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
snd_card_free(card);
return err;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) {
snd_card_free(card);
return err;
}
if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) {
snd_card_free(card);
return err;
}
#endif
if ((err = snd_cs46xx_mixer(chip)) < 0) {
snd_card_free(card);
return err;
......@@ -118,6 +128,12 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
snd_card_free(card);
return err;
}
if ((err = snd_cs46xx_start_dsp(chip)) < 0) {
snd_card_free(card);
return err;
}
snd_cs46xx_gameport(chip);
strcpy(card->driver, "CS46xx");
......@@ -132,6 +148,7 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
snd_card_free(card);
return err;
}
pci_set_drvdata(pci, chip);
dev++;
return 0;
......
......@@ -15,8 +15,8 @@
* TODO:
* - Secondary CODEC on some soundcards
* - SPDIF input support for other sample rates then 48khz
* - Independent PCM channels for rear output
* - Posibility to mix the SPDIF output with analog sources.
* - PCM channels for Center and LFE on secondary codec
*
* NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which
* is default configuration), no SPDIF, no secondary codec, no
......@@ -269,23 +269,35 @@ static void snd_cs46xx_ac97_write(ac97_t *ac97,
unsigned short val)
{
cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return);
#ifndef CONFIG_SND_CS46XX_NEW_DSP
int val2 = 0;
#endif
int codec_index = -1;
/* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */
if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] || chip->nr_ac97_codecs == 0)
codec_index = CS46XX_PRIMARY_CODEC_INDEX;
/* UGGLY: nr_ac97_codecs == 0 secondary codec detection is in progress */
/* UGGLY: nr_ac97_codecs == 1 secondary codec detection is in progress */
else if (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] || chip->nr_ac97_codecs == 1)
codec_index = CS46XX_SECONDARY_CODEC_INDEX;
else
snd_assert(0,return);
chip->active_ctrl(chip, 1);
#ifndef CONFIG_SND_CS46XX_NEW_DSP
if (reg == AC97_CD)
val2 = snd_cs46xx_codec_read(chip, AC97_CD, codec_index);
#endif
snd_cs46xx_codec_write(chip, reg, val, codec_index);
#ifndef CONFIG_SND_CS46XX_NEW_DSP
/* Benny: I've not found *one* soundcard where
this code below could do any sense, and
with the HW mixering it's anyway broken, with
more then 1 PCM stream the amplifier will not
be turned off by unmuting CD channel. So just
lets skip it.
*/
/*
* Adjust power if the mixer is selected/deselected according
......@@ -323,6 +335,7 @@ static void snd_cs46xx_ac97_write(ac97_t *ac97,
}
chip->active_ctrl(chip, -1);
#endif
}
......@@ -432,9 +445,33 @@ static void snd_cs46xx_reset(cs46xx_t *chip)
snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
}
static int cs46xx_wait_for_fifo(cs46xx_t * chip,int retry_timeout)
{
u32 i, status;
/*
* Make sure the previous FIFO write operation has completed.
*/
for(i = 0; i < 50; i++){
status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
if( !(status & SERBST_WBSY) )
break;
mdelay(retry_timeout);
}
if(status & SERBST_WBSY) {
snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n");
return -EINVAL;
}
return 0;
}
static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip)
{
int idx, loop, powerdown = 0;
int idx, powerdown = 0;
unsigned int tmp;
/*
......@@ -457,18 +494,17 @@ static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip)
/*
* Fill all 256 sample FIFO locations.
*/
for (idx = 0; idx < 256; idx++) {
for (idx = 0; idx < 0xFF; idx++) {
/*
* Make sure the previous FIFO write operation has completed.
*/
for (loop = 0; loop < 5; loop++) {
udelay(50);
if (!(snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY))
break;
}
if (snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY) {
if (cs46xx_wait_for_fifo(chip,1)) {
snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);
if (powerdown)
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
break;
}
/*
* Write the serial port FIFO index.
......@@ -692,6 +728,8 @@ static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream,
snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_sframes_t diff;
cs46xx_pcm_t * cpcm;
int buffer_size = runtime->period_size * CS46XX_FRAGS * 4;
cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
diff = runtime->control->appl_ptr - cpcm->appl_ptr;
......@@ -702,11 +740,11 @@ static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream,
}
cpcm->sw_ready += frames << cpcm->shift;
cpcm->appl_ptr = runtime->control->appl_ptr + frames;
while (cpcm->hw_ready < CS46XX_BUFFER_SIZE &&
while (cpcm->hw_ready < buffer_size &&
cpcm->sw_ready > 0) {
size_t hw_to_end = CS46XX_BUFFER_SIZE - cpcm->hw_data;
size_t hw_to_end = buffer_size - cpcm->hw_data;
size_t sw_to_end = cpcm->sw_bufsize - cpcm->sw_data;
size_t bytes = CS46XX_BUFFER_SIZE - cpcm->hw_ready;
size_t bytes = buffer_size - cpcm->hw_ready;
if (cpcm->sw_ready < bytes)
bytes = cpcm->sw_ready;
if (hw_to_end < bytes)
......@@ -717,7 +755,7 @@ static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream,
runtime->dma_area + cpcm->sw_data,
bytes);
cpcm->hw_data += bytes;
if (cpcm->hw_data == CS46XX_BUFFER_SIZE)
if (cpcm->hw_data == buffer_size)
cpcm->hw_data = 0;
cpcm->sw_data += bytes;
if (cpcm->sw_data == cpcm->sw_bufsize)
......@@ -734,6 +772,7 @@ static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream,
cs46xx_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr;
int buffer_size = runtime->period_size * CS46XX_FRAGS * 4;
if (diff) {
if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
diff += runtime->boundary;
......@@ -743,7 +782,7 @@ static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream,
chip->capt.appl_ptr = runtime->control->appl_ptr + frames;
while (chip->capt.hw_ready > 0 &&
chip->capt.sw_ready < chip->capt.sw_bufsize) {
size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->capt.hw_data;
size_t hw_to_end = buffer_size - chip->capt.hw_data;
size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data;
size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready;
if (chip->capt.hw_ready < bytes)
......@@ -756,7 +795,7 @@ static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream,
chip->capt.hw_area + chip->capt.hw_data,
bytes);
chip->capt.hw_data += bytes;
if (chip->capt.hw_data == CS46XX_BUFFER_SIZE)
if (chip->capt.hw_data == buffer_size)
chip->capt.hw_data = 0;
chip->capt.sw_data += bytes;
if (chip->capt.sw_data == chip->capt.sw_bufsize)
......@@ -789,6 +828,7 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_
size_t ptr;
cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
ssize_t bytes;
int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
snd_assert (cpcm->pcm_channel,return -ENXIO);
......@@ -801,7 +841,7 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_
bytes = ptr - cpcm->hw_io;
if (bytes < 0)
bytes += CS46XX_BUFFER_SIZE;
bytes += buffer_size;
cpcm->hw_io = ptr;
cpcm->hw_ready -= bytes;
cpcm->sw_io += bytes;
......@@ -823,8 +863,10 @@ static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t
cs46xx_t *chip = snd_pcm_substream_chip(substream);
size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr;
ssize_t bytes = ptr - chip->capt.hw_io;
int buffer_size = substream->runtime->period_size * CS46XX_FRAGS * 4;
if (bytes < 0)
bytes += CS46XX_BUFFER_SIZE;
bytes += buffer_size;
chip->capt.hw_io = ptr;
chip->capt.hw_ready += bytes;
chip->capt.sw_io += bytes;
......@@ -910,7 +952,9 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
snd_cs46xx_playback_transfer(substream, 0);
/* raise playback volume */
snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0x80008000);
cs46xx_dsp_scb_set_volume (chip,cpcm->pcm_channel->pcm_reader_scb,
chip->dsp_spos_instance->dac_volume_right,
chip->dsp_spos_instance->dac_volume_left);
#else
if (substream->runtime->periods != CS46XX_FRAGS)
snd_cs46xx_playback_transfer(substream, 0);
......@@ -925,7 +969,8 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* mute channel */
snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0xffffffff);
cs46xx_dsp_scb_set_volume (chip,cpcm->pcm_channel->pcm_reader_scb,0,0);
if (!cpcm->pcm_channel->unlinked)
cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
#else
......@@ -978,6 +1023,44 @@ static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream,
return result;
}
static int _cs46xx_adjust_sample_rate (cs46xx_t *chip, cs46xx_pcm_t *cpcm,
int sample_rate)
{
/* if this is the only PCMReaderSCB child under current
SrcTask then there no need to re-create pcm-channel */
if ( cpcm->pcm_channel->src_scb->ref_count == 1 &&
cpcm->pcm_channel->sample_rate != sample_rate &&
/* never set a 0 khz sample rate */
sample_rate) {
/* sample rate not set or we can reuse
the same SRC*/
cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,sample_rate);
cpcm->pcm_channel->sample_rate = sample_rate;
}
/* if there is more then 1 PCMReaderSCB child's under current
SrcTask then we must recreate channel */
if (cpcm->pcm_channel->sample_rate != sample_rate &&
cpcm->pcm_channel->src_scb->ref_count != 1 &&
/* never set a 0 khz sample rate */
sample_rate) {
int unlinked = cpcm->pcm_channel->unlinked;
cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm,
cpcm->hw_addr,
cpcm->pcm_channel->pcm_channel_id)) == NULL) {
snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
up (&chip->spos_mutex);
return -ENXIO;
}
if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
cpcm->pcm_channel->sample_rate = sample_rate;
}
return 0;
}
static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
......@@ -985,26 +1068,87 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream,
snd_pcm_runtime_t *runtime = substream->runtime;
cs46xx_pcm_t *cpcm;
int err;
cs46xx_t *chip = snd_pcm_substream_chip(substream);
int sample_rate = params_rate(hw_params);
int period_size = params_period_size(hw_params);
cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
down (&chip->spos_mutex);
snd_assert (cpcm->pcm_channel != NULL);
/* if IEC958 is opened in AC3 mode dont adjust SRCTask is not
used so dont adjust sample rate */
if (cpcm->pcm_channel->pcm_channel_id != DSP_IEC958_CHANNEL ||
!(chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_AC3_MODE)) {
if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
return -ENXIO;
}
}
if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size * 4)) {
up (&chip->spos_mutex);
return -EINVAL;
}
snd_printdd ("period_size (%d), periods (%d)\n",
period_size, params_periods(hw_params));
#endif
if (params_periods(hw_params) == CS46XX_FRAGS) {
if (runtime->dma_area != cpcm->hw_area)
snd_pcm_lib_free_pages(substream);
runtime->dma_area = cpcm->hw_area;
runtime->dma_addr = cpcm->hw_addr;
runtime->dma_bytes = cpcm->hw_size;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (cpcm->pcm_channel->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
substream->ops = &snd_cs46xx_playback_ops;
} else if (cpcm->pcm_channel->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
substream->ops = &snd_cs46xx_playback_rear_ops;
} else if (cpcm->pcm_channel->pcm_channel_id == DSP_IEC958_CHANNEL) {
substream->ops = &snd_cs46xx_playback_iec958_ops;
} else {
snd_assert(0);
}
#else
substream->ops = &snd_cs46xx_playback_ops;
#endif
} else {
if (runtime->dma_area == cpcm->hw_area) {
runtime->dma_area = NULL;
runtime->dma_addr = 0;
runtime->dma_bytes = 0;
}
if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
up (&chip->spos_mutex);
#endif
return err;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (cpcm->pcm_channel->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
substream->ops = &snd_cs46xx_playback_indirect_ops;
} else if (cpcm->pcm_channel->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
substream->ops = &snd_cs46xx_playback_indirect_rear_ops;
} else if (cpcm->pcm_channel->pcm_channel_id == DSP_IEC958_CHANNEL) {
substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
} else {
snd_assert(0);
}
#else
substream->ops = &snd_cs46xx_playback_indirect_ops;
#endif
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
up (&chip->spos_mutex);
#endif
return 0;
}
......@@ -1038,35 +1182,10 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
down (&chip->spos_mutex);
if ( cpcm->pcm_channel->src_scb->ref_count == 1 &&
cpcm->pcm_channel->sample_rate != runtime->rate) {
/* sample rate not set or we can reuse
the same SRC*/
cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,runtime->rate);
cpcm->pcm_channel->sample_rate = runtime->rate;
}
if (cpcm->pcm_channel->sample_rate != runtime->rate &&
cpcm->pcm_channel->src_scb->ref_count != 1) {
int unlinked = cpcm->pcm_channel->unlinked;
cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr)) == NULL) {
snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
up (&chip->spos_mutex);
return -ENXIO;
}
if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
cpcm->pcm_channel->sample_rate = runtime->rate;
}
snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
pfie &= ~0x0000f03f;
up (&chip->spos_mutex);
#else
/* old dsp */
pfie = snd_cs46xx_peek(chip, BA1_PFIE);
......@@ -1101,6 +1220,7 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
cpcm->appl_ptr = 0;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2);
tmp &= ~0x000003ff;
tmp |= (4 << cpcm->shift) - 1;
......@@ -1128,7 +1248,13 @@ static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream,
cs46xx_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
int err;
int period_size = params_period_size(hw_params);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
snd_printdd ("capture period size (%d)\n",period_size);
cs46xx_dsp_pcm_ostream_set_period (chip,period_size * 4);
#endif
if (runtime->periods == CS46XX_FRAGS) {
if (runtime->dma_area != chip->capt.hw_area)
snd_pcm_lib_free_pages(substream);
......@@ -1285,8 +1411,8 @@ static snd_pcm_hardware_t snd_cs46xx_playback =
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (256 * 1024),
.period_bytes_min = CS46XX_PERIOD_SIZE,
.period_bytes_max = CS46XX_PERIOD_SIZE,
.period_bytes_min = CS46XX_MIN_PERIOD_SIZE,
.period_bytes_max = CS46XX_MAX_PERIOD_SIZE,
.periods_min = CS46XX_FRAGS,
.periods_max = 1024,
.fifo_size = 0,
......@@ -1305,13 +1431,23 @@ static snd_pcm_hardware_t snd_cs46xx_capture =
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = (256 * 1024),
.period_bytes_min = CS46XX_PERIOD_SIZE,
.period_bytes_max = CS46XX_PERIOD_SIZE,
.period_bytes_min = CS46XX_MIN_PERIOD_SIZE,
.period_bytes_max = CS46XX_MAX_PERIOD_SIZE,
.periods_min = CS46XX_FRAGS,
.periods_max = 1024,
.fifo_size = 0,
};
static unsigned int period_sizes[] = { 8, 16, 32, 64, 128, 256, 512 };
#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0])
static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
.count = PERIOD_SIZES,
.list = period_sizes,
.mask = 0
};
static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime)
{
cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return);
......@@ -1320,7 +1456,7 @@ static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime)
snd_magic_kfree(cpcm);
}
static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id)
{
cs46xx_t *chip = snd_pcm_substream_chip(substream);
cs46xx_pcm_t * cpcm;
......@@ -1342,7 +1478,7 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
cpcm->substream = substream;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
down (&chip->spos_mutex);
cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr);
cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr,pcm_channel_id);
if (cpcm->pcm_channel == NULL) {
snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
......@@ -1352,6 +1488,8 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
return -ENOMEM;
}
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
&hw_constraints_period_sizes);
up (&chip->spos_mutex);
#else
chip->playback_pcm = cpcm; /* HACK */
......@@ -1365,6 +1503,52 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
return 0;
}
static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
{
snd_printdd("open front channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static int snd_cs46xx_playback_open_rear(snd_pcm_substream_t * substream)
{
snd_printdd("open rear channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
}
static int snd_cs46xx_playback_open_iec958(snd_pcm_substream_t * substream)
{
cs46xx_t *chip = snd_pcm_substream_chip(substream);
snd_printdd("open raw iec958 channel\n");
down (&chip->spos_mutex);
cs46xx_iec958_pre_open (chip);
up (&chip->spos_mutex);
return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
}
static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream);
static int snd_cs46xx_playback_close_iec958(snd_pcm_substream_t * substream)
{
int err;
cs46xx_t *chip = snd_pcm_substream_chip(substream);
snd_printdd("close raw iec958 channel\n");
err = snd_cs46xx_playback_close(substream);
down (&chip->spos_mutex);
cs46xx_iec958_post_close (chip);
up (&chip->spos_mutex);
return err;
}
#endif
static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream)
{
cs46xx_t *chip = snd_pcm_substream_chip(substream);
......@@ -1380,6 +1564,10 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream)
chip->active_ctrl(chip, 1);
chip->amplifier_ctrl(chip, 1);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
&hw_constraints_period_sizes);
#endif
return 0;
}
......@@ -1422,6 +1610,55 @@ static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream)
return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
snd_pcm_ops_t snd_cs46xx_playback_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
.trigger = snd_cs46xx_playback_trigger,
.pointer = snd_cs46xx_playback_direct_pointer,
};
snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
.trigger = snd_cs46xx_playback_trigger,
.copy = snd_cs46xx_playback_copy,
.pointer = snd_cs46xx_playback_indirect_pointer,
};
snd_pcm_ops_t snd_cs46xx_playback_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
.trigger = snd_cs46xx_playback_trigger,
.pointer = snd_cs46xx_playback_direct_pointer,
};
snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
.trigger = snd_cs46xx_playback_trigger,
.copy = snd_cs46xx_playback_copy,
.pointer = snd_cs46xx_playback_indirect_pointer,
};
#endif
snd_pcm_ops_t snd_cs46xx_playback_ops = {
.open = snd_cs46xx_playback_open,
.close = snd_cs46xx_playback_close,
......@@ -1476,6 +1713,20 @@ static void snd_cs46xx_pcm_free(snd_pcm_t *pcm)
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static void snd_cs46xx_pcm_rear_free(snd_pcm_t *pcm)
{
cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return);
chip->pcm_rear = NULL;
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static void snd_cs46xx_pcm_iec958_free(snd_pcm_t *pcm)
{
cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return);
chip->pcm_iec958 = NULL;
snd_pcm_lib_preallocate_free_for_all(pcm);
}
#define MAX_PLAYBACK_CHANNELS (DSP_MAX_PCM_CHANNELS - 1)
#else
#define MAX_PLAYBACK_CHANNELS 1
......@@ -1490,6 +1741,7 @@ int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
*rpcm = NULL;
if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0)
return err;
pcm->private_data = chip;
pcm->private_free = snd_cs46xx_pcm_free;
......@@ -1505,13 +1757,74 @@ int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
if (rpcm)
*rpcm = pcm;
return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
int __devinit snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
{
snd_pcm_t *pcm;
int err;
if (rpcm)
*rpcm = NULL;
if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
return err;
pcm->private_data = chip;
pcm->private_free = snd_cs46xx_pcm_rear_free;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_rear_ops);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - Rear");
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
if (rpcm)
*rpcm = pcm;
return 0;
}
int __devinit snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
{
snd_pcm_t *pcm;
int err;
if (rpcm)
*rpcm = NULL;
if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0)
return err;
pcm->private_data = chip;
pcm->private_free = snd_cs46xx_pcm_iec958_free;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_iec958_ops);
/* global setup */
pcm->info_flags = 0;
strcpy(pcm->name, "CS46xx - IEC958");
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
if (rpcm)
*rpcm = pcm;
return 0;
}
#endif
/*
* Mixer routines
*/
static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97)
{
cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return);
......@@ -1534,7 +1847,7 @@ static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 32767;
uinfo->value.integer.max = 0x7fff;
return 0;
}
......@@ -1556,32 +1869,66 @@ static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *
(0xffff - ucontrol->value.integer.value[1]));
unsigned int old = snd_cs46xx_peek(chip, reg);
int change = (old != val);
if (change) {
snd_cs46xx_poke(chip, reg, val);
}
return change;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* NOTE: this updates the current left and right volume
that should be automatically updated by the DSP and
not touched by the host. But for some strange reason
the DSP only updates the right channel volume, so with
this dirty hack we force updating the right and left
channel volume.
*/
snd_cs46xx_poke(chip, reg + 4, val);
/* shadow the SPDIF input volume */
if (reg == (ASYNCRX_SCB_ADDR + 0xE) << 2) {
/* FIXME: I known this is uggly ...
any other suggestion ?
*/
chip->dsp_spos_instance->spdif_input_volume = val;
}
#endif
static int snd_cs46xx_vol_dac_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->dac_volume_right;
ucontrol->value.integer.value[1] = chip->dsp_spos_instance->dac_volume_left;
return 0;
}
static int snd_cs46xx_vol_dac_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
int change = 0;
if (chip->dsp_spos_instance->dac_volume_right != ucontrol->value.integer.value[0] ||
chip->dsp_spos_instance->dac_volume_left != ucontrol->value.integer.value[1]) {
cs46xx_dsp_set_dac_volume(chip,
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1]);
change = 1;
}
return change;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static int snd_cs46xx_vol_iec958_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_input_volume_right;
ucontrol->value.integer.value[1] = chip->dsp_spos_instance->spdif_input_volume_left;
return 0;
}
static int snd_cs46xx_vol_iec958_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
int change = 0;
if (chip->dsp_spos_instance->spdif_input_volume_right != ucontrol->value.integer.value[0] ||
chip->dsp_spos_instance->spdif_input_volume_left != ucontrol->value.integer.value[1]) {
cs46xx_dsp_set_iec958_volume (chip,
ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1]);
change = 1;
}
return change;
}
static int snd_mixer_boolean_info(snd_kcontrol_t *kcontrol,
snd_ctl_elem_info_t *uinfo)
......@@ -1600,7 +1947,7 @@ static int snd_cs46xx_iec958_get(snd_kcontrol_t *kcontrol,
int reg = kcontrol->private_value;
if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT)
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out;
ucontrol->value.integer.value[0] = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
else
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
......@@ -1615,13 +1962,15 @@ static int snd_cs46xx_iec958_put(snd_kcontrol_t *kcontrol,
switch (kcontrol->private_value) {
case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
change = chip->dsp_spos_instance->spdif_status_out;
down (&chip->spos_mutex);
change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
if (ucontrol->value.integer.value[0] && !change)
cs46xx_dsp_enable_spdif_out(chip);
else if (change && !ucontrol->value.integer.value[0])
cs46xx_dsp_disable_spdif_out(chip);
res = (change != chip->dsp_spos_instance->spdif_status_out);
res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
up (&chip->spos_mutex);
break;
case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
change = chip->dsp_spos_instance->spdif_status_in;
......@@ -1686,6 +2035,35 @@ static int snd_cs46xx_pcm_capture_get(snd_kcontrol_t *kcontrol,
return 0;
}
static int snd_cs46xx_iec958_ac3_mode_get(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
if (!ins->spdif_status_out & DSP_SPDIF_STATUS_AC3_MODE)
ucontrol->value.integer.value[0] = 1;
else
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int snd_cs46xx_iec958_ac3_mode_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
{
cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
int old = ins->spdif_status_out;
if (ucontrol->value.integer.value[0])
ins->spdif_status_out |= DSP_SPDIF_STATUS_AC3_MODE;
else
ins->spdif_status_out &= ~DSP_SPDIF_STATUS_AC3_MODE;
return (old != ins->spdif_status_out);
}
static int snd_cs46xx_pcm_capture_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
{
......@@ -1720,7 +2098,7 @@ static int snd_herc_spdif_select_get(snd_kcontrol_t *kcontrol,
}
/*
* Game Theatre XP card - EGPIO[0] is used to select SDPIF input optical or coaxial.
* Game Theatre XP card - EGPIO[0] is used to select SPDIF input optical or coaxial.
*/
static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
......@@ -1818,13 +2196,13 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
.info = snd_cs46xx_vol_info,
#ifndef CONFIG_SND_CS46XX_NEW_DSP
.get = snd_cs46xx_vol_get,
.put = snd_cs46xx_vol_put,
#ifndef CONFIG_SND_CS46XX_NEW_DSP
.private_value = BA1_PVOL,
#else
.private_value = (MASTERMIX_SCB_ADDR + 0xE) << 2,
.get = snd_cs46xx_vol_dac_get,
.put = snd_cs46xx_vol_dac_put,
#endif
},
......@@ -1863,6 +2241,13 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
.put = snd_cs46xx_iec958_put,
.private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC 958 AC3 Mode Switch",
.info = snd_mixer_boolean_info,
.get = snd_cs46xx_iec958_ac3_mode_get,
.put = snd_cs46xx_iec958_ac3_mode_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC 958 Input Switch",
......@@ -1875,8 +2260,8 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC 958 Input Volume",
.info = snd_cs46xx_vol_info,
.get = snd_cs46xx_vol_get,
.put = snd_cs46xx_vol_put,
.get = snd_cs46xx_vol_iec958_get,
.put = snd_cs46xx_vol_iec958_put,
.private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2,
},
#endif
......@@ -1927,6 +2312,48 @@ static snd_kcontrol_new_t snd_hercules_controls[] __devinitdata = {
.put = snd_herc_spdif_select_put,
},
};
static void snd_cs46xx_sec_codec_reset (ac97_t * ac97)
{
signed long end_time;
int err;
/* reset to defaults */
snd_ac97_write(ac97, AC97_RESET, 0);
/* set codec in extended mode */
snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
udelay(50);
/* it's necessary to wait awhile until registers are accessible after RESET */
/* because the PCM or MASTER volume registers can be modified, */
/* the REC_GAIN register is used for tests */
end_time = jiffies + HZ;
do {
unsigned short ext_mid;
/* use preliminary reads to settle the communication */
snd_ac97_read(ac97, AC97_RESET);
snd_ac97_read(ac97, AC97_VENDOR_ID1);
snd_ac97_read(ac97, AC97_VENDOR_ID2);
/* modem? */
ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
if (ext_mid != 0xffff && (ext_mid & 1) != 0)
return;
/* test if we can write to the record gain volume register */
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
return;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100);
} while (time_after_eq(end_time, jiffies));
snd_printk("CS46xx secondary codec dont respond!\n");
}
#endif
int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
......@@ -2001,11 +2428,16 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
/* well, one codec only ... */
goto _end;
_ok2:
/* set secondary codec in extended mode */
/* use custom reset to set secondary codec in
extended mode */
ac97.reset = snd_cs46xx_sec_codec_reset;
if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])) < 0)
return err;
chip->nr_ac97_codecs = 2;
/* add cs4630 mixer controls */
_end:
/* dosoundcard specific mixer setup */
......@@ -2016,6 +2448,7 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
/* add cs4630 mixer controls */
for (idx = 0; idx < sizeof(snd_cs46xx_controls) /
sizeof(snd_cs46xx_controls[0]); idx++) {
snd_kcontrol_t *kctl;
......@@ -2491,10 +2924,8 @@ static int snd_cs46xx_dev_free(snd_device_t *device)
/*
* initialize chip
*/
static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
{
unsigned int tmp;
int timeout;
/*
......@@ -2580,6 +3011,11 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
*/
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE);
/*
* Enable FIFO Host Bypass
*/
snd_cs46xx_pokeBA0(chip, BA0_SERBCF, SERBCF_HBP);
/*
* Fill the serial port FIFOs with silence.
*/
......@@ -2598,6 +3034,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
snd_cs46xx_pokeBA0(chip, BA0_SERC7, SERC7_ASDI2EN);
snd_cs46xx_pokeBA0(chip, BA0_SERC3, 0);
......@@ -2608,6 +3045,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
mdelay(5);
/*
* Wait for the codec ready signal from the AC97 codec.
*/
......@@ -2660,6 +3098,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
#endif
/*
* Wait until we've sampled input slots 3 and 4 as valid, meaning that
* the codec is pumping ADC data across the AC-link.
......@@ -2688,7 +3127,10 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
* Now, assert valid frame and the slot 3 and 4 valid bits. This will
* commense the transfer of digital audio data to the AC97 codec.
*/
snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4 | ACOSV_SLV7 | ACOSV_SLV8);
snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4 |
ACOSV_SLV7 | ACOSV_SLV8);
/*
* Power down the DAC and ADC. We will power them up (if) when we need
......@@ -2703,11 +3145,19 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
/* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */
/* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */
return 0;
}
/*
* start and load DSP
*/
int __devinit snd_cs46xx_start_dsp(cs46xx_t *chip)
{
unsigned int tmp;
/*
* Reset the processor.
*/
snd_cs46xx_reset(chip);
/*
* Download the image to the processor.
*/
......@@ -2741,7 +3191,6 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
if (cs46xx_dsp_scb_and_task_init(chip) < 0)
return -EIO;
snd_printdd("[get here]\n");
#else
/* old image */
if (snd_cs46xx_download_image(chip) < 0) {
......@@ -2785,19 +3234,24 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait)
tmp |= 0x00000001;
snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* set the attenuation to 0dB */
snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
snd_cs46xx_poke(chip, (MASTERMIX_SCB_ADDR + 0xE) << 2, 0x80008000);
snd_cs46xx_poke(chip, (VARIDECIMATE_SCB_ADDR + 0xE) << 2, 0x80008000);
/*
* Initialize cs46xx SPDIF controller
*/
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* time countdown enable */
cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000000);
/* SPDIF input MASTER ENABLE */
cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff);
/* mute spdif out */
cs46xx_dsp_disable_spdif_out(chip);
#else
/* set the attenuation to 0dB */
snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
#endif
return 0;
......@@ -2812,15 +3266,26 @@ static void amp_none(cs46xx_t *chip, int change)
{
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static int voyetra_setup_eapd_slot(cs46xx_t *chip)
{
int i;
u32 idx;
u16 modem_power,pin_config,logic_type,valid_slots,status;
snd_printd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
u32 idx, valid_slots,tmp,powerdown = 0;
u16 modem_power,pin_config,logic_type;
snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
/*
* See if the devices are powered down. If so, we must power them up first
* or they will not respond.
*/
tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
if (!(tmp & CLKCR1_SWCE)) {
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
powerdown = 1;
}
/*
* Clear PRA. The Bonzo chip will be used for GPIO not for modem
* stuff.
......@@ -2831,45 +3296,50 @@ static int voyetra_setup_eapd_slot(cs46xx_t *chip)
}
modem_power = snd_cs46xx_codec_read (chip,
BA0_AC97_EXT_MODEM_POWER,
AC97_EXTENDED_MSTATUS,
CS46XX_SECONDARY_CODEC_INDEX);
modem_power &=0xFEFF;
snd_cs46xx_codec_write(chip,
BA0_AC97_EXT_MODEM_POWER, modem_power,
AC97_EXTENDED_MSTATUS, modem_power,
CS46XX_SECONDARY_CODEC_INDEX);
/*
* Set GPIO pin's 7 and 8 so that they are configured for output.
*/
pin_config = snd_cs46xx_codec_read (chip,
BA0_AC97_GPIO_PIN_CONFIG,
AC97_GPIO_CFG,
CS46XX_SECONDARY_CODEC_INDEX);
pin_config &=0x27F;
snd_cs46xx_codec_write(chip,
BA0_AC97_GPIO_PIN_CONFIG, pin_config,
AC97_GPIO_CFG, pin_config,
CS46XX_SECONDARY_CODEC_INDEX);
/*
* Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
*/
logic_type = snd_cs46xx_codec_read(chip, BA0_AC97_GPIO_PIN_TYPE,
logic_type = snd_cs46xx_codec_read(chip, AC97_GPIO_POLARITY,
CS46XX_SECONDARY_CODEC_INDEX);
logic_type &=0x27F;
snd_cs46xx_codec_write (chip, BA0_AC97_GPIO_PIN_TYPE, logic_type,
snd_cs46xx_codec_write (chip, AC97_GPIO_POLARITY, logic_type,
CS46XX_SECONDARY_CODEC_INDEX);
valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
valid_slots |= 0x200;
snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
if ( cs46xx_wait_for_fifo(chip,1) ) {
snd_printdd("FIFO is busy\n");
return -EINVAL;
}
/*
* Fill slots 12 with the correct value for the GPIO pins.
*/
for(idx = 0x90; idx <= 0x9F; idx++) {
/*
* Initialize the fifo so that bits 7 and 8 are on.
*
......@@ -2879,20 +3349,11 @@ static int voyetra_setup_eapd_slot(cs46xx_t *chip)
snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
/*
* Make sure the previous FIFO write operation has completed.
* Wait for command to complete
*/
for(i = 0; i < 5; i++){
status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
if ( cs46xx_wait_for_fifo(chip,200) ) {
snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx);
if( !(status & SERBST_WBSY) ) {
break;
}
mdelay(100);
}
if(status & SERBST_WBSY) {
snd_printk( KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() " \
"Failure to write the GPIO pins for slot 12.\n");
return -EINVAL;
}
......@@ -2907,6 +3368,16 @@ static int voyetra_setup_eapd_slot(cs46xx_t *chip)
snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
}
/* wait for last command to complete */
cs46xx_wait_for_fifo(chip,200);
/*
* Now, if we powered up the devices, then power them back down again.
* This is kinda ugly, but should never happen.
*/
if (powerdown)
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
return 0;
}
#endif
......@@ -2981,6 +3452,18 @@ static void amp_hercules(cs46xx_t *chip, int change)
}
}
static void voyetra_mixer_init (cs46xx_t *chip)
{
snd_printdd ("initializing Voyetra mixer\n");
/* turnon Amplifier and leave it on */
chip->amplifier_ctrl(chip, 1);
/* Enable SPDIF out */
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
}
static void hercules_mixer_init (cs46xx_t *chip)
{
#ifdef CONFIG_SND_CS46XX_NEW_DSP
......@@ -3113,7 +3596,7 @@ struct cs_card_type
static struct cs_card_type __initdata cards[] = {
{0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL, NULL},
{0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL, NULL},
{0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL, voyetra_mixer_init},
{0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL, NULL},
{0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
{0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
......@@ -3286,7 +3769,7 @@ int __devinit snd_cs46xx_create(snd_card_t * card,
for (cp = &cards[0]; cp->name; cp++) {
if (cp->vendor == ss_vendor && cp->id == ss_card) {
snd_printd("hack for %s enabled\n", cp->name);
snd_printd ("hack for %s enabled\n", cp->name);
if (cp->init)
cp->init(chip);
chip->amplifier_ctrl = cp->amp;
......
......@@ -35,9 +35,17 @@
#define CS46XX_BA1_REG_SIZE 0x0100
#define CS46XX_PERIOD_SIZE 2048
#ifdef CONFIG_SND_CS46XX_NEW_DSP
#define CS46XX_MIN_PERIOD_SIZE 1
#define CS46XX_MAX_PERIOD_SIZE 1024*1024
#else
#define CS46XX_MIN_PERIOD_SIZE 2048
#define CS46XX_MAX_PERIOD_SIZE 2048
#endif
#define CS46XX_FRAGS 2
#define CS46XX_BUFFER_SIZE CS46XX_PERIOD_SIZE * CS46XX_FRAGS
/* #define CS46XX_BUFFER_SIZE CS46XX_MAX_PERIOD_SIZE * CS46XX_FRAGS */
#define SCB_NO_PARENT 0
#define SCB_ON_PARENT_NEXT_SCB 1
......@@ -51,6 +59,10 @@ extern snd_pcm_ops_t snd_cs46xx_playback_ops;
extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;
extern snd_pcm_ops_t snd_cs46xx_capture_ops;
extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;
extern snd_pcm_ops_t snd_cs46xx_playback_rear_ops;
extern snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops;
extern snd_pcm_ops_t snd_cs46xx_playback_iec958_ops;
extern snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops;
/*
......@@ -96,6 +108,7 @@ int snd_cs46xx_download (cs46xx_t *chip,u32 *src,unsigned lon
unsigned long len);
int snd_cs46xx_clear_BA1(cs46xx_t *chip,unsigned long offset,unsigned long len);
int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip);
int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip);
int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip);
int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip);
int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip);
......@@ -185,7 +198,8 @@ dsp_scb_descriptor_t * cs46xx_dsp_create_magic_snoop_scb(cs46xx_t * chip,char *
dsp_scb_descriptor_t * snoop_scb,
dsp_scb_descriptor_t * parent_scb,
int scb_child_type);
pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr);
pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr,
int pcm_channel_id);
void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,
pcm_channel_descriptor_t * pcm_channel);
void cs46xx_dsp_set_src_sample_rate(cs46xx_t * chip,dsp_scb_descriptor_t * src,
......@@ -196,4 +210,13 @@ dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_desc
u16 addr,char * scb_name);
int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src);
int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src);
int cs46xx_iec958_pre_open (cs46xx_t *chip);
int cs46xx_iec958_post_close (cs46xx_t *chip);
int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip,
pcm_channel_descriptor_t * pcm_channel,
int period_size);
int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip,
int period_size);
int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 right,u16 left);
int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 right,u16 left);
#endif /* __CS46XX_LIB_H__ */
......@@ -257,7 +257,10 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
ins->spdif_in_sample_rate = 48000;
/* maximize volume */
ins->spdif_input_volume = 0x80008000;
ins->dac_volume_right = 0x8000;
ins->dac_volume_left = 0x8000;
ins->spdif_input_volume_right = 0x8000;
ins->spdif_input_volume_left = 0x8000;
return ins;
}
......@@ -1017,11 +1020,10 @@ int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip)
dsp_scb_descriptor_t * codec_in_scb;
dsp_scb_descriptor_t * src_task_scb;
dsp_scb_descriptor_t * master_mix_scb;
dsp_scb_descriptor_t * rear_mix_scb;
dsp_scb_descriptor_t * record_mix_scb;
dsp_scb_descriptor_t * write_back_scb;
dsp_scb_descriptor_t * vari_decimate_scb;
dsp_scb_descriptor_t * pcm_serial_input_task;
dsp_scb_descriptor_t * asynch_tx_scb;
dsp_scb_descriptor_t * sec_codec_out_scb;
dsp_scb_descriptor_t * magic_snoop_scb;
......@@ -1095,6 +1097,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip)
ins->the_null_scb->sub_list_ptr = ins->the_null_scb;
ins->the_null_scb->next_scb_ptr = ins->the_null_scb;
ins->the_null_scb->parent_scb_ptr = NULL;
cs46xx_dsp_proc_register_scb_desc (chip,ins->the_null_scb);
}
{
......@@ -1309,46 +1312,38 @@ int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip)
/* create secondary CODEC output */
sec_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_II",0x0010,0x0040,
OUTPUTSNOOP_SCB_ADDR,
REAR_MIXER_SCB_ADDR,
SEC_CODECOUT_SCB_ADDR,codec_in_scb,
SCB_ON_PARENT_NEXT_SCB);
if (!sec_codec_out_scb) goto _fail_end;
/* create the rear PCM channel mixer SCB */
rear_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RearMixerSCB",
MIX_SAMPLE_BUF3,
REAR_MIXER_SCB_ADDR,
sec_codec_out_scb,
SCB_ON_PARENT_SUBLIST_SCB);
ins->rear_mix_scb = rear_mix_scb;
if (!rear_mix_scb) goto _fail_end;
/* the magic snooper */
magic_snoop_scb = cs46xx_dsp_create_magic_snoop_scb (chip,"MagicSnoopSCB_I",OUTPUTSNOOP_SCB_ADDR,
OUTPUT_SNOOP_BUFFER,
codec_out_scb,
sec_codec_out_scb,
SCB_ON_PARENT_SUBLIST_SCB);
if (!magic_snoop_scb) goto _fail_end;
ins->ref_snoop_scb = magic_snoop_scb;
/* The asynch. transfer task */
asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
SPDIFO_SCB_INST,
SPDIFO_IP_OUTPUT_BUFFER1,
magic_snoop_scb,
SCB_ON_PARENT_NEXT_SCB);
if (!asynch_tx_scb) goto _fail_end;
/* pcm input */
pcm_serial_input_task = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II",
PCMSERIALINII_SCB_ADDR,
magic_snoop_scb,asynch_tx_scb,
SCB_ON_PARENT_SUBLIST_SCB);
if (!pcm_serial_input_task) goto _fail_end;
if (!magic_snoop_scb) goto _fail_end;
ins->ref_snoop_scb = magic_snoop_scb;
/* SP IO access */
if (!cs46xx_dsp_create_spio_write_scb(chip,"SPIOWriteSCB",SPIOWRITE_SCB_ADDR,
asynch_tx_scb,
magic_snoop_scb,
SCB_ON_PARENT_NEXT_SCB))
goto _fail_end;
/* SPDIF input sampel rate converter */
src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_SPDIFI",
SRC_OUTPUT_BUF1,
......@@ -1505,6 +1500,7 @@ int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry)
};
spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST);
snd_assert(spdifo_scb_desc, return -EIO);
spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST);
snd_assert(spdifi_scb_desc, return -EIO);
......@@ -1530,6 +1526,11 @@ int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry)
is the FG task tree */
fg_entry->parent_scb_ptr = spdifo_scb_desc;
/* for proc fs */
cs46xx_dsp_proc_register_scb_desc (chip,spdifo_scb_desc);
cs46xx_dsp_proc_register_scb_desc (chip,spdifi_scb_desc);
cs46xx_dsp_proc_register_scb_desc (chip,async_codec_scb_desc);
/* Async MASTER ENABLE, affects both SPDIF input and output */
snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
}
......@@ -1537,7 +1538,7 @@ int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry)
return 0;
}
int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip)
int cs46xx_dsp_enable_spdif_hw (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
......@@ -1547,29 +1548,11 @@ int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip)
/* SPDIF output MASTER ENABLE */
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x80000000);
/* right and left validate bit
NOTE: 0x80000000 and enables the SCMC protection on stream
*/
/* right and left validate bit */
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x00000000 | (1 << 13) | (1 << 12));
/* monitor state */
ins->spdif_status_out = 1;
return 0;
}
int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
/* disable SPDIF output FIFO slot */
snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, 0);
/* SPDIF output MASTER DISABLE */
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x0);
/* monitor state */
ins->spdif_status_out = 0;
ins->spdif_status_out |= DSP_SPDIF_STATUS_HW_ENABLED;
return 0;
}
......@@ -1605,8 +1588,10 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
cs46xx_src_link(chip,ins->spdif_in_src);
/* restore SPDIF input volume */
snd_cs46xx_poke(chip, (ASYNCRX_SCB_ADDR + 0xE) << 2, ins->spdif_input_volume);
snd_cs46xx_poke(chip, (ASYNCRX_SCB_ADDR + 0xF) << 2, ins->spdif_input_volume);
cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,
ins->spdif_input_volume_right,
ins->spdif_input_volume_left);
spin_unlock_irq(&chip->reg_lock);
/* set SPDIF input sample rate and unmute
......@@ -1739,3 +1724,41 @@ int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data)
return 0;
}
int cs46xx_dsp_set_dac_volume (cs46xx_t * chip,u16 right,u16 left)
{
int i;
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
down(&chip->spos_mutex);
ins->dac_volume_right = right;
ins->dac_volume_left = left;
for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
if (ins->pcm_channels[i].active &&
!ins->pcm_channels[i].unlinked) {
cs46xx_dsp_scb_set_volume (chip,ins->pcm_channels[i].pcm_reader_scb,
right,left);
}
}
up(&chip->spos_mutex);
return 0;
}
int cs46xx_dsp_set_iec958_volume (cs46xx_t * chip,u16 right,u16 left) {
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
down(&chip->spos_mutex);
cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,
right,left);
ins->spdif_input_volume_right = right;
ins->spdif_input_volume_left = left;
up(&chip->spos_mutex);
return 0;
}
......@@ -73,7 +73,10 @@ typedef enum {
#define SPDIFI_IP_OUTPUT_BUFFER1 0x0E00
#define SPDIFO_IP_OUTPUT_BUFFER1 0x1000
#define MIX_SAMPLE_BUF1 0x1400
#define MIX_SAMPLE_BUF2 0x3000
#define MIX_SAMPLE_BUF2 0x2D00
#define MIX_SAMPLE_BUF3 0x2E00
#define MIX_SAMPLE_BUF4 0x2F00
#define MIX_SAMPLE_BUF5 0x3000
/* Task stack address */
#define HFG_STACK 0x066A
......@@ -104,6 +107,8 @@ typedef enum {
#define OUTPUTSNOOPII_SCB_ADDR 0x150
#define PCMSERIALIN_PCM_SCB_ADDR 0x160
#define RECORD_MIXER_SCB_ADDR 0x170
#define REAR_MIXER_SCB_ADDR 0x180
#define SPDIF_MIXER_SCB_ADDR 0x190
/* hyperforground SCB's*/
#define HFG_TREE_SCB 0xBA0
......@@ -123,6 +128,7 @@ typedef enum {
#define SCBfuncEntryPtr 0xA
#define SRCCorPerGof 0x2
#define SRCPhiIncr6Int26Frac 0xd
#define SCBVolumeCtrl 0xe
/* conf */
#define UseASER1Input 1
......@@ -179,5 +185,22 @@ typedef enum {
#define SP_SPDOUT_CONTROL 0x804D
#define SP_SPDOUT_CSUV 0x808E
static inline void cs46xx_dsp_spos_update_scb (cs46xx_t * chip,dsp_scb_descriptor_t * scb)
{
/* update nextSCB and subListPtr in SCB */
snd_cs46xx_poke(chip,
(scb->address + SCBsubListPtr) << 2,
(scb->sub_list_ptr->address << 0x10) |
(scb->next_scb_ptr->address));
}
static inline void cs46xx_dsp_scb_set_volume (cs46xx_t * chip,dsp_scb_descriptor_t * scb,
u16 right,u16 left) {
unsigned int val = ((0xffff - right) << 16 | (0xffff - left));
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
}
#endif /* __DSP_SPOS_H__ */
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
......@@ -150,16 +150,10 @@ static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
spin_lock_irqsave(&chip->reg_lock, flags);
/* update parent first entry in DSP RAM */
snd_cs46xx_poke(chip,
(scb->parent_scb_ptr->address + SCBsubListPtr) << 2,
(scb->parent_scb_ptr->sub_list_ptr->address << 0x10) |
(scb->parent_scb_ptr->next_scb_ptr->address));
cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
/* then update entry in DSP RAM */
snd_cs46xx_poke(chip,
(scb->address + SCBsubListPtr) << 2,
(scb->sub_list_ptr->address << 0x10) |
(scb->next_scb_ptr->address));
cs46xx_dsp_spos_update_scb(chip,scb);
scb->parent_scb_ptr = NULL;
spin_unlock_irqrestore(&chip->reg_lock, flags);
......@@ -173,6 +167,7 @@ static void _dsp_clear_sample_buffer (cs46xx_t *chip, u32 sample_buffer_addr, in
for (i = 0; i < dword_count ; ++i ) {
writel(0, dst);
dst += 4;
}
}
......@@ -328,11 +323,9 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
}
spin_lock_irqsave(&chip->reg_lock, flags);
/* update entry in DSP RAM */
snd_cs46xx_poke(chip,
(scb->parent_scb_ptr->address + SCBsubListPtr) << 2,
(scb->parent_scb_ptr->sub_list_ptr->address << 0x10) |
(scb->parent_scb_ptr->next_scb_ptr->address));
cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
......@@ -610,7 +603,7 @@ cs46xx_dsp_create_src_task_scb(cs46xx_t * chip,char * scb_name,
src_buffer_addr << 0x10,
0x04000000,
{
0xffff,0xffff,
0x8000,0x8000,
0xffff,0xffff
}
};
......@@ -665,7 +658,7 @@ cs46xx_dsp_create_mix_only_scb(cs46xx_t * chip,char * scb_name,
/* D */ 0,
{
/* E */ 0x8000,0x8000,
/* F */ 0x8000,0x8000
/* F */ 0xffff,0xffff
}
};
......@@ -853,7 +846,7 @@ cs46xx_dsp_create_asynch_fg_tx_scb(cs46xx_t * chip,char * scb_name,u32 dest,
rate etc */
0x18000000, /* Phi increment for approx 32k operation */
0x8000,0x8000, /* Volume controls are unused at this time */
0x8000,0x8000
0xffff,0xffff
};
scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_tx_scb,
......@@ -1093,15 +1086,46 @@ static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
};
pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr)
pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,
u32 sample_rate, void * private_data,
u32 hw_dma_addr,
int pcm_channel_id)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb;
dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb, * mixer_scb = NULL;
/*dsp_scb_descriptor_t * pcm_parent_scb;*/
char scb_name[DSP_MAX_SCB_NAME];
int i,pcm_index = -1, insert_point, src_index = -1;
unsigned long flags;
switch (pcm_channel_id) {
case DSP_PCM_MAIN_CHANNEL:
mixer_scb = ins->master_mix_scb;
break;
case DSP_PCM_REAR_CHANNEL:
mixer_scb = ins->rear_mix_scb;
break;
case DSP_PCM_CENTER_CHANNEL:
/* TODO */
snd_assert(0);
break;
case DSP_PCM_LFE_CHANNEL:
/* TODO */
snd_assert(0);
break;
case DSP_IEC958_CHANNEL:
snd_assert (ins->asynch_tx_scb != NULL, return NULL);
mixer_scb = ins->asynch_tx_scb;
if (ins->spdif_status_out & DSP_SPDIF_STATUS_AC3_MODE) {
snd_printdd ("IEC958 opened in AC3 mode\n");
/*src_scb = ins->asynch_tx_scb;
ins->asynch_tx_scb->ref_count ++;*/
}
break;
default:
snd_assert (0);
return NULL;
}
/* default sample rate is 44100 */
if (!sample_rate) sample_rate = 44100;
......@@ -1114,7 +1138,9 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
if (i == CS46XX_DSP_CAPTURE_CHANNEL) continue;
if (ins->pcm_channels[i].active) {
if (!src_scb && ins->pcm_channels[i].sample_rate == sample_rate) {
if (!src_scb &&
ins->pcm_channels[i].sample_rate == sample_rate &&
ins->pcm_channels[i].mixer_scb == mixer_scb) {
src_scb = ins->pcm_channels[i].src_scb;
ins->pcm_channels[i].src_scb->ref_count ++;
src_index = ins->pcm_channels[i].src_slot;
......@@ -1148,11 +1174,11 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
snd_assert (src_index != -1,return NULL);
/* we need to create a new SRC SCB */
if (ins->master_mix_scb->sub_list_ptr == ins->the_null_scb) {
src_parent_scb = ins->master_mix_scb;
if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
src_parent_scb = mixer_scb;
insert_point = SCB_ON_PARENT_SUBLIST_SCB;
} else {
src_parent_scb = find_next_free_scb(chip,ins->master_mix_scb->sub_list_ptr);
src_parent_scb = find_next_free_scb(chip,mixer_scb->sub_list_ptr);
insert_point = SCB_ON_PARENT_NEXT_SCB;
}
......@@ -1172,6 +1198,8 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
return NULL;
}
if (pcm_channel_id != DSP_IEC958_CHANNEL ||
!(ins->spdif_status_out & DSP_SPDIF_STATUS_AC3_MODE))
cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate);
ins->nsrc_scb ++;
......@@ -1180,7 +1208,8 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
snd_printdd( "dsp_spos: creating PCM \"%s\"\n",scb_name);
snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
pcm_channel_id);
pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
pcm_reader_buffer_addr[pcm_index],
......@@ -1206,12 +1235,91 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
ins->pcm_channels[pcm_index].src_slot = src_index;
ins->pcm_channels[pcm_index].active = 1;
ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
ins->pcm_channels[pcm_index].pcm_channel_id = pcm_channel_id;
ins->npcm_channels ++;
spin_unlock_irqrestore(&chip->reg_lock, flags);
return (ins->pcm_channels + pcm_index);
}
int cs46xx_dsp_pcm_channel_set_period (cs46xx_t * chip,
pcm_channel_descriptor_t * pcm_channel,
int period_size)
{
u32 temp = snd_cs46xx_peek (chip,pcm_channel->pcm_reader_scb->address << 2);
temp &= ~DMA_RQ_C1_SOURCE_SIZE_MASK;
switch (period_size) {
case 2048:
temp |= DMA_RQ_C1_SOURCE_MOD1024;
break;
case 1024:
temp |= DMA_RQ_C1_SOURCE_MOD512;
break;
case 512:
temp |= DMA_RQ_C1_SOURCE_MOD256;
break;
case 256:
temp |= DMA_RQ_C1_SOURCE_MOD128;
break;
case 128:
temp |= DMA_RQ_C1_SOURCE_MOD64;
break;
case 64:
temp |= DMA_RQ_C1_SOURCE_MOD32;
break;
case 32:
temp |= DMA_RQ_C1_SOURCE_MOD16;
break;
default:
snd_printdd ("period size (%d) not supported by HW\n");
return -EINVAL;
}
snd_cs46xx_poke (chip,pcm_channel->pcm_reader_scb->address << 2,temp);
return 0;
}
int cs46xx_dsp_pcm_ostream_set_period (cs46xx_t * chip,
int period_size)
{
u32 temp = snd_cs46xx_peek (chip,WRITEBACK_SCB_ADDR << 2);
temp &= ~DMA_RQ_C1_DEST_SIZE_MASK;
switch (period_size) {
case 2048:
temp |= DMA_RQ_C1_DEST_MOD1024;
break;
case 1024:
temp |= DMA_RQ_C1_DEST_MOD512;
break;
case 512:
temp |= DMA_RQ_C1_DEST_MOD256;
break;
case 256:
temp |= DMA_RQ_C1_DEST_MOD128;
break;
case 128:
temp |= DMA_RQ_C1_DEST_MOD64;
break;
case 64:
temp |= DMA_RQ_C1_DEST_MOD32;
break;
case 32:
temp |= DMA_RQ_C1_DEST_MOD16;
break;
default:
snd_printdd ("period size (%d) not supported by HW\n");
return -EINVAL;
}
snd_cs46xx_poke (chip,WRITEBACK_SCB_ADDR << 2,temp);
return 0;
}
void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
......@@ -1293,17 +1401,13 @@ int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
/* update entry in DSP RAM */
spin_lock_irqsave(&chip->reg_lock, flags);
snd_cs46xx_poke(chip,
(pcm_channel->pcm_reader_scb->address + SCBsubListPtr) << 2,
(pcm_channel->pcm_reader_scb->sub_list_ptr->address << 0x10) |
(pcm_channel->pcm_reader_scb->next_scb_ptr->address));
snd_cs46xx_poke(chip,
(parent_scb->address + SCBsubListPtr) << 2,
(parent_scb->sub_list_ptr->address << 0x10) |
(parent_scb->next_scb_ptr->address));
/* update SCB entry in DSP RAM */
cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
/* update parent SCB entry */
cs46xx_dsp_spos_update_scb(chip,parent_scb);
pcm_channel->unlinked = 0;
spin_unlock_irqrestore(&chip->reg_lock, flags);
......@@ -1358,14 +1462,16 @@ void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u
spin_lock_irqsave(&chip->reg_lock, flags);
/* mute SCB */
snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
/* cs46xx_dsp_scb_set_volume (chip,src,0,0); */
snd_cs46xx_poke(chip, (src->address + SRCCorPerGof) << 2,
((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
snd_cs46xx_poke(chip, (src->address + SRCPhiIncr6Int26Frac) << 2, phiIncr);
/* raise volume */
snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0x80008000);
/* cs46xx_dsp_scb_set_volume (chip,src,0x7fff,0x7fff); */
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
......@@ -1399,7 +1505,7 @@ int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src)
snd_assert (src->parent_scb_ptr != NULL, return -EINVAL );
/* mute SCB */
snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
cs46xx_dsp_scb_set_volume (chip,src,0,0);
_dsp_unlink_scb (chip,src);
......@@ -1425,10 +1531,144 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
src->parent_scb_ptr = parent_scb;
/* update entry in DSP RAM */
snd_cs46xx_poke(chip,
(parent_scb->address + SCBsubListPtr) << 2,
(parent_scb->sub_list_ptr->address << 0x10) |
(parent_scb->next_scb_ptr->address));
cs46xx_dsp_spos_update_scb(chip,parent_scb);
return 0;
}
int cs46xx_dsp_enable_spdif_out (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
cs46xx_dsp_enable_spdif_hw (chip);
}
/* dont touch anything if SPDIF is open */
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
/* when cs46xx_iec958_post_close(...) is called it
will call this function if necesary depending on
this bit */
ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
return -EBUSY;
}
snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL);
snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL);
/* reset output snooper sample buffer pointer */
snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
(OUTPUT_SNOOP_BUFFER + 0x10) << 0x10 );
/* The asynch. transfer task */
ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
SPDIFO_SCB_INST,
SPDIFO_IP_OUTPUT_BUFFER1,
ins->master_mix_scb,
SCB_ON_PARENT_NEXT_SCB);
if (!ins->asynch_tx_scb) return -ENOMEM;
ins->spdif_pcm_input_scb = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II",
PCMSERIALINII_SCB_ADDR,
ins->ref_snoop_scb,
ins->asynch_tx_scb,
SCB_ON_PARENT_SUBLIST_SCB);
if (!ins->spdif_pcm_input_scb) return -ENOMEM;
/* monitor state */
ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
return 0;
}
int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
/* dont touch anything if SPDIF is open */
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
return -EBUSY;
}
/* check integrety */
snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL);
snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL);
snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL);
cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
ins->spdif_pcm_input_scb = NULL;
ins->asynch_tx_scb = NULL;
/* clear buffer to prevent any undesired noise */
_dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
/* monitor state */
ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
return 0;
}
int cs46xx_iec958_pre_open (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
/* remove AsynchFGTxSCB and and PCMSerialInput_II */
cs46xx_dsp_disable_spdif_out (chip);
/* save state */
ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
}
/* if not enabled already */
if (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) {
cs46xx_dsp_enable_spdif_hw (chip);
}
/* Create the asynch. transfer task for playback */
ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
SPDIFO_SCB_INST,
SPDIFO_IP_OUTPUT_BUFFER1,
ins->master_mix_scb,
SCB_ON_PARENT_NEXT_SCB);
if (ins->spdif_status_out & DSP_SPDIF_STATUS_AC3_MODE)
/* set left (13), right validity bit (12) , and non-audio(1) and profsional bit (0) */
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x00000000 | (1 << 13) | (1 << 12) | (1 << 1) | 1);
ins->spdif_status_out |= DSP_SPDIF_STATUS_PLAYBACK_OPEN;
return 0;
}
int cs46xx_iec958_post_close (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
ins->spdif_status_out &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
/* restore settings */
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x00000000 | (1 << 13) | (1 << 12));
/* deallocate stuff */
cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
ins->asynch_tx_scb = NULL;
/* restore state */
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
cs46xx_dsp_enable_spdif_out (chip);
}
return 0;
}
......@@ -37,6 +37,7 @@
#endif
#define SNDRV_GET_ID
#include <sound/initval.h>
#include <sound/asoundef.h>
#define chip_t ensoniq_t
......@@ -109,25 +110,27 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */
#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */
#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */
#define ES_1371_SPDIF_EN (1<<26) /* SPDIF enable */
#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24) /* joystick port mapping */
#define ES_1373_TEST_BIT (1<<29) /* should be set to 0 for normal operation */
#define ES_1373_RECEN_B (1<<28) /* mix record with playback for I2S/SPDIF out */
#define ES_1373_SPDIF_THRU (1<<26) /* 0 = SPDIF thru mode, 1 = SPDIF == dig out */
#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24)/* joystick port mapping */
#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */
#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03)
#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f) /* GPIO in [3:0] pins - R/O */
#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16) /* clock divide ratio for DAC2 */
#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */
#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */
#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */
#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff) /* clock divide ratio for DAC2 */
#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16) /* GPIO out [3:0] pins - W/R */
#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */
#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */
#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */
#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */
#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */
#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */
#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12) /* fixed frequency clock for DAC1 */
#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12)/* fixed frequency clock for DAC1 */
#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */
#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */
#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */
#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */
#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = ADC; 1 = I2S */
#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = AC'97 ADC; 1 = I2S */
#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */
#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */
#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */
......@@ -146,9 +149,11 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */
#define ES_INTR (1<<31) /* Interrupt is pending */
#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */
#define ES_1371_ST_SPDIF_EN (1<<18) /* SPDIF enable */
#define ES_1371_ST_SPDIF_TEST (1<<17) /* SPDIF test */
#define ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */
#define ES_1373_SPDIF_EN (1<<18) /* SPDIF enable */
#define ES_1373_SPDIF_TEST (1<<17) /* SPDIF test */
#define ES_1371_TEST (1<<16) /* test ASIC */
#define ES_1373_GPIO_INT(i) (((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */
#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */
#define ES_1370_CBUSY (1<<9) /* CODEC is busy */
#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */
......@@ -191,29 +196,29 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff)
#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */
#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */
#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */
#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */
#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f) /* address of the sample rate converter */
#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */
#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */
#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */
#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */
#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */
#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */
#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */
#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */
#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */
#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */
#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */
#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */
#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */
#define ES_1371_JFAST (1<<31) /* fast joystick timing */
#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */
#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */
#define ES_1371_VMPUO(o) (((o)&0x03)<<27) /* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
#define ES_1371_VMPUO(o) (((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
#define ES_1371_VMPUM (0x03<<27) /* mask for above */
#define ES_1371_VMPUI(i) (((i)>>27)&0x03) /* base register address */
#define ES_1371_VCDCO(o) (((o)&0x03)<<25) /* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
#define ES_1371_VMPUI(i) (((i)>>27)&0x03)/* base register address */
#define ES_1371_VCDCO(o) (((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
#define ES_1371_VCDCM (0x03<<25) /* mask for above */
#define ES_1371_VCDCI(i) (((i)>>25)&0x03) /* CODEC address */
#define ES_1371_VCDCI(i) (((i)>>25)&0x03)/* CODEC address */
#define ES_1371_FIRQ (1<<24) /* force an interrupt */
#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */
#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */
......@@ -228,14 +233,16 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */
#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */
#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */
#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */
#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */
#define ES_P2_END_INCO(o) (((o)&0x07)<<19) /* binary offset value to increment / loop end */
#define ES_P2_END_INCO(o) (((o)&0x07)<<19)/* binary offset value to increment / loop end */
#define ES_P2_END_INCM (0x07<<19) /* mask for above */
#define ES_P2_END_INCI(i) (((i)>>16)&0x07) /* binary offset value to increment / loop end */
#define ES_P2_ST_INCO(o) (((o)&0x07)<<16) /* binary offset value to increment / start */
#define ES_P2_END_INCI(i) (((i)>>16)&0x07)/* binary offset value to increment / loop end */
#define ES_P2_ST_INCO(o) (((o)&0x07)<<16)/* binary offset value to increment / start */
#define ES_P2_ST_INCM (0x07<<16) /* mask for above */
#define ES_P2_ST_INCI(i) (((i)<<16)&0x07) /* binary offset value to increment / start */
#define ES_P2_ST_INCI(i) (((i)<<16)&0x07)/* binary offset value to increment / start */
#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */
#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */
#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */
......@@ -386,6 +393,10 @@ struct _snd_ensoniq {
snd_rawmidi_substream_t *midi_input;
snd_rawmidi_substream_t *midi_output;
unsigned int spdif;
unsigned int spdif_default;
unsigned int spdif_stream;
snd_info_entry_t *proc_entry;
#ifdef CHIP1370
......@@ -1032,6 +1043,10 @@ static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream)
ensoniq->playback1_substream = substream;
runtime->hw = snd_ensoniq_playback1;
snd_pcm_set_sync(substream);
spin_lock_irq(&ensoniq->reg_lock);
if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
ensoniq->spdif_stream = ensoniq->spdif_default;
spin_unlock_irq(&ensoniq->reg_lock);
#ifdef CHIP1370
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_rates);
......@@ -1051,6 +1066,10 @@ static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream)
ensoniq->playback2_substream = substream;
runtime->hw = snd_ensoniq_playback2;
snd_pcm_set_sync(substream);
spin_lock_irq(&ensoniq->reg_lock);
if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
ensoniq->spdif_stream = ensoniq->spdif_default;
spin_unlock_irq(&ensoniq->reg_lock);
#ifdef CHIP1370
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_clock);
......@@ -1241,6 +1260,116 @@ static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t
#ifdef CHIP1371
static int snd_ens1373_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int snd_ens1373_spdif_default_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff;
spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
static int snd_ens1373_spdif_default_put(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
unsigned int val;
int change;
val = ((u32)ucontrol->value.iec958.status[0] << 0) |
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
spin_lock_irq(&ensoniq->reg_lock);
change = ensoniq->spdif_default != val;
ensoniq->spdif_default = val;
if (change && ensoniq->playback1_substream == NULL && ensoniq->playback2_substream == NULL)
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
static int snd_ens1373_spdif_mask_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
ucontrol->value.iec958.status[0] = 0xff;
ucontrol->value.iec958.status[1] = 0xff;
ucontrol->value.iec958.status[2] = 0xff;
ucontrol->value.iec958.status[3] = 0xff;
return 0;
}
static int snd_ens1373_spdif_stream_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff;
spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
static int snd_ens1373_spdif_stream_put(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
unsigned int val;
int change;
val = ((u32)ucontrol->value.iec958.status[0] << 0) |
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
spin_lock_irq(&ensoniq->reg_lock);
change = ensoniq->spdif_stream != val;
ensoniq->spdif_stream = val;
if (change && (ensoniq->playback1_substream != NULL || ensoniq->playback2_substream != NULL))
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
static snd_kcontrol_new_t snd_ens1373_spdif_default __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = snd_ens1373_spdif_info,
.get = snd_ens1373_spdif_default_get,
.put = snd_ens1373_spdif_default_put,
};
static snd_kcontrol_new_t snd_ens1373_spdif_mask __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
.info = snd_ens1373_spdif_info,
.get = snd_ens1373_spdif_mask_get
};
static snd_kcontrol_new_t snd_ens1373_spdif_stream __devinitdata =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
.info = snd_ens1373_spdif_info,
.get = snd_ens1373_spdif_stream_get,
.put = snd_ens1373_spdif_stream_put
};
#define ES1371_SPDIF(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \
.get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put }
......@@ -1257,32 +1386,30 @@ static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *
static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
unsigned long flags;
spin_lock_irqsave(&ensoniq->reg_lock, flags);
ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1371_SPDIF_EN ? 1 : 0;
spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
spin_lock_irq(&ensoniq->reg_lock);
ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0;
spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol);
unsigned long flags;
unsigned int nval1, nval2;
int change;
nval1 = ucontrol->value.integer.value[0] ? ES_1371_SPDIF_EN : 0;
nval2 = ucontrol->value.integer.value[0] ? ES_1371_ST_SPDIF_EN : 0;
spin_lock_irqsave(&ensoniq->reg_lock, flags);
change = (ensoniq->ctrl & ES_1371_SPDIF_EN) != nval1;
ensoniq->ctrl &= ~ES_1371_SPDIF_EN;
nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0;
nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0;
spin_lock_irq(&ensoniq->reg_lock);
change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1;
ensoniq->ctrl &= ~ES_1373_SPDIF_THRU;
ensoniq->ctrl |= nval1;
ensoniq->cssr &= ~ES_1371_ST_SPDIF_EN;
ensoniq->cssr &= ~ES_1373_SPDIF_EN;
ensoniq->cssr |= nval2;
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
......@@ -1299,7 +1426,7 @@ static struct {
unsigned short vid; /* vendor ID */
unsigned short did; /* device ID */
unsigned char rev; /* revision */
} es1371_spdif_present[] = {
} __devinitdata es1371_spdif_present[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
......@@ -1325,9 +1452,29 @@ static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq)
if (ensoniq->pci->vendor == es1371_spdif_present[idx].vid &&
ensoniq->pci->device == es1371_spdif_present[idx].did &&
ensoniq->rev == es1371_spdif_present[idx].rev) {
snd_kcontrol_t *kctl = snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq);
snd_kcontrol_t *kctl;
int index = 0;
ensoniq->spdif_default = ensoniq->spdif_stream = SNDRV_PCM_DEFAULT_CON_SPDIF;
outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
kctl->id.index = 1;
index++;
kctl = snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq);
kctl->id.index = index;
snd_ctl_add(card, kctl);
kctl = snd_ctl_new1(&snd_ens1373_spdif_default, ensoniq);
kctl->id.index = index;
snd_ctl_add(card, kctl);
kctl = snd_ctl_new1(&snd_ens1373_spdif_mask, ensoniq);
kctl->id.index = index;
snd_ctl_add(card, kctl);
kctl = snd_ctl_new1(&snd_ens1373_spdif_stream, ensoniq);
kctl->id.index = index;
snd_ctl_add(card, kctl);
break;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment