On older 715 machines you'll find the technically identical chip
called 'Vivace'. Both Harmony and Vicace are supported by this driver.
- Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@linuxcare.com>
+ Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@onefishtwo.ca>
Copyright 2000-2003 (c) Helge Deller <deller@gmx.de>
Copyright 2001 (c) Matthieu Delahaye <delahaym@esiee.fr>
Copyright 2001 (c) Jean-Christophe Vaugeois <vaugeoij@esiee.fr>
+ Copyright 2004 (c) Stuart Brady <sdbrady@ntlworld.com>
TODO:
#define GAIN_RO_MASK ( 0x3f << GAIN_RO_SHIFT)
-#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT)
-#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT)
-#define MAX_VOLUME_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT)
+#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT)
+#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT)
+#define MAX_MONITOR_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT)
+
+#define MIXER_INTERNAL SOUND_MIXER_LINE1
+#define MIXER_LINEOUT SOUND_MIXER_LINE2
+#define MIXER_HEADPHONES SOUND_MIXER_LINE3
+
+#define MASK_INTERNAL SOUND_MASK_LINE1
+#define MASK_LINEOUT SOUND_MASK_LINE2
+#define MASK_HEADPHONES SOUND_MASK_LINE3
/*
* Channels Mask in mixer register
int count = 0;
int frame_size;
int buf_to_fill;
+ int fresh_buffer;
if (!harmony.format_initialized) {
if (harmony_format_auto_detect(buffer, total_count))
buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play);
- if (harmony.play_offset)
+ if (harmony.play_offset) {
buf_to_fill--;
+ buf_to_fill += MAX_BUFS;
+ }
buf_to_fill %= MAX_BUFS;
-
+
+ fresh_buffer = (harmony.play_offset == 0);
+
/* Figure out the size of the frame */
- if ((total_count-count) > HARMONY_BUF_SIZE - harmony.play_offset) {
+ if ((total_count-count) >= HARMONY_BUF_SIZE - harmony.play_offset) {
frame_size = HARMONY_BUF_SIZE - harmony.play_offset;
} else {
frame_size = total_count - count;
CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset),
frame_size);
- if (!harmony.play_offset)
+ if (fresh_buffer)
harmony.nb_filled_play++;
count += frame_size;
switch (ival) {
case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break;
case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break;
- case AFMT_S16_LE: /* fall through, but not really supported */
- case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR;
- ival = AFMT_S16_BE;
- break;
+ case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break;
default: {
DPRINTK(KERN_WARNING PFX
"unsupported sound format 0x%04x requested.\n",
ival);
- return -EINVAL;
+ ival = AFMT_S16_BE;
+ return put_user(ival, (int *) arg);
}
}
harmony_set_format(new_format);
+ return 0;
} else {
switch (harmony.data_format) {
case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break;
case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break;
default: ival = 0;
}
+ return put_user(ival, (int *) arg);
}
- return put_user(ival, (int *) arg);
case SOUND_PCM_READ_RATE:
ival = harmony.dac_rate;
if (ival != 0 && ival != 1)
return -EINVAL;
harmony_set_stereo(ival);
- return put_user(ival, (int *) arg);
+ return 0;
+
+ case SNDCTL_DSP_CHANNELS:
+ if (get_user(ival, (int *) arg))
+ return -EFAULT;
+ if (ival != 1 && ival != 2) {
+ ival = harmony.stereo_select == HARMONY_SS_MONO ? 1 : 2;
+ return put_user(ival, (int *) arg);
+ }
+ harmony_set_stereo(ival-1);
+ return 0;
case SNDCTL_DSP_GETBLKSIZE:
ival = HARMONY_BUF_SIZE;
int right_level;
switch (channel) {
- case SOUND_MIXER_OGAIN:
+ case SOUND_MIXER_VOLUME:
left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT;
right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT;
left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL);
right_level= to_oss_level(right_level, MAX_INPUT_LEVEL);
return (right_level << 8)+left_level;
- case SOUND_MIXER_VOLUME:
+ case SOUND_MIXER_MONITOR:
left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT;
- left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL);
- return left_level;
+ left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL);
+ return (left_level << 8)+left_level;
}
return -EINVAL;
}
right_level = (value & 0x0000ff00) >> 8;
left_level = value & 0x000000ff;
+ if (right_level > 100) right_level = 100;
+ if (left_level > 100) left_level = 100;
switch (channel) {
- case SOUND_MIXER_OGAIN:
+ case SOUND_MIXER_VOLUME:
right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL);
left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL);
new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL);
harmony_mixer_set_gain();
return (new_right_level << 8) + new_left_level;
- case SOUND_MIXER_VOLUME:
- left_level = to_harmony_level(100-left_level, MAX_VOLUME_LEVEL);
- new_left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL);
- harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK)| (left_level << GAIN_MA_SHIFT);
+ case SOUND_MIXER_MONITOR:
+ left_level = to_harmony_level(100-left_level, MAX_MONITOR_LEVEL);
+ new_left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL);
+ harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK) | (left_level << GAIN_MA_SHIFT);
harmony_mixer_set_gain();
- return new_left_level;
+ return (new_left_level << 8) + new_left_level;
}
return -EINVAL;
{
int new_input_line;
int new_input_mask;
-
- if ((recmask & SOUND_MASK_LINE)) {
+ int current_input_line;
+
+ current_input_line = (harmony.current_gain & GAIN_IS_MASK)
+ >> GAIN_IS_SHIFT;
+ if ((current_input_line && ((recmask & SOUND_MASK_LINE) || !(recmask & SOUND_MASK_MIC))) ||
+ (!current_input_line && ((recmask & SOUND_MASK_LINE) && !(recmask & SOUND_MASK_MIC)))) {
new_input_line = 0;
new_input_mask = SOUND_MASK_LINE;
- } else {
+ } else {
new_input_line = 1;
new_input_mask = SOUND_MASK_MIC;
}
{
int outmask = 0;
- if (harmony.current_gain & GAIN_HE_MASK) outmask |=SOUND_MASK_PHONEOUT;
- if (harmony.current_gain & GAIN_LE_MASK) outmask |=SOUND_MASK_LINE;
- if (harmony.current_gain & GAIN_SE_MASK) outmask |=SOUND_MASK_SPEAKER;
+ if (harmony.current_gain & GAIN_SE_MASK) outmask |= MASK_INTERNAL;
+ if (harmony.current_gain & GAIN_LE_MASK) outmask |= MASK_LINEOUT;
+ if (harmony.current_gain & GAIN_HE_MASK) outmask |= MASK_HEADPHONES;
return outmask;
}
static int harmony_mixer_set_outmask(int outmask)
{
- if (outmask & SOUND_MASK_PHONEOUT)
- harmony.current_gain |= GAIN_HE_MASK;
+ if (outmask & MASK_INTERNAL)
+ harmony.current_gain |= GAIN_SE_MASK;
else
- harmony.current_gain &= ~GAIN_HE_MASK;
+ harmony.current_gain &= ~GAIN_SE_MASK;
- if (outmask & SOUND_MASK_LINE)
+ if (outmask & MASK_LINEOUT)
harmony.current_gain |= GAIN_LE_MASK;
else
harmony.current_gain &= ~GAIN_LE_MASK;
- if (outmask & SOUND_MASK_SPEAKER)
- harmony.current_gain |= GAIN_SE_MASK;
+ if (outmask & MASK_HEADPHONES)
+ harmony.current_gain |= GAIN_HE_MASK;
else
- harmony.current_gain &= ~GAIN_SE_MASK;
+ harmony.current_gain &= ~GAIN_HE_MASK;
harmony_mixer_set_gain();
- return (outmask & (SOUND_MASK_PHONEOUT | SOUND_MASK_LINE | SOUND_MASK_SPEAKER));
+ return (outmask & (MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES));
}
/*
ret = SOUND_CAP_EXCL_INPUT;
break;
case MIXER_READ(SOUND_MIXER_STEREODEVS):
- ret = SOUND_MASK_IGAIN | SOUND_MASK_OGAIN;
+ ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN;
break;
case MIXER_READ(SOUND_MIXER_RECMASK):
ret = SOUND_MASK_MIC | SOUND_MASK_LINE;
break;
case MIXER_READ(SOUND_MIXER_DEVMASK):
- ret = SOUND_MASK_OGAIN | SOUND_MASK_IGAIN |
- SOUND_MASK_VOLUME;
+ ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN |
+ SOUND_MASK_MONITOR;
break;
case MIXER_READ(SOUND_MIXER_OUTMASK):
- ret = SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
- SOUND_MASK_PHONEOUT;
+ ret = MASK_INTERNAL | MASK_LINEOUT |
+ MASK_HEADPHONES;
break;
case MIXER_WRITE(SOUND_MIXER_RECSRC):
ret = harmony_mixer_get_outmask();
break;
- case MIXER_WRITE(SOUND_MIXER_OGAIN):
- case MIXER_WRITE(SOUND_MIXER_IGAIN):
case MIXER_WRITE(SOUND_MIXER_VOLUME):
+ case MIXER_WRITE(SOUND_MIXER_IGAIN):
+ case MIXER_WRITE(SOUND_MIXER_MONITOR):
ret = harmony_mixer_set_level(cmd & 0xff, val);
break;
- case MIXER_READ(SOUND_MIXER_OGAIN):
- case MIXER_READ(SOUND_MIXER_IGAIN):
case MIXER_READ(SOUND_MIXER_VOLUME):
+ case MIXER_READ(SOUND_MIXER_IGAIN):
+ case MIXER_READ(SOUND_MIXER_MONITOR):
ret = harmony_mixer_get_level(cmd & 0xff);
break;
return -EBUSY;
}
- harmony.dev = dev;
-
- /* Set the HPA of harmony */
- harmony.hpa = (struct harmony_hpa *)dev->hpa;
-
- if (!harmony.dev->irq) {
+ if (!dev->irq) {
printk(KERN_ERR PFX "no irq found\n");
return -ENODEV;
}
+ /* Set the HPA of harmony */
+ harmony.hpa = (struct harmony_hpa *)dev->hpa;
+ harmony.dev = dev;
+
/* Grab the ID and revision from the device */
id = gsc_readb(&harmony.hpa->id);
if ((id | 1) != 0x15) {
}
-MODULE_AUTHOR("Alex DeVries <alex@linuxcare.com>");
+MODULE_AUTHOR("Alex DeVries <alex@onefishtwo.ca>");
MODULE_DESCRIPTION("Harmony sound driver");
MODULE_LICENSE("GPL");