* The low level mixer driver for the SoundBlaster Pro and SB16 cards.
* Copyright by Hannu Savolainen 1993
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* Added code to support the Sound Galaxy NX Pro mixer.
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO)
static int mixer_initialized
= 0;
static int supported_rec_devices
;
static int supported_devices
;
sb_setmixer (unsigned int port
, unsigned int value
)
OUTB ((unsigned char) (port
& 0xff), MIXER_ADDR
); /* Select register */
OUTB ((unsigned char) (value
& 0xff), MIXER_DATA
);
sb_getmixer (unsigned int port
)
OUTB ((unsigned char) (port
& 0xff), MIXER_ADDR
); /* Select register */
sb_mixer_set_stereo (int mode
)
sb_setmixer (OUT_FILTER
, ((sb_getmixer (OUT_FILTER
) & ~STEREO_DAC
)
| (mode
? STEREO_DAC
: MONO_DAC
)));
* 1 Only a plain Sound Blaster Pro style mixer detected.
* 2 The Sound Galaxy NX Pro mixer detected.
* Detect the mixer by changing parameters of two volume channels. If the
* values read back match with the values written, the mixer is there (is
sb_setmixer (FM_VOL
, 0xff);
sb_setmixer (VOC_VOL
, 0x33);
if (sb_getmixer (FM_VOL
) != 0xff)
if (sb_getmixer (VOC_VOL
) != 0x33)
/* Attempt to detect the SG NX Pro by check for valid bass/treble
oldbass
= sb_getmixer (BASS_LVL
);
oldtreble
= sb_getmixer (TREBLE_LVL
);
sb_setmixer (BASS_LVL
, 0xaa);
sb_setmixer (TREBLE_LVL
, 0x55);
if ((sb_getmixer (BASS_LVL
) != 0xaa) ||
(sb_getmixer (TREBLE_LVL
) != 0x55))
retcode
= 1; /* 1 == Only SB Pro detected */
retcode
= 2; /* 2 == SG NX Pro detected */
/* Restore register in either case since SG NX Pro has EEPROM with
* 'preferred' values stored.
sb_setmixer (BASS_LVL
, oldbass
);
sb_setmixer (TREBLE_LVL
, oldtreble
);
change_bits (unsigned char *regval
, int dev
, int chn
, int newval
)
mask
= (1 << (*iomap
)[dev
][chn
].nbits
) - 1;
newval
= ((newval
* mask
) + 50) / 100; /* Scale it */
shift
= (*iomap
)[dev
][chn
].bitoffs
- (*iomap
)[dev
][LEFT_CHN
].nbits
+ 1;
*regval
&= ~(mask
<< shift
); /* Filter out the previous value */
*regval
|= (newval
& mask
) << shift
; /* Set the new value */
if (!((1 << dev
) & supported_devices
))
return RET_ERROR (EINVAL
);
sb_mixer_set (int dev
, int value
)
int left
= value
& 0x000000ff;
int right
= (value
& 0x0000ff00) >> 8;
return RET_ERROR (EINVAL
);
if (!(supported_devices
& (1 << dev
))) /* Not supported */
return RET_ERROR (EINVAL
);
regoffs
= (*iomap
)[dev
][LEFT_CHN
].regno
;
return RET_ERROR (EINVAL
);
val
= sb_getmixer (regoffs
);
change_bits (&val
, dev
, LEFT_CHN
, left
);
levels
[dev
] = left
| (left
<< 8);
if ((*iomap
)[dev
][RIGHT_CHN
].regno
!= regoffs
) /* Change register */
sb_setmixer (regoffs
, val
); /* Save the old one */
regoffs
= (*iomap
)[dev
][RIGHT_CHN
].regno
;
return left
| (left
<< 8); /* Just left channel present */
val
= sb_getmixer (regoffs
); /* Read the new one */
change_bits (&val
, dev
, RIGHT_CHN
, right
);
sb_setmixer (regoffs
, val
);
levels
[dev
] = left
| (right
<< 8);
return left
| (right
<< 8);
sb_setmixer (RECORD_SRC
, (sb_getmixer (RECORD_SRC
) & ~7) | (src
& 0x7));
unsigned char regimageL
, regimageR
;
devmask
= mask
& supported_rec_devices
;
if (devmask
!= SOUND_MASK_MIC
&&
devmask
!= SOUND_MASK_LINE
&&
devmask
!= SOUND_MASK_CD
)
{ /* More than one devices selected. Drop the
if (devmask
!= SOUND_MASK_MIC
&&
devmask
!= SOUND_MASK_LINE
&&
devmask
!= SOUND_MASK_CD
)
{ /* More than one devices selected. Default to
devmask
= SOUND_MASK_MIC
;
if (devmask
^ recmask
) /* Input source changed */
devmask
= SOUND_MASK_MIC
;
regimageL
= regimageR
= 0;
for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
regimageL
|= sb16_recmasks_L
[i
];
regimageR
|= sb16_recmasks_R
[i
];
sb_setmixer (SB16_IMASK_L
, regimageL
);
sb_setmixer (SB16_IMASK_R
, regimageR
);
sb_mixer_ioctl (int dev
, unsigned int cmd
, unsigned int arg
)
if (((cmd
>> 8) & 0xff) == 'M')
return IOCTL_OUT (arg
, set_recmask (IOCTL_IN (arg
)));
return IOCTL_OUT (arg
, sb_mixer_set (cmd
& 0xff, IOCTL_IN (arg
)));
switch (cmd
& 0xff) /* Return parameters */
return IOCTL_OUT (arg
, recmask
);
case SOUND_MIXER_DEVMASK
:
return IOCTL_OUT (arg
, supported_devices
);
case SOUND_MIXER_STEREODEVS
:
return IOCTL_OUT (arg
, supported_devices
&
~(SOUND_MASK_MIC
| SOUND_MASK_SPEAKER
));
case SOUND_MIXER_RECMASK
:
return IOCTL_OUT (arg
, supported_rec_devices
);
return IOCTL_OUT (arg
, mixer_caps
);
return IOCTL_OUT (arg
, sb_mixer_get (cmd
& 0xff));
return RET_ERROR (EINVAL
);
static struct mixer_operations sb_mixer_operations
=
for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
sb_mixer_set (i
, levels
[i
]);
set_recmask (SOUND_MASK_MIC
);
* Returns a code depending on whether a SG NX Pro was detected.
* 0 == Plain SB 16 or SB Pro
* 1 == SG NX Pro detected.
* Used to update message.
sb_mixer_init (int major_model
)
sb_setmixer (0x00, 0); /* Reset mixer */
mixerstat
= detect_mixer ();
return 0; /* No mixer. Why? */
mixer_model
= major_model
;
mixer_caps
= SOUND_CAP_EXCL_INPUT
;
{ /* A SGNXPRO was detected */
supported_devices
= SGNXPRO_MIXER_DEVICES
;
supported_rec_devices
= SGNXPRO_RECORDING_DEVICES
;
{ /* Otherwise plain SB Pro */
supported_devices
= SBPRO_MIXER_DEVICES
;
supported_rec_devices
= SBPRO_RECORDING_DEVICES
;
supported_devices
= SB16_MIXER_DEVICES
;
supported_rec_devices
= SB16_RECORDING_DEVICES
;
printk ("SB Warning: Unsupported mixer type\n");
mixer_devs
[num_mixers
++] = &sb_mixer_operations
;