Commit e2094b53 authored by Jaroslav Kysela's avatar Jaroslav Kysela Committed by Jaroslav Kysela

[PATCH] ALSA update [7/12] - 2002/08/26

  - AC'97 codec
    - added ac97_can_amap() condition
    - removed powerup/powerdown sequence when sample rate is changed
    - added ac97_is_rev22 check function
    - added AC97_EI_* defines
    - available rates are in array
  - CS46xx
    - improved the SCB link mechanism
    - SMP deadlock should have been fixed now
  - OSS mixer emulation
    - added the proc interface for the configuration of OSS mixer volumes
  - rawmidi midlevel
    - removed unused snd_rawmidi_transmit_reset and snd_rawmidi_receive_reset functions
  - USB MIDI driver
    - integration of USB MIDI driver into USB audio driver by Clemens
  - intel8x0 - the big intel8x0 driver update
    - added support for ICH4
    - rewrited I/O (the third AC'97 codec registers are available only through memory)
    - code cleanups
    - ALI5455 specific code
    - added proc interface
  - VIA686, VIA8233
    - set the max periods to 128
parent a2755d79
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ #define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */
#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ #define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */
#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ #define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */
#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR DAC Rate */ #define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */
#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ #define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */
#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ #define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */
#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ #define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */
...@@ -68,25 +68,43 @@ ...@@ -68,25 +68,43 @@
#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ #define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */
#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ #define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */
/* extended audio ID bit defines */
#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */
#define AC97_EI_DRA 0x0002 /* Double rate supported */
#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */
#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */
#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */
#define AC97_EI_DACS_SLOT_SHIFT 4
#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */
#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */
#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */
#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */
#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */
#define AC97_EI_REV_22 0x0100 /* AC'97 revision 2.2 */
#define AC97_EI_REV_SHIFT 8
#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_EI_ADDR_SHIFT 14
/* extended audio status and control bit defines */ /* extended audio status and control bit defines */
#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ #define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */
#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ #define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */
#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ #define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */
#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ #define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */
#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */
#define AC97_EA_SPSA_SLOT_SHIFT 4
#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */
#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */
#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */
#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */
#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ #define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */
#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ #define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */
#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ #define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */
#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ #define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */
#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ #define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */
#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ #define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */
#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ #define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */
#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ #define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */
#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ #define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */
#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */
#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */
#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */
#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */
#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */
/* S/PDIF control bit defines */ /* S/PDIF control bit defines */
#define AC97_SC_PRO 0x0001 /* Professional status */ #define AC97_SC_PRO 0x0001 /* Professional status */
...@@ -145,8 +163,16 @@ ...@@ -145,8 +163,16 @@
#define AC97_CS_SPDIF (1<<2) /* Cirrus Logic uses funky SPDIF */ #define AC97_CS_SPDIF (1<<2) /* Cirrus Logic uses funky SPDIF */
#define AC97_CX_SPDIF (1<<3) /* Conexant's spdif interface */ #define AC97_CX_SPDIF (1<<3) /* Conexant's spdif interface */
/* /* rates indexes */
#define AC97_RATES_FRONT_DAC 0
#define AC97_RATES_SURR_DAC 1
#define AC97_RATES_LFE_DAC 2
#define AC97_RATES_ADC 3
#define AC97_RATES_MIC_ADC 4
#define AC97_RATES_SPDIF 5
/*
*
*/ */
typedef struct _snd_ac97 ac97_t; typedef struct _snd_ac97 ac97_t;
...@@ -172,11 +198,7 @@ struct _snd_ac97 { ...@@ -172,11 +198,7 @@ struct _snd_ac97 {
unsigned int scaps; /* driver capabilities */ unsigned int scaps; /* driver capabilities */
unsigned int flags; /* specific code */ unsigned int flags; /* specific code */
unsigned int clock; /* AC'97 clock (usually 48000Hz) */ unsigned int clock; /* AC'97 clock (usually 48000Hz) */
unsigned int rates_front_dac; unsigned int rates[6]; /* see AC97_RATES_* defines */
unsigned int rates_surr_dac;
unsigned int rates_lfe_dac;
unsigned int rates_adc;
unsigned int rates_mic_adc;
unsigned int spdif_status; unsigned int spdif_status;
unsigned short regs[0x80]; /* register cache */ unsigned short regs[0x80]; /* register cache */
unsigned int limited_regs; /* allow limited registers only */ unsigned int limited_regs; /* allow limited registers only */
...@@ -192,6 +214,17 @@ struct _snd_ac97 { ...@@ -192,6 +214,17 @@ struct _snd_ac97 {
} spec; } spec;
}; };
/* conditions */
static inline int ac97_is_rev22(ac97_t * ac97)
{
return (ac97->ext_id & AC97_EI_REV_MASK) == AC97_EI_REV_22;
}
static inline int ac97_can_amap(ac97_t * ac97)
{
return (ac97->ext_id & AC97_EI_AMAP) != 0;
}
/* functions */
int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97); int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97);
void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value);
......
...@@ -1817,6 +1817,8 @@ struct _snd_cs46xx { ...@@ -1817,6 +1817,8 @@ struct _snd_cs46xx {
int current_gpio; int current_gpio;
#endif #endif
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
struct semaphore spos_mutex;
dsp_spos_instance_t * dsp_spos_instance; dsp_spos_instance_t * dsp_spos_instance;
#else /* for compatibility */ #else /* for compatibility */
cs46xx_pcm_t *playback_pcm; cs46xx_pcm_t *playback_pcm;
......
...@@ -109,6 +109,7 @@ typedef struct _dsp_scb_descriptor_t { ...@@ -109,6 +109,7 @@ typedef struct _dsp_scb_descriptor_t {
snd_info_entry_t *proc_info; snd_info_entry_t *proc_info;
int ref_count; int ref_count;
spinlock_t lock;
int deleted; int deleted;
} dsp_scb_descriptor_t; } dsp_scb_descriptor_t;
...@@ -141,8 +142,6 @@ typedef struct _dsp_spos_instance_t { ...@@ -141,8 +142,6 @@ typedef struct _dsp_spos_instance_t {
segment_desc_t code; segment_desc_t code;
/* PCM playback */ /* PCM playback */
struct semaphore pcm_mutex;
dsp_scb_descriptor_t * master_mix_scb; dsp_scb_descriptor_t * master_mix_scb;
int npcm_channels; int npcm_channels;
int nsrc_scb; int nsrc_scb;
...@@ -162,7 +161,6 @@ typedef struct _dsp_spos_instance_t { ...@@ -162,7 +161,6 @@ typedef struct _dsp_spos_instance_t {
snd_info_entry_t * proc_sample_dump_info_entry; snd_info_entry_t * proc_sample_dump_info_entry;
/* SCB's descriptors */ /* SCB's descriptors */
struct semaphore scb_mutex;
int nscb; int nscb;
int scb_highest_frag_index; int scb_highest_frag_index;
dsp_scb_descriptor_t scbs[DSP_MAX_SCB_DESC]; dsp_scb_descriptor_t scbs[DSP_MAX_SCB_DESC];
......
...@@ -34,6 +34,8 @@ typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixe ...@@ -34,6 +34,8 @@ typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixe
typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index); typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index);
typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index); typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index);
#define SNDRV_OSS_MAX_MIXERS 32
struct _snd_oss_mixer_slot { struct _snd_oss_mixer_slot {
int number; int number;
int stereo: 1; int stereo: 1;
...@@ -50,12 +52,14 @@ struct _snd_oss_mixer { ...@@ -50,12 +52,14 @@ struct _snd_oss_mixer {
snd_card_t *card; snd_card_t *card;
char id[16]; char id[16];
char name[32]; char name[32];
snd_mixer_oss_slot_t slots[32]; /* OSS mixer slots */ snd_mixer_oss_slot_t slots[SNDRV_OSS_MAX_MIXERS]; /* OSS mixer slots */
unsigned int mask_recsrc; /* exclusive recsrc mask */ unsigned int mask_recsrc; /* exclusive recsrc mask */
snd_mixer_oss_get_recsrce_t get_recsrc; snd_mixer_oss_get_recsrce_t get_recsrc;
snd_mixer_oss_put_recsrce_t put_recsrc; snd_mixer_oss_put_recsrce_t put_recsrc;
void *private_data_recsrc; void *private_data_recsrc;
void (*private_free_recsrc)(snd_mixer_oss_t *mixer); void (*private_free_recsrc)(snd_mixer_oss_t *mixer);
struct semaphore reg_mutex;
snd_info_entry_t *proc_entry;
/* --- */ /* --- */
int oss_recsrc; int oss_recsrc;
}; };
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#define MPU401_HW_YMFPCI 14 /* YMF DS-XG PCI */ #define MPU401_HW_YMFPCI 14 /* YMF DS-XG PCI */
#define MPU401_HW_CMIPCI 15 /* CMIPCI MPU-401 UART */ #define MPU401_HW_CMIPCI 15 /* CMIPCI MPU-401 UART */
#define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */ #define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */
#define MPU401_HW_INTEL8X0 17 /* Intel8x0 driver */
#define MPU401_MODE_BIT_INPUT 0 #define MPU401_MODE_BIT_INPUT 0
#define MPU401_MODE_BIT_OUTPUT 1 #define MPU401_MODE_BIT_OUTPUT 1
......
...@@ -140,9 +140,9 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) ...@@ -140,9 +140,9 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
#define snd_usb_audio_t_magic 0xa15a3e01 #define snd_usb_audio_t_magic 0xa15a3e01
#define usb_mixer_elem_info_t_magic 0xa15a3e02 #define usb_mixer_elem_info_t_magic 0xa15a3e02
#define snd_usb_stream_t_magic 0xa15a3e03 #define snd_usb_stream_t_magic 0xa15a3e03
#define usbmidi_t_magic 0xa15a3f01 #define snd_usb_midi_t_magic 0xa15a3f01
#define usbmidi_out_endpoint_t_magic 0xa15a3f02 #define snd_usb_midi_out_endpoint_t_magic 0xa15a3f02
#define usbmidi_in_endpoint_t_magic 0xa15a3f03 #define snd_usb_midi_in_endpoint_t_magic 0xa15a3f03
#else #else
......
/* include/version.h. Generated automatically by configure. */ /* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc3" #define CONFIG_SND_VERSION "0.9.0rc3"
#define CONFIG_SND_DATE " (Wed Aug 21 14:00:18 2002 UTC)" #define CONFIG_SND_DATE " (Mon Aug 26 16:28:35 2002 UTC)"
...@@ -95,6 +95,7 @@ ifeq ($(CONFIG_SND_SB16_CSP),y) ...@@ -95,6 +95,7 @@ ifeq ($(CONFIG_SND_SB16_CSP),y)
obj-$(CONFIG_SND_SB16) += snd-hwdep.o obj-$(CONFIG_SND_SB16) += snd-hwdep.o
obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
endif endif
obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd.o
obj-m := $(sort $(obj-m)) obj-m := $(sort $(obj-m))
......
...@@ -472,6 +472,8 @@ struct slot { ...@@ -472,6 +472,8 @@ struct slot {
int channels; int channels;
snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT]; snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT];
unsigned int capture_item; unsigned int capture_item;
struct snd_mixer_oss_assign_table *assigned;
unsigned int allocated: 1;
}; };
static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index) static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
...@@ -809,10 +811,26 @@ static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, c ...@@ -809,10 +811,26 @@ static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, c
static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn) static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
{ {
kfree(chn->private_data); struct slot *p = (struct slot *)chn->private_data;
if (p) {
if (p->allocated && p->assigned) {
kfree(p->assigned->name);
kfree(p->assigned);
}
kfree(p);
}
}
static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
{
int idx = rslot->number; /* remember this */
if (rslot->private_free)
rslot->private_free(rslot);
memset(rslot, 0, sizeof(*rslot));
rslot->number = idx;
} }
static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr) static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated)
{ {
struct slot slot; struct slot slot;
struct slot *pslot; struct slot *pslot;
...@@ -823,49 +841,49 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o ...@@ -823,49 +841,49 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o
memset(&slot, 0, sizeof(slot)); memset(&slot, 0, sizeof(slot));
if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
SNDRV_MIXER_OSS_ITEM_GLOBAL)) SNDRV_MIXER_OSS_ITEM_GLOBAL))
return; return 0;
sprintf(str, "%s Switch", ptr->name); sprintf(str, "%s Switch", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_GSWITCH)) SNDRV_MIXER_OSS_ITEM_GSWITCH))
return; return 0;
sprintf(str, "%s Route", ptr->name); sprintf(str, "%s Route", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_GROUTE)) SNDRV_MIXER_OSS_ITEM_GROUTE))
return; return 0;
sprintf(str, "%s Volume", ptr->name); sprintf(str, "%s Volume", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_GVOLUME)) SNDRV_MIXER_OSS_ITEM_GVOLUME))
return; return 0;
sprintf(str, "%s Playback Switch", ptr->name); sprintf(str, "%s Playback Switch", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_PSWITCH)) SNDRV_MIXER_OSS_ITEM_PSWITCH))
return; return 0;
sprintf(str, "%s Playback Route", ptr->name); sprintf(str, "%s Playback Route", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_PROUTE)) SNDRV_MIXER_OSS_ITEM_PROUTE))
return; return 0;
sprintf(str, "%s Playback Volume", ptr->name); sprintf(str, "%s Playback Volume", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_PVOLUME)) SNDRV_MIXER_OSS_ITEM_PVOLUME))
return; return 0;
sprintf(str, "%s Capture Switch", ptr->name); sprintf(str, "%s Capture Switch", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_CSWITCH)) SNDRV_MIXER_OSS_ITEM_CSWITCH))
return; return 0;
sprintf(str, "%s Capture Route", ptr->name); sprintf(str, "%s Capture Route", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_CROUTE)) SNDRV_MIXER_OSS_ITEM_CROUTE))
return; return 0;
sprintf(str, "%s Capture Volume", ptr->name); sprintf(str, "%s Capture Volume", ptr->name);
if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
SNDRV_MIXER_OSS_ITEM_CVOLUME)) SNDRV_MIXER_OSS_ITEM_CVOLUME))
return; return 0;
if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) { if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
snd_ctl_elem_info_t uinfo; snd_ctl_elem_info_t uinfo;
memset(&uinfo, 0, sizeof(uinfo)); memset(&uinfo, 0, sizeof(uinfo));
if (kctl->info(kctl, &uinfo)) if (kctl->info(kctl, &uinfo))
return; return 0;
strcpy(str, ptr->name); strcpy(str, ptr->name);
if (!strcmp(str, "Master")) if (!strcmp(str, "Master"))
strcpy(str, "Mix"); strcpy(str, "Mix");
...@@ -878,7 +896,7 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o ...@@ -878,7 +896,7 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o
for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) { for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) {
uinfo.value.enumerated.item = slot.capture_item; uinfo.value.enumerated.item = slot.capture_item;
if (kctl->info(kctl, &uinfo)) if (kctl->info(kctl, &uinfo))
return; return 0;
if (!strcmp(uinfo.value.enumerated.name, str)) { if (!strcmp(uinfo.value.enumerated.name, str)) {
slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
break; break;
...@@ -888,10 +906,13 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o ...@@ -888,10 +906,13 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o
} }
if (slot.present != 0) { if (slot.present != 0) {
pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL); pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
snd_runtime_check(pslot != NULL, return); snd_runtime_check(pslot != NULL, return -ENOMEM);
*pslot = slot; *pslot = slot;
pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
pslot->assigned = ptr;
pslot->allocated = ptr_allocated;
rslot = &mixer->slots[ptr->oss_id]; rslot = &mixer->slots[ptr->oss_id];
mixer_slot_clear(rslot);
rslot->stereo = slot.channels > 1 ? 1 : 0; rslot->stereo = slot.channels > 1 ? 1 : 0;
rslot->get_volume = snd_mixer_oss_get_volume1; rslot->get_volume = snd_mixer_oss_get_volume1;
rslot->put_volume = snd_mixer_oss_put_volume1; rslot->put_volume = snd_mixer_oss_put_volume1;
...@@ -907,6 +928,156 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o ...@@ -907,6 +928,156 @@ static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_o
} }
rslot->private_data = pslot; rslot->private_data = pslot;
rslot->private_free = snd_mixer_oss_slot_free; rslot->private_free = snd_mixer_oss_slot_free;
return 1;
}
return 0;
}
/*
*/
#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
MIXER_VOL(VOLUME),
MIXER_VOL(BASS),
MIXER_VOL(TREBLE),
MIXER_VOL(SYNTH),
MIXER_VOL(PCM),
MIXER_VOL(SPEAKER),
MIXER_VOL(LINE),
MIXER_VOL(MIC),
MIXER_VOL(CD),
MIXER_VOL(IMIX),
MIXER_VOL(ALTPCM),
MIXER_VOL(RECLEV),
MIXER_VOL(IGAIN),
MIXER_VOL(OGAIN),
MIXER_VOL(LINE1),
MIXER_VOL(LINE2),
MIXER_VOL(LINE3),
MIXER_VOL(DIGITAL1),
MIXER_VOL(DIGITAL2),
MIXER_VOL(DIGITAL3),
MIXER_VOL(PHONEIN),
MIXER_VOL(PHONEOUT),
MIXER_VOL(VIDEO),
MIXER_VOL(RADIO),
MIXER_VOL(MONITOR),
};
/*
* /proc interface
*/
static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
snd_info_buffer_t * buffer)
{
snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
int i;
down(&mixer->reg_mutex);
for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
struct slot *p;
if (! oss_mixer_names[i])
continue;
p = (struct slot *)mixer->slots[i].private_data;
snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
if (p && p->assigned)
snd_iprintf(buffer, "\"%s\" %d\n",
p->assigned->name,
p->assigned->index);
else
snd_iprintf(buffer, "\"\" 0\n");
}
up(&mixer->reg_mutex);
}
static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
snd_info_buffer_t * buffer)
{
snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
char line[128], str[32], idxstr[16], *cptr;
int ch, idx;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
cptr = snd_info_get_str(str, line, sizeof(str));
for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
break;
if (ch >= SNDRV_OSS_MAX_MIXERS) {
snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
continue;
}
cptr = snd_info_get_str(str, cptr, sizeof(str));
if (! *str) {
/* remove the entry */
down(&mixer->reg_mutex);
mixer_slot_clear(&mixer->slots[ch]);
up(&mixer->reg_mutex);
continue;
}
snd_info_get_str(idxstr, cptr, sizeof(idxstr));
idx = simple_strtoul(idxstr, NULL, 10);
if (idx >= 0x4000) { /* too big */
snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
continue;
}
down(&mixer->reg_mutex);
slot = (struct slot *)mixer->slots[ch].private_data;
if (slot && slot->assigned &&
slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
/* not changed */
goto __unlock;
tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
if (! tbl) {
snd_printk(KERN_ERR "mixer_oss: no memory\n");
goto __unlock;
}
tbl->oss_id = ch;
tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
if (! tbl->name) {
kfree(tbl);
goto __unlock;
}
tbl->index = idx;
if (snd_mixer_oss_build_input(mixer, tbl, 1) <= 0) {
kfree(tbl->name);
kfree(tbl);
}
__unlock:
up(&mixer->reg_mutex);
}
}
static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
{
snd_info_entry_t *entry;
entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
mixer->card->proc_root);
if (! entry)
return;
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
entry->c.text.read_size = 8192;
entry->c.text.read = snd_mixer_oss_proc_read;
entry->c.text.write_size = 8192;
entry->c.text.write = snd_mixer_oss_proc_write;
entry->private_data = mixer;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
mixer->proc_entry = entry;
}
static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
{
if (mixer->proc_entry) {
snd_info_unregister(mixer->proc_entry);
mixer->proc_entry = NULL;
} }
} }
...@@ -945,9 +1116,9 @@ static void snd_mixer_oss_build(snd_mixer_oss_t *mixer) ...@@ -945,9 +1116,9 @@ static void snd_mixer_oss_build(snd_mixer_oss_t *mixer)
int idx; int idx;
for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++) for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++)
snd_mixer_oss_build_input(mixer, &table[idx]); snd_mixer_oss_build_input(mixer, &table[idx], 0);
if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL) if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL)
snd_mixer_oss_build_input(mixer, &fm_table); snd_mixer_oss_build_input(mixer, &fm_table, 0);
if (mixer->mask_recsrc) { if (mixer->mask_recsrc) {
mixer->get_recsrc = snd_mixer_oss_get_recsrc2; mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
mixer->put_recsrc = snd_mixer_oss_put_recsrc2; mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
...@@ -968,7 +1139,7 @@ static int snd_mixer_oss_free1(void *private) ...@@ -968,7 +1139,7 @@ static int snd_mixer_oss_free1(void *private)
card = mixer->card; card = mixer->card;
snd_assert(mixer == card->mixer_oss, return -ENXIO); snd_assert(mixer == card->mixer_oss, return -ENXIO);
card->mixer_oss = NULL; card->mixer_oss = NULL;
for (idx = 0; idx < 31; idx++) { for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
snd_mixer_oss_slot_t *chn = &mixer->slots[idx]; snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
if (chn->private_free) if (chn->private_free)
chn->private_free(chn); chn->private_free(chn);
...@@ -987,6 +1158,7 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag) ...@@ -987,6 +1158,7 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag)
mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL); mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL);
if (mixer == NULL) if (mixer == NULL)
return -ENOMEM; return -ENOMEM;
init_MUTEX(&mixer->reg_mutex);
sprintf(name, "mixer%i%i", card->number, 0); sprintf(name, "mixer%i%i", card->number, 0);
if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
card, 0, card, 0,
...@@ -1001,16 +1173,18 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag) ...@@ -1001,16 +1173,18 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag)
snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
card->number, card->number,
name); name);
for (idx = 0; idx < 31; idx++) for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
mixer->slots[idx].number = idx; mixer->slots[idx].number = idx;
card->mixer_oss = mixer; card->mixer_oss = mixer;
snd_mixer_oss_build(mixer); snd_mixer_oss_build(mixer);
snd_mixer_oss_proc_init(mixer);
} else { } else {
snd_mixer_oss_t *mixer = card->mixer_oss; snd_mixer_oss_t *mixer = card->mixer_oss;
if (mixer == NULL) if (mixer == NULL)
return 0; return 0;
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
snd_mixer_oss_proc_done(mixer);
return snd_mixer_oss_free1(mixer); return snd_mixer_oss_free1(mixer);
} }
return 0; return 0;
......
...@@ -173,11 +173,11 @@ int snd_pcm_hw_refine(snd_pcm_substream_t *substream, ...@@ -173,11 +173,11 @@ int snd_pcm_hw_refine(snd_pcm_substream_t *substream,
continue; continue;
#ifdef RULES_DEBUG #ifdef RULES_DEBUG
printk("%s = ", snd_pcm_hw_param_names[k]); printk("%s = ", snd_pcm_hw_param_names[k]);
printk("%x -> ", *m); printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif #endif
changed = snd_mask_refine(m, constrs_mask(constrs, k)); changed = snd_mask_refine(m, constrs_mask(constrs, k));
#ifdef RULES_DEBUG #ifdef RULES_DEBUG
printk("%x\n", *m); printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif #endif
if (changed) if (changed)
params->cmask |= 1 << k; params->cmask |= 1 << k;
......
...@@ -820,11 +820,6 @@ int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, ...@@ -820,11 +820,6 @@ int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream)
{
/* TODO: reset current state */
}
int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count) int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count)
{ {
unsigned long flags; unsigned long flags;
...@@ -969,11 +964,6 @@ static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff ...@@ -969,11 +964,6 @@ static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff
return result; return result;
} }
void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream)
{
/* TODO: reset current state */
}
int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream)
{ {
snd_rawmidi_runtime_t *runtime = substream->runtime; snd_rawmidi_runtime_t *runtime = substream->runtime;
...@@ -1564,9 +1554,7 @@ EXPORT_SYMBOL(snd_rawmidi_input_params); ...@@ -1564,9 +1554,7 @@ EXPORT_SYMBOL(snd_rawmidi_input_params);
EXPORT_SYMBOL(snd_rawmidi_drop_output); EXPORT_SYMBOL(snd_rawmidi_drop_output);
EXPORT_SYMBOL(snd_rawmidi_drain_output); EXPORT_SYMBOL(snd_rawmidi_drain_output);
EXPORT_SYMBOL(snd_rawmidi_drain_input); EXPORT_SYMBOL(snd_rawmidi_drain_input);
EXPORT_SYMBOL(snd_rawmidi_receive_reset);
EXPORT_SYMBOL(snd_rawmidi_receive); EXPORT_SYMBOL(snd_rawmidi_receive);
EXPORT_SYMBOL(snd_rawmidi_transmit_reset);
EXPORT_SYMBOL(snd_rawmidi_transmit_empty); EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
EXPORT_SYMBOL(snd_rawmidi_transmit_peek); EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
EXPORT_SYMBOL(snd_rawmidi_transmit_ack); EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
......
...@@ -73,6 +73,7 @@ obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-mi ...@@ -73,6 +73,7 @@ obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-mi
obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o
obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
obj-$(CONFIG_SND_USB_AUDIO) += snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-virmidi.o
obj-m := $(sort $(obj-m)) obj-m := $(sort $(obj-m))
......
...@@ -50,7 +50,6 @@ static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) ...@@ -50,7 +50,6 @@ static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus)
if (stat & 0x10) { /* framing error */ if (stat & 0x10) { /* framing error */
gus->gf1.uart_framing++; gus->gf1.uart_framing++;
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
snd_rawmidi_receive_reset(gus->midi_substream_input);
continue; continue;
} }
byte = snd_gf1_uart_get(gus); byte = snd_gf1_uart_get(gus);
...@@ -58,7 +57,6 @@ static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) ...@@ -58,7 +57,6 @@ static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus)
snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
if (stat & 0x20) { if (stat & 0x20) {
gus->gf1.uart_overrun++; gus->gf1.uart_overrun++;
snd_rawmidi_receive_reset(gus->midi_substream_input);
} }
} }
} }
......
...@@ -1501,7 +1501,7 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) ...@@ -1501,7 +1501,7 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97)
goto __access_ok; goto __access_ok;
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/100); schedule_timeout(HZ/100);
} while (end_time - (signed long)jiffies >= 0); } while (time_after_eq(end_time, jiffies));
snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err);
snd_ac97_free(ac97); snd_ac97_free(ac97);
return -ENXIO; return -ENXIO;
...@@ -1536,31 +1536,38 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) ...@@ -1536,31 +1536,38 @@ int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97)
goto __ready_ok; goto __ready_ok;
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10); schedule_timeout(HZ/10);
} while (end_time - (signed long)jiffies >= 0); } while (time_after_eq(end_time, jiffies));
snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr); snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr);
__ready_ok: __ready_ok:
if (ac97->clock == 0) if (ac97->clock == 0)
ac97->clock = 48000; /* standard value */ ac97->clock = 48000; /* standard value */
ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;
if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */
snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
if (ac97->ext_id & 0x0001) { /* VRA support */ if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac); snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc); snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]);
} else { } else {
ac97->rates_front_dac = SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
ac97->rates_adc = SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
} }
if (ac97->ext_id & 0x0008) { /* MIC VRA support */ if (ac97->ext_id & AC97_EI_SPDIF) {
snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc); /* codec specific code (patch) should override these values */
ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000;
}
if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]);
} else { } else {
ac97->rates_mic_adc = SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
} }
if (ac97->ext_id & 0x0080) { /* SDAC support */ if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac); snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
ac97->scaps |= AC97_SCAP_SURROUND_DAC; ac97->scaps |= AC97_SCAP_SURROUND_DAC;
} }
if (ac97->ext_id & 0x0100) { /* LDAC support */ if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac); snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
} }
/* additional initializations */ /* additional initializations */
...@@ -1849,54 +1856,35 @@ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) ...@@ -1849,54 +1856,35 @@ int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate)
{ {
unsigned short mask; unsigned short mask;
unsigned int tmp; unsigned int tmp;
signed long end_time;
switch (reg) { switch (reg) {
case AC97_PCM_MIC_ADC_RATE: case AC97_PCM_MIC_ADC_RATE:
mask = 0x0000; mask = 0x0000;
if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0) /* MIC VRA */ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */
if (rate != 48000) if (rate != 48000)
return -EINVAL; return -EINVAL;
break; break;
case AC97_PCM_FRONT_DAC_RATE: case AC97_PCM_FRONT_DAC_RATE:
mask = 0x0200; mask = 0x0200;
if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */
if (rate != 48000) if (rate != 48000)
return -EINVAL; return -EINVAL;
break; break;
case AC97_PCM_LR_ADC_RATE: case AC97_PCM_LR_ADC_RATE:
mask = 0x0100; mask = 0x0100;
if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */
if (rate != 48000) if (rate != 48000)
return -EINVAL; return -EINVAL;
break; break;
case AC97_SPDIF:
return 0;
default: return -EINVAL; default: return -EINVAL;
} }
tmp = ((unsigned int)rate * ac97->clock) / 48000; tmp = ((unsigned int)rate * ac97->clock) / 48000;
if (tmp > 65535) if (tmp > 65535)
return -EINVAL; return -EINVAL;
snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask);
end_time = jiffies + (HZ / 50);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
} while (end_time - (signed long)jiffies >= 0);
snd_ac97_update(ac97, reg, tmp & 0xffff); snd_ac97_update(ac97, reg, tmp & 0xffff);
udelay(10); snd_ac97_read(ac97, reg);
// XXXX update spdif rate here too?
snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0);
end_time = jiffies + (HZ / 50);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
} while (end_time - (signed long)jiffies >= 0);
end_time = jiffies + (HZ / 10);
do {
if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003)
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
} while (end_time - (signed long)jiffies >= 0);
return 0; return 0;
} }
......
...@@ -166,6 +166,7 @@ int patch_cirrus_spdif(ac97_t * ac97) ...@@ -166,6 +166,7 @@ int patch_cirrus_spdif(ac97_t * ac97)
*/ */
ac97->flags |= AC97_CS_SPDIF; ac97->flags |= AC97_CS_SPDIF;
ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
ac97->ext_id |= AC97_EA_SPDIF; /* force the detection of spdif */ ac97->ext_id |= AC97_EA_SPDIF; /* force the detection of spdif */
snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080); snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
return 0; return 0;
......
...@@ -855,6 +855,7 @@ static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream, ...@@ -855,6 +855,7 @@ static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream,
if (copy_from_user(hwbuf, src, bytes)) if (copy_from_user(hwbuf, src, bytes))
return -EFAULT; return -EFAULT;
spin_lock_irq(&runtime->lock); spin_lock_irq(&runtime->lock);
snd_cs46xx_playback_transfer(substream, frames); snd_cs46xx_playback_transfer(substream, frames);
spin_unlock_irq(&runtime->lock); spin_unlock_irq(&runtime->lock);
...@@ -885,15 +886,17 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, ...@@ -885,15 +886,17 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
{ {
cs46xx_t *chip = snd_pcm_substream_chip(substream); cs46xx_t *chip = snd_pcm_substream_chip(substream);
/*snd_pcm_runtime_t *runtime = substream->runtime;*/ /*snd_pcm_runtime_t *runtime = substream->runtime;*/
int result = 0;
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
#else
spin_lock(&chip->reg_lock);
#endif #endif
int result = 0;
spin_lock(&chip->reg_lock);
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
if (! cpcm->pcm_channel) { if (! cpcm->pcm_channel) {
spin_unlock(&chip->reg_lock);
return -ENXIO; return -ENXIO;
} }
#endif #endif
...@@ -905,6 +908,7 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, ...@@ -905,6 +908,7 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel); cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
if (substream->runtime->periods != CS46XX_FRAGS) if (substream->runtime->periods != CS46XX_FRAGS)
snd_cs46xx_playback_transfer(substream, 0); snd_cs46xx_playback_transfer(substream, 0);
/* raise playback volume */ /* raise playback volume */
snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0x80008000); snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0x80008000);
#else #else
...@@ -920,6 +924,8 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, ...@@ -920,6 +924,8 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
/* mute channel */
snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0xffffffff);
if (!cpcm->pcm_channel->unlinked) if (!cpcm->pcm_channel->unlinked)
cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel); cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
#else #else
...@@ -934,7 +940,11 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, ...@@ -934,7 +940,11 @@ static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,
result = -EINVAL; result = -EINVAL;
break; break;
} }
#ifndef CONFIG_SND_CS46XX_NEW_DSP
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
#endif
return result; return result;
} }
...@@ -964,6 +974,7 @@ static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream, ...@@ -964,6 +974,7 @@ static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream,
break; break;
} }
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
return result; return result;
} }
...@@ -994,6 +1005,7 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, ...@@ -994,6 +1005,7 @@ static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream,
return err; return err;
substream->ops = &snd_cs46xx_playback_indirect_ops; substream->ops = &snd_cs46xx_playback_indirect_ops;
} }
return 0; return 0;
} }
...@@ -1011,6 +1023,7 @@ static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream) ...@@ -1011,6 +1023,7 @@ static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream)
runtime->dma_area = NULL; runtime->dma_area = NULL;
runtime->dma_addr = 0; runtime->dma_addr = 0;
runtime->dma_bytes = 0; runtime->dma_bytes = 0;
return 0; return 0;
} }
...@@ -1025,7 +1038,8 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream) ...@@ -1025,7 +1038,8 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
down (&chip->dsp_spos_instance->pcm_mutex); down (&chip->spos_mutex);
if ( cpcm->pcm_channel->src_scb->ref_count == 1 && if ( cpcm->pcm_channel->src_scb->ref_count == 1 &&
cpcm->pcm_channel->sample_rate != runtime->rate) { cpcm->pcm_channel->sample_rate != runtime->rate) {
/* sample rate not set or we can reuse /* sample rate not set or we can reuse
...@@ -1034,23 +1048,25 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream) ...@@ -1034,23 +1048,25 @@ static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream)
cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,runtime->rate); cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,runtime->rate);
cpcm->pcm_channel->sample_rate = runtime->rate; cpcm->pcm_channel->sample_rate = runtime->rate;
} }
up (&chip->dsp_spos_instance->pcm_mutex);
if (cpcm->pcm_channel->sample_rate != runtime->rate && if (cpcm->pcm_channel->sample_rate != runtime->rate &&
cpcm->pcm_channel->src_scb->ref_count != 1) { cpcm->pcm_channel->src_scb->ref_count != 1) {
int unlinked = cpcm->pcm_channel->unlinked;
cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel); 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) { 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"); snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
up (&chip->spos_mutex);
return -ENXIO; return -ENXIO;
} }
cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel); if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
cpcm->pcm_channel->sample_rate = runtime->rate; cpcm->pcm_channel->sample_rate = runtime->rate;
} }
pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 ); pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
pfie &= ~0x0000f03f; pfie &= ~0x0000f03f;
up (&chip->spos_mutex);
#else #else
/* old dsp */ /* old dsp */
pfie = snd_cs46xx_peek(chip, BA1_PFIE); pfie = snd_cs46xx_peek(chip, BA1_PFIE);
...@@ -1130,6 +1146,7 @@ static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, ...@@ -1130,6 +1146,7 @@ static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream,
return err; return err;
substream->ops = &snd_cs46xx_capture_indirect_ops; substream->ops = &snd_cs46xx_capture_indirect_ops;
} }
return 0; return 0;
} }
...@@ -1143,6 +1160,7 @@ static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream) ...@@ -1143,6 +1160,7 @@ static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream)
runtime->dma_area = NULL; runtime->dma_area = NULL;
runtime->dma_addr = 0; runtime->dma_addr = 0;
runtime->dma_bytes = 0; runtime->dma_bytes = 0;
return 0; return 0;
} }
...@@ -1158,6 +1176,7 @@ static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream) ...@@ -1158,6 +1176,7 @@ static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream)
chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0; chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0;
chip->capt.appl_ptr = 0; chip->capt.appl_ptr = 0;
snd_cs46xx_set_capture_sample_rate(chip, runtime->rate); snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
return 0; return 0;
} }
...@@ -1183,6 +1202,7 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1183,6 +1202,7 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0); status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) { for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
if (i <= 15) { if (i <= 15) {
if ( status1 & (1 << i) ) { if ( status1 & (1 << i) ) {
...@@ -1193,7 +1213,7 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1193,7 +1213,7 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (ins->pcm_channels[i].active && if (ins->pcm_channels[i].active &&
ins->pcm_channels[i].private_data && ins->pcm_channels[i].private_data &&
!ins->pcm_channels[i].unlinked) { !ins->pcm_channels[i].unlinked) {
cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer); cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
snd_pcm_period_elapsed(cpcm->substream); snd_pcm_period_elapsed(cpcm->substream);
} }
} }
...@@ -1203,16 +1223,13 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1203,16 +1223,13 @@ static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (ins->pcm_channels[i].active && if (ins->pcm_channels[i].active &&
ins->pcm_channels[i].private_data && ins->pcm_channels[i].private_data &&
!ins->pcm_channels[i].unlinked) { !ins->pcm_channels[i].unlinked) {
cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer); cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
snd_pcm_period_elapsed(cpcm->substream); snd_pcm_period_elapsed(cpcm->substream);
} }
} }
} }
continue;
_invalid_pointer:
printk (KERN_ERR "cs46xx: (interrupt) invalid pointer at pcm_channel[%d]\n",i);
} }
#else #else
/* old dsp */ /* old dsp */
if ((status1 & HISR_VC0) && chip->playback_pcm) { if ((status1 & HISR_VC0) && chip->playback_pcm) {
...@@ -1324,16 +1341,18 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream) ...@@ -1324,16 +1341,18 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
cpcm->substream = substream; cpcm->substream = substream;
#ifdef CONFIG_SND_CS46XX_NEW_DSP #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);
if (cpcm->pcm_channel == NULL) { if (cpcm->pcm_channel == NULL) {
snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n"); snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
snd_magic_kfree(cpcm); snd_magic_kfree(cpcm);
up (&chip->spos_mutex);
return -ENOMEM; return -ENOMEM;
} }
cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel); up (&chip->spos_mutex);
#else #else
chip->playback_pcm = cpcm; /* HACK */ chip->playback_pcm = cpcm; /* HACK */
#endif #endif
...@@ -1342,6 +1361,7 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream) ...@@ -1342,6 +1361,7 @@ static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream)
substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
chip->active_ctrl(chip, 1); chip->active_ctrl(chip, 1);
chip->amplifier_ctrl(chip, 1); chip->amplifier_ctrl(chip, 1);
return 0; return 0;
} }
...@@ -1353,10 +1373,13 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) ...@@ -1353,10 +1373,13 @@ static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream)
return -ENOMEM; return -ENOMEM;
chip->capt.substream = substream; chip->capt.substream = substream;
substream->runtime->hw = snd_cs46xx_capture; substream->runtime->hw = snd_cs46xx_capture;
if (chip->accept_valid) if (chip->accept_valid)
substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID; substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
chip->active_ctrl(chip, 1); chip->active_ctrl(chip, 1);
chip->amplifier_ctrl(chip, 1); chip->amplifier_ctrl(chip, 1);
return 0; return 0;
} }
...@@ -1369,10 +1392,12 @@ static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) ...@@ -1369,10 +1392,12 @@ static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream)
cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
down (&chip->spos_mutex);
if (cpcm->pcm_channel) { if (cpcm->pcm_channel) {
cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel); cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
cpcm->pcm_channel = NULL; cpcm->pcm_channel = NULL;
} }
up (&chip->spos_mutex);
#else #else
chip->playback_pcm = NULL; chip->playback_pcm = NULL;
#endif #endif
...@@ -1381,6 +1406,7 @@ static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) ...@@ -1381,6 +1406,7 @@ static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream)
snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
chip->active_ctrl(chip, -1); chip->active_ctrl(chip, -1);
chip->amplifier_ctrl(chip, -1); chip->amplifier_ctrl(chip, -1);
return 0; return 0;
} }
...@@ -1392,6 +1418,7 @@ static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream) ...@@ -1392,6 +1418,7 @@ static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream)
snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr);
chip->active_ctrl(chip, -1); chip->active_ctrl(chip, -1);
chip->amplifier_ctrl(chip, -1); chip->amplifier_ctrl(chip, -1);
return 0; return 0;
} }
...@@ -1550,6 +1577,7 @@ static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ...@@ -1550,6 +1577,7 @@ static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *
} }
#endif #endif
} }
return change; return change;
} }
...@@ -1575,6 +1603,7 @@ static int snd_cs46xx_iec958_get(snd_kcontrol_t *kcontrol, ...@@ -1575,6 +1603,7 @@ static int snd_cs46xx_iec958_get(snd_kcontrol_t *kcontrol,
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out; ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out;
else else
ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in; ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
return 0; return 0;
} }
...@@ -1719,7 +1748,6 @@ static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol, ...@@ -1719,7 +1748,6 @@ static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol,
#endif /* CONFIG_SND_CS46XX_NEW_DSP */ #endif /* CONFIG_SND_CS46XX_NEW_DSP */
#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO #ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol, static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol,
snd_ctl_elem_info_t *uinfo) snd_ctl_elem_info_t *uinfo)
...@@ -1979,6 +2007,7 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip) ...@@ -1979,6 +2007,7 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
/* add cs4630 mixer controls */ /* add cs4630 mixer controls */
_end: _end:
/* dosoundcard specific mixer setup */ /* dosoundcard specific mixer setup */
if (chip->mixer_init) { if (chip->mixer_init) {
snd_printdd ("calling chip->mixer_init(chip);\n"); snd_printdd ("calling chip->mixer_init(chip);\n");
...@@ -2447,7 +2476,7 @@ static int snd_cs46xx_free(cs46xx_t *chip) ...@@ -2447,7 +2476,7 @@ static int snd_cs46xx_free(cs46xx_t *chip)
chip->active_ctrl(chip, -chip->amplifier); chip->active_ctrl(chip, -chip->amplifier);
#ifdef CONFIG_SND_CS46XX_NEW_DSP #ifdef CONFIG_SND_CS46XX_NEW_DSP
cs46xx_dsp_spos_destroy(chip->dsp_spos_instance); cs46xx_dsp_spos_destroy(chip);
#endif #endif
snd_magic_kfree(chip); snd_magic_kfree(chip);
return 0; return 0;
...@@ -2920,7 +2949,6 @@ static void amp_voyetra(cs46xx_t *chip, int change) ...@@ -2920,7 +2949,6 @@ static void amp_voyetra(cs46xx_t *chip, int change)
#endif #endif
} }
static void hercules_init(cs46xx_t *chip) static void hercules_init(cs46xx_t *chip)
{ {
/* default: AMP off, and SPDIF input optical */ /* default: AMP off, and SPDIF input optical */
...@@ -2928,6 +2956,7 @@ static void hercules_init(cs46xx_t *chip) ...@@ -2928,6 +2956,7 @@ static void hercules_init(cs46xx_t *chip)
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0); snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
} }
/* /*
* Game Theatre XP card - EGPIO[2] is used to enable the external amp. * Game Theatre XP card - EGPIO[2] is used to enable the external amp.
*/ */
...@@ -2940,6 +2969,7 @@ static void amp_hercules(cs46xx_t *chip, int change) ...@@ -2940,6 +2969,7 @@ static void amp_hercules(cs46xx_t *chip, int change)
chip->amplifier += change; chip->amplifier += change;
if (chip->amplifier && !old) { if (chip->amplifier && !old) {
snd_printdd ("Hercules amplifier ON\n"); snd_printdd ("Hercules amplifier ON\n");
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */ EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
...@@ -2980,6 +3010,7 @@ static void hercules_mixer_init (cs46xx_t *chip) ...@@ -2980,6 +3010,7 @@ static void hercules_mixer_init (cs46xx_t *chip)
#endif #endif
} }
#if 0 #if 0
/* /*
* Untested * Untested
......
...@@ -84,7 +84,7 @@ static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offs ...@@ -84,7 +84,7 @@ static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offs
} }
dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip); dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip);
void cs46xx_dsp_spos_destroy (dsp_spos_instance_t * instance); void cs46xx_dsp_spos_destroy (cs46xx_t * chip);
int cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module); int cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module);
symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type); symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type);
symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip,u32 address,int symbol_type); symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip,u32 address,int symbol_type);
......
...@@ -222,16 +222,13 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip) ...@@ -222,16 +222,13 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
return NULL; return NULL;
memset(ins, 0, sizeof(*ins)); memset(ins, 0, sizeof(*ins));
init_MUTEX(&ins->scb_mutex);
init_MUTEX(&ins->pcm_mutex);
/* better to use vmalloc for this big table */ /* better to use vmalloc for this big table */
ins->symbol_table.nsymbols = 0; ins->symbol_table.nsymbols = 0;
ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS); ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS);
ins->symbol_table.highest_frag_index = 0; ins->symbol_table.highest_frag_index = 0;
if (ins->symbol_table.symbols == NULL) { if (ins->symbol_table.symbols == NULL) {
cs46xx_dsp_spos_destroy(ins); cs46xx_dsp_spos_destroy(chip);
return NULL; return NULL;
} }
...@@ -240,7 +237,7 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip) ...@@ -240,7 +237,7 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL); ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
if (ins->code.data == NULL) { if (ins->code.data == NULL) {
cs46xx_dsp_spos_destroy(ins); cs46xx_dsp_spos_destroy(chip);
return NULL; return NULL;
} }
...@@ -251,7 +248,7 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip) ...@@ -251,7 +248,7 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL); ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL);
if (ins->modules == NULL) { if (ins->modules == NULL) {
cs46xx_dsp_spos_destroy(ins); cs46xx_dsp_spos_destroy(chip);
return NULL; return NULL;
} }
...@@ -265,19 +262,19 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip) ...@@ -265,19 +262,19 @@ dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip)
return ins; return ins;
} }
void cs46xx_dsp_spos_destroy (dsp_spos_instance_t * ins) void cs46xx_dsp_spos_destroy (cs46xx_t * chip)
{ {
int i; int i;
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
snd_assert(ins != NULL, return); snd_assert(ins != NULL, return);
down(&ins->scb_mutex); down(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) { for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue; if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
} }
up(&ins->scb_mutex);
if (ins->code.data) if (ins->code.data)
kfree(ins->code.data); kfree(ins->code.data);
...@@ -286,6 +283,7 @@ void cs46xx_dsp_spos_destroy (dsp_spos_instance_t * ins) ...@@ -286,6 +283,7 @@ void cs46xx_dsp_spos_destroy (dsp_spos_instance_t * ins)
if (ins->modules) if (ins->modules)
kfree(ins->modules); kfree(ins->modules);
kfree(ins); kfree(ins);
up(&chip->spos_mutex);
} }
int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module) int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module)
...@@ -479,6 +477,7 @@ static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buff ...@@ -479,6 +477,7 @@ static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buff
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
int i,j; int i,j;
down(&chip->spos_mutex);
snd_iprintf(buffer, "MODULES:\n"); snd_iprintf(buffer, "MODULES:\n");
for ( i = 0; i < ins->nmodules; ++i ) { for ( i = 0; i < ins->nmodules; ++i ) {
snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name); snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
...@@ -491,6 +490,7 @@ static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buff ...@@ -491,6 +490,7 @@ static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buff
desc->segment_type,desc->offset, desc->size); desc->segment_type,desc->offset, desc->size);
} }
} }
up(&chip->spos_mutex);
} }
static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
...@@ -500,6 +500,7 @@ static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_bu ...@@ -500,6 +500,7 @@ static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_bu
int i,j,col; int i,j,col;
unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
down(&chip->spos_mutex);
snd_iprintf(buffer, "TASK TREES:\n"); snd_iprintf(buffer, "TASK TREES:\n");
for ( i = 0; i < ins->ntask; ++i) { for ( i = 0; i < ins->ntask; ++i) {
snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name); snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
...@@ -516,6 +517,7 @@ static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_bu ...@@ -516,6 +517,7 @@ static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_bu
} }
snd_iprintf(buffer,"\n"); snd_iprintf(buffer,"\n");
up(&chip->spos_mutex);
} }
static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
...@@ -524,6 +526,7 @@ static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t ...@@ -524,6 +526,7 @@ static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
int i; int i;
down(&chip->spos_mutex);
snd_iprintf(buffer, "SCB's:\n"); snd_iprintf(buffer, "SCB's:\n");
for ( i = 0; i < ins->nscb; ++i) { for ( i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) if (ins->scbs[i].deleted)
...@@ -546,6 +549,7 @@ static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t ...@@ -546,6 +549,7 @@ static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t
} }
snd_iprintf(buffer,"\n"); snd_iprintf(buffer,"\n");
up(&chip->spos_mutex);
} }
static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
...@@ -809,12 +813,14 @@ int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip) ...@@ -809,12 +813,14 @@ int cs46xx_dsp_proc_init (snd_card_t * card, cs46xx_t *chip)
} }
ins->proc_scb_info_entry = entry; ins->proc_scb_info_entry = entry;
down(&chip->spos_mutex);
/* register/update SCB's entries on proc */ /* register/update SCB's entries on proc */
for (i = 0; i < ins->nscb; ++i) { for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue; if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i)); cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
} }
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -854,12 +860,12 @@ int cs46xx_dsp_proc_done (cs46xx_t *chip) ...@@ -854,12 +860,12 @@ int cs46xx_dsp_proc_done (cs46xx_t *chip)
ins->proc_task_info_entry = NULL; ins->proc_task_info_entry = NULL;
} }
down(&ins->scb_mutex); down(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) { for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue; if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
} }
up(&ins->scb_mutex); up(&chip->spos_mutex);
if (ins->proc_dsp_dir) { if (ins->proc_dsp_dir) {
snd_info_unregister (ins->proc_dsp_dir); snd_info_unregister (ins->proc_dsp_dir);
...@@ -930,6 +936,7 @@ static dsp_scb_descriptor_t * _map_scb (cs46xx_t *chip,char * name,u32 dest) ...@@ -930,6 +936,7 @@ static dsp_scb_descriptor_t * _map_scb (cs46xx_t *chip,char * name,u32 dest)
ins->scbs[index].proc_info = NULL; ins->scbs[index].proc_info = NULL;
ins->scbs[index].ref_count = 1; ins->scbs[index].ref_count = 1;
ins->scbs[index].deleted = 0; ins->scbs[index].deleted = 0;
spin_lock_init(&ins->scbs[index].lock);
desc = (ins->scbs + index); desc = (ins->scbs + index);
ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER); ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
...@@ -1313,9 +1320,11 @@ int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip) ...@@ -1313,9 +1320,11 @@ int cs46xx_dsp_scb_and_task_init (cs46xx_t *chip)
codec_out_scb, codec_out_scb,
sec_codec_out_scb, sec_codec_out_scb,
SCB_ON_PARENT_SUBLIST_SCB); SCB_ON_PARENT_SUBLIST_SCB);
if (!magic_snoop_scb) goto _fail_end; if (!magic_snoop_scb) goto _fail_end;
ins->ref_snoop_scb = magic_snoop_scb; ins->ref_snoop_scb = magic_snoop_scb;
/* The asynch. transfer task */ /* The asynch. transfer task */
asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR, asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
SPDIFO_SCB_INST, SPDIFO_SCB_INST,
...@@ -1576,6 +1585,8 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip) ...@@ -1576,6 +1585,8 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL); snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
snd_assert (ins->spdif_in_src != NULL,return -EINVAL); snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
down(&chip->spos_mutex);
/* create and start the asynchronous receiver SCB */ /* create and start the asynchronous receiver SCB */
ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB", ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
ASYNCRX_SCB_ADDR, ASYNCRX_SCB_ADDR,
...@@ -1586,6 +1597,7 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip) ...@@ -1586,6 +1597,7 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
save_flags(flags); save_flags(flags);
cli(); cli();
/* reset SPDIF input sample buffer pointer */ /* reset SPDIF input sample buffer pointer */
snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2, snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
(SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC); (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC);
...@@ -1605,6 +1617,7 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip) ...@@ -1605,6 +1617,7 @@ int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
/* monitor state */ /* monitor state */
ins->spdif_status_in = 1; ins->spdif_status_in = 1;
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -1616,6 +1629,7 @@ int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip) ...@@ -1616,6 +1629,7 @@ int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip)
snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL); snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
snd_assert (ins->spdif_in_src != NULL,return -EINVAL); snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
down(&chip->spos_mutex);
/* Remove the asynchronous receiver SCB */ /* Remove the asynchronous receiver SCB */
cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb); cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
ins->asynch_rx_scb = NULL; ins->asynch_rx_scb = NULL;
...@@ -1624,6 +1638,7 @@ int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip) ...@@ -1624,6 +1638,7 @@ int cs46xx_dsp_disable_spdif_in (cs46xx_t *chip)
/* monitor state */ /* monitor state */
ins->spdif_status_in = 0; ins->spdif_status_in = 0;
up(&chip->spos_mutex);
/* restore amplifier */ /* restore amplifier */
chip->active_ctrl(chip, -1); chip->active_ctrl(chip, -1);
...@@ -1639,8 +1654,10 @@ int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip) ...@@ -1639,8 +1654,10 @@ int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip)
snd_assert (ins->pcm_input == NULL,return -EINVAL); snd_assert (ins->pcm_input == NULL,return -EINVAL);
snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL); snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
down(&chip->spos_mutex);
ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR, ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
"PCMSerialInput_Wave"); "PCMSerialInput_Wave");
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -1651,8 +1668,10 @@ int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip) ...@@ -1651,8 +1668,10 @@ int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip)
snd_assert (ins->pcm_input != NULL,return -EINVAL); snd_assert (ins->pcm_input != NULL,return -EINVAL);
down(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->pcm_input); cs46xx_dsp_remove_scb (chip,ins->pcm_input);
ins->pcm_input = NULL; ins->pcm_input = NULL;
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -1664,8 +1683,10 @@ int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip) ...@@ -1664,8 +1683,10 @@ int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip)
snd_assert (ins->adc_input == NULL,return -EINVAL); snd_assert (ins->adc_input == NULL,return -EINVAL);
snd_assert (ins->codec_in_scb != NULL,return -EINVAL); snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
down(&chip->spos_mutex);
ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR, ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
"PCMSerialInput_ADC"); "PCMSerialInput_ADC");
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -1676,8 +1697,10 @@ int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip) ...@@ -1676,8 +1697,10 @@ int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip)
snd_assert (ins->adc_input != NULL,return -EINVAL); snd_assert (ins->adc_input != NULL,return -EINVAL);
down(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->adc_input); cs46xx_dsp_remove_scb (chip,ins->adc_input);
ins->adc_input = NULL; ins->adc_input = NULL;
up(&chip->spos_mutex);
return 0; return 0;
} }
...@@ -1697,7 +1720,7 @@ int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data) ...@@ -1697,7 +1720,7 @@ int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data)
snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR << 2), temp); snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR << 2), temp);
snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */ snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */
snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2*/ snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */
/* Poke this location to tell the task to start */ /* Poke this location to tell the task to start */
snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10); snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10);
......
...@@ -75,7 +75,7 @@ static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buf ...@@ -75,7 +75,7 @@ static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buf
ins = chip->dsp_spos_instance; ins = chip->dsp_spos_instance;
down(&ins->scb_mutex); down(&chip->spos_mutex);
snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name); snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
for (col = 0,j = 0;j < 0x10; j++,col++) { for (col = 0,j = 0;j < 0x10; j++,col++) {
...@@ -103,7 +103,7 @@ static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buf ...@@ -103,7 +103,7 @@ static void cs46xx_dsp_proc_scb_info_read (snd_info_entry_t *entry, snd_info_buf
scb->task_entry->address); scb->task_entry->address);
snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count); snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);
up(&ins->scb_mutex); up(&chip->spos_mutex);
} }
static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb) static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
...@@ -180,11 +180,10 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb) ...@@ -180,11 +180,10 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb)
{ {
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
down(&ins->scb_mutex);
/* check integrety */ /* check integrety */
snd_assert ( (scb->index >= 0 && snd_assert ( (scb->index >= 0 &&
scb->index < ins->nscb && scb->index < ins->nscb &&
(ins->scbs + scb->index) == scb), goto _end ); (ins->scbs + scb->index) == scb), return );
#if 0 #if 0
/* cant remove a SCB with childs before /* cant remove a SCB with childs before
...@@ -194,10 +193,12 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb) ...@@ -194,10 +193,12 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb)
goto _end); goto _end);
#endif #endif
spin_lock(&scb->lock);
_dsp_unlink_scb (chip,scb); _dsp_unlink_scb (chip,scb);
spin_unlock(&scb->lock);
cs46xx_dsp_proc_free_scb_desc(scb); cs46xx_dsp_proc_free_scb_desc(scb);
snd_assert (scb->scb_symbol != NULL, goto _end); snd_assert (scb->scb_symbol != NULL, return );
remove_symbol (chip,scb->scb_symbol); remove_symbol (chip,scb->scb_symbol);
ins->scbs[scb->index].deleted = 1; ins->scbs[scb->index].deleted = 1;
...@@ -219,11 +220,6 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb) ...@@ -219,11 +220,6 @@ void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb)
ins->scbs[i - 1].index = i - 1; ins->scbs[i - 1].index = i - 1;
} }
#endif #endif
#ifdef CONFIG_SND_DEBUG
_end:
#endif
up(&ins->scb_mutex);
} }
...@@ -248,7 +244,6 @@ void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * sc ...@@ -248,7 +244,6 @@ void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * sc
snd_info_entry_t * entry; snd_info_entry_t * entry;
proc_scb_info_t * scb_info; proc_scb_info_t * scb_info;
down(&ins->scb_mutex);
/* register to proc */ /* register to proc */
if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL && if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
scb->proc_info == NULL) { scb->proc_info == NULL) {
...@@ -275,7 +270,6 @@ void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * sc ...@@ -275,7 +270,6 @@ void cs46xx_dsp_proc_register_scb_desc (cs46xx_t *chip,dsp_scb_descriptor_t * sc
scb->proc_info = entry; scb->proc_info = entry;
} }
up(&ins->scb_mutex);
} }
static dsp_scb_descriptor_t * static dsp_scb_descriptor_t *
...@@ -289,8 +283,7 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest, ...@@ -289,8 +283,7 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
unsigned long flags; unsigned long flags;
down(&ins->scb_mutex); snd_assert (ins->the_null_scb != NULL,return NULL);
snd_assert (ins->the_null_scb != NULL,goto _fail_end);
/* fill the data that will be wroten to DSP */ /* fill the data that will be wroten to DSP */
scb_data[SCBsubListPtr] = scb_data[SCBsubListPtr] =
...@@ -321,17 +314,17 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest, ...@@ -321,17 +314,17 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
/* link to parent SCB */ /* link to parent SCB */
if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) { if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb), snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
goto _fail_end); return NULL);
scb->parent_scb_ptr->next_scb_ptr = scb; scb->parent_scb_ptr->next_scb_ptr = scb;
} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) { } else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb), snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
goto _fail_end); return NULL);
scb->parent_scb_ptr->sub_list_ptr = scb; scb->parent_scb_ptr->sub_list_ptr = scb;
} else { } else {
snd_assert (0,goto _fail_end); snd_assert (0,return NULL);
} }
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
...@@ -345,17 +338,9 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest, ...@@ -345,17 +338,9 @@ _dsp_create_generic_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest,
} }
up(&ins->scb_mutex);
cs46xx_dsp_proc_register_scb_desc (chip,scb); cs46xx_dsp_proc_register_scb_desc (chip,scb);
return scb; return scb;
#ifdef CONFIG_SND_DEBUG
_fail_end:
up(&ins->scb_mutex);
return NULL;
#endif
} }
dsp_scb_descriptor_t * dsp_scb_descriptor_t *
...@@ -914,6 +899,7 @@ cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest, ...@@ -914,6 +899,7 @@ cs46xx_dsp_create_asynch_fg_rx_scb(cs46xx_t * chip,char * scb_name,u32 dest,
/* There is no correct initial value, it will depend upon the detected /* There is no correct initial value, it will depend upon the detected
rate etc */ rate etc */
0x18000000, 0x18000000,
/* Mute stream */ /* Mute stream */
0x8000,0x8000, 0x8000,0x8000,
0xffff,0xffff 0xffff,0xffff
...@@ -1111,13 +1097,11 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1111,13 +1097,11 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
{ {
dsp_spos_instance_t * ins = chip->dsp_spos_instance; 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;
dsp_scb_descriptor_t * pcm_parent_scb; /*dsp_scb_descriptor_t * pcm_parent_scb;*/
char scb_name[DSP_MAX_SCB_NAME]; char scb_name[DSP_MAX_SCB_NAME];
int i,pcm_index = -1, insert_point, src_index = -1; int i,pcm_index = -1, insert_point, src_index = -1;
unsigned long flags; unsigned long flags;
down(&ins->pcm_mutex);
/* default sample rate is 44100 */ /* default sample rate is 44100 */
if (!sample_rate) sample_rate = 44100; if (!sample_rate) sample_rate = 44100;
...@@ -1142,7 +1126,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1142,7 +1126,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
if (pcm_index == -1) { if (pcm_index == -1) {
snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n"); snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
goto _end; return NULL;
} }
if (src_scb == NULL) { if (src_scb == NULL) {
...@@ -1150,7 +1134,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1150,7 +1134,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
if (ins->nsrc_scb >= DSP_MAX_SRC_NR) { if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!"); snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
goto _end; return NULL;
} }
/* find a free slot */ /* find a free slot */
...@@ -1161,7 +1145,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1161,7 +1145,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
break; break;
} }
} }
snd_assert (src_index != -1,goto _end); snd_assert (src_index != -1,return NULL);
/* we need to create a new SRC SCB */ /* we need to create a new SRC SCB */
if (ins->master_mix_scb->sub_list_ptr == ins->the_null_scb) { if (ins->master_mix_scb->sub_list_ptr == ins->the_null_scb) {
...@@ -1185,32 +1169,13 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1185,32 +1169,13 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
if (!src_scb) { if (!src_scb) {
snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n"); snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
goto _end; return NULL;
} }
cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate); cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate);
ins->nsrc_scb ++; ins->nsrc_scb ++;
/* insert point for the PCMreader task */
pcm_parent_scb = src_scb;
insert_point = SCB_ON_PARENT_SUBLIST_SCB;
} else {
/* if channel is unlinked then src_scb->sub_list_ptr == null_scb, and
that's a correct state.
snd_assert (src_scb->sub_list_ptr != ins->the_null_scb, goto _end);
*/
if (src_scb->sub_list_ptr != ins->the_null_scb) {
pcm_parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr);
insert_point = SCB_ON_PARENT_NEXT_SCB;
} else {
pcm_parent_scb = src_scb;
insert_point = SCB_ON_PARENT_SUBLIST_SCB;
} }
}
snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index); snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
...@@ -1223,19 +1188,20 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1223,19 +1188,20 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
(pcm_index * 0x10) + 0x200, (pcm_index * 0x10) + 0x200,
pcm_index, /* virtual channel 0-31 */ pcm_index, /* virtual channel 0-31 */
hw_dma_addr, /* pcm hw addr */ hw_dma_addr, /* pcm hw addr */
pcm_parent_scb, NULL, /* parent SCB ptr */
insert_point); 0 /* insert point */
);
if (!pcm_scb) { if (!pcm_scb) {
snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n"); snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
goto _end; return NULL;
} }
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
ins->pcm_channels[pcm_index].sample_rate = sample_rate; ins->pcm_channels[pcm_index].sample_rate = sample_rate;
ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb; ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
ins->pcm_channels[pcm_index].src_scb = src_scb; ins->pcm_channels[pcm_index].src_scb = src_scb;
ins->pcm_channels[pcm_index].unlinked = 0; ins->pcm_channels[pcm_index].unlinked = 1;
ins->pcm_channels[pcm_index].private_data = private_data; ins->pcm_channels[pcm_index].private_data = private_data;
ins->pcm_channels[pcm_index].src_slot = src_index; ins->pcm_channels[pcm_index].src_slot = src_index;
ins->pcm_channels[pcm_index].active = 1; ins->pcm_channels[pcm_index].active = 1;
...@@ -1243,12 +1209,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa ...@@ -1243,12 +1209,7 @@ pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sa
ins->npcm_channels ++; ins->npcm_channels ++;
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
up(&ins->pcm_mutex);
return (ins->pcm_channels + pcm_index); return (ins->pcm_channels + pcm_index);
_end:
up(&ins->pcm_mutex);
return NULL;
} }
void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
...@@ -1256,11 +1217,9 @@ void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * ...@@ -1256,11 +1217,9 @@ void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t *
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
unsigned long flags; unsigned long flags;
down(&ins->pcm_mutex); snd_assert(pcm_channel->active, return );
snd_assert(ins->npcm_channels > 0, return );
snd_assert(pcm_channel->active,goto _end); snd_assert(pcm_channel->src_scb->ref_count > 0, return );
snd_assert(ins->npcm_channels > 0,goto _end);
snd_assert(pcm_channel->src_scb->ref_count > 0,goto _end);
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
pcm_channel->unlinked = 1; pcm_channel->unlinked = 1;
...@@ -1276,18 +1235,11 @@ void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * ...@@ -1276,18 +1235,11 @@ void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t *
cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb); cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR, snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
goto _end); return );
ins->src_scb_slots[pcm_channel->src_slot] = 0; ins->src_scb_slots[pcm_channel->src_slot] = 0;
ins->nsrc_scb --; ins->nsrc_scb --;
} }
#ifdef CONFIG_SND_DEBUG
_end:
#endif
up(&ins->pcm_mutex);
} }
int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
...@@ -1295,14 +1247,15 @@ int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channe ...@@ -1295,14 +1247,15 @@ int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channe
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
unsigned long flags; unsigned long flags;
down(&ins->pcm_mutex); snd_assert(pcm_channel->active,return -EIO);
down(&ins->scb_mutex); snd_assert(ins->npcm_channels > 0,return -EIO);
snd_assert(pcm_channel->active,goto _end); spin_lock(&pcm_channel->src_scb->lock);
snd_assert(ins->npcm_channels > 0,goto _end);
if (pcm_channel->unlinked) if (pcm_channel->unlinked) {
goto _end; spin_unlock(&pcm_channel->src_scb->lock);
return -EIO;
}
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
pcm_channel->unlinked = 1; pcm_channel->unlinked = 1;
...@@ -1310,10 +1263,7 @@ int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channe ...@@ -1310,10 +1263,7 @@ int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channe
_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb); _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
_end: spin_unlock(&pcm_channel->src_scb->lock);
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return 0; return 0;
} }
...@@ -1324,25 +1274,32 @@ int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) ...@@ -1324,25 +1274,32 @@ int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb; dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb;
unsigned long flags; unsigned long flags;
down(&ins->pcm_mutex); spin_lock(&pcm_channel->src_scb->lock);
down(&ins->scb_mutex);
if (pcm_channel->unlinked == 0) if (pcm_channel->unlinked == 0) {
goto _end; spin_unlock(&pcm_channel->src_scb->lock);
return -EIO;
}
if (src_scb->sub_list_ptr == ins->the_null_scb) {
parent_scb = src_scb; parent_scb = src_scb;
parent_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
} else { if (src_scb->sub_list_ptr != ins->the_null_scb) {
parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr); src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
parent_scb->next_scb_ptr = pcm_channel->pcm_reader_scb; pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
} }
src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; ); snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb; pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
/* update entry in DSP RAM */ /* update entry in DSP RAM */
spin_lock_irqsave(&chip->reg_lock, flags); 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, snd_cs46xx_poke(chip,
(parent_scb->address + SCBsubListPtr) << 2, (parent_scb->address + SCBsubListPtr) << 2,
(parent_scb->sub_list_ptr->address << 0x10) | (parent_scb->sub_list_ptr->address << 0x10) |
...@@ -1351,13 +1308,11 @@ int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel) ...@@ -1351,13 +1308,11 @@ int cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
pcm_channel->unlinked = 0; pcm_channel->unlinked = 0;
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
_end: spin_unlock(&pcm_channel->src_scb->lock);
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return 0; return 0;
} }
#define GOF_PER_SEC 200 #define GOF_PER_SEC 200
void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u32 rate) void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u32 rate)
...@@ -1414,8 +1369,7 @@ void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u ...@@ -1414,8 +1369,7 @@ void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
} }
dsp_scb_descriptor_t * dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
u16 addr,char * scb_name) u16 addr,char * scb_name)
{ {
dsp_spos_instance_t * ins = chip->dsp_spos_instance; dsp_spos_instance_t * ins = chip->dsp_spos_instance;
...@@ -1442,27 +1396,14 @@ cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source, ...@@ -1442,27 +1396,14 @@ cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src) int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src)
{ {
dsp_spos_instance_t * ins = chip->dsp_spos_instance; snd_assert (src->parent_scb_ptr != NULL, return -EINVAL );
down(&ins->pcm_mutex);
down(&ins->scb_mutex);
snd_assert (src->parent_scb_ptr != NULL,goto _fail_end);
/* mute SCB */ /* mute SCB */
snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff); snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
_dsp_unlink_scb (chip,src); _dsp_unlink_scb (chip,src);
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return 0; return 0;
_fail_end:
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return -EINVAL;
} }
int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src) int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
...@@ -1471,11 +1412,8 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src) ...@@ -1471,11 +1412,8 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
dsp_scb_descriptor_t * parent_scb; dsp_scb_descriptor_t * parent_scb;
unsigned int flags; unsigned int flags;
down(&ins->pcm_mutex); snd_assert (src->parent_scb_ptr == NULL, return -EINVAL );
down(&ins->scb_mutex); snd_assert(ins->master_mix_scb !=NULL, return -EINVAL );
snd_assert (src->parent_scb_ptr == NULL,goto _fail_end);
snd_assert(ins->master_mix_scb !=NULL,goto _fail_end);
if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) { if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr); parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
...@@ -1497,14 +1435,5 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src) ...@@ -1497,14 +1435,5 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return 0; return 0;
_fail_end:
up(&ins->scb_mutex);
up(&ins->pcm_mutex);
return -EINVAL;
} }
...@@ -1628,7 +1628,7 @@ static int snd_ice1712_capture_open(snd_pcm_substream_t * substream) ...@@ -1628,7 +1628,7 @@ static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
ice->capture_con_substream = substream; ice->capture_con_substream = substream;
runtime->hw = snd_ice1712_capture; runtime->hw = snd_ice1712_capture;
runtime->hw.rates = ice->ac97->rates_adc; runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
return 0; return 0;
...@@ -1660,8 +1660,6 @@ static int snd_ice1712_capture_close(snd_pcm_substream_t * substream) ...@@ -1660,8 +1660,6 @@ static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
{ {
ice1712_t *ice = snd_pcm_substream_chip(substream); ice1712_t *ice = snd_pcm_substream_chip(substream);
/* disable ADC power */
snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
ice->capture_con_substream = NULL; ice->capture_con_substream = NULL;
return 0; return 0;
} }
...@@ -2396,14 +2394,6 @@ static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice) ...@@ -2396,14 +2394,6 @@ static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice)
return 0; return 0;
} }
static void snd_ice1712_ac97_init(ac97_t *ac97)
{
// ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
}
static void snd_ice1712_mixer_free_ac97(ac97_t *ac97) static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
{ {
ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
...@@ -2419,7 +2409,6 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) ...@@ -2419,7 +2409,6 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
memset(&ac97, 0, sizeof(ac97)); memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_ice1712_ac97_write; ac97.write = snd_ice1712_ac97_write;
ac97.read = snd_ice1712_ac97_read; ac97.read = snd_ice1712_ac97_read;
ac97.init = snd_ice1712_ac97_init;
ac97.private_data = ice; ac97.private_data = ice;
ac97.private_free = snd_ice1712_mixer_free_ac97; ac97.private_free = snd_ice1712_mixer_free_ac97;
if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
...@@ -2437,7 +2426,6 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) ...@@ -2437,7 +2426,6 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
memset(&ac97, 0, sizeof(ac97)); memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_ice1712_pro_ac97_write; ac97.write = snd_ice1712_pro_ac97_write;
ac97.read = snd_ice1712_pro_ac97_read; ac97.read = snd_ice1712_pro_ac97_read;
ac97.init = snd_ice1712_ac97_init;
ac97.private_data = ice; ac97.private_data = ice;
ac97.private_free = snd_ice1712_mixer_free_ac97; ac97.private_free = snd_ice1712_mixer_free_ac97;
if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
*/ */
...@@ -41,7 +42,7 @@ ...@@ -41,7 +42,7 @@
#include <sound/initval.h> #include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012"); MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_CLASSES("{sound}"); MODULE_CLASSES("{sound}");
MODULE_DEVICES("{{Intel,82801AA-ICH}," MODULE_DEVICES("{{Intel,82801AA-ICH},"
...@@ -53,7 +54,8 @@ MODULE_DEVICES("{{Intel,82801AA-ICH}," ...@@ -53,7 +54,8 @@ MODULE_DEVICES("{{Intel,82801AA-ICH},"
"{SiS,SI7012}," "{SiS,SI7012},"
"{NVidia,NForce Audio}," "{NVidia,NForce Audio},"
"{AMD,AMD768}," "{AMD,AMD768},"
"{AMD,AMD8111}}"); "{AMD,AMD8111},"
"{ALI,M5455}}");
#define SUPPORT_JOYSTICK 1 #define SUPPORT_JOYSTICK 1
#define SUPPORT_MIDI 1 #define SUPPORT_MIDI 1
...@@ -126,50 +128,58 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di ...@@ -126,50 +128,58 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di
#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 #define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1
#endif #endif
#define DEVICE_INTEL 0 enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI };
#define DEVICE_SIS 1
#define ICHREG(x) ICH_REG_##x
#define DEFINE_REGSET(name,base) \
enum { \
ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \
ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \
ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \
ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
};
/* busmaster blocks */
DEFINE_REGSET(OFF, 0); /* offset */
DEFINE_REGSET(PI, 0x00); /* PCM in */
DEFINE_REGSET(PO, 0x10); /* PCM out */
DEFINE_REGSET(MC, 0x20); /* Mic in */
/* ICH4 busmater blocks */
DEFINE_REGSET(MC2, 0x40); /* Mic in 2 */
DEFINE_REGSET(PI2, 0x50); /* PCM in 2 */
DEFINE_REGSET(SP, 0x60); /* SPDIF out */
#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x) /* values for each busmaster block */
#define ICHREG2(ice, x) ((ice)->bmport + x)
/* capture block */ /* LVI */
#define ICH_REG_PI_BDBAR 0x00 /* dword - buffer descriptor list base address */
#define ICH_REG_PI_CIV 0x04 /* byte - current index value */
#define ICH_REG_PI_LVI 0x05 /* byte - last valid index */
#define ICH_REG_LVI_MASK 0x1f #define ICH_REG_LVI_MASK 0x1f
#define ICH_REG_PI_SR 0x06 /* byte - status register */
/* SR */
#define ICH_FIFOE 0x10 /* FIFO error */ #define ICH_FIFOE 0x10 /* FIFO error */
#define ICH_BCIS 0x08 /* buffer completion interrupt status */ #define ICH_BCIS 0x08 /* buffer completion interrupt status */
#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */ #define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */
#define ICH_CELV 0x02 /* current equals last valid */ #define ICH_CELV 0x02 /* current equals last valid */
#define ICH_DCH 0x01 /* DMA controller halted */ #define ICH_DCH 0x01 /* DMA controller halted */
#define ICH_REG_PI_PICB 0x08 /* word - position in current buffer */
#define ICH_REG_PI_PIV 0x0a /* byte - prefetched index value */ /* PIV */
#define ICH_REG_PIV_MASK 0x1f /* mask */ #define ICH_REG_PIV_MASK 0x1f /* mask */
#define ICH_REG_PI_CR 0x0b /* byte - control register */
/* CR */
#define ICH_IOCE 0x10 /* interrupt on completion enable */ #define ICH_IOCE 0x10 /* interrupt on completion enable */
#define ICH_FEIE 0x08 /* fifo error interrupt enable */ #define ICH_FEIE 0x08 /* fifo error interrupt enable */
#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */ #define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */
#define ICH_RESETREGS 0x02 /* reset busmaster registers */ #define ICH_RESETREGS 0x02 /* reset busmaster registers */
#define ICH_STARTBM 0x01 /* start busmaster operation */ #define ICH_STARTBM 0x01 /* start busmaster operation */
/* playback block */
#define ICH_REG_PO_BDBAR 0x10 /* dword - buffer descriptor list base address */
#define ICH_REG_PO_CIV 0x14 /* byte - current index value */
#define ICH_REG_PO_LVI 0x15 /* byte - last valid command */
#define ICH_REG_PO_SR 0x16 /* byte - status register */
#define ICH_REG_PO_PICB 0x18 /* word - position in current buffer */
#define ICH_REG_PO_PIV 0x1a /* byte - prefetched index value */
#define ICH_REG_PO_CR 0x1b /* byte - control register */
/* mic capture block */
#define ICH_REG_MC_BDBAR 0x20 /* dword - buffer descriptor list base address */
#define ICH_REG_MC_CIV 0x24 /* byte - current index value */
#define ICH_REG_MC_LVI 0x25 /* byte - last valid command */
#define ICH_REG_MC_SR 0x26 /* byte - status register */
#define ICH_REG_MC_PICB 0x28 /* word - position in current buffer */
#define ICH_REG_MC_PIV 0x2a /* byte - prefetched index value */
#define ICH_REG_MC_CR 0x2b /* byte - control register */
/* global block */ /* global block */
#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */ #define ICH_REG_GLOB_CNT 0x2c /* dword - global control */
#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */
#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ #define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */
#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ #define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */
#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ #define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */
...@@ -181,16 +191,24 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di ...@@ -181,16 +191,24 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di
#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */ #define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */
#define ICH_GIE 0x00000001 /* GPI interrupt enable */ #define ICH_GIE 0x00000001 /* GPI interrupt enable */
#define ICH_REG_GLOB_STA 0x30 /* dword - global status */ #define ICH_REG_GLOB_STA 0x30 /* dword - global status */
#define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */
#define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */
#define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */
#define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */
#define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */
#define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */
#define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */
#define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */
#define ICH_MD3 0x00020000 /* modem power down semaphore */ #define ICH_MD3 0x00020000 /* modem power down semaphore */
#define ICH_AD3 0x00010000 /* audio power down semaphore */ #define ICH_AD3 0x00010000 /* audio power down semaphore */
#define ICH_RCS 0x00008000 /* read completion status */ #define ICH_RCS 0x00008000 /* read completion status */
#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */ #define ICH_BIT3 0x00004000 /* bit 3 slot 12 */
#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */ #define ICH_BIT2 0x00002000 /* bit 2 slot 12 */
#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */ #define ICH_BIT1 0x00001000 /* bit 1 slot 12 */
#define ICH_SRI 0x00000800 /* secondary resume interrupt */ #define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */
#define ICH_PRI 0x00000400 /* primary resume interrupt */ #define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */
#define ICH_SCR 0x00000200 /* secondary codec ready */ #define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */
#define ICH_PCR 0x00000100 /* primary codec ready */ #define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */
#define ICH_MCINT 0x00000080 /* MIC capture interrupt */ #define ICH_MCINT 0x00000080 /* MIC capture interrupt */
#define ICH_POINT 0x00000040 /* playback interrupt */ #define ICH_POINT 0x00000040 /* playback interrupt */
#define ICH_PIINT 0x00000020 /* capture interrupt */ #define ICH_PIINT 0x00000020 /* capture interrupt */
...@@ -199,15 +217,78 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di ...@@ -199,15 +217,78 @@ MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},di
#define ICH_GSCI 0x00000001 /* GPI status change interrupt */ #define ICH_GSCI 0x00000001 /* GPI status change interrupt */
#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */ #define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */
#define ICH_CAS 0x01 /* codec access semaphore */ #define ICH_CAS 0x01 /* codec access semaphore */
#define ICH_REG_SDM 0x80
#define ICH_DI2L_MASK 0x000000c0 /* PCM In 2, Mic In 2 data in line */
#define ICH_DI2L_SHIFT 6
#define ICH_DI1L_MASK 0x00000030 /* PCM In 1, Mic In 1 data in line */
#define ICH_DI1L_SHIFT 4
#define ICH_SE 0x00000008 /* steer enable */
#define ICH_LDI_MASK 0x00000003 /* last codec read data input */
#define ICH_MAX_FRAGS 32 /* max hw frags */ #define ICH_MAX_FRAGS 32 /* max hw frags */
/*
* registers for Ali5455
*/
/* ALi 5455 busmaster blocks */
DEFINE_REGSET(AL_PI, 0x40); /* ALi PCM in */
DEFINE_REGSET(AL_PO, 0x50); /* Ali PCM out */
DEFINE_REGSET(AL_MC, 0x60); /* Ali Mic in */
DEFINE_REGSET(AL_CDC_SPO, 0x70); /* Ali Codec SPDIF out */
DEFINE_REGSET(AL_CLR_SPI, 0xa0); /* Ali Controller SPDIF in */
DEFINE_REGSET(AL_CLR_SPO, 0xb0); /* Ali Controller SPDIF out */
enum {
ICH_REG_ALI_SCR = 0x00, /* System Control Register */
ICH_REG_ALI_SSR = 0x04, /* System Status Register */
ICH_REG_ALI_DMACR = 0x08, /* DMA Control Register */
ICH_REG_ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */
ICH_REG_ALI_INTERFACECR = 0x10, /* Interface Control Register */
ICH_REG_ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */
ICH_REG_ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */
ICH_REG_ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */
ICH_REG_ALI_CPR = 0x20, /* Command Port Register */
ICH_REG_ALI_SPR = 0x24, /* Status Port Register */
ICH_REG_ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */
ICH_REG_ALI_TTSR = 0x30, /* Transmit Tag Slot Register */
ICH_REG_ALI_RTSR = 0x34, /* Receive Tag Slot Register */
ICH_REG_ALI_CSPSR = 0x38, /* Command/Status Port Status Register */
ICH_REG_ALI_CAS = 0x3c, /* Codec Write Semaphore Register */
ICH_REG_ALI_SPDIFCSR = 0xf8, /* spdif channel status register */
ICH_REG_ALI_SPDIFICS = 0xfc /* spdif interface control/status */
};
/* interrupts for the whole chip by interrupt status register finish */
#define ALI_INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */
#define ALI_INT_SPDIFIN (1<<22)
#define ALI_INT_CODECSPDIFOUT (1<<19)
#define ALI_INT_MICIN (1<<18)
#define ALI_INT_PCMOUT (1<<17)
#define ALI_INT_PCMIN (1<<16)
#define ALI_INT_CPRAIS (1<<7)
#define ALI_INT_SPRAIS (1<<5)
#define ALI_INT_GPIO (1<<1)
#define ALI_INT_MASK (ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
#define ALI_PCM_CH4 0x100
#define ALI_PCM_CH6 0x200
#define ALI_PCM_MASK (ALI_PCM_CH4 | ALI_PCM_CH6)
/* /*
* *
*/ */
enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
typedef struct { typedef struct {
unsigned long reg_offset; unsigned int ichd; /* ich device number */
unsigned long reg_offset; /* offset to bmaddr */
u32 *bdbar; /* CPU address (32bit) */ u32 *bdbar; /* CPU address (32bit) */
unsigned int bdbar_addr; /* PCI bus address (32bit) */ unsigned int bdbar_addr; /* PCI bus address (32bit) */
snd_pcm_substream_t *substream; snd_pcm_substream_t *substream;
...@@ -221,11 +302,20 @@ typedef struct { ...@@ -221,11 +302,20 @@ typedef struct {
int lvi_frag; int lvi_frag;
int ack; int ack;
int ack_reload; int ack_reload;
unsigned int ack_bit;
unsigned int roff_sr;
unsigned int roff_picb;
unsigned int int_sta_mask; /* interrupt status mask */
unsigned int ali_slot; /* ALI DMA slot */
ac97_t *ac97;
unsigned short ac97_rate_regs[3];
int ac97_rates_idx;
#ifdef CONFIG_PM #ifdef CONFIG_PM
unsigned char civ_saved; unsigned char civ_saved;
unsigned char piv_saved; unsigned char piv_saved;
unsigned short picb_saved; unsigned short picb_saved;
#endif #endif
snd_info_entry_t *proc_entry;
} ichdev_t; } ichdev_t;
typedef struct _snd_intel8x0 intel8x0_t; typedef struct _snd_intel8x0 intel8x0_t;
...@@ -242,27 +332,34 @@ struct _snd_intel8x0 { ...@@ -242,27 +332,34 @@ struct _snd_intel8x0 {
int irq; int irq;
unsigned long port; unsigned int mmio;
struct resource *res_port; unsigned long addr;
unsigned long bmport; unsigned long remap_addr;
struct resource *res_bmport; struct resource *res;
unsigned int bm_mmio;
unsigned long bmaddr;
unsigned long remap_bmaddr;
struct resource *res_bm;
struct pci_dev *pci; struct pci_dev *pci;
snd_card_t *card; snd_card_t *card;
snd_pcm_t *pcm; snd_pcm_t *pcm;
snd_pcm_t *pcm_mic; snd_pcm_t *pcm_mic;
ichdev_t playback; snd_pcm_t *pcm_mic2;
ichdev_t capture; snd_pcm_t *pcm2;
ichdev_t capture_mic; snd_pcm_t *pcm_spdif;
snd_pcm_t *pcm_ac97spdif;
ichdev_t ichd[6];
int multi4: 1, int multi4: 1,
multi6: 1; multi6: 1,
int in_ac97_init: 1; smp20bit: 1;
int no_codec_check: 1; int in_ac97_init: 1,
in_sdin_init: 1;
ac97_t *ac97; ac97_t *ac97[3];
ac97_t *ac97sec; unsigned int ac97_sdin[3];
snd_rawmidi_t *rmidi; snd_rawmidi_t *rmidi;
...@@ -270,15 +367,11 @@ struct _snd_intel8x0 { ...@@ -270,15 +367,11 @@ struct _snd_intel8x0 {
spinlock_t ac97_lock; spinlock_t ac97_lock;
snd_info_entry_t *proc_entry; snd_info_entry_t *proc_entry;
u32 bdbars_count;
u32 *bdbars; u32 *bdbars;
dma_addr_t bdbars_addr; dma_addr_t bdbars_addr;
u32 int_sta_reg; /* interrupt status register */
unsigned int reg_pi_sr; u32 int_sta_mask; /* interrupt status mask */
unsigned int reg_pi_picb;
unsigned int reg_po_sr;
unsigned int reg_po_picb;
unsigned int reg_mc_sr;
unsigned int reg_mc_picb;
#ifdef CONFIG_PM #ifdef CONFIG_PM
int in_suspend; int in_suspend;
...@@ -290,34 +383,131 @@ static struct pci_device_id snd_intel8x0_ids[] __devinitdata = { ...@@ -290,34 +383,131 @@ static struct pci_device_id snd_intel8x0_ids[] __devinitdata = {
{ 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */
{ 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */
{ 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */
{ 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH4 */ { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */
{ 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */
{ 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */
{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */
{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */
{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */
{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */
{ 0, } { 0, }
}; };
MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids); MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
/*
* Lowlevel I/O - busmaster
*/
static u8 igetbyte(intel8x0_t *chip, u32 offset)
{
if (chip->bm_mmio)
return readb(chip->remap_bmaddr + offset);
else
return inb(chip->bmaddr + offset);
}
static u16 igetword(intel8x0_t *chip, u32 offset)
{
if (chip->bm_mmio)
return readw(chip->remap_bmaddr + offset);
else
return inw(chip->bmaddr + offset);
}
static u32 igetdword(intel8x0_t *chip, u32 offset)
{
if (chip->bm_mmio)
return readl(chip->remap_bmaddr + offset);
else
return inl(chip->bmaddr + offset);
}
static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
{
if (chip->bm_mmio)
writeb(val, chip->remap_bmaddr + offset);
else
return outb(val, chip->bmaddr + offset);
}
static void iputword(intel8x0_t *chip, u32 offset, u16 val)
{
if (chip->bm_mmio)
writew(val, chip->remap_bmaddr + offset);
else
return outw(val, chip->bmaddr + offset);
}
static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
{
if (chip->bm_mmio)
writel(val, chip->remap_bmaddr + offset);
else
return outl(val, chip->bmaddr + offset);
}
/*
* Lowlevel I/O - AC'97 registers
*/
static u16 iagetword(intel8x0_t *chip, u32 offset)
{
if (chip->mmio)
return readw(chip->remap_addr + offset);
else
return inw(chip->addr + offset);
}
static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
{
if (chip->mmio)
writew(val, chip->remap_addr + offset);
else
return outw(val, chip->addr + offset);
}
/* /*
* Basic I/O * Basic I/O
*/ */
/*
* access to AC97 codec via normal i/o (for ICH and SIS7012)
*/
static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec) static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
{ {
int time; int time;
/* codec ready ? */ if (codec > 2)
if (! chip->no_codec_check) {
if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0)
return -EIO; return -EIO;
if (chip->in_sdin_init) {
/* we don't know the ready bit assignment at the moment */
/* so we check any */
codec = ICH_PCR | ICH_SCR | ICH_TCR;
} else {
if (chip->device_type == DEVICE_INTEL_ICH4) {
switch (chip->ac97_sdin[codec]) {
case 0: codec = ICH_PCR; break;
case 1: codec = ICH_SCR; break;
case 2: codec = ICH_TCR; break;
}
} else {
switch (codec) {
case 0: codec = ICH_PCR; break;
case 1: codec = ICH_SCR; break;
case 2: codec = ICH_TCR; break;
} }
}
}
/* codec ready ? */
if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
return -EIO;
/* Anyone holding a semaphore for 1 msec should be shot... */ /* Anyone holding a semaphore for 1 msec should be shot... */
time = 100; time = 100;
do { do {
if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS)) if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
return 0; return 0;
udelay(10); udelay(10);
} while (time--); } while (time--);
...@@ -326,8 +516,8 @@ static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec) ...@@ -326,8 +516,8 @@ static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
* reset the semaphore. So even if you don't get the semaphore, still * reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */ * continue the access. We don't need the semaphore anyway. */
snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n", snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA))); igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
inw(chip->port); /* clear semaphore flag */ iagetword(chip, 0); /* clear semaphore flag */
/* I don't care about the semaphore */ /* I don't care about the semaphore */
return -EBUSY; return -EBUSY;
} }
...@@ -343,7 +533,7 @@ static void snd_intel8x0_codec_write(ac97_t *ac97, ...@@ -343,7 +533,7 @@ static void snd_intel8x0_codec_write(ac97_t *ac97,
if (! chip->in_ac97_init) if (! chip->in_ac97_init)
snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
} }
outw(val, chip->port + reg + ac97->num * 0x80); iaputword(chip, reg + ac97->num * 0x80, val);
spin_unlock(&chip->ac97_lock); spin_unlock(&chip->ac97_lock);
} }
...@@ -360,10 +550,10 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, ...@@ -360,10 +550,10 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
res = 0xffff; res = 0xffff;
} else { } else {
res = inw(chip->port + reg + ac97->num * 0x80); res = iagetword(chip, reg + ac97->num * 0x80);
if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) { if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */ /* reset RCS and preserve other R/WC bits */
outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA)); iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
if (! chip->in_ac97_init) if (! chip->in_ac97_init)
snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg); snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
res = 0xffff; res = 0xffff;
...@@ -373,46 +563,85 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, ...@@ -373,46 +563,85 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
return res; return res;
} }
static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd) /*
* access to AC97 for Ali5455
*/
static int snd_intel8x0_ali_codec_ready(intel8x0_t *chip, int mask)
{ {
unsigned char val = 0; int count = 0;
unsigned long port = chip->bmport + ichdev->reg_offset; for (count = 0; count < 0x7f; count++) {
int val = igetbyte(chip, ICHREG(ALI_CSPSR));
if (val & mask)
return 0;
}
snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
return -EBUSY;
}
switch (cmd) { static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip)
case SNDRV_PCM_TRIGGER_START: {
case SNDRV_PCM_TRIGGER_RESUME: int time = 100;
val = ICH_IOCE | ICH_STARTBM; do {
break; if (igetdword(chip, ICHREG(ALI_CAS)) & 0x80000000)
case SNDRV_PCM_TRIGGER_STOP: return snd_intel8x0_ali_codec_ready(chip, 0x08);
case SNDRV_PCM_TRIGGER_SUSPEND: udelay(1);
val = 0; } while (time--);
break; snd_printk(KERN_WARNING "intel8x0: AC97 codec semaphore timeout.\n");
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: return -EBUSY;
val = ICH_IOCE; }
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
val = ICH_IOCE | ICH_STARTBM; {
break; intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0);
default: unsigned short data, reg2;
return -EINVAL;
spin_lock(&chip->ac97_lock);
if (snd_intel8x0_ali_codec_semaphore(chip))
goto __err;
reg |= 0x0080;
iputword(chip, ICHREG(ALI_CPR) + 2, reg | 0x0080);
if (snd_intel8x0_ali_codec_ready(chip, 0x02))
goto __err;
data = igetword(chip, ICHREG(ALI_SPR));
reg2 = igetword(chip, ICHREG(ALI_SPR) + 2);
if (reg != reg2) {
snd_printd(KERN_WARNING "intel8x0: AC97 read not completed?\n");
goto __err;
} }
outb(val, port + ICH_REG_PI_CR); spin_unlock(&chip->ac97_lock);
if (cmd == SNDRV_PCM_TRIGGER_STOP) { return data;
/* reset whole DMA things */ __err:
while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ; spin_unlock(&chip->ac97_lock);
outb(ICH_RESETREGS, port + ICH_REG_PI_CR); return 0xffff;
}
static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val)
{
intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
spin_lock(&chip->ac97_lock);
if (snd_intel8x0_ali_codec_semaphore(chip)) {
spin_unlock(&chip->ac97_lock);
return;
} }
return 0; iputword(chip, ICHREG(ALI_CPR), val);
iputbyte(chip, ICHREG(ALI_CPR) + 2, reg);
snd_intel8x0_ali_codec_ready(chip, 0x01);
spin_unlock(&chip->ac97_lock);
} }
/*
* DMA I/O
*/
static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
{ {
int idx; int idx;
u32 *bdbar = ichdev->bdbar; u32 *bdbar = ichdev->bdbar;
unsigned long port = chip->bmport + ichdev->reg_offset; unsigned long port = ichdev->reg_offset;
int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1; int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1;
outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR); iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
if (ichdev->size == ichdev->fragsize) { if (ichdev->size == ichdev->fragsize) {
ichdev->ack_reload = ichdev->ack = 2; ichdev->ack_reload = ichdev->ack = 2;
ichdev->fragsize1 = ichdev->fragsize >> 1; ichdev->fragsize1 = ichdev->fragsize >> 1;
...@@ -436,7 +665,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) ...@@ -436,7 +665,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
} }
ichdev->frags = ichdev->size / ichdev->fragsize; ichdev->frags = ichdev->size / ichdev->fragsize;
} }
outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI); iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags; ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0; ichdev->position = 0;
#if 0 #if 0
...@@ -444,7 +673,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) ...@@ -444,7 +673,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1); ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
#endif #endif
/* clear interrupts */ /* clear interrupts */
outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
} }
/* /*
...@@ -453,7 +682,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) ...@@ -453,7 +682,7 @@ static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev)
static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev) static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
{ {
unsigned long port = chip->bmport + ichdev->reg_offset; unsigned long port = ichdev->reg_offset;
int ack = 0; int ack = 0;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
...@@ -461,80 +690,116 @@ static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev) ...@@ -461,80 +690,116 @@ static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
ichdev->position %= ichdev->size; ichdev->position %= ichdev->size;
ichdev->lvi++; ichdev->lvi++;
ichdev->lvi &= ICH_REG_LVI_MASK; ichdev->lvi &= ICH_REG_LVI_MASK;
outb(ichdev->lvi, port + ICH_REG_PI_LVI); iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
ichdev->lvi_frag++; ichdev->lvi_frag++;
ichdev->lvi_frag %= ichdev->frags; ichdev->lvi_frag %= ichdev->frags;
ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1; ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR)); // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
if ((ack = (--ichdev->ack == 0)) != 0) if ((ack = (--ichdev->ack == 0)) != 0)
ichdev->ack = ichdev->ack_reload; ichdev->ack = ichdev->ack_reload;
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
if (ack && ichdev->substream) if (ack && ichdev->substream)
snd_pcm_period_elapsed(ichdev->substream); snd_pcm_period_elapsed(ichdev->substream);
outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
} }
static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return); intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return);
ichdev_t *ichdev;
unsigned int status; unsigned int status;
int i;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
status = inl(ICHREG(chip, GLOB_STA)); status = igetdword(chip, chip->int_sta_reg);
if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) { if ((status & chip->int_sta_mask) == 0) {
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
return; return;
} }
/* ack first */ /* ack first */
outl(status & (ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA)); iputdword(chip, chip->int_sta_reg, status & ~chip->int_sta_mask);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
if (status & ICH_POINT) for (i = 0; i < chip->bdbars_count; i++) {
snd_intel8x0_update(chip, &chip->playback); ichdev = &chip->ichd[i];
if (status & ICH_PIINT) if (status & ichdev->int_sta_mask)
snd_intel8x0_update(chip, &chip->capture); snd_intel8x0_update(chip, ichdev);
if (status & ICH_MCINT) }
snd_intel8x0_update(chip, &chip->capture_mic);
} }
/* /*
* PCM part * PCM part
*/ */
static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream, static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
unsigned int cmd,
void *arg)
{
int result;
result = snd_pcm_lib_ioctl(substream, cmd, arg);
if (result < 0)
return result;
return 0;
}
static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream,
unsigned int cmd,
void *arg)
{
int result;
result = snd_pcm_lib_ioctl(substream, cmd, arg);
if (result < 0)
return result;
return 0;
}
static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
ichdev_t *ichdev = get_ichdev(substream);
unsigned char val = 0;
unsigned long port = ichdev->reg_offset;
return snd_intel8x0_trigger(chip, &chip->playback, cmd); switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
val = ICH_IOCE | ICH_STARTBM;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
val = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = ICH_IOCE;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM;
break;
default:
return -EINVAL;
}
iputbyte(chip, port + ICH_REG_OFF_CR, val);
if (cmd == SNDRV_PCM_TRIGGER_STOP) {
/* reset whole DMA things */
while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
}
return 0;
} }
static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd) static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
ichdev_t *ichdev = get_ichdev(substream);
unsigned long port = ichdev->reg_offset;
return snd_intel8x0_trigger(chip, &chip->capture, cmd); switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
iputbyte(chip, port + ICH_REG_OFF_CR, 0);
/* reset whole DMA things */
while (!(igetbyte(chip, port + ICH_REG_OFF_CR)))
;
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
/* clear interrupts */
iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
iputdword(chip, ICHREG(ALI_INTERRUPTSR),
igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & (1 << (ichdev->ali_slot + 8)));
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
iputbyte(chip, port + ICH_REG_OFF_CR, 0);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
break;
default:
return -EINVAL;
}
return 0;
} }
static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream, static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
...@@ -550,72 +815,52 @@ static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream) ...@@ -550,72 +815,52 @@ static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream)
static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels) static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels)
{ {
unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK; unsigned int cnt = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_246_MASK;
if (chip->multi4 && channels == 4) if (chip->multi4 && channels == 4)
cnt |= ICH_PCM_4; cnt |= ICH_PCM_4;
else if (chip->multi6 && channels == 6) else if (chip->multi6 && channels == 6)
cnt |= ICH_PCM_6; cnt |= ICH_PCM_6;
outl(cnt, ICHREG(chip, GLOB_CNT)); iputdword(chip, ICHREG(GLOB_CNT), cnt);
} }
static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream) static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
ichdev_t *ichdev = get_ichdev(substream);
int i;
chip->playback.physbuf = runtime->dma_addr; ichdev->physbuf = runtime->dma_addr;
chip->playback.size = snd_pcm_lib_buffer_bytes(substream); ichdev->size = snd_pcm_lib_buffer_bytes(substream);
chip->playback.fragsize = snd_pcm_lib_period_bytes(substream); ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
if (ichdev->ichd == ICHD_PCMOUT && chip->device_type != DEVICE_ALI) {
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
snd_intel8x0_setup_multi_channels(chip, runtime->channels); snd_intel8x0_setup_multi_channels(chip, runtime->channels);
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); }
snd_intel8x0_setup_periods(chip, &chip->playback); for (i = 0; i < 3; i++)
return 0; if (ichdev->ac97_rate_regs[i])
} snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate);
snd_intel8x0_setup_periods(chip, ichdev);
static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
chip->capture.physbuf = runtime->dma_addr;
chip->capture.size = snd_pcm_lib_buffer_bytes(substream);
chip->capture.fragsize = snd_pcm_lib_period_bytes(substream);
snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
snd_intel8x0_setup_periods(chip, &chip->capture);
return 0; return 0;
} }
static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream) static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
size_t ptr;
ptr = chip->playback.fragsize1;
if (chip->device_type == DEVICE_SIS)
ptr -= inw(ICHREG2(chip,chip->reg_po_picb));
else
ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
ptr += chip->playback.position;
return bytes_to_frames(substream->runtime, ptr);
}
static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
ichdev_t *ichdev = get_ichdev(substream);
size_t ptr; size_t ptr;
ptr = chip->capture.fragsize1; ptr = ichdev->fragsize1;
if (chip->device_type == DEVICE_SIS) if (chip->device_type == DEVICE_SIS)
ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)); ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
else else
ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1; ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
ptr += chip->capture.position; ptr += ichdev->position;
return bytes_to_frames(substream->runtime, ptr); return bytes_to_frames(substream->runtime, ptr);
} }
static snd_pcm_hardware_t snd_intel8x0_playback = static snd_pcm_hardware_t snd_intel8x0_stream =
{ {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
...@@ -623,27 +868,7 @@ static snd_pcm_hardware_t snd_intel8x0_playback = ...@@ -623,27 +868,7 @@ static snd_pcm_hardware_t snd_intel8x0_playback =
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME), SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = 0, .rates = SNDRV_PCM_RATE_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 128 * 1024,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static snd_pcm_hardware_t snd_intel8x0_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = 0,
.rate_min = 8000, .rate_min = 8000,
.rate_max = 48000, .rate_max = 48000,
.channels_min = 2, .channels_min = 2,
...@@ -680,15 +905,16 @@ static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = { ...@@ -680,15 +905,16 @@ static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = {
.mask = 0, .mask = 0,
}; };
static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
int err; int err;
chip->playback.substream = substream; ichdev->substream = substream;
runtime->hw = snd_intel8x0_playback; runtime->hw = snd_intel8x0_stream;
runtime->hw.rates = chip->ac97->rates_front_dac; if (ichdev->ac97_rates_idx >= 0)
runtime->hw.rates = ichdev->ac97->rates[ichdev->ac97_rates_idx];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
if (chip->device_type == DEVICE_SIS) { if (chip->device_type == DEVICE_SIS) {
...@@ -697,6 +923,17 @@ static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) ...@@ -697,6 +923,17 @@ static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
} }
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err; return err;
runtime->private_data = ichdev;
return 0;
}
static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
int err;
err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
if (chip->multi6) { if (chip->multi6) {
runtime->hw.channels_max = 6; runtime->hw.channels_max = 6;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
...@@ -707,72 +944,270 @@ static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) ...@@ -707,72 +944,270 @@ static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
return 0; return 0;
} }
static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream) static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
int err;
chip->capture.substream = substream; chip->ichd[ICHD_PCMOUT].substream = NULL;
runtime->hw = snd_intel8x0_capture;
runtime->hw.rates = chip->ac97->rates_adc;
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000;
if (chip->device_type == DEVICE_SIS) {
runtime->hw.buffer_bytes_max = 64*1024;
runtime->hw.period_bytes_max = 64*1024;
}
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
return 0; return 0;
} }
static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream) static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->playback.substream = NULL; return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
/* disable DAC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
return 0;
} }
static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream) static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->capture.substream = NULL; chip->ichd[ICHD_PCMIN].substream = NULL;
/* disable ADC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
return 0; return 0;
} }
static snd_pcm_ops_t snd_intel8x0_playback_ops = { static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream)
.open = snd_intel8x0_playback_open, {
.close = snd_intel8x0_playback_close, intel8x0_t *chip = snd_pcm_substream_chip(substream);
.ioctl = snd_intel8x0_playback_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_playback_prepare,
.trigger = snd_intel8x0_playback_trigger,
.pointer = snd_intel8x0_playback_pointer,
};
static snd_pcm_ops_t snd_intel8x0_capture_ops = { return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
.open = snd_intel8x0_capture_open, }
.close = snd_intel8x0_capture_close,
.ioctl = snd_intel8x0_capture_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_capture_prepare,
.trigger = snd_intel8x0_capture_trigger,
.pointer = snd_intel8x0_capture_pointer,
};
static void snd_intel8x0_pcm_free(snd_pcm_t *pcm) static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream)
{ {
intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->pcm = NULL;
chip->ichd[ICHD_MIC].substream = NULL;
return 0;
}
static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
}
static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ICHD_MIC2].substream = NULL;
return 0;
}
static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
}
static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ICHD_PCM2IN].substream = NULL;
return 0;
}
static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_SPBAR]);
}
static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ICHD_SPBAR].substream = NULL;
return 0;
}
static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
}
static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
return 0;
}
static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
}
static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ALID_SPDIFIN].substream = NULL;
return 0;
}
static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
}
static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream)
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
chip->ichd[ALID_SPDIFOUT].substream = NULL;
return 0;
}
static snd_pcm_ops_t snd_intel8x0_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = {
.open = snd_intel8x0_mic2_open,
.close = snd_intel8x0_mic2_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_capture2_ops = {
.open = snd_intel8x0_capture2_open,
.close = snd_intel8x0_capture2_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_spdif_ops = {
.open = snd_intel8x0_spdif_open,
.close = snd_intel8x0_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_ali_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_ali_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_ali_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = {
.open = snd_intel8x0_ali_ac97spdifout_open,
.close = snd_intel8x0_ali_ac97spdifout_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_ali_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = {
.open = snd_intel8x0_ali_spdifin_open,
.close = snd_intel8x0_ali_spdifin_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = {
.open = snd_intel8x0_ali_spdifout_open,
.close = snd_intel8x0_ali_spdifout_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
.trigger = snd_intel8x0_pcm_trigger,
.pointer = snd_intel8x0_pcm_pointer,
};
static void snd_intel8x0_pcm_free(snd_pcm_t *pcm)
{
intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
chip->pcm = NULL;
snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_lib_preallocate_free_for_all(pcm);
} }
...@@ -787,8 +1222,13 @@ static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** ...@@ -787,8 +1222,13 @@ static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t **
if (err < 0) if (err < 0)
return err; return err;
if (chip->device_type == DEVICE_ALI) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_capture_ops);
} else {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
}
pcm->private_data = chip; pcm->private_data = chip;
pcm->private_free = snd_intel8x0_pcm_free; pcm->private_free = snd_intel8x0_pcm_free;
...@@ -807,198 +1247,432 @@ static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** ...@@ -807,198 +1247,432 @@ static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t **
* PCM code - MIC * PCM code - MIC
*/ */
static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream, static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
unsigned int cmd,
void *arg)
{ {
int result; intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
result = snd_pcm_lib_ioctl(substream, cmd, arg); chip->pcm_mic = NULL;
if (result < 0)
return result;
return 0;
} }
static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream, static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
int cmd)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); snd_pcm_t *pcm;
int err;
return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd); if (rpcm)
} *rpcm = NULL;
err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 0, 1, &pcm);
if (err < 0)
return err;
static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
{ chip->device_type == DEVICE_ALI ?
intel8x0_t *chip = snd_pcm_substream_chip(substream); &snd_intel8x0_ali_capture_mic_ops :
snd_pcm_runtime_t *runtime = substream->runtime; &snd_intel8x0_capture_mic_ops);
chip->capture_mic.physbuf = runtime->dma_addr; pcm->private_data = chip;
chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream); pcm->private_free = snd_intel8x0_pcm_mic_free;
chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream); pcm->info_flags = 0;
snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate); sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
snd_intel8x0_setup_periods(chip, &chip->capture_mic);
chip->pcm_mic = pcm;
if (rpcm)
*rpcm = pcm;
return 0; return 0;
} }
static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream) /*
* PCM code - MIC2
*/
static void snd_intel8x0_pcm_mic2_free(snd_pcm_t *pcm)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
size_t ptr; chip->pcm_mic2 = NULL;
}
ptr = chip->capture_mic.fragsize1; static int __devinit snd_intel8x0_pcm_mic2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
if (chip->device_type == DEVICE_SIS) {
ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)); snd_pcm_t *pcm;
else int err;
ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1;
ptr += chip->capture_mic.position; if (rpcm)
return bytes_to_frames(substream->runtime, ptr); *rpcm = NULL;
err = snd_pcm_new(chip->card, "Intel ICH - MIC2 ADC", device, 0, 1, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic2_ops);
pcm->private_data = chip;
pcm->private_free = snd_intel8x0_pcm_mic2_free;
pcm->info_flags = 0;
sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname);
chip->pcm_mic2 = pcm;
if (rpcm)
*rpcm = pcm;
return 0;
} }
static snd_pcm_hardware_t snd_intel8x0_capture_mic = /*
* PCM code - capture2
*/
static void snd_intel8x0_pcm_capture2_free(snd_pcm_t *pcm)
{ {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
SNDRV_PCM_INFO_BLOCK_TRANSFER | chip->pcm2 = NULL;
SNDRV_PCM_INFO_MMAP_VALID | }
SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = 0,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 128 * 1024,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream) static int __devinit snd_intel8x0_pcm_capture2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); snd_pcm_t *pcm;
snd_pcm_runtime_t *runtime = substream->runtime; int err;
chip->capture_mic.substream = substream; if (rpcm)
runtime->hw = snd_intel8x0_capture_mic; *rpcm = NULL;
runtime->hw.rates = chip->ac97->rates_mic_adc; err = snd_pcm_new(chip->card, "Intel ICH - ADC2", device, 0, 1, &pcm);
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (err < 0)
runtime->hw.rate_min = 48000; return err;
if (chip->device_type == DEVICE_SIS) {
runtime->hw.buffer_bytes_max = 64*1024; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture2_ops);
runtime->hw.period_bytes_max = 64*1024;
} pcm->private_data = chip;
pcm->private_free = snd_intel8x0_pcm_capture2_free;
pcm->info_flags = 0;
sprintf(pcm->name, "%s - ADC2", chip->card->shortname);
chip->pcm2 = pcm;
if (rpcm)
*rpcm = pcm;
return 0; return 0;
} }
static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream) /*
* PCM code - S/PDIF
*/
static void snd_intel8x0_pcm_spdif_free(snd_pcm_t *pcm)
{
intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
chip->pcm_spdif = NULL;
}
static int __devinit snd_intel8x0_pcm_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
{ {
intel8x0_t *chip = snd_pcm_substream_chip(substream); snd_pcm_t *pcm;
int err;
if (rpcm)
*rpcm = NULL;
err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 0, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_spdif_ops);
chip->capture_mic.substream = NULL; pcm->private_data = chip;
/* disable ADC power */ pcm->private_free = snd_intel8x0_pcm_spdif_free;
snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000); pcm->info_flags = 0;
sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
chip->pcm_spdif = pcm;
if (rpcm)
*rpcm = pcm;
return 0; return 0;
} }
static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = { /*
.open = snd_intel8x0_capture_mic_open, * PCM code - ALI S/PDIF
.close = snd_intel8x0_capture_mic_close, */
.ioctl = snd_intel8x0_capture_mic_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_capture_mic_prepare,
.trigger = snd_intel8x0_capture_mic_trigger,
.pointer = snd_intel8x0_capture_mic_pointer,
};
static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm) static void snd_intel8x0_ali_spdif_free(snd_pcm_t *pcm)
{ {
intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
chip->pcm_mic = NULL; chip->pcm_spdif = NULL;
} }
static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
{ {
snd_pcm_t *pcm; snd_pcm_t *pcm;
int err; int err;
if (rpcm) if (rpcm)
*rpcm = NULL; *rpcm = NULL;
err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm); err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 1, &pcm);
if (err < 0) if (err < 0)
return err; return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_spdifout_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_spdifin_ops);
pcm->private_data = chip; pcm->private_data = chip;
pcm->private_free = snd_intel8x0_pcm_mic_free; pcm->private_free = snd_intel8x0_ali_spdif_free;
pcm->info_flags = 0; pcm->info_flags = 0;
sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname); sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
chip->pcm_mic = pcm; chip->pcm_spdif = pcm;
if (rpcm) if (rpcm)
*rpcm = pcm; *rpcm = pcm;
return 0; return 0;
} }
/* /*
* Mixer part * PCM code - ALI AC'97 S/PDIF
*/ */
static void snd_intel8x0_codec_init(ac97_t *ac97) static void snd_intel8x0_ali_ac97spdif_free(snd_pcm_t *pcm)
{ {
// intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
chip->pcm_ac97spdif = NULL;
}
/* disable DAC & ADC power */ static int __devinit snd_intel8x0_ali_ac97spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); {
/* disable center DAC/surround DAC/LFE DAC/MIC ADC */ snd_pcm_t *pcm;
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); int err;
if (rpcm)
*rpcm = NULL;
err = snd_pcm_new(chip->card, "ALI - AC97 IEC958", device, 0, 1, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_ac97spdifout_ops);
pcm->private_data = chip;
pcm->private_free = snd_intel8x0_ali_ac97spdif_free;
pcm->info_flags = 0;
sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname);
chip->pcm_ac97spdif = pcm;
if (rpcm)
*rpcm = pcm;
return 0;
} }
/*
* Mixer part
*/
static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
{ {
intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
if (ac97->num == 0) { chip->ac97[ac97->num] = NULL;
chip->ac97 = NULL;
} else {
chip->ac97sec = NULL;
}
} }
static struct _ac97_rate_regs {
unsigned int ichd;
unsigned short regs[3];
short rates_idx;
} ac97_rate_regs[] = {
{ ICHD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
{ ICHD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
{ ICHD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
{ ICHD_MIC2, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
{ ICHD_PCM2IN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
{ ICHD_SPBAR, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
};
static struct _ac97_ali_rate_regs {
unsigned int ichd;
unsigned short regs[3];
short rates_idx;
} ac97_ali_rate_regs[] = {
{ ALID_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
{ ALID_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
{ ALID_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
{ ALID_AC97SPDIFOUT, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
{ ALID_SPDIFOUT, { 0, 0, 0 }, -1 },
{ ALID_SPDIFIN, { 0, 0, 0 }, -1 },
};
static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
{ {
ac97_t ac97; ac97_t ac97, *x97;
int err; ichdev_t *ichdev;
int err, i, channels = 2, codecs;
unsigned int glob_sta = 0;
for (i = 0; i <= ICHD_LAST; i++) {
if (chip->device_type != DEVICE_ALI) {
struct _ac97_rate_regs *aregs;
aregs = &ac97_rate_regs[i];
ichdev = &chip->ichd[aregs->ichd];
ichdev->ac97_rate_regs[0] = aregs->regs[0];
ichdev->ac97_rate_regs[1] = aregs->regs[1];
ichdev->ac97_rate_regs[2] = aregs->regs[2];
ichdev->ac97_rates_idx = aregs->rates_idx;
} else {
struct _ac97_ali_rate_regs *aregs;
aregs = &ac97_ali_rate_regs[i];
ichdev = &chip->ichd[aregs->ichd];
ichdev->ac97_rate_regs[0] = aregs->regs[0];
ichdev->ac97_rate_regs[1] = aregs->regs[1];
ichdev->ac97_rate_regs[2] = aregs->regs[2];
ichdev->ac97_rates_idx = aregs->rates_idx;
}
}
chip->in_ac97_init = 1; chip->in_ac97_init = 1;
memset(&ac97, 0, sizeof(ac97)); memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_intel8x0_codec_write;
ac97.read = snd_intel8x0_codec_read;
ac97.init = snd_intel8x0_codec_init;
ac97.private_data = chip; ac97.private_data = chip;
ac97.private_free = snd_intel8x0_mixer_free_ac97; ac97.private_free = snd_intel8x0_mixer_free_ac97;
if (ac97_clock >= 8000 && ac97_clock <= 48000) if (ac97_clock >= 8000 && ac97_clock <= 48000)
ac97.clock = ac97_clock; ac97.clock = ac97_clock;
else else
ac97.clock = 48000; ac97.clock = 48000;
if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) if (chip->device_type != DEVICE_ALI) {
glob_sta = igetdword(chip, ICHREG(GLOB_STA));
ac97.write = snd_intel8x0_codec_write;
ac97.read = snd_intel8x0_codec_read;
if (glob_sta & ICH_PCM_6)
channels = 6;
else if (glob_sta & ICH_PCM_4)
channels = 4;
if (chip->device_type == DEVICE_INTEL_ICH4) {
codecs = 0;
if (glob_sta & ICH_PCR)
codecs++;
if (glob_sta & ICH_SCR)
codecs++;
if (glob_sta & ICH_TCR)
codecs++;
chip->in_sdin_init = 1;
for (i = 0; i < codecs; i++) {
ac97.num = i;
snd_intel8x0_codec_read(&ac97, 0);
chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
}
ac97.num = 0;
chip->in_sdin_init = 0;
} else {
codecs = glob_sta & ICH_SCR ? 2 : 1;
}
} else {
ac97.write = snd_intel8x0_ali_codec_write;
ac97.read = snd_intel8x0_ali_codec_read;
channels = 6;
codecs = 1;
}
if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
return err; return err;
#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */ chip->ac97[0] = x97;
if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) { chip->ichd[ICHD_PCMOUT].ac97 = x97;
ac97.num = 1; chip->ichd[ICHD_PCMIN].ac97 = x97;
ac97.addr = 1; if (x97->ext_id & AC97_EI_VRM)
snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec); chip->ichd[ICHD_MIC].ac97 = x97;
if (x97->ext_id & AC97_EI_SPDIF) {
if (chip->device_type != DEVICE_ALI)
chip->ichd[ICHD_SPBAR].ac97 = x97;
else
chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
} }
#endif /* make sure, that we have DACs at right slot for rev2.2 */
if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6)) if (ac97_is_rev22(x97))
return 0; snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 0);
if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) || /* can we have more AC'97 codecs with ALI chipset? */
(chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) { if (chip->device_type == DEVICE_ALI)
goto __end;
/* AnalogDevices CNR boards uses special codec chaining */
/* skip standard test method for secondary codecs in this case */
if (x97->flags & AC97_AD_MULTI) {
codecs = 1;
goto __skip_secondary;
}
if (codecs < 2)
goto __skip_secondary;
for (i = 1; i < codecs; i++) {
if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
return err;
chip->ac97[i] = x97;
if (chip->device_type == DEVICE_INTEL_ICH4 && chip->ichd[ICHD_PCM2IN].ac97 == NULL)
chip->ichd[ICHD_PCM2IN].ac97 = x97;
if (x97->ext_id & AC97_EI_VRM) {
if (chip->ichd[ICHD_MIC].ac97 == NULL)
chip->ichd[ICHD_MIC].ac97 = x97;
else if (chip->device_type == DEVICE_INTEL_ICH4 &&
chip->ichd[ICHD_MIC2].ac97 == NULL &&
chip->ichd[ICHD_PCM2IN].ac97 == x97)
chip->ichd[ICHD_MIC2].ac97 = x97;
}
if (x97->ext_id & AC97_EI_SPDIF) {
if (chip->device_type != DEVICE_ALI) {
if (chip->ichd[ICHD_SPBAR].ac97 == NULL)
chip->ichd[ICHD_SPBAR].ac97 = x97;
} else {
if (chip->ichd[ALID_AC97SPDIFOUT].ac97 == NULL)
chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
}
}
}
__skip_secondary:
if (chip->device_type == DEVICE_INTEL_ICH4) {
u8 tmp = igetbyte(chip, ICHREG(SDM));
tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
if (chip->ichd[ICHD_PCM2IN].ac97) {
tmp |= ICH_SE; /* steer enable for multiple SDINs */
tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
tmp |= chip->ac97_sdin[chip->ichd[ICHD_PCM2IN].ac97->num] << ICH_DI2L_SHIFT;
} else {
tmp &= ~ICH_SE;
}
iputbyte(chip, ICHREG(SDM), tmp);
}
for (i = 0; i < 3; i++) {
if ((x97 = chip->ac97[i]) == NULL)
continue;
if (x97->scaps & AC97_SCAP_SURROUND_DAC)
chip->multi4 = 1; chip->multi4 = 1;
if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) || }
(chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC))) for (i = 0; i < 3 && chip->multi4; i++) {
if ((x97 = chip->ac97[i]) == NULL)
continue;
if (x97->scaps & AC97_SCAP_CENTER_LFE_DAC)
chip->multi6 = 1;
}
if (codecs > 1) {
/* assign right slots for rev2.2 codecs */
i = 1;
if (chip->multi4)
goto __6ch;
for ( ; i < codecs; i++) {
if (ac97_is_rev22(x97 = chip->ac97[i])) {
snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 1);
chip->multi4 = 1;
break;
}
}
__6ch:
for ( ; i < codecs && chip->multi4; i++) {
if (ac97_is_rev22(x97 = chip->ac97[i])) {
snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 2);
chip->multi6 = 1;
break;
}
}
/* ok, some older codecs might support only AMAP */
if (!chip->multi4) {
for (i = 1; i < codecs; i++) {
if (ac97_can_amap(x97 = chip->ac97[i])) {
if (x97->addr == 1) {
chip->multi4 = 1;
break;
}
}
}
for ( ; i < codecs && chip->multi4; i++) {
if (ac97_can_amap(x97 = chip->ac97[i])) {
if (x97->addr == 2) {
chip->multi6 = 1; chip->multi6 = 1;
break;
}
}
}
}
} }
__end:
chip->in_ac97_init = 0; chip->in_ac97_init = 0;
return 0; return 0;
} }
...@@ -1008,6 +1682,16 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) ...@@ -1008,6 +1682,16 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
* *
*/ */
static void do_ali_reset(intel8x0_t *chip)
{
iputdword(chip, ICHREG(ALI_SCR), 0x8000000);
iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
iputdword(chip, ICHREG(ALI_INTERFACECR), 0x04080002); /* no spdif? */
iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
}
static void do_delay(intel8x0_t *chip) static void do_delay(intel8x0_t *chip)
{ {
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -1020,109 +1704,168 @@ static void do_delay(intel8x0_t *chip) ...@@ -1020,109 +1704,168 @@ static void do_delay(intel8x0_t *chip)
schedule_timeout(1); schedule_timeout(1);
} }
static int snd_intel8x0_chip_init(intel8x0_t *chip) static int snd_intel8x0_ich_chip_init(intel8x0_t *chip)
{ {
signed long end_time; signed long end_time;
unsigned int cnt; unsigned int cnt, status, nstatus;
/* put logic to right state */ /* put logic to right state */
/* first clear status bits */ /* first clear status bits */
cnt = inl(ICHREG(chip, GLOB_STA)); cnt = igetdword(chip, ICHREG(GLOB_STA));
outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA)); iputdword(chip, ICHREG(GLOB_STA), cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT));
/* ACLink on, 2 channels */ /* ACLink on, 2 channels */
cnt = inl(ICHREG(chip, GLOB_CNT)); cnt = igetdword(chip, ICHREG(GLOB_CNT));
cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
/* finish cold or do warm reset */ /* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
outl(cnt, ICHREG(chip, GLOB_CNT)); iputdword(chip, ICHREG(GLOB_CNT), cnt);
end_time = (jiffies + (HZ / 4)) + 1; end_time = (jiffies + (HZ / 4)) + 1;
do { do {
if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0) if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
goto __ok; goto __ok;
do_delay(chip); do_delay(chip);
} while (end_time - (signed long)jiffies >= 0); } while (time_after_eq(end_time, jiffies));
snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT))); snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO; return -EIO;
__ok: __ok:
/* wait for primary codec ready status. /* wait for any codec ready status.
* Once it becomes ready it should remain ready * Once it becomes ready it should remain ready
* as long as we do not disable the ac97 link. * as long as we do not disable the ac97 link.
*/ */
end_time = jiffies + HZ / 10; end_time = jiffies + HZ;
do { do {
if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR) status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
if (status)
goto __ok1; goto __ok1;
do_delay(chip); do_delay(chip);
} while (end_time - (signed long)jiffies >= 0); } while (time_after_eq(end_time, jiffies));
if (chip->pci->device == PCI_DEVICE_ID_INTEL_ICH4) { snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
/* FIXME: ICH4 may have no PCR and SCR bits... */ return -EIO;
snd_printd(KERN_INFO "intel8x0: no codec is probed, perhaps PCR and SCR bits are deactivated.\n");
chip->no_codec_check = 1; __ok1:
if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
goto __ok3;
/* wait for other codecs ready status. No secondary codecs? , ok */
end_time = jiffies + HZ / 4;
do {
nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
if (nstatus != status) {
status = nstatus;
goto __ok2; goto __ok2;
} }
/* check a bit longer... */ do_delay(chip);
end_time = jiffies + HZ; } while (time_after_eq(end_time, jiffies));
__ok2:
if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
goto __ok3;
/* wait for other codecs ready status. No other secondary codecs? , ok */
/* end_time is not initialized here */
do { do {
if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR) nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
goto __ok1; if (nstatus != status) {
status = nstatus;
goto __ok2;
}
do_delay(chip); do_delay(chip);
} while (end_time - (signed long)jiffies >= 0); } while (time_after_eq(end_time, jiffies));
snd_printk(KERN_ERR "codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA)));
__ok3:
return 0;
}
static int snd_intel8x0_ali_chip_init(intel8x0_t *chip)
{
u32 reg;
int i = 0;
reg = igetdword(chip, ICHREG(ALI_SCR));
if ((reg & 2) == 0) /* Cold required */
reg |= 2;
else
reg |= 1; /* Warm */
reg &= ~0x80000000; /* ACLink on */
iputdword(chip, ICHREG(ALI_SCR), reg);
for (i = 0; i < HZ / 2; i++) {
if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
goto __ok;
do_delay(chip);
}
snd_printk(KERN_ERR "AC'97 reset failed.\n");
return -EIO; return -EIO;
__ok1: __ok:
/* wait for secondary codec ready status. No secondary codec? , ok */ for (i = 0; i < HZ / 2; i++) {
/* the end_time variable is not initialized again */ reg = igetdword(chip, ICHREG(ALI_RTSR));
do { if (reg & 0x80) /* primary codec */
if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR)
break; break;
iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
do_delay(chip); do_delay(chip);
} while (end_time - (signed long)jiffies >= 0); }
__ok2: do_ali_reset(chip);
inw(chip->port); /* clear semaphore flag */ return 0;
}
static int snd_intel8x0_chip_init(intel8x0_t *chip)
{
int i, err;
if (chip->device_type != DEVICE_ALI)
err = snd_intel8x0_ich_chip_init(chip);
else
err = snd_intel8x0_ali_chip_init(chip);
if (err < 0)
return err;
iagetword(chip, 0); /* clear semaphore flag */
/* disable interrupts */ /* disable interrupts */
outb(0x00, ICHREG(chip, PI_CR)); for (i = 0; i < chip->bdbars_count; i++)
outb(0x00, ICHREG(chip, PO_CR)); iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
outb(0x00, ICHREG(chip, MC_CR));
/* reset channels */ /* reset channels */
outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); for (i = 0; i < chip->bdbars_count; i++)
outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
/* initialize Buffer Descriptor Lists */ /* initialize Buffer Descriptor Lists */
outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR)); for (i = 0; i < chip->bdbars_count; i++)
outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR)); iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR));
return 0; return 0;
} }
static void snd_intel8x0_proc_done(intel8x0_t * chip);
static int snd_intel8x0_free(intel8x0_t *chip) static int snd_intel8x0_free(intel8x0_t *chip)
{ {
int i;
if (chip->irq < 0) if (chip->irq < 0)
goto __hw_end; goto __hw_end;
/* disable interrupts */ /* disable interrupts */
outb(0x00, ICHREG(chip, PI_CR)); for (i = 0; i < chip->bdbars_count; i++)
outb(0x00, ICHREG(chip, PO_CR)); iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
outb(0x00, ICHREG(chip, MC_CR));
/* reset channels */ /* reset channels */
outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); for (i = 0; i < chip->bdbars_count; i++)
outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
/* --- */ /* --- */
synchronize_irq(chip->irq); synchronize_irq(chip->irq);
__hw_end: __hw_end:
snd_intel8x0_proc_done(chip);
if (chip->bdbars) if (chip->bdbars)
snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
if (chip->res_port) { if (chip->remap_addr)
release_resource(chip->res_port); iounmap((void *) chip->remap_addr);
kfree_nocheck(chip->res_port); if (chip->remap_bmaddr)
iounmap((void *) chip->remap_bmaddr);
if (chip->res) {
release_resource(chip->res);
kfree_nocheck(chip->res);
} }
if (chip->res_bmport) { if (chip->res_bm) {
release_resource(chip->res_bmport); release_resource(chip->res_bm);
kfree_nocheck(chip->res_bmport); kfree_nocheck(chip->res_bm);
} }
if (chip->irq >= 0) if (chip->irq >= 0)
free_irq(chip->irq, (void *)chip); free_irq(chip->irq, (void *)chip);
...@@ -1154,6 +1897,7 @@ static void intel8x0_suspend(intel8x0_t *chip) ...@@ -1154,6 +1897,7 @@ static void intel8x0_suspend(intel8x0_t *chip)
static void intel8x0_resume(intel8x0_t *chip) static void intel8x0_resume(intel8x0_t *chip)
{ {
snd_card_t *card = chip->card; snd_card_t *card = chip->card;
int i;
snd_power_lock(card); snd_power_lock(card);
if (card->power_state == SNDRV_CTL_POWER_D0) if (card->power_state == SNDRV_CTL_POWER_D0)
...@@ -1161,7 +1905,9 @@ static void intel8x0_resume(intel8x0_t *chip) ...@@ -1161,7 +1905,9 @@ static void intel8x0_resume(intel8x0_t *chip)
pci_enable_device(chip->pci); pci_enable_device(chip->pci);
snd_intel8x0_chip_init(chip); snd_intel8x0_chip_init(chip);
snd_ac97_resume(chip->ac97); for (i = 0; i < 3; i++)
if (chip->ac97[i])
snd_ac97_resume(chip->ac97[i]);
chip->in_suspend = 0; chip->in_suspend = 0;
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
...@@ -1222,12 +1968,13 @@ static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_sta ...@@ -1222,12 +1968,13 @@ static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_sta
static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
{ {
snd_pcm_substream_t *subs; snd_pcm_substream_t *subs;
ichdev_t *ichdev;
unsigned long port; unsigned long port;
unsigned long pos, t; unsigned long pos, t;
unsigned long flags; unsigned long flags;
struct timeval start_time, stop_time; struct timeval start_time, stop_time;
if (chip->ac97->clock != 48000) if (chip->ac97[0]->clock != 48000)
return; /* specified in module option */ return; /* specified in module option */
subs = chip->pcm->streams[0].substream; subs = chip->pcm->streams[0].substream;
...@@ -1235,19 +1982,20 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) ...@@ -1235,19 +1982,20 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
snd_printk("no playback buffer allocated - aborting measure ac97 clock\n"); snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
return; return;
} }
chip->playback.physbuf = subs->dma_addr; ichdev = &chip->ichd[ICHD_PCMOUT];
chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE; ichdev->physbuf = subs->dma_addr;
chip->playback.substream = NULL; /* don't process interrupts */ ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE;
ichdev->substream = NULL; /* don't process interrupts */
/* set rate */ /* set rate */
if (snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000) < 0) { if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97->clock); snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97[0]->clock);
return; return;
} }
snd_intel8x0_setup_periods(chip, &chip->playback); snd_intel8x0_setup_periods(chip, ichdev);
port = chip->bmport + chip->playback.reg_offset; port = ichdev->reg_offset;
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); /* trigger */
do_gettimeofday(&start_time); do_gettimeofday(&start_time);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
#if 0 #if 0
...@@ -1259,18 +2007,18 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) ...@@ -1259,18 +2007,18 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
#endif #endif
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
/* check the position */ /* check the position */
pos = chip->playback.fragsize1; pos = ichdev->fragsize1;
if (chip->device_type == DEVICE_SIS) if (chip->device_type == DEVICE_SIS)
pos -= inw(ICHREG2(chip,chip->reg_po_picb)); pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
else else
pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1; pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
pos += chip->playback.position; pos += ichdev->position;
do_gettimeofday(&stop_time); do_gettimeofday(&stop_time);
outb(0, port + ICH_REG_PI_CR); /* stop */ iputbyte(chip, port + ICH_REG_OFF_CR, 0); /* stop */
/* reset whole DMA things */ /* reset whole DMA things */
while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
; ;
outb(ICH_RESETREGS, port + ICH_REG_PI_CR); iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);
t = stop_time.tv_sec - start_time.tv_sec; t = stop_time.tv_sec - start_time.tv_sec;
...@@ -1290,8 +2038,60 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) ...@@ -1290,8 +2038,60 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
else if (pos < 47500 || pos > 48500) else if (pos < 47500 || pos > 48500)
/* not 48000Hz, tuning the clock.. */ /* not 48000Hz, tuning the clock.. */
chip->ac97->clock = (chip->ac97->clock * 48000) / pos; chip->ac97[0]->clock = (chip->ac97[0]->clock * 48000) / pos;
printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock); printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97[0]->clock);
}
static void snd_intel8x0_proc_read(snd_info_entry_t * entry,
snd_info_buffer_t * buffer)
{
intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return);
unsigned int tmp;
snd_iprintf(buffer, "Intel8x0\n\n");
if (chip->device_type == DEVICE_ALI)
return;
tmp = igetdword(chip, ICHREG(GLOB_STA));
snd_iprintf(buffer, "Global control : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
snd_iprintf(buffer, "Global status : 0x%08x\n", tmp);
if (chip->device_type == DEVICE_INTEL_ICH4)
snd_iprintf(buffer, "SDM : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
snd_iprintf(buffer, "AC'97 codecs ready :%s%s%s%s\n",
tmp & ICH_PCR ? " primary" : "",
tmp & ICH_SCR ? " secondary" : "",
tmp & ICH_TCR ? " tertiary" : "",
(tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
if (chip->device_type == DEVICE_INTEL_ICH4)
snd_iprintf(buffer, "AC'97 codecs SDIN : %i %i %i\n",
chip->ac97_sdin[0],
chip->ac97_sdin[1],
chip->ac97_sdin[2]);
}
static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip)
{
snd_info_entry_t *entry;
if ((entry = snd_info_create_card_entry(chip->card, "intel8x0", chip->card->proc_root)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
entry->c.text.read_size = 2048;
entry->c.text.read = snd_intel8x0_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
}
}
chip->proc_entry = entry;
}
static void snd_intel8x0_proc_done(intel8x0_t * chip)
{
if (chip->proc_entry) {
snd_info_unregister(chip->proc_entry);
chip->proc_entry = NULL;
}
} }
static int snd_intel8x0_dev_free(snd_device_t *device) static int snd_intel8x0_dev_free(snd_device_t *device)
...@@ -1306,10 +2106,22 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, ...@@ -1306,10 +2106,22 @@ static int __devinit snd_intel8x0_create(snd_card_t * card,
intel8x0_t ** r_intel8x0) intel8x0_t ** r_intel8x0)
{ {
intel8x0_t *chip; intel8x0_t *chip;
int err; int err, i;
unsigned int int_sta_masks;
ichdev_t *ichdev;
static snd_device_ops_t ops = { static snd_device_ops_t ops = {
.dev_free = snd_intel8x0_dev_free, .dev_free = snd_intel8x0_dev_free,
}; };
static u32 intel_int_sta_masks[6] = {
ICH_PIINT, ICH_POINT, ICH_MCINT, ICH_M2INT, ICH_P2INT, ICH_SPINT
};
static u32 ali_int_sta_masks[6] = {
ALI_INT_PCMIN, ALI_INT_PCMOUT, ALI_INT_MICIN,
ALI_INT_CODECSPDIFOUT, ALI_INT_SPDIFIN, ALI_INT_SPDIFOUT
};
static u32 ali_reg_offsets[6] = {
0x40, 0x50, 0x60, 0x70, 0xa0, 0xb0
};
*r_intel8x0 = NULL; *r_intel8x0 = NULL;
...@@ -1325,20 +2137,51 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, ...@@ -1325,20 +2137,51 @@ static int __devinit snd_intel8x0_create(snd_card_t * card,
chip->card = card; chip->card = card;
chip->pci = pci; chip->pci = pci;
chip->irq = -1; chip->irq = -1;
chip->port = pci_resource_start(pci, 0); snd_intel8x0_proc_init(chip);
if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and higher */
chip->mmio = chip->bm_mmio = 1;
chip->addr = pci_resource_start(pci, 2);
sprintf(chip->ac97_name, "%s - AC'97", card->shortname); sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
if ((chip->res_port = request_region(chip->port, 256, chip->ac97_name)) == NULL) { if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) {
snd_intel8x0_free(chip); snd_intel8x0_free(chip);
snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1);
return -EBUSY; return -EBUSY;
} }
chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512);
if (chip->remap_addr == 0) {
snd_intel8x0_free(chip);
snd_printk("AC'97 space ioremap problem\n");
return -EIO;
}
sprintf(chip->ctrl_name, "%s - Controller", card->shortname); sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
chip->bmport = pci_resource_start(pci, 1); chip->bmaddr = pci_resource_start(pci, 3);
if ((chip->res_bmport = request_region(chip->bmport, 64, chip->ctrl_name)) == NULL) { if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) {
snd_intel8x0_free(chip);
snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1);
return -EBUSY;
}
chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256);
if (chip->remap_bmaddr == 0) {
snd_intel8x0_free(chip); snd_intel8x0_free(chip);
snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1); snd_printk("Controller space ioremap problem\n");
return -EIO;
}
} else {
chip->addr = pci_resource_start(pci, 0);
sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) {
snd_intel8x0_free(chip);
snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1);
return -EBUSY; return -EBUSY;
} }
sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
chip->bmaddr = pci_resource_start(pci, 1);
if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) {
snd_intel8x0_free(chip);
snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1);
return -EBUSY;
}
}
if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
snd_intel8x0_free(chip); snd_intel8x0_free(chip);
snd_printk("unable to grab IRQ %d\n", pci->irq); snd_printk("unable to grab IRQ %d\n", pci->irq);
...@@ -1349,27 +2192,36 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, ...@@ -1349,27 +2192,36 @@ static int __devinit snd_intel8x0_create(snd_card_t * card,
synchronize_irq(chip->irq); synchronize_irq(chip->irq);
/* initialize offsets */ /* initialize offsets */
chip->reg_pi_sr = ICH_REG_PI_SR; for (i = 0; i <= ICHD_LAST; i++) {
chip->reg_pi_picb = ICH_REG_PI_PICB; ichdev = &chip->ichd[i];
chip->reg_po_sr = ICH_REG_PO_SR; ichdev->ichd = i;
chip->reg_po_picb = ICH_REG_PO_PICB; ichdev->reg_offset = i * 0x10 + (i >= 0x30 ? 0x10 : 0);
chip->reg_mc_sr = ICH_REG_MC_SR; ichdev->roff_sr = ICH_REG_OFF_SR;
chip->reg_mc_picb = ICH_REG_MC_PICB; ichdev->roff_picb = ICH_REG_OFF_PICB;
if (device_type == DEVICE_SIS) { ichdev->int_sta_mask = device_type == DEVICE_ALI ? ali_int_sta_masks[i] : intel_int_sta_masks[i];
chip->reg_pi_sr = ICH_REG_PI_PICB; }
chip->reg_pi_picb = ICH_REG_PI_SR; switch (device_type) {
chip->reg_po_sr = ICH_REG_PO_PICB; case DEVICE_SIS:
chip->reg_po_picb = ICH_REG_PO_SR; for (i = 0; i <= ICHD_LAST; i++) {
chip->reg_mc_sr = ICH_REG_MC_PICB; ichdev = &chip->ichd[i];
chip->reg_mc_picb = ICH_REG_MC_SR; ichdev->roff_sr = ICH_REG_OFF_PICB;
} ichdev->roff_picb = ICH_REG_OFF_SR;
chip->playback.reg_offset = 0x10; }
chip->capture.reg_offset = 0; break;
chip->capture_mic.reg_offset = 0x20; case DEVICE_ALI:
for (i = 0; i <= ALID_LAST; i++) {
ichdev = &chip->ichd[i];
ichdev->reg_offset = ali_reg_offsets[i];
ichdev->ali_slot = i + 1; /* is this right for last three devices? --jk */
}
}
/* allocate buffer descriptor lists */ /* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */ /* the start of each lists must be aligned to 8 bytes */
chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); chip->bdbars_count = 3;
if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI)
chip->bdbars_count = 6;
chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
if (chip->bdbars == NULL) { if (chip->bdbars == NULL) {
snd_intel8x0_free(chip); snd_intel8x0_free(chip);
return -ENOMEM; return -ENOMEM;
...@@ -1384,12 +2236,15 @@ static int __devinit snd_intel8x0_create(snd_card_t * card, ...@@ -1384,12 +2236,15 @@ static int __devinit snd_intel8x0_create(snd_card_t * card,
return -ENOMEM; return -ENOMEM;
} }
#endif #endif
chip->playback.bdbar = chip->bdbars; /* crop to 32bit */ int_sta_masks = 0;
chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr; for (i = 0; i < chip->bdbars_count; i++) {
chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2; ichdev = &chip->ichd[i];
chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2);
chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2; ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; int_sta_masks |= ichdev->int_sta_mask;
}
chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
chip->int_sta_mask = int_sta_masks;
if ((err = snd_intel8x0_chip_init(chip)) < 0) { if ((err = snd_intel8x0_chip_init(chip)) < 0) {
snd_intel8x0_free(chip); snd_intel8x0_free(chip);
...@@ -1469,15 +2324,52 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci, ...@@ -1469,15 +2324,52 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
snd_card_free(card); snd_card_free(card);
return err; return err;
} }
if (chip->ac97->ext_id & 0x0008) { /* MIC VRM */ /* activate MIC PCM only when associated AC'97 codec */
if (chip->ichd[ICHD_MIC].ac97) {
if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) { if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) {
snd_card_free(card); snd_card_free(card);
return err; return err;
} }
} }
if (chip->device_type == DEVICE_ALI) {
if ((err = snd_intel8x0_ali_spdif(chip, pcm_dev++, NULL)) < 0) {
snd_card_free(card);
return err;
}
}
/* activate AC'97 S/PDIF only when associated AC'97 codec */
if (chip->bdbars_count > 3) {
err = 0;
if (chip->device_type == DEVICE_ALI) {
if (chip->ichd[ALID_AC97SPDIFOUT].ac97)
err = snd_intel8x0_ali_ac97spdif(chip, pcm_dev++, NULL);
} else {
if (chip->ichd[ICHD_SPBAR].ac97)
err = snd_intel8x0_pcm_spdif(chip, pcm_dev++, NULL);
}
if (err < 0) {
snd_card_free(card);
return err;
}
if (chip->device_type != DEVICE_ALI) {
/* activate MIC2 only when associated AC'97 codec */
if (chip->ichd[ICHD_MIC2].ac97)
if ((err = snd_intel8x0_pcm_mic2(chip, pcm_dev++, NULL)) < 0) {
snd_card_free(card);
return err;
}
/* activate PCM2IN only when associated AC'97 codec */
if (chip->ichd[ICHD_PCM2IN].ac97)
if ((err = snd_intel8x0_pcm_capture2(chip, pcm_dev++, NULL)) < 0) {
snd_card_free(card);
return err;
}
}
}
if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) { if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) {
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0,
snd_mpu_port[dev], 0, snd_mpu_port[dev], 0,
-1, 0, &chip->rmidi)) < 0) { -1, 0, &chip->rmidi)) < 0) {
printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]); printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]);
...@@ -1487,7 +2379,7 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci, ...@@ -1487,7 +2379,7 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
snd_mpu_port[dev] = 0; snd_mpu_port[dev] = 0;
sprintf(card->longname, "%s at 0x%lx, irq %i", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq); card->shortname, chip->addr, chip->irq);
if (! snd_ac97_clock[dev]) if (! snd_ac97_clock[dev])
intel8x0_measure_ac97_clock(chip); intel8x0_measure_ac97_clock(chip);
......
...@@ -605,7 +605,7 @@ static snd_pcm_hardware_t snd_via686a_playback = ...@@ -605,7 +605,7 @@ static snd_pcm_hardware_t snd_via686a_playback =
.period_bytes_min = 32, .period_bytes_min = 32,
.period_bytes_max = 128 * 1024, .period_bytes_max = 128 * 1024,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 128,
.fifo_size = 0, .fifo_size = 0,
}; };
...@@ -624,7 +624,7 @@ static snd_pcm_hardware_t snd_via686a_capture = ...@@ -624,7 +624,7 @@ static snd_pcm_hardware_t snd_via686a_capture =
.period_bytes_min = 32, .period_bytes_min = 32,
.period_bytes_max = 128 * 1024, .period_bytes_max = 128 * 1024,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 128,
.fifo_size = 0, .fifo_size = 0,
}; };
...@@ -636,21 +636,17 @@ static int snd_via686a_playback_open(snd_pcm_substream_t * substream) ...@@ -636,21 +636,17 @@ static int snd_via686a_playback_open(snd_pcm_substream_t * substream)
chip->playback.substream = substream; chip->playback.substream = substream;
runtime->hw = snd_via686a_playback; runtime->hw = snd_via686a_playback;
runtime->hw.rates = chip->ac97->rates_front_dac; runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err; return err;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err; return err;
#if 0 /* we may remove following constaint when we modify table entries
/* applying the following constraint together with the power-of-2 rule in interrupt */
* above may result in too narrow space.
* this one is not strictly necessary, so let's disable it.
*/
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err; return err;
#endif
return 0; return 0;
} }
...@@ -662,17 +658,15 @@ static int snd_via686a_capture_open(snd_pcm_substream_t * substream) ...@@ -662,17 +658,15 @@ static int snd_via686a_capture_open(snd_pcm_substream_t * substream)
chip->capture.substream = substream; chip->capture.substream = substream;
runtime->hw = snd_via686a_capture; runtime->hw = snd_via686a_capture;
runtime->hw.rates = chip->ac97->rates_adc; runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err; return err;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err; return err;
#if 0
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err; return err;
#endif
return 0; return 0;
} }
...@@ -683,8 +677,6 @@ static int snd_via686a_playback_close(snd_pcm_substream_t * substream) ...@@ -683,8 +677,6 @@ static int snd_via686a_playback_close(snd_pcm_substream_t * substream)
clean_via_table(&chip->playback, substream, chip->pci); clean_via_table(&chip->playback, substream, chip->pci);
snd_pcm_sgbuf_delete(substream); snd_pcm_sgbuf_delete(substream);
chip->playback.substream = NULL; chip->playback.substream = NULL;
/* disable DAC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
return 0; return 0;
} }
...@@ -695,8 +687,6 @@ static int snd_via686a_capture_close(snd_pcm_substream_t * substream) ...@@ -695,8 +687,6 @@ static int snd_via686a_capture_close(snd_pcm_substream_t * substream)
clean_via_table(&chip->capture, substream, chip->pci); clean_via_table(&chip->capture, substream, chip->pci);
snd_pcm_sgbuf_delete(substream); snd_pcm_sgbuf_delete(substream);
chip->capture.substream = NULL; chip->capture.substream = NULL;
/* disable ADC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
return 0; return 0;
} }
...@@ -764,16 +754,6 @@ static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** r ...@@ -764,16 +754,6 @@ static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** r
* Mixer part * Mixer part
*/ */
static void snd_via686a_codec_init(ac97_t *ac97)
{
// via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
/* disable DAC & ADC power */
snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
}
static void snd_via686a_mixer_free_ac97(ac97_t *ac97) static void snd_via686a_mixer_free_ac97(ac97_t *ac97)
{ {
via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
...@@ -788,7 +768,6 @@ static int __devinit snd_via686a_mixer(via686a_t *chip) ...@@ -788,7 +768,6 @@ static int __devinit snd_via686a_mixer(via686a_t *chip)
memset(&ac97, 0, sizeof(ac97)); memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_via686a_codec_write; ac97.write = snd_via686a_codec_write;
ac97.read = snd_via686a_codec_read; ac97.read = snd_via686a_codec_read;
ac97.init = snd_via686a_codec_init;
ac97.wait = snd_via686a_codec_wait; ac97.wait = snd_via686a_codec_wait;
ac97.private_data = chip; ac97.private_data = chip;
ac97.private_free = snd_via686a_mixer_free_ac97; ac97.private_free = snd_via686a_mixer_free_ac97;
......
...@@ -591,7 +591,7 @@ static snd_pcm_hardware_t snd_via8233_playback = ...@@ -591,7 +591,7 @@ static snd_pcm_hardware_t snd_via8233_playback =
.period_bytes_min = 32, .period_bytes_min = 32,
.period_bytes_max = 128 * 1024, .period_bytes_max = 128 * 1024,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 128,
.fifo_size = 0, .fifo_size = 0,
}; };
...@@ -610,7 +610,7 @@ static snd_pcm_hardware_t snd_via8233_capture = ...@@ -610,7 +610,7 @@ static snd_pcm_hardware_t snd_via8233_capture =
.period_bytes_min = 32, .period_bytes_min = 32,
.period_bytes_max = 128 * 1024, .period_bytes_max = 128 * 1024,
.periods_min = 2, .periods_min = 2,
.periods_max = 1024, .periods_max = 128,
.fifo_size = 0, .fifo_size = 0,
}; };
...@@ -634,17 +634,15 @@ static int snd_via8233_playback_open(snd_pcm_substream_t * substream) ...@@ -634,17 +634,15 @@ static int snd_via8233_playback_open(snd_pcm_substream_t * substream)
chip->playback.substream = substream; chip->playback.substream = substream;
runtime->hw = snd_via8233_playback; runtime->hw = snd_via8233_playback;
runtime->hw.rates = chip->ac97->rates_front_dac; runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err; return err;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err; return err;
#if 0
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err; return err;
#endif
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
return 0; return 0;
} }
...@@ -657,17 +655,15 @@ static int snd_via8233_capture_open(snd_pcm_substream_t * substream) ...@@ -657,17 +655,15 @@ static int snd_via8233_capture_open(snd_pcm_substream_t * substream)
chip->capture.substream = substream; chip->capture.substream = substream;
runtime->hw = snd_via8233_capture; runtime->hw = snd_via8233_capture;
runtime->hw.rates = chip->ac97->rates_adc; runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000; runtime->hw.rate_min = 48000;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err; return err;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err; return err;
#if 0
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err; return err;
#endif
return 0; return 0;
} }
...@@ -679,8 +675,6 @@ static int snd_via8233_playback_close(snd_pcm_substream_t * substream) ...@@ -679,8 +675,6 @@ static int snd_via8233_playback_close(snd_pcm_substream_t * substream)
clean_via_table(&chip->playback, substream, chip->pci); clean_via_table(&chip->playback, substream, chip->pci);
snd_pcm_sgbuf_delete(substream); snd_pcm_sgbuf_delete(substream);
chip->playback.substream = NULL; chip->playback.substream = NULL;
/* disable DAC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
return 0; return 0;
} }
...@@ -692,8 +686,6 @@ static int snd_via8233_capture_close(snd_pcm_substream_t * substream) ...@@ -692,8 +686,6 @@ static int snd_via8233_capture_close(snd_pcm_substream_t * substream)
clean_via_table(&chip->capture, substream, chip->pci); clean_via_table(&chip->capture, substream, chip->pci);
snd_pcm_sgbuf_delete(substream); snd_pcm_sgbuf_delete(substream);
chip->capture.substream = NULL; chip->capture.substream = NULL;
/* disable ADC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
return 0; return 0;
} }
...@@ -760,16 +752,6 @@ static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** r ...@@ -760,16 +752,6 @@ static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** r
* Mixer part * Mixer part
*/ */
static void snd_via8233_codec_init(ac97_t *ac97)
{
// via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
/* disable DAC & ADC power */
snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
}
static void snd_via8233_mixer_free_ac97(ac97_t *ac97) static void snd_via8233_mixer_free_ac97(ac97_t *ac97)
{ {
via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
...@@ -784,7 +766,6 @@ static int __devinit snd_via8233_mixer(via8233_t *chip) ...@@ -784,7 +766,6 @@ static int __devinit snd_via8233_mixer(via8233_t *chip)
memset(&ac97, 0, sizeof(ac97)); memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_via8233_codec_write; ac97.write = snd_via8233_codec_write;
ac97.read = snd_via8233_codec_read; ac97.read = snd_via8233_codec_read;
ac97.init = snd_via8233_codec_init;
ac97.private_data = chip; ac97.private_data = chip;
ac97.private_free = snd_via8233_mixer_free_ac97; ac97.private_free = snd_via8233_mixer_free_ac97;
ac97.clock = chip->ac97_clock; ac97.clock = chip->ac97_clock;
......
CONFIG_SND_USB_AUDIO CONFIG_SND_USB_AUDIO
Say 'Y' or 'M' to include support for USB audio devices. Say 'Y' or 'M' to include support for USB audio devices.
To support USB MIDI devices, you need to enable ALSA sequencer support
CONFIG_SND_USB_MIDI (CONFIG_SND_SEQUENCER).
Say 'Y' or 'M' to include support for MIDI devices connected via USB.
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
mainmenu_option next_comment mainmenu_option next_comment
comment 'ALSA USB devices' comment 'ALSA USB devices'
dep_tristate 'USB Audio driver' CONFIG_SND_USB_AUDIO $CONFIG_SND dep_tristate 'USB Audio/MIDI driver' CONFIG_SND_USB_AUDIO $CONFIG_SND
dep_tristate 'USB MIDI driver' CONFIG_SND_USB_MIDI $CONFIG_SND $CONFIG_SND_SEQUENCER
endmenu endmenu
...@@ -3,10 +3,14 @@ ...@@ -3,10 +3,14 @@
# #
snd-usb-audio-objs := usbaudio.o usbmixer.o snd-usb-audio-objs := usbaudio.o usbmixer.o
ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
snd-usb-midi-objs := usbmidi.o snd-usb-midi-objs := usbmidi.o
endif
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o
obj-$(CONFIG_SND_USB_MIDI) += snd-usb-midi.o ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-midi.o
endif
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/seq_device.h>
#define SNDRV_GET_ID #define SNDRV_GET_ID
#include <sound/initval.h> #include <sound/initval.h>
...@@ -1234,6 +1235,7 @@ static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1234,6 +1235,7 @@ static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
static void usb_audio_disconnect(struct usb_device *dev, void *ptr); static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
static struct usb_device_id usb_audio_ids [] = { static struct usb_device_id usb_audio_ids [] = {
#include "usbquirks.h"
{ .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
.bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL }, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL },
...@@ -1716,10 +1718,13 @@ static int snd_usb_audio_stream_new(snd_usb_audio_t *chip, unsigned char *buffer ...@@ -1716,10 +1718,13 @@ static int snd_usb_audio_stream_new(snd_usb_audio_t *chip, unsigned char *buffer
/* /*
* parse audio control descriptor and create pcm streams * parse audio control descriptor and create pcm/midi streams
*/ */
static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif, static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
const snd_usb_audio_quirk_t *quirk);
static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
unsigned char *buffer, int buflen) unsigned char *buffer, int buflen)
{ {
struct usb_device *dev = chip->dev; struct usb_device *dev = chip->dev;
...@@ -1752,6 +1757,15 @@ static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif, ...@@ -1752,6 +1757,15 @@ static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif,
continue; continue;
} }
iface = &config->interface[j]; iface = &config->interface[j];
if (iface->altsetting[0].bInterfaceClass == USB_CLASS_AUDIO &&
iface->altsetting[0].bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
if (snd_usb_create_midi_interface(chip, j, NULL) < 0) {
snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
continue;
}
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
continue;
}
if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO ||
iface->altsetting[0].bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) { iface->altsetting[0].bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) {
snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, iface->altsetting[0].bInterfaceClass); snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, iface->altsetting[0].bInterfaceClass);
...@@ -1808,6 +1822,37 @@ static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif, ...@@ -1808,6 +1822,37 @@ static int snd_usb_create_pcm(snd_usb_audio_t *chip, int ctrlif,
return 0; return 0;
} }
static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
const snd_usb_audio_quirk_t *quirk)
{
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
snd_seq_device_t *seq_device;
snd_usb_midi_t *umidi;
int err;
err = snd_seq_device_new(chip->card, chip->next_seq_device,
SNDRV_SEQ_DEV_ID_USBMIDI,
sizeof(snd_usb_midi_t), &seq_device);
if (err < 0)
return err;
chip->next_seq_device++;
strcpy(seq_device->name, chip->card->shortname);
umidi = (snd_usb_midi_t *)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
umidi->chip = chip;
umidi->ifnum = ifnum;
umidi->quirk = quirk;
umidi->seq_client = -1;
#endif
return 0;
}
static inline int snd_usb_create_quirk(snd_usb_audio_t *chip, int ifnum,
const snd_usb_audio_quirk_t *quirk)
{
/* in the future, there may be quirks for PCM devices */
return snd_usb_create_midi_interface(chip, ifnum, quirk);
}
/* /*
* free the chip instance * free the chip instance
...@@ -1835,7 +1880,9 @@ static int snd_usb_audio_dev_free(snd_device_t *device) ...@@ -1835,7 +1880,9 @@ static int snd_usb_audio_dev_free(snd_device_t *device)
/* /*
* create a chip instance and set its names. * create a chip instance and set its names.
*/ */
static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, snd_usb_audio_t **rchip) static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev,
const snd_usb_audio_quirk_t *quirk,
snd_usb_audio_t **rchip)
{ {
snd_usb_audio_t *chip; snd_usb_audio_t *chip;
int err, len; int err, len;
...@@ -1858,18 +1905,50 @@ static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, snd_us ...@@ -1858,18 +1905,50 @@ static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev, snd_us
} }
strcpy(card->driver, "USB-Audio"); strcpy(card->driver, "USB-Audio");
strcpy(card->shortname, "USB Audio Driver");
/* retrieve the device string as shortname */
if (dev->descriptor.iProduct)
len = usb_string(dev, dev->descriptor.iProduct,
card->shortname, sizeof(card->shortname));
else
len = 0;
if (len <= 0) {
if (quirk && quirk->product_name) {
strncpy(card->shortname, quirk->product_name, sizeof(card->shortname) - 1);
card->shortname[sizeof(card->shortname) - 1] = '\0';
} else {
sprintf(card->shortname, "USB Device %#04x:%#04x",
dev->descriptor.idVendor, dev->descriptor.idProduct);
}
}
/* retrieve the vendor and device strings as longname */ /* retrieve the vendor and device strings as longname */
len = usb_string(dev, 1, card->longname, sizeof(card->longname) - 1); if (dev->descriptor.iManufacturer)
if (len <= 0) len = usb_string(dev, dev->descriptor.iManufacturer,
card->longname, sizeof(card->longname) - 1);
else
len = 0; len = 0;
else { if (len <= 0) {
if (quirk && quirk->vendor_name) {
strncpy(card->longname, quirk->vendor_name, sizeof(card->longname) - 2);
card->longname[sizeof(card->longname) - 2] = '\0';
len = strlen(card->longname);
} else {
len = 0;
}
}
if (len > 0) {
card->longname[len] = ' '; card->longname[len] = ' ';
len++; len++;
} }
card->longname[len] = 0; card->longname[len] = '\0';
usb_string(dev, 2, card->longname + len, sizeof(card->longname) - len); if ((!dev->descriptor.iProduct
|| usb_string(dev, dev->descriptor.iProduct,
card->longname + len, sizeof(card->longname) - len) <= 0)
&& quirk && quirk->product_name) {
strncpy(card->longname + len, quirk->product_name, sizeof(card->longname) - len - 1);
card->longname[sizeof(card->longname) - 1] = '\0';
}
*rchip = chip; *rchip = chip;
return 0; return 0;
...@@ -1926,12 +2005,16 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1926,12 +2005,16 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_config_descriptor *config = dev->actconfig; struct usb_config_descriptor *config = dev->actconfig;
const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)id->driver_info;
unsigned char *buffer; unsigned char *buffer;
unsigned int index; unsigned int index;
int i, buflen; int i, buflen;
snd_card_t *card; snd_card_t *card;
snd_usb_audio_t *chip; snd_usb_audio_t *chip;
if (quirk && ifnum != quirk->ifnum)
return NULL;
if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
snd_printk(KERN_ERR "cannot set configuration (value 0x%x)\n", config->bConfigurationValue); snd_printk(KERN_ERR "cannot set configuration (value 0x%x)\n", config->bConfigurationValue);
return NULL; return NULL;
...@@ -1966,7 +2049,7 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1966,7 +2049,7 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
snd_printk(KERN_ERR "cannot create a card instance %d\n", i); snd_printk(KERN_ERR "cannot create a card instance %d\n", i);
goto __error; goto __error;
} }
if (snd_usb_audio_create(card, dev, &chip) < 0) { if (snd_usb_audio_create(card, dev, quirk, &chip) < 0) {
snd_card_free(card); snd_card_free(card);
goto __error; goto __error;
} }
...@@ -1980,10 +2063,15 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -1980,10 +2063,15 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
} }
} }
if (snd_usb_create_pcm(chip, ifnum, buffer, buflen) < 0) if (!quirk) {
if (snd_usb_create_streams(chip, ifnum, buffer, buflen) < 0)
goto __error; goto __error;
if (snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0) if (snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0)
goto __error; goto __error;
} else {
if (snd_usb_create_quirk(chip, ifnum, quirk) < 0)
goto __error;
}
/* we are allowed to call snd_card_register() many times */ /* we are allowed to call snd_card_register() many times */
if (snd_card_register(chip->card) < 0) { if (snd_card_register(chip->card) < 0) {
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define USB_SUBCLASS_AUDIO_CONTROL 0x01 #define USB_SUBCLASS_AUDIO_CONTROL 0x01
#define USB_SUBCLASS_AUDIO_STREAMING 0x02 #define USB_SUBCLASS_AUDIO_STREAMING 0x02
#define USB_SUBCLASS_MIDI_STREAMING 0x03
#define USB_DT_CS_DEVICE 0x21 #define USB_DT_CS_DEVICE 0x21
#define USB_DT_CS_CONFIG 0x22 #define USB_DT_CS_CONFIG 0x22
...@@ -56,6 +57,8 @@ ...@@ -56,6 +57,8 @@
#define EP_GENERAL 0x01 #define EP_GENERAL 0x01
#define MS_GENERAL 0x01
/* endpoint attributes */ /* endpoint attributes */
#define EP_ATTR_MASK 0x0c #define EP_ATTR_MASK 0x0c
#define EP_ATTR_ASYNC 0x04 #define EP_ATTR_ASYNC 0x04
...@@ -115,6 +118,11 @@ ...@@ -115,6 +118,11 @@
#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS 0x2006 #define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS 0x2006
/* maximum number of endpoints per interface */
#define MIDI_MAX_ENDPOINTS 2
#define SNDRV_SEQ_DEV_ID_USBMIDI "usb-midi"
/* /*
*/ */
...@@ -130,6 +138,51 @@ struct snd_usb_audio { ...@@ -130,6 +138,51 @@ struct snd_usb_audio {
struct list_head pcm_list; /* list of pcm streams */ struct list_head pcm_list; /* list of pcm streams */
int pcm_devs; int pcm_devs;
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
int next_seq_device;
#endif
};
/*
* Information about devices with broken descriptors
*/
typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
struct snd_usb_audio_quirk {
const char *vendor_name;
const char *product_name;
int ifnum;
/* MIDI specific */
struct snd_usb_midi_endpoint_info {
int16_t epnum; /* ep number, -1 autodetect */
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
} endpoints[MIDI_MAX_ENDPOINTS];
};
/*
* USB MIDI sequencer device data
*/
typedef struct snd_usb_midi snd_usb_midi_t;
typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t;
typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t;
typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;
struct snd_usb_midi {
/* filled by usbaudio.c */
snd_usb_audio_t *chip;
int ifnum;
const snd_usb_audio_quirk_t *quirk;
/* used internally in usbmidi.c */
int seq_client;
struct snd_usb_midi_endpoint {
snd_usb_midi_out_endpoint_t *out;
snd_usb_midi_in_endpoint_t *in;
snd_rawmidi_t *rmidi[0x10];
} endpoints[MIDI_MAX_ENDPOINTS];
}; };
......
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/semaphore.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/minors.h> #include <sound/minors.h>
#include <sound/asequencer.h> #include <sound/asequencer.h>
...@@ -48,59 +47,13 @@ ...@@ -48,59 +47,13 @@
#include <sound/seq_kernel.h> #include <sound/seq_kernel.h>
#include <sound/seq_virmidi.h> #include <sound/seq_virmidi.h>
#include <sound/seq_midi_event.h> #include <sound/seq_midi_event.h>
#define SNDRV_GET_ID
#include <sound/initval.h> #include <sound/initval.h>
#include "usbaudio.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("USB MIDI"); MODULE_DESCRIPTION("USB MIDI");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");
MODULE_CLASSES("{sound}"); MODULE_CLASSES("{sound}");
MODULE_DEVICES("{{Generic,USB MIDI},"
"{Roland/EDIROL,PC-300},"
"{Roland/EDIROL,SC-8820},"
"{Roland/EDIROL,SC-8850},"
"{Roland/EDIROL,SC-D70},"
"{Roland/EDIROL,SD-20},"
"{Roland/EDIROL,SD-80},"
"{Roland/EDIROL,SD-90},"
"{Roland/EDIROL,SK-500},"
"{Roland/EDIROL,U-8},"
"{Roland/EDIROL,UA-100(G)},"
"{Roland/EDIROL,UA-700},"
"{Roland/EDIROL,UM-1(S)},"
"{Roland/EDIROL,UM-2(E)},"
"{Roland/EDIROL,UM-4},"
"{Roland/EDIROL,UM-550},"
"{Roland/EDIROL,UM-880},"
"{Roland/EDIROL,XV-5050},"
"{Yamaha,MU1000},"
"{Yamaha,UX256}}");
static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int snd_vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor id of this card */
static int snd_pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product id of this card */
static int snd_int_transfer[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Use interrupt transfers for this card */
MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(snd_index, "Index value for USB MIDI.");
MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
MODULE_PARM_DESC(snd_id, "ID string for USB MIDI.");
MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(snd_enable, "Enable USB MIDI.");
MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
MODULE_PARM(snd_vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(snd_vid, "USB Vendor ID for USB MIDI.");
MODULE_PARM_SYNTAX(snd_vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
MODULE_PARM(snd_pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(snd_pid, "USB Product ID for USB MIDI.");
MODULE_PARM_SYNTAX(snd_pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
MODULE_PARM(snd_int_transfer, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(snd_int_transfer, "Use interrupt transfers for USB MIDI input.");
MODULE_PARM_SYNTAX(snd_int_transfer, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC ",skill:advanced");
/* size of the per-endpoint output buffer, must be a multiple of 4 */ /* size of the per-endpoint output buffer, must be a multiple of 4 */
#define OUTPUT_BUFFER_SIZE 0x400 #define OUTPUT_BUFFER_SIZE 0x400
...@@ -108,26 +61,6 @@ MODULE_PARM_SYNTAX(snd_int_transfer, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC ...@@ -108,26 +61,6 @@ MODULE_PARM_SYNTAX(snd_int_transfer, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC
/* max. size of incoming sysex messages */ /* max. size of incoming sysex messages */
#define INPUT_BUFFER_SIZE 0x200 #define INPUT_BUFFER_SIZE 0x200
#define MAX_ENDPOINTS 2
#define SNDRV_SEQ_DEV_ID_USBMIDI "usb-midi"
#ifndef USB_SUBCLASS_MIDISTREAMING
#define USB_SUBCLASS_MIDISTREAMING 3
#endif
#ifndef USB_DT_CS_INTERFACE
#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE)
#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT)
#endif
#ifndef USB_DST_MS_HEADER
#define USB_DST_MS_HEADER 0x01
#define USB_DST_MS_GENERAL 0x01
#define USB_DST_MS_HEADER_SIZE 7
#define USB_DST_MS_GENERAL_SIZE 4
#endif
typedef struct usb_driver usb_driver_t; typedef struct usb_driver usb_driver_t;
typedef struct usb_device usb_device_t; typedef struct usb_device usb_device_t;
typedef struct usb_device_id usb_device_id_t; typedef struct usb_device_id usb_device_id_t;
...@@ -153,47 +86,11 @@ struct usb_ms_endpoint_descriptor { ...@@ -153,47 +86,11 @@ struct usb_ms_endpoint_descriptor {
__u8 baAssocJackID[0]; __u8 baAssocJackID[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
typedef struct usbmidi usbmidi_t;
typedef struct usbmidi_device_info usbmidi_device_info_t;
typedef struct usbmidi_endpoint_info usbmidi_endpoint_info_t;
typedef struct usbmidi_endpoint usbmidi_endpoint_t;
typedef struct usbmidi_out_endpoint usbmidi_out_endpoint_t;
typedef struct usbmidi_out_port usbmidi_out_port_t; typedef struct usbmidi_out_port usbmidi_out_port_t;
typedef struct usbmidi_in_endpoint usbmidi_in_endpoint_t;
typedef struct usbmidi_in_port usbmidi_in_port_t; typedef struct usbmidi_in_port usbmidi_in_port_t;
/* struct snd_usb_midi_out_endpoint {
* Describes the capabilities of a USB MIDI device. snd_usb_midi_t* umidi;
* This structure is filled after parsing the USB descriptors,
* or is supplied explicitly for broken devices.
*/
struct usbmidi_device_info {
char vendor[32]; /* vendor name */
char product[32]; /* device name */
int16_t ifnum; /* interface number */
struct usbmidi_endpoint_info {
int16_t epnum; /* endpoint number,
-1: autodetect (first ep only) */
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
} endpoints[MAX_ENDPOINTS];
};
struct usbmidi {
snd_card_t* card;
usb_device_t* usb_device;
int dev;
int seq_client;
usbmidi_device_info_t device_info;
struct usbmidi_endpoint {
usbmidi_out_endpoint_t* out;
usbmidi_in_endpoint_t* in;
snd_rawmidi_t* rmidi[0x10];
} endpoints[MAX_ENDPOINTS];
};
struct usbmidi_out_endpoint {
usbmidi_t* umidi;
struct urb* urb; struct urb* urb;
int max_transfer; /* size of urb buffer */ int max_transfer; /* size of urb buffer */
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
...@@ -204,16 +101,16 @@ struct usbmidi_out_endpoint { ...@@ -204,16 +101,16 @@ struct usbmidi_out_endpoint {
spinlock_t buffer_lock; spinlock_t buffer_lock;
struct usbmidi_out_port { struct usbmidi_out_port {
usbmidi_out_endpoint_t* ep; snd_usb_midi_out_endpoint_t* ep;
uint8_t cable; /* cable number << 4 */ uint8_t cable; /* cable number << 4 */
uint8_t sysex_len; uint8_t sysex_len;
uint8_t sysex[2]; uint8_t sysex[2];
} ports[0x10]; } ports[0x10];
}; };
struct usbmidi_in_endpoint { struct snd_usb_midi_in_endpoint {
usbmidi_t* umidi; snd_usb_midi_t* umidi;
usbmidi_endpoint_t* ep; snd_usb_midi_endpoint_t* ep;
struct urb* urb; struct urb* urb;
struct usbmidi_in_port { struct usbmidi_in_port {
int seq_port; int seq_port;
...@@ -221,10 +118,7 @@ struct usbmidi_in_endpoint { ...@@ -221,10 +118,7 @@ struct usbmidi_in_endpoint {
} ports[0x10]; } ports[0x10];
}; };
static int snd_usbmidi_card_used[SNDRV_CARDS]; static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep);
static DECLARE_MUTEX(snd_usbmidi_open_mutex);
static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep);
/* /*
* Submits the URB, with error handling. * Submits the URB, with error handling.
...@@ -255,7 +149,7 @@ static int snd_usbmidi_urb_error(int status) ...@@ -255,7 +149,7 @@ static int snd_usbmidi_urb_error(int status)
/* /*
* Converts a USB MIDI packet into an ALSA sequencer event. * Converts a USB MIDI packet into an ALSA sequencer event.
*/ */
static void snd_usbmidi_input_packet(usbmidi_in_endpoint_t* ep, static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
uint8_t packet[4]) uint8_t packet[4])
{ {
static const uint8_t cin_length[] = { static const uint8_t cin_length[] = {
...@@ -285,7 +179,7 @@ static void snd_usbmidi_input_packet(usbmidi_in_endpoint_t* ep, ...@@ -285,7 +179,7 @@ static void snd_usbmidi_input_packet(usbmidi_in_endpoint_t* ep,
*/ */
static void snd_usbmidi_in_urb_complete(struct urb* urb) static void snd_usbmidi_in_urb_complete(struct urb* urb)
{ {
usbmidi_in_endpoint_t* ep = snd_magic_cast(usbmidi_in_endpoint_t, urb->context, return); snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return);
if (urb->status == 0) { if (urb->status == 0) {
uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer; uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer;
...@@ -300,14 +194,14 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb) ...@@ -300,14 +194,14 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
} }
if (!usb_pipeint(urb->pipe)) { if (!usb_pipeint(urb->pipe)) {
urb->dev = ep->umidi->usb_device; urb->dev = ep->umidi->chip->dev;
snd_usbmidi_submit_urb(urb, GFP_ATOMIC); snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
} }
} }
static void snd_usbmidi_out_urb_complete(struct urb* urb) static void snd_usbmidi_out_urb_complete(struct urb* urb)
{ {
usbmidi_out_endpoint_t* ep = snd_magic_cast(usbmidi_out_endpoint_t, urb->context, return); snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return);
unsigned long flags; unsigned long flags;
if (urb->status < 0) { if (urb->status < 0) {
...@@ -324,7 +218,7 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb) ...@@ -324,7 +218,7 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
* (after the reception of one or more sequencer events, or after completion * (after the reception of one or more sequencer events, or after completion
* of the previous transfer). ep->buffer_lock must be held. * of the previous transfer). ep->buffer_lock must be held.
*/ */
static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep) static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep)
{ {
int len; int len;
uint8_t* buffer; uint8_t* buffer;
...@@ -361,14 +255,14 @@ static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep) ...@@ -361,14 +255,14 @@ static void snd_usbmidi_do_output(usbmidi_out_endpoint_t* ep)
} }
if (len > 0) { if (len > 0) {
ep->urb->dev = ep->umidi->usb_device; ep->urb->dev = ep->umidi->chip->dev;
snd_usbmidi_submit_urb(ep->urb, GFP_ATOMIC); snd_usbmidi_submit_urb(ep->urb, GFP_ATOMIC);
} }
} }
static void snd_usbmidi_out_tasklet(unsigned long data) static void snd_usbmidi_out_tasklet(unsigned long data)
{ {
usbmidi_out_endpoint_t* ep = snd_magic_cast(usbmidi_out_endpoint_t, (void*)data, return); snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, (void*)data, return);
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&ep->buffer_lock, flags); spin_lock_irqsave(&ep->buffer_lock, flags);
...@@ -382,7 +276,7 @@ static void snd_usbmidi_out_tasklet(unsigned long data) ...@@ -382,7 +276,7 @@ static void snd_usbmidi_out_tasklet(unsigned long data)
static void output_packet(usbmidi_out_port_t* port, static void output_packet(usbmidi_out_port_t* port,
uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3) uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3)
{ {
usbmidi_out_endpoint_t* ep = port->ep; snd_usb_midi_out_endpoint_t* ep = port->ep;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&ep->buffer_lock, flags); spin_lock_irqsave(&ep->buffer_lock, flags);
...@@ -568,7 +462,7 @@ static int snd_usbmidi_event_input(snd_seq_event_t* ev, int direct, ...@@ -568,7 +462,7 @@ static int snd_usbmidi_event_input(snd_seq_event_t* ev, int direct,
* Frees an input endpoint. * Frees an input endpoint.
* May be called when ep hasn't been initialized completely. * May be called when ep hasn't been initialized completely.
*/ */
static void snd_usbmidi_in_endpoint_delete(usbmidi_in_endpoint_t* ep) static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep)
{ {
int i; int i;
...@@ -586,53 +480,54 @@ static void snd_usbmidi_in_endpoint_delete(usbmidi_in_endpoint_t* ep) ...@@ -586,53 +480,54 @@ static void snd_usbmidi_in_endpoint_delete(usbmidi_in_endpoint_t* ep)
} }
/* /*
* Searches for an alternate setting in which the endpoint uses interrupt * For Roland devices, use the alternate setting which uses interrupt
* transfers for input. * transfers for input.
*/ */
static int snd_usbmidi_get_int_ep(usbmidi_t* umidi, uint8_t epnum, static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
usb_endpoint_descriptor_t** descriptor) uint8_t epnum)
{ {
usb_interface_t* intf; usb_interface_t* intf;
int i, j; usb_interface_descriptor_t* intfd;
*descriptor = NULL; if (umidi->chip->dev->descriptor.idVendor != 0x0582)
intf = usb_ifnum_to_if(umidi->usb_device, umidi->device_info.ifnum); return NULL;
if (!intf) intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
return -ENXIO; if (!intf || intf->num_altsetting != 2)
for (i = 0; i < intf->num_altsetting; ++i) { return NULL;
usb_interface_descriptor_t* intfd = &intf->altsetting[i];
for (j = 0; j < intfd->bNumEndpoints; ++j) { intfd = &intf->altsetting[0];
usb_endpoint_descriptor_t* epd = &intfd->endpoint[j]; if (intfd->bNumEndpoints != 2 ||
if ((epd->bEndpointAddress & (USB_ENDPOINT_NUMBER_MASK | USB_ENDPOINT_DIR_MASK)) == (epnum | USB_DIR_IN) && (intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
(epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { (intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
usb_set_interface(umidi->usb_device, return NULL;
intfd->bInterfaceNumber,
intfd = &intf->altsetting[1];
if (intfd->bNumEndpoints != 2 ||
(intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
(intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
return NULL;
usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber,
intfd->bAlternateSetting); intfd->bAlternateSetting);
*descriptor = &intfd->endpoint[j]; return &intfd->endpoint[1];
return 0;
}
}
}
return -ENXIO;
} }
/* /*
* Creates an input endpoint, and initalizes input ports. * Creates an input endpoint, and initalizes input ports.
* ALSA ports are created later. * ALSA ports are created later.
*/ */
static int snd_usbmidi_in_endpoint_create(usbmidi_t* umidi, static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi,
usbmidi_endpoint_info_t* ep_info, snd_usb_midi_endpoint_info_t* ep_info,
usbmidi_endpoint_t* rep) snd_usb_midi_endpoint_t* rep)
{ {
usbmidi_in_endpoint_t* ep; snd_usb_midi_in_endpoint_t* ep;
int do_int_transfer; usb_endpoint_descriptor_t* int_epd;
usb_endpoint_descriptor_t* epd;
void* buffer; void* buffer;
unsigned int pipe; unsigned int pipe;
int length, i, err; int length, i, err;
rep->in = NULL; rep->in = NULL;
ep = snd_magic_kcalloc(usbmidi_in_endpoint_t, 0, GFP_KERNEL); ep = snd_magic_kcalloc(snd_usb_midi_in_endpoint_t, 0, GFP_KERNEL);
if (!ep) if (!ep)
return -ENOMEM; return -ENOMEM;
ep->umidi = umidi; ep->umidi = umidi;
...@@ -640,34 +535,28 @@ static int snd_usbmidi_in_endpoint_create(usbmidi_t* umidi, ...@@ -640,34 +535,28 @@ static int snd_usbmidi_in_endpoint_create(usbmidi_t* umidi,
for (i = 0; i < 0x10; ++i) for (i = 0; i < 0x10; ++i)
ep->ports[i].seq_port = -1; ep->ports[i].seq_port = -1;
do_int_transfer = snd_int_transfer[umidi->dev]; int_epd = snd_usbmidi_get_int_epd(umidi, ep_info->epnum);
if (do_int_transfer) {
if (snd_usbmidi_get_int_ep(umidi, ep_info->epnum, &epd) < 0) {
printk(KERN_WARNING "snd-usb-midi: interrupt endpoint not found\n");
do_int_transfer = 0;
}
}
ep->urb = usb_alloc_urb(0, GFP_KERNEL); ep->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ep->urb) { if (!ep->urb) {
snd_usbmidi_in_endpoint_delete(ep); snd_usbmidi_in_endpoint_delete(ep);
return -ENOMEM; return -ENOMEM;
} }
if (do_int_transfer) if (int_epd)
pipe = usb_rcvintpipe(umidi->usb_device, ep_info->epnum); pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->epnum);
else else
pipe = usb_rcvbulkpipe(umidi->usb_device, ep_info->epnum); pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->epnum);
length = usb_maxpacket(umidi->usb_device, pipe, 0); length = usb_maxpacket(umidi->chip->dev, pipe, 0);
buffer = kmalloc(length, GFP_KERNEL); buffer = kmalloc(length, GFP_KERNEL);
if (!buffer) { if (!buffer) {
snd_usbmidi_in_endpoint_delete(ep); snd_usbmidi_in_endpoint_delete(ep);
return -ENOMEM; return -ENOMEM;
} }
if (do_int_transfer) if (int_epd)
FILL_INT_URB(ep->urb, umidi->usb_device, pipe, buffer, length, FILL_INT_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
snd_usbmidi_in_urb_complete, ep, epd->bInterval); snd_usbmidi_in_urb_complete, ep, int_epd->bInterval);
else else
FILL_BULK_URB(ep->urb, umidi->usb_device, pipe, buffer, length, FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
snd_usbmidi_in_urb_complete, ep); snd_usbmidi_in_urb_complete, ep);
for (i = 0; i < 0x10; ++i) for (i = 0; i < 0x10; ++i)
...@@ -697,7 +586,7 @@ static int snd_usbmidi_count_bits(uint16_t x) ...@@ -697,7 +586,7 @@ static int snd_usbmidi_count_bits(uint16_t x)
* Frees an output endpoint. * Frees an output endpoint.
* May be called when ep hasn't been initialized completely. * May be called when ep hasn't been initialized completely.
*/ */
static void snd_usbmidi_out_endpoint_delete(usbmidi_out_endpoint_t* ep) static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep)
{ {
if (ep->tasklet.func) if (ep->tasklet.func)
tasklet_kill(&ep->tasklet); tasklet_kill(&ep->tasklet);
...@@ -715,17 +604,17 @@ static void snd_usbmidi_out_endpoint_delete(usbmidi_out_endpoint_t* ep) ...@@ -715,17 +604,17 @@ static void snd_usbmidi_out_endpoint_delete(usbmidi_out_endpoint_t* ep)
* Creates an output endpoint, and initializes output ports. * Creates an output endpoint, and initializes output ports.
* ALSA ports are created later. * ALSA ports are created later.
*/ */
static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi, static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi,
usbmidi_endpoint_info_t* ep_info, snd_usb_midi_endpoint_info_t* ep_info,
usbmidi_endpoint_t* rep) snd_usb_midi_endpoint_t* rep)
{ {
usbmidi_out_endpoint_t* ep; snd_usb_midi_out_endpoint_t* ep;
int i; int i;
unsigned int pipe; unsigned int pipe;
void* buffer; void* buffer;
rep->out = NULL; rep->out = NULL;
ep = snd_magic_kcalloc(usbmidi_out_endpoint_t, 0, GFP_KERNEL); ep = snd_magic_kcalloc(snd_usb_midi_out_endpoint_t, 0, GFP_KERNEL);
if (!ep) if (!ep)
return -ENOMEM; return -ENOMEM;
ep->umidi = umidi; ep->umidi = umidi;
...@@ -735,14 +624,14 @@ static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi, ...@@ -735,14 +624,14 @@ static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi,
snd_usbmidi_out_endpoint_delete(ep); snd_usbmidi_out_endpoint_delete(ep);
return -ENOMEM; return -ENOMEM;
} }
pipe = usb_sndbulkpipe(umidi->usb_device, ep_info->epnum); pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->epnum);
ep->max_transfer = usb_maxpacket(umidi->usb_device, pipe, 1) & ~3; ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1) & ~3;
buffer = kmalloc(ep->max_transfer, GFP_KERNEL); buffer = kmalloc(ep->max_transfer, GFP_KERNEL);
if (!buffer) { if (!buffer) {
snd_usbmidi_out_endpoint_delete(ep); snd_usbmidi_out_endpoint_delete(ep);
return -ENOMEM; return -ENOMEM;
} }
FILL_BULK_URB(ep->urb, umidi->usb_device, pipe, buffer, FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer,
ep->max_transfer, snd_usbmidi_out_urb_complete, ep); ep->max_transfer, snd_usbmidi_out_urb_complete, ep);
spin_lock_init(&ep->buffer_lock); spin_lock_init(&ep->buffer_lock);
...@@ -763,17 +652,17 @@ static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi, ...@@ -763,17 +652,17 @@ static int snd_usbmidi_out_endpoint_create(usbmidi_t* umidi,
*/ */
static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device) static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
{ {
usbmidi_t* umidi; snd_usb_midi_t* umidi;
int i, j; int i, j;
umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device); umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
if (umidi->seq_client >= 0) { if (umidi->seq_client >= 0) {
snd_seq_delete_kernel_client(umidi->seq_client); snd_seq_delete_kernel_client(umidi->seq_client);
umidi->seq_client = -1; umidi->seq_client = -1;
} }
for (i = 0; i < MAX_ENDPOINTS; ++i) { for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
usbmidi_endpoint_t* ep = &umidi->endpoints[i]; snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i];
if (ep->out) { if (ep->out) {
snd_usbmidi_out_endpoint_delete(ep->out); snd_usbmidi_out_endpoint_delete(ep->out);
ep->out = NULL; ep->out = NULL;
...@@ -784,7 +673,7 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device) ...@@ -784,7 +673,7 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
} }
for (j = 0; j < 0x10; ++j) for (j = 0; j < 0x10; ++j)
if (ep->rmidi[j]) { if (ep->rmidi[j]) {
snd_device_free(umidi->card, ep->rmidi[j]); snd_device_free(umidi->chip->card, ep->rmidi[j]);
ep->rmidi[j] = NULL; ep->rmidi[j] = NULL;
} }
} }
...@@ -796,10 +685,9 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device) ...@@ -796,10 +685,9 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
* the ALSA port for each input/output port pair in the endpoint. * the ALSA port for each input/output port pair in the endpoint.
* *port_idx is the port number, which must be unique over all endpoints. * *port_idx is the port number, which must be unique over all endpoints.
*/ */
static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep, static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi, int ep, int* port_idx,
int* port_idx) snd_usb_midi_endpoint_info_t* ep_info)
{ {
usbmidi_endpoint_info_t* ep_info = &umidi->device_info.endpoints[ep];
int c, err; int c, err;
int cap, type, port; int cap, type, port;
int out, in; int out, in;
...@@ -830,8 +718,8 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep, ...@@ -830,8 +718,8 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep,
/* TODO: read type bits from element descriptor */ /* TODO: read type bits from element descriptor */
type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
/* TODO: read port name from jack descriptor */ /* TODO: read port name from jack descriptor */
sprintf(port_name, "%s Port %d", snprintf(port_name, sizeof(port_name), "%s Port %d",
umidi->device_info.product, *port_idx); umidi->chip->card->shortname, *port_idx);
port = snd_seq_event_port_attach(umidi->seq_client, port = snd_seq_event_port_attach(umidi->seq_client,
&port_callback, &port_callback,
cap, type, port_name); cap, type, port_name);
...@@ -845,7 +733,7 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep, ...@@ -845,7 +733,7 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep,
if (*port_idx < SNDRV_MINOR_RAWMIDIS) { if (*port_idx < SNDRV_MINOR_RAWMIDIS) {
snd_rawmidi_t *rmidi; snd_rawmidi_t *rmidi;
snd_virmidi_dev_t *rdev; snd_virmidi_dev_t *rdev;
err = snd_virmidi_new(umidi->card, *port_idx, &rmidi); err = snd_virmidi_new(umidi->chip->card, *port_idx, &rmidi);
if (err < 0) if (err < 0)
return err; return err;
rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
...@@ -853,9 +741,9 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep, ...@@ -853,9 +741,9 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep,
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
rdev->client = umidi->seq_client; rdev->client = umidi->seq_client;
rdev->port = port; rdev->port = port;
err = snd_device_register(umidi->card, rmidi); err = snd_device_register(umidi->chip->card, rmidi);
if (err < 0) { if (err < 0) {
snd_device_free(umidi->card, rmidi); snd_device_free(umidi->chip->card, rmidi);
return err; return err;
} }
umidi->endpoints[ep].rmidi[c] = rmidi; umidi->endpoints[ep].rmidi[c] = rmidi;
...@@ -866,219 +754,45 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep, ...@@ -866,219 +754,45 @@ static int snd_usbmidi_create_endpoint_ports(usbmidi_t* umidi, int ep,
} }
/* /*
* Create the endpoints and their ports. * Creates the endpoints and their ports.
*/ */
static int snd_usbmidi_create_endpoints(usbmidi_t* umidi) static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi,
snd_usb_midi_endpoint_info_t* endpoints)
{ {
int i, err, port_idx = 0; int i, err, port_idx = 0;
for (i = 0; i < MAX_ENDPOINTS; ++i) { for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
usbmidi_endpoint_info_t* ep_info = &umidi->device_info.endpoints[i]; if (!endpoints[i].epnum)
if (!ep_info->epnum)
continue; continue;
if (ep_info->out_cables) { if (endpoints[i].out_cables) {
err = snd_usbmidi_out_endpoint_create(umidi, ep_info, err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i],
&umidi->endpoints[i]); &umidi->endpoints[i]);
if (err < 0) if (err < 0)
return err; return err;
} }
if (ep_info->in_cables) { if (endpoints[i].in_cables) {
err = snd_usbmidi_in_endpoint_create(umidi, ep_info, err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i],
&umidi->endpoints[i]); &umidi->endpoints[i]);
if (err < 0) if (err < 0)
return err; return err;
} }
err = snd_usbmidi_create_endpoint_ports(umidi, i, &port_idx); err = snd_usbmidi_create_endpoint_ports(umidi, i, &port_idx,
&endpoints[i]);
if (err < 0) if (err < 0)
return err; return err;
printk(KERN_INFO "snd-usb-midi: endpoint %d: created %d output and %d input ports\n", printk(KERN_INFO "snd-usb-midi: endpoint %d: created %d output and %d input ports\n",
ep_info->epnum, endpoints[i].epnum,
snd_usbmidi_count_bits(ep_info->out_cables), snd_usbmidi_count_bits(endpoints[i].out_cables),
snd_usbmidi_count_bits(ep_info->in_cables)); snd_usbmidi_count_bits(endpoints[i].in_cables));
} }
return 0; return 0;
} }
/* /*
* Initialize the sequencer device. * Returns MIDIStreaming device capabilities.
*/ */
static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device) static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi,
{ snd_usb_midi_endpoint_info_t* endpoints)
usbmidi_t* umidi;
snd_seq_client_callback_t client_callback;
snd_seq_client_info_t client_info;
int i, err;
umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
memset(&client_callback, 0, sizeof(client_callback));
client_callback.allow_output = 1;
client_callback.allow_input = 1;
umidi->seq_client = snd_seq_create_kernel_client(umidi->card, 0,
&client_callback);
if (umidi->seq_client < 0)
return umidi->seq_client;
memset(&client_info, 0, sizeof(client_info));
client_info.client = umidi->seq_client;
client_info.type = KERNEL_CLIENT;
sprintf(client_info.name, "%s %s",
umidi->device_info.vendor, umidi->device_info.product);
snd_seq_kernel_client_ctl(umidi->seq_client,
SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
&client_info);
err = snd_usbmidi_create_endpoints(umidi);
if (err < 0) {
snd_usbmidi_seq_device_delete(seq_device);
return err;
}
for (i = 0; i < MAX_ENDPOINTS; ++i)
if (umidi->endpoints[i].in)
snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb,
GFP_KERNEL);
return 0;
}
static int snd_usbmidi_card_create(usb_device_t* usb_device,
usbmidi_device_info_t* device_info,
snd_card_t** rcard)
{
snd_card_t* card;
snd_seq_device_t* seq_device;
usbmidi_t* umidi;
int dev, err;
if (rcard)
*rcard = NULL;
down(&snd_usbmidi_open_mutex);
for (dev = 0; dev < SNDRV_CARDS; ++dev) {
if (snd_enable[dev] && !snd_usbmidi_card_used[dev] &&
(snd_vid[dev] == -1 ||
snd_vid[dev] == usb_device->descriptor.idVendor) &&
(snd_pid[dev] == -1 ||
snd_pid[dev] == usb_device->descriptor.idProduct))
break;
}
if (dev >= SNDRV_CARDS) {
up(&snd_usbmidi_open_mutex);
return -ENOENT;
}
card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
if (!card) {
up(&snd_usbmidi_open_mutex);
return -ENOMEM;
}
strcpy(card->driver, "USB MIDI");
snprintf(card->shortname, sizeof(card->shortname), "%s %s",
device_info->vendor, device_info->product);
snprintf(card->longname, sizeof(card->longname), "%s %s at %03d/%03d if %d",
device_info->vendor, device_info->product,
usb_device->bus->busnum, usb_device->devnum,
device_info->ifnum);
card->private_data = (void*)dev;
err = snd_seq_device_new(card, 0, SNDRV_SEQ_DEV_ID_USBMIDI,
sizeof(usbmidi_t), &seq_device);
if (err < 0) {
snd_card_free(card);
up(&snd_usbmidi_open_mutex);
return err;
}
strcpy(seq_device->name, card->shortname);
umidi = (usbmidi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
umidi->card = card;
umidi->usb_device = usb_device;
umidi->dev = dev;
umidi->seq_client = -1;
umidi->device_info = *device_info;
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
up(&snd_usbmidi_open_mutex);
return err;
}
snd_usbmidi_card_used[dev] = 1;
up(&snd_usbmidi_open_mutex);
if (rcard)
*rcard = card;
return 0;
}
/*
* If the first endpoint isn't specified, use the first endpoint in the
* first alternate setting of the interface.
*/
static int snd_usbmidi_detect_endpoint(usb_device_t* usb_device,
usbmidi_device_info_t* device_info)
{
usb_interface_t* intf;
usb_interface_descriptor_t* intfd;
usb_endpoint_descriptor_t* epd;
if (device_info->endpoints[0].epnum == -1) {
intf = usb_ifnum_to_if(usb_device, device_info->ifnum);
if (!intf || intf->num_altsetting < 1)
return -ENOENT;
intfd = intf->altsetting;
if (intfd->bNumEndpoints < 1)
return -ENOENT;
epd = intfd->endpoint;
device_info->endpoints[0].epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
}
return 0;
}
/*
* Searches for the alternate setting with the greatest number of bulk transfer
* endpoints.
*/
static usb_interface_descriptor_t* snd_usbmidi_get_altsetting(usb_device_t* usb_device,
usb_interface_t* intf)
{
int i, best = -1;
int best_out = 0, best_in = 0;
usb_interface_descriptor_t* intfd;
if (intf->num_altsetting == 1)
return &intf->altsetting[0];
for (i = 0; i < intf->num_altsetting; ++i) {
int out = 0, in = 0, j;
for (j = 0; j < intf->altsetting[i].bNumEndpoints; ++j) {
usb_endpoint_descriptor_t* ep = &intf->altsetting[i].endpoint[j];
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
if (ep->bEndpointAddress & USB_DIR_IN)
++in;
else
++out;
}
}
if ((out >= best_out && in >= best_in) &&
(out > best_out || in > best_in)) {
best_out = out;
best_in = in;
best = i;
}
}
if (best < 0)
return NULL;
intfd = &intf->altsetting[best];
usb_set_interface(usb_device, intfd->bInterfaceNumber, intfd->bAlternateSetting);
return intfd;
}
/*
* Returns MIDIStreaming device capabilities in device_info.
*/
static int snd_usbmidi_get_ms_info(usb_device_t* usb_device,
unsigned int ifnum,
usbmidi_device_info_t* device_info)
{ {
usb_interface_t* intf; usb_interface_t* intf;
usb_interface_descriptor_t* intfd; usb_interface_descriptor_t* intfd;
...@@ -1087,34 +801,17 @@ static int snd_usbmidi_get_ms_info(usb_device_t* usb_device, ...@@ -1087,34 +801,17 @@ static int snd_usbmidi_get_ms_info(usb_device_t* usb_device,
usb_ms_endpoint_descriptor_t* ms_ep; usb_ms_endpoint_descriptor_t* ms_ep;
int i, epidx; int i, epidx;
memset(device_info, 0, sizeof(*device_info)); memset(endpoints, 0, sizeof(*endpoints) * MIDI_MAX_ENDPOINTS);
if (usb_device->descriptor.iManufacturer == 0 ||
usb_string(usb_device, usb_device->descriptor.iManufacturer,
device_info->vendor, sizeof(device_info->vendor)) < 0)
sprintf(device_info->vendor, "Unknown Vendor %x", usb_device->descriptor.idVendor);
if (usb_device->descriptor.iProduct == 0 ||
usb_string(usb_device, usb_device->descriptor.iProduct,
device_info->product, sizeof(device_info->product)) < 0)
sprintf(device_info->product, "Unknown Device %x", usb_device->descriptor.idProduct);
intf = usb_ifnum_to_if(usb_device, ifnum); intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
if (!intf) if (!intf)
return -ENXIO; return -ENXIO;
device_info->ifnum = ifnum; intfd = &intf->altsetting[0];
printk(KERN_INFO "snd-usb-midi: using interface %d\n",
intf->altsetting[0].bInterfaceNumber);
intfd = snd_usbmidi_get_altsetting(usb_device, intf);
if (!intfd) {
printk(KERN_ERR "snd-usb-midi: could not determine altsetting\n");
return -ENXIO;
}
ms_header = (usb_ms_header_descriptor_t*)intfd->extra; ms_header = (usb_ms_header_descriptor_t*)intfd->extra;
if (intfd->extralen >= USB_DST_MS_HEADER_SIZE && if (intfd->extralen >= 7 &&
ms_header->bLength >= USB_DST_MS_HEADER_SIZE && ms_header->bLength >= 7 &&
ms_header->bDescriptorType == USB_DT_CS_INTERFACE && ms_header->bDescriptorType == USB_DT_CS_INTERFACE &&
ms_header->bDescriptorSubtype == USB_DST_MS_HEADER) ms_header->bDescriptorSubtype == HEADER)
printk(KERN_INFO "snd-usb-midi: MIDIStreaming version %02x.%02x\n", printk(KERN_INFO "snd-usb-midi: MIDIStreaming version %02x.%02x\n",
ms_header->bcdMSC[1], ms_header->bcdMSC[0]); ms_header->bcdMSC[1], ms_header->bcdMSC[0]);
else else
...@@ -1126,242 +823,124 @@ static int snd_usbmidi_get_ms_info(usb_device_t* usb_device, ...@@ -1126,242 +823,124 @@ static int snd_usbmidi_get_ms_info(usb_device_t* usb_device,
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
continue; continue;
ms_ep = (usb_ms_endpoint_descriptor_t*)ep->extra; ms_ep = (usb_ms_endpoint_descriptor_t*)ep->extra;
if (ep->extralen < USB_DST_MS_GENERAL_SIZE || if (ep->extralen < 4 ||
ms_ep->bLength < USB_DST_MS_GENERAL_SIZE || ms_ep->bLength < 4 ||
ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT || ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT ||
ms_ep->bDescriptorSubtype != USB_DST_MS_GENERAL) ms_ep->bDescriptorSubtype != MS_GENERAL)
continue; continue;
if (device_info->endpoints[epidx].epnum != 0 && if (endpoints[epidx].epnum != 0 &&
device_info->endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) { endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) {
++epidx; ++epidx;
if (epidx >= MAX_ENDPOINTS) { if (epidx >= MIDI_MAX_ENDPOINTS) {
printk(KERN_WARNING "snd-usb-midi: too many endpoints\n"); printk(KERN_WARNING "snd-usb-midi: too many endpoints\n");
break; break;
} }
} }
device_info->endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if (ep->bEndpointAddress & USB_DIR_IN) { if (ep->bEndpointAddress & USB_DIR_IN) {
device_info->endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
} else { } else {
device_info->endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1; endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
} }
printk(KERN_INFO "snd-usb-midi: detected %d %s jack(s) on endpoint %d\n", printk(KERN_INFO "snd-usb-midi: detected %d %s jack(s) on endpoint %d\n",
ms_ep->bNumEmbMIDIJack, ms_ep->bNumEmbMIDIJack,
ep->bEndpointAddress & USB_DIR_IN ? "input" : "output", ep->bEndpointAddress & USB_DIR_IN ? "input" : "output",
device_info->endpoints[epidx].epnum); endpoints[epidx].epnum);
} }
return 0; return 0;
} }
/* /*
* Returns device capabilities, either explicitly supplied or from the * If the first endpoint isn't specified, use the first endpoint in the
* class-specific descriptors. * first alternate setting of the interface.
*/
static int snd_usbmidi_get_device_info(usb_device_t* usb_device,
unsigned int ifnum,
const usb_device_id_t* usb_device_id,
usbmidi_device_info_t* device_info)
{
if (usb_device_id->driver_info) {
usbmidi_device_info_t* id_info = (usbmidi_device_info_t*)usb_device_id->driver_info;
if (ifnum != id_info->ifnum)
return -ENXIO;
*device_info = *id_info;
if (snd_usbmidi_detect_endpoint(usb_device, device_info) < 0)
return -ENXIO;
} else {
if (snd_usbmidi_get_ms_info(usb_device, ifnum, device_info) < 0)
return -ENXIO;
}
return 0;
}
/*
* Probes for a supported device.
*/ */
static void* snd_usbmidi_usb_probe(usb_device_t* device, static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi,
unsigned int ifnum, snd_usb_midi_endpoint_info_t* endpoint)
const usb_device_id_t* device_id)
{ {
usbmidi_device_info_t device_info; usb_interface_t* intf;
snd_card_t* card = NULL; usb_interface_descriptor_t* intfd;
int err; usb_endpoint_descriptor_t* epd;
if (snd_usbmidi_get_device_info(device, ifnum, device_id, if (endpoint->epnum == -1) {
&device_info) == 0) { intf = usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
printk(KERN_INFO "snd-usb-midi: detected %s %s\n", if (!intf || intf->num_altsetting < 1)
device_info.vendor, device_info.product); return -ENOENT;
err = snd_usbmidi_card_create(device, &device_info, &card); intfd = intf->altsetting;
if (err < 0) if (intfd->bNumEndpoints < 1)
snd_printk(KERN_ERR "cannot create card (error code %d)\n", err); return -ENOENT;
epd = intfd->endpoint;
endpoint->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
} }
return card; return 0;
} }
/* /*
* Frees the device. * Initialize the sequencer device.
*/ */
static void snd_usbmidi_usb_disconnect(usb_device_t* usb_device, void* ptr) static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
{ {
snd_card_t* card = (snd_card_t*)ptr; snd_usb_midi_t* umidi;
int dev = (int)card->private_data; usb_device_t* dev;
snd_seq_client_callback_t client_callback;
snd_card_free(card); snd_seq_client_info_t client_info;
down(&snd_usbmidi_open_mutex); snd_usb_midi_endpoint_info_t endpoints[MIDI_MAX_ENDPOINTS];
snd_usbmidi_card_used[dev] = 0; int i, err;
up(&snd_usbmidi_open_mutex);
}
/* umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
* Information about devices with broken descriptors.
*/
static usbmidi_device_info_t snd_usbmidi_yamaha_ux256_info = { memset(&client_callback, 0, sizeof(client_callback));
/* from NetBSD's umidi driver */ client_callback.allow_output = 1;
.vendor = "Yamaha", .product = "UX256", client_callback.allow_input = 1;
.ifnum = 0, umidi->seq_client = snd_seq_create_kernel_client(umidi->chip->card, 0,
.endpoints = {{ -1, 0xffff, 0x00ff }} &client_callback);
}; if (umidi->seq_client < 0)
static usbmidi_device_info_t snd_usbmidi_yamaha_mu1000_info = { return umidi->seq_client;
/* from Nagano Daisuke's usb-midi driver */
.vendor = "Yamaha", .product = "MU1000",
.ifnum = 0,
.endpoints = {{ 1, 0x000f, 0x0001 }}
};
/*
* There ain't no such thing as a standard-compliant Roland device.
* Apparently, Roland decided not to risk to have wrong entries in the USB
* descriptors. The consequence is that class-specific descriptors are
* conspicuous by their absence.
*
* And now you may guess which company was responsible for writing the
* USB Device Class Definition for MIDI Devices.
*/
static usbmidi_device_info_t snd_usbmidi_roland_ua100_info = {
.vendor = "Roland", .product = "UA-100",
.ifnum = 2,
.endpoints = {{ -1, 0x0007, 0x0007 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_um4_info = {
.vendor = "EDIROL", .product = "UM-4",
.ifnum = 2,
.endpoints = {{ -1, 0x000f, 0x000f }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sc8850_info = {
.vendor = "Roland", .product = "SC-8850",
.ifnum = 2,
.endpoints = {{ -1, 0x003f, 0x003f }}
};
static usbmidi_device_info_t snd_usbmidi_roland_u8_info = {
.vendor = "Roland", .product = "U-8",
.ifnum = 2,
.endpoints = {{ -1, 0x0003, 0x0003 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_um2_info = {
.vendor = "EDIROL", .product = "UM-2",
.ifnum = 2,
.endpoints = {{ -1, 0x0003, 0x0003 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sc8820_info = {
.vendor = "Roland", .product = "SC-8820",
.ifnum = 2,
.endpoints = {{ -1, 0x0013, 0x0013 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_pc300_info = {
.vendor = "Roland", .product = "PC-300",
.ifnum = 2,
.endpoints = {{ -1, 0x0001, 0x0001 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_um1_info = {
.vendor = "EDIROL", .product = "UM-1",
.ifnum = 2,
.endpoints = {{ -1, 0x0001, 0x0001 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sk500_info = {
.vendor = "Roland", .product = "SK-500",
.ifnum = 2,
.endpoints = {{ -1, 0x0013, 0x0013 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_scd70_info = {
.vendor = "Roland", .product = "SC-D70",
.ifnum = 2,
.endpoints = {{ -1, 0x0007, 0x0007 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_xv5050_info = {
.vendor = "Roland", .product = "XV-5050",
.ifnum = 0,
.endpoints = {{ -1, 0x0001, 0x0001 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_um880_info = {
.vendor = "EDIROL", .product = "UM-880",
.ifnum = 0,
.endpoints = {{ -1, 0x01ff, 0x01ff }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sd90_info = {
.vendor = "EDIROL", .product = "SD-90",
.ifnum = 2,
.endpoints = {{ -1, 0x000f, 0x000f }}
};
static usbmidi_device_info_t snd_usbmidi_roland_um550_info = {
.vendor = "EDIROL", .product = "UM-550",
.ifnum = 0,
.endpoints = {{ -1, 0x003f, 0x003f }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sd20_info = {
.vendor = "EDIROL", .product = "SD-20",
.ifnum = 0,
.endpoints = {{ -1, 0x0003, 0x0007 }}
};
static usbmidi_device_info_t snd_usbmidi_roland_sd80_info = {
.vendor = "EDIROL", .product = "SD-80",
.ifnum = 0,
.endpoints = {{ -1, 0x000f, 0x000f }}
};
static usbmidi_device_info_t snd_usbmidi_roland_ua700_info = {
.vendor = "EDIROL", .product = "UA-700",
.ifnum = 3,
.endpoints = {{ -1, 0x0003, 0x0003 }}
};
#define USBMIDI_NONCOMPLIANT_DEVICE(vid, pid, name) \ memset(&client_info, 0, sizeof(client_info));
USB_DEVICE(vid, pid), \ client_info.client = umidi->seq_client;
driver_info: (unsigned long)&snd_usbmidi_##name##_info client_info.type = KERNEL_CLIENT;
static usb_device_id_t snd_usbmidi_usb_id_table[] = { dev = umidi->chip->dev;
{ match_flags: USB_DEVICE_ID_MATCH_INT_CLASS | if (dev->descriptor.iProduct)
USB_DEVICE_ID_MATCH_INT_SUBCLASS, err = usb_string(dev, dev->descriptor.iProduct,
bInterfaceClass: USB_CLASS_AUDIO, client_info.name, sizeof(client_info.name));
bInterfaceSubClass: USB_SUBCLASS_MIDISTREAMING }, else
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0499, 0x1000, yamaha_ux256) }, err = 0;
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0499, 0x1001, yamaha_mu1000) }, if (err <= 0) {
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0000, roland_ua100) }, if (umidi->quirk && umidi->quirk->product_name) {
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0002, roland_um4) }, strncpy(client_info.name, umidi->quirk->product_name,
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0003, roland_sc8850) }, sizeof(client_info.name) - 1);
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0004, roland_u8) }, client_info.name[sizeof(client_info.name) - 1] = '\0';
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0005, roland_um2) }, } else {
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0007, roland_sc8820) }, sprintf(client_info.name, "USB Device %#04x:%#04x",
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0008, roland_pc300) }, dev->descriptor.idVendor, dev->descriptor.idProduct);
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0009, roland_um1) }, }
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x000b, roland_sk500) }, }
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x000c, roland_scd70) }, snd_seq_kernel_client_ctl(umidi->seq_client,
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0012, roland_xv5050) }, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0014, roland_um880) }, &client_info);
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0016, roland_sd90) },
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0023, roland_um550) },
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0027, roland_sd20) },
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x0029, roland_sd80) },
{ USBMIDI_NONCOMPLIANT_DEVICE(0x0582, 0x002b, roland_ua700) },
{ /* terminator */ }
};
MODULE_DEVICE_TABLE(usb, snd_usbmidi_usb_id_table); if (umidi->quirk) {
memcpy(endpoints, umidi->quirk->endpoints, sizeof(endpoints));
err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]);
} else {
err = snd_usbmidi_get_ms_info(umidi, endpoints);
}
if (err < 0) {
snd_usbmidi_seq_device_delete(seq_device);
return err;
}
err = snd_usbmidi_create_endpoints(umidi, endpoints);
if (err < 0) {
snd_usbmidi_seq_device_delete(seq_device);
return err;
}
static usb_driver_t snd_usbmidi_usb_driver = { for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
.name = "snd-usb-midi", if (umidi->endpoints[i].in)
.probe = snd_usbmidi_usb_probe, snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb,
.disconnect = snd_usbmidi_usb_disconnect, GFP_KERNEL);
.id_table = snd_usbmidi_usb_id_table, return 0;
.driver_list = LIST_HEAD_INIT(snd_usbmidi_usb_driver.driver_list) }
};
static int __init snd_usbmidi_module_init(void) static int __init snd_usbmidi_module_init(void)
{ {
...@@ -1369,51 +948,15 @@ static int __init snd_usbmidi_module_init(void) ...@@ -1369,51 +948,15 @@ static int __init snd_usbmidi_module_init(void)
snd_usbmidi_seq_device_new, snd_usbmidi_seq_device_new,
snd_usbmidi_seq_device_delete snd_usbmidi_seq_device_delete
}; };
int err;
err = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_USBMIDI, &ops, return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_USBMIDI, &ops,
sizeof(usbmidi_t)); sizeof(snd_usb_midi_t));
if (err < 0)
return err;
err = usb_register(&snd_usbmidi_usb_driver);
if (err < 0) {
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI);
return err;
}
return 0;
} }
static void __exit snd_usbmidi_module_exit(void) static void __exit snd_usbmidi_module_exit(void)
{ {
usb_deregister(&snd_usbmidi_usb_driver);
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI); snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI);
} }
module_init(snd_usbmidi_module_init) module_init(snd_usbmidi_module_init)
module_exit(snd_usbmidi_module_exit) module_exit(snd_usbmidi_module_exit)
#ifndef MODULE
/*
* format is snd-usb-midi=snd_enable,snd_index,snd_id,
* snd_vid,snd_pid,snd_int_transfer
*/
static int __init snd_usbmidi_module_setup(char* str)
{
static unsigned __initdata nr_dev = 0;
if (nr_dev >= SNDRV_CARDS)
return 0;
(void)(get_option(&str, &snd_enable[nr_dev]) == 2 &&
get_option(&str, &snd_index[nr_dev]) == 2 &&
get_id(&str, &snd_id[nr_dev]) == 2 &&
get_option(&str, &snd_vid[nr_dev]) == 2 &&
get_option(&str, &snd_pid[nr_dev]) == 2 &&
get_option(&str, &snd_int_transfer[nr_dev]) == 2);
++nr_dev;
return 1;
}
__setup("snd-usb-midi=", snd_usbmidi_module_setup);
#endif /* !MODULE */
/*
* ALSA USB Audio Driver
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>,
* Clemens Ladisch <clemens@ladisch.de>
*
*
* 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
*/
/*
* The contents of this file are part of the driver's id_table.
*
* In a perfect world, this file would be empty.
*/
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
{
/* from NetBSD's umidi driver */
USB_DEVICE(0x0499, 0x1000), /* Yamaha UX256 */
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0xffff,
.in_cables = 0x00ff
}
}
}
},
{
/* from Nagano Daisuke's usb-midi driver */
USB_DEVICE(0x0499, 0x1001), /* Yamaha MU1000 */
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.ifnum = 0,
.endpoints = {
{
.epnum = 1,
.out_cables = 0x000f,
.in_cables = 0x0001
}
}
}
},
/*
* I don't know whether the following Yamaha devices need entries or not:
* 0x1002 MU2000 0x1008 UX96
* 0x1003 MU500 0x1009 UX16
* 0x1004 UW500 0x100e S08
* 0x1005 MOTIF6 0x100f CLP-150
* 0x1006 MOTIF7 0x1010 CLP-170
* 0x1007 MOTIF8
*/
/*
* Once upon a time people thought, "Wouldn't it be nice if there was a
* standard for USB MIDI devices, so that device drivers would not be forced
* to know about the quirks of specific devices?" So Roland went ahead and
* wrote the USB Device Class Definition for MIDI Devices, and the USB-IF
* endorsed it, and now everybody designing USB MIDI devices does so in
* agreement with this standard (or at least tries to).
*
* And if you prefer a happy end, you can imagine that Roland devices set a
* good example. Instead of being completely fucked up due to the lack of
* class-specific descriptors.
*/
{
USB_DEVICE(0x0582, 0x0000),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "UA-100",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
}
}
},
{
USB_DEVICE(0x0582, 0x0002),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-4",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
}
},
{
USB_DEVICE(0x0582, 0x0003),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-8850",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
}
}
},
{
USB_DEVICE(0x0582, 0x0004),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "U-8",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
}
},
{
USB_DEVICE(0x0582, 0x0005),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-2",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
}
},
{
USB_DEVICE(0x0582, 0x0007),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-8820",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
}
}
},
{
USB_DEVICE(0x0582, 0x0008),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "PC-300",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
}
},
{
USB_DEVICE(0x0582, 0x0009),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-1",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
}
},
{
USB_DEVICE(0x0582, 0x000b),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SK-500",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
}
}
},
{
USB_DEVICE(0x0582, 0x000c),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-D70",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
}
}
},
{
USB_DEVICE(0x0582, 0x0012),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "XV-5050",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
}
},
{
USB_DEVICE(0x0582, 0x0014),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-880",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
}
}
},
{
USB_DEVICE(0x0582, 0x0016),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-90",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
}
},
{
USB_DEVICE(0x0582, 0x0023),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-550",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
}
}
},
{
USB_DEVICE(0x0582, 0x0027),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-20",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0007
}
}
}
},
{
USB_DEVICE(0x0582, 0x0029),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-80",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
}
},
{
USB_DEVICE(0x0582, 0x002b),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UA-700",
.ifnum = 3,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
}
},
#endif /* CONFIG_SND_SEQUENCER(_MODULE) */
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