MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
-#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
-#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
-#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
-#endif
-
/*
* CM8x38 registers definition
*/
#define CM_REG_FM_PCI 0x50
/*
- * for CMI-8338 .. this is not valid for CMI-8738.
+ * access from SB-mixer port
*/
#define CM_REG_EXTENT_IND 0xf0
#define CM_VPHONE_MASK 0xe0 /* Phone volume control (0-3) << 5 */
#define CM_VSPKM 0x08 /* Speaker mute control, default high */
#define CM_RLOOPREN 0x04 /* Rec. R-channel enable */
#define CM_RLOOPLEN 0x02 /* Rec. L-channel enable */
+#define CM_VADMIC3 0x01 /* Mic record boost */
/*
* CMI-8338 spec ver 0.5 (this is not valid for CMI-8738):
#define CM_EXTENT_SYNTH 0x4
-/*
- * pci ids
- */
-#ifndef PCI_VENDOR_ID_CMEDIA
-#define PCI_VENDOR_ID_CMEDIA 0x13F6
-#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
-#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
-#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
-#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
-#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
-#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
-#endif
-#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
-#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
-#endif
-
/*
* channels for playback / capture
*/
/* read/write operations for dword register */
-inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data)
+static inline void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data)
{
outl(data, cm->iobase + cmd);
}
-inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd)
+
+static inline unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd)
{
return inl(cm->iobase + cmd);
}
/* read/write operations for word register */
-inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data)
+static inline void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data)
{
outw(data, cm->iobase + cmd);
}
-inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd)
+
+static inline unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd)
{
return inw(cm->iobase + cmd);
}
/* read/write operations for byte register */
-inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data)
+static inline void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data)
{
outb(data, cm->iobase + cmd);
}
-inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd)
+static inline unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd)
{
return inb(cm->iobase + cmd);
}
/* bit operations for dword register */
-static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
{
- unsigned int val;
- val = inl(cm->iobase + cmd);
+ unsigned int val, oval;
+ val = oval = inl(cm->iobase + cmd);
val |= flag;
+ if (val == oval)
+ return 0;
outl(val, cm->iobase + cmd);
+ return 1;
}
-static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
{
- unsigned int val;
- val = inl(cm->iobase + cmd);
+ unsigned int val, oval;
+ val = oval = inl(cm->iobase + cmd);
val &= ~flag;
+ if (val == oval)
+ return 0;
outl(val, cm->iobase + cmd);
+ return 1;
}
-#if 0 // not used
/* bit operations for byte register */
-static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
{
- unsigned char val;
- val = inb(cm->iobase + cmd);
+ unsigned char val, oval;
+ val = oval = inb(cm->iobase + cmd);
val |= flag;
+ if (val == oval)
+ return 0;
outb(val, cm->iobase + cmd);
+ return 1;
}
-static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
{
- unsigned char val;
- val = inb(cm->iobase + cmd);
+ unsigned char val, oval;
+ val = oval = inb(cm->iobase + cmd);
val &= ~flag;
+ if (val == oval)
+ return 0;
outb(val, cm->iobase + cmd);
+ return 1;
}
-#endif
/*
static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
.info = snd_cmipci_spdif_mask_info,
.get = snd_cmipci_spdif_mask_get,
CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
- CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
+ CMIPCI_MIXER_SW_MONO("Mic Boost Playback Switch", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
+ CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
+ CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
+ CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+ CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
};
/*
DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
#endif
DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
-DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
-DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+// DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+// DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
// DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */
DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
}
+static int snd_cmipci_line_in_mode_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static inline unsigned int get_line_in_mode(cmipci_t *cm)
+{
+ unsigned int val;
+ if (cm->chip_version >= 39) {
+ val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL);
+ if (val & CM_LINE_AS_BASS)
+ return 2;
+ }
+ val = snd_cmipci_read_b(cm, CM_REG_MIXER1);
+ if (val & CM_SPK4)
+ return 1;
+ return 0;
+}
+
+static int snd_cmipci_line_in_mode_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&cm->reg_lock);
+ ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_line_in_mode_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ int change;
+
+ spin_lock_irq(&cm->reg_lock);
+ if (ucontrol->value.enumerated.item[0] == 2)
+ change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+ else
+ change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+ if (ucontrol->value.enumerated.item[0] == 1)
+ change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+ else
+ change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+ spin_unlock_irq(&cm->reg_lock);
+ return change;
+}
+
+static int snd_cmipci_mic_in_mode_info(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_info_t *uinfo)
+{
+ static char *texts[2] = { "Mic-In", "Center/LFE Output" };
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_cmipci_mic_in_mode_get(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ /* same bit as spdi_phase */
+ spin_lock_irq(&cm->reg_lock);
+ ucontrol->value.enumerated.item[0] =
+ (snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
+ spin_unlock_irq(&cm->reg_lock);
+ return 0;
+}
+
+static int snd_cmipci_mic_in_mode_put(snd_kcontrol_t *kcontrol,
+ snd_ctl_elem_value_t *ucontrol)
+{
+ cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+ int change;
+
+ spin_lock_irq(&cm->reg_lock);
+ if (ucontrol->value.enumerated.item[0])
+ change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+ else
+ change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+ spin_unlock_irq(&cm->reg_lock);
+ return change;
+}
+
/* both for CM8338/8738 */
static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = {
DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
- DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear),
+ {
+ .name = "Line-In Mode",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_cmipci_line_in_mode_info,
+ .get = snd_cmipci_line_in_mode_get,
+ .put = snd_cmipci_line_in_mode_put,
+ },
};
/* for non-multichannel chips */
/* only for model 039 or later */
static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
- DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
- DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
+ {
+ .name = "Mic-In Mode",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_cmipci_mic_in_mode_info,
+ .get = snd_cmipci_mic_in_mode_get,
+ .put = snd_cmipci_mic_in_mode_put,
+ }
};
/* card control switches */
if ((err = pci_enable_device(pci)) < 0)
return err;
- cm = kcalloc(1, sizeof(*cm), GFP_KERNEL);
+ cm = kzalloc(sizeof(*cm), GFP_KERNEL);
if (cm == NULL) {
pci_disable_device(pci);
return -ENOMEM;
static struct pci_driver driver = {
.name = "C-Media PCI",
+ .owner = THIS_MODULE,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
.remove = __devexit_p(snd_cmipci_remove),
static int __init alsa_card_cmipci_init(void)
{
- return pci_module_init(&driver);
+ return pci_register_driver(&driver);
}
static void __exit alsa_card_cmipci_exit(void)