schedule_timeout(1 + ms * HZ / 1000);
}
+#define SW_INPUT_VOLUME_SCALE 4
+#define SW_INPUT_VOLUME_DEFAULT (128 / SW_INPUT_VOLUME_SCALE)
+
+extern int expand_bal; /* Balance factor for expanding (not volume!) */
+extern int expand_read_bal; /* Balance factor for reading */
+extern uint software_input_volume; /* software implemented recording volume! */
+
#endif /* _dmasound_h_ */
* - Support for snapper & better tumbler integration by Toby Sargeant
* - Headphone detect for scremer by Julien Blache
* - More tumbler fixed by Andreas Schwab
+ * 11/29/2003 [0.8.1] - Renzo Davoli (King Enzo)
+ * - Support for Snapper line in
+ * - snapper input resampling (for rates < 44100)
+ * - software line gain control
*/
/* GENERAL FIXME/TODO: check that the assumptions about what is written to
#include <linux/spinlock.h>
#include <linux/kmod.h>
#include <linux/interrupt.h>
+#include <linux/input.h>
#include <asm/semaphore.h>
#ifdef CONFIG_ADB_CUDA
#include <linux/cuda.h>
static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
static int awacs_rate_index;
static int awacs_subframe;
-static int awacs_spkr_vol;
static struct device_node* awacs_node;
static struct device_node* i2s_node;
/* for (soft) sample rate translations */
int expand_bal; /* Balance factor for expanding (not volume!) */
+int expand_read_bal; /* Balance factor for expanding reads (not volume!) */
/*** Low level stuff *********************************************************/
static void awacs_write(int val);
static int awacs_get_volume(int reg, int lshift);
static int awacs_volume_setter(int volume, int n, int mute, int lshift);
-static void awacs_mksound(unsigned int hz, unsigned int ticks);
-static void awacs_nosound(unsigned long xx);
/*** Mid level stuff **********************************************************/
extern TRANS transAwacsNormal ;
extern TRANS transAwacsExpand ;
extern TRANS transAwacsNormalRead ;
+extern TRANS transAwacsExpandRead ;
extern int daca_init(void);
extern void daca_cleanup(void);
rc = IOCTL_OUT(arg, 0);
break;
case SOUND_MIXER_READ_RECMASK:
- data = 0;
+ // XXX FIXME: find a way to check what is really available */
+ data = SOUND_MASK_LINE | SOUND_MASK_MIC;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECSRC:
- data = 0;
+ if (awacs_reg[0] & MASK_MUX_AUDIN)
+ data |= SOUND_MASK_LINE;
+ if (awacs_reg[0] & MASK_MUX_MIC)
+ data |= SOUND_MASK_MIC;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECSRC:
set_frame_rate(dmasound.soft.speed, catchRadius) ;
tolerance = (catchRadius * dmasound.hard.speed) / 100;
- if (dmasound.soft.speed >= dmasound.hard.speed - tolerance)
+ if (dmasound.soft.speed >= dmasound.hard.speed - tolerance) {
dmasound.trans_write = &transAwacsNormal;
- else
+ dmasound.trans_read = &transAwacsNormalRead;
+ } else {
dmasound.trans_write = &transAwacsExpand;
- dmasound.trans_read = &transAwacsNormalRead;
+ dmasound.trans_read = &transAwacsExpandRead;
+ }
if (awacs) {
if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
}
expand_bal = -dmasound.soft.speed;
+ expand_read_bal = -dmasound.soft.speed;
}
static int PMacSetFormat(int format)
spin_unlock_irqrestore(&dmasound.lock, flags);
}
-static struct timer_list beep_timer = TIMER_INITIALIZER(awacs_nosound, 0, 0);
-
-#if 0 /* would need to go through the input layer in 2.6, later.. --hch */
-/* we generate the beep with a single dbdma command that loops a buffer
- forever - without generating interrupts.
- So, to stop it you have to stop dma output as per awacs_nosound.
-*/
-
-static void awacs_mksound(unsigned int hz, unsigned int ticks)
+/*
+ * We generate the beep with a single dbdma command that loops a buffer
+ * forever - without generating interrupts.
+ *
+ * So, to stop it you have to stop dma output as per awacs_nosound.
+ */
+static int awacs_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
{
unsigned long flags;
int beep_speed = 0;
static int beep_nsamples_cache;
static int beep_volume_cache;
+ if (type != EV_SND)
+ return -1;
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 1000;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
if (beep_buf == NULL)
- return;
+ return -1;
/* quick-hack fix for DACA, Burgundy & Tumbler */
beep_speed = i;
srate = awacs_freqs[beep_speed];
}
+
if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
-#if 1
- /* this is a hack for broken X server code */
- hz = 750;
- ticks = 12;
-#else
/* cancel beep currently playing */
awacs_nosound(0);
- return;
-#endif
+ return 0;
}
+
spin_lock_irqsave(&dmasound.lock, flags);
- del_timer(&beep_timer);
- if (ticks) {
- beep_timer.expires = jiffies + ticks;
- add_timer(&beep_timer);
- }
if (beep_playing || write_sq.active || beep_buf == NULL) {
spin_unlock_irqrestore(&dmasound.lock, flags);
- return; /* too hard, sorry :-( */
+ return -1; /* too hard, sorry :-( */
}
beep_playing = 1;
st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
out_le32(&awacs_txdma->control, RUN | (RUN << 16));
}
spin_unlock_irqrestore(&dmasound.lock, flags);
+
+ return 0;
}
-#endif
/* used in init and for wake-up */
#ifdef CONFIG_ADB_CUDA
struct adb_request req;
- awacs_spkr_vol = spkr_vol;
if (sys_ctrler != SYS_CTRLER_CUDA)
return;
return 0 ;
}
+static struct input_dev awacs_beep_dev = {
+ .evbit = { BIT(EV_SND) },
+ .sndbit = { BIT(SND_BELL) | BIT(SND_TONE) },
+ .event = awacs_beep_event,
+ .name = "dmasound beeper",
+ .phys = "macio/input0", /* what the heck is this?? */
+ .id = {
+ .bustype = BUS_HOST,
+ },
+};
+
int __init dmasound_awacs_init(void)
{
struct device_node *io = NULL, *info = NULL;
/* shut out chips that do output only.
* may need to extend this to machines which have no inputs - even tho'
* they use screamer - IIRC one of the powerbooks is like this.
- *
- * FIXME: Actually, some TUMBLER and SNAPPER do have inputs...
*/
- if (awacs_revision != AWACS_TUMBLER &&
- awacs_revision != AWACS_SNAPPER &&
- awacs_revision != AWACS_DACA) {
+ if (awacs_revision != AWACS_DACA) {
dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
dmasound.mach.record = PMacRecord ;
}
break ;
}
+ /*
+ * XXX: we should handle errors here, but that would mean
+ * rewriting the whole init code. later..
+ */
+ input_register_device(&awacs_beep_dev);
+
return dmasound_init();
}
static void __exit dmasound_awacs_cleanup(void)
{
+ input_unregister_device(&awacs_beep_dev);
+
switch (awacs_revision) {
case AWACS_TUMBLER:
case AWACS_SNAPPER:
break;
}
dmasound_deinit();
+
}
MODULE_DESCRIPTION("PowerMac built-in audio driver.");
static int irq_installed;
#endif /* MODULE */
+/* software implemented recording volume! */
+uint software_input_volume = SW_INPUT_VOLUME_SCALE * SW_INPUT_VOLUME_DEFAULT;
+EXPORT_SYMBOL(software_input_volume);
+
/* control over who can modify resources shared between play/record */
static mode_t shared_resource_owner;
static int shared_resources_initialised;
return dmasound.mach.setFormat(format);
}
+
static int sound_set_speed(int speed)
{
if (speed < 0)
result = IOCTL_OUT(arg, format);
if (result < 0)
return result;
- if (format != data)
+ if (format != data && data != AFMT_QUERY)
return -EINVAL;
return 0;
} else
return -EINVAL ;
- break ;
case SNDCTL_DSP_SUBDIVIDE:
return -EINVAL ;
- break;
case SNDCTL_DSP_SETFRAGMENT:
/* we can do this independently for the two queues - with the
proviso that for fds opened O_RDWR we cannot separate the
* Tobias Sargeant <tobias.sargeant@bigpond.com>
* Based upon tas3001c.c by Christopher C. Chimelis <chris@debian.org>:
*
- * TODO:
- * -----
- * * Enable control over input line 2 (is this connected?)
- * * Implement sleep support (at least mute everything and
- * * set gains to minimum during sleep)
+ * Input support by Renzo Davoli <renzo@cs.unibo.it>
*
*/
{
if (reg_num==TAS3004_REG_MCR ||
reg_num==TAS3004_REG_BASS ||
- reg_num==TAS3004_REG_TREBLE) {
+ reg_num==TAS3004_REG_TREBLE ||
+ reg_num==TAS3004_REG_ANALOG_CTRL) {
return tas_write_byte_register(&self->super,
(uint)reg_num,
*data,
{
if (reg_num==TAS3004_REG_MCR ||
reg_num==TAS3004_REG_BASS ||
- reg_num==TAS3004_REG_TREBLE) {
+ reg_num==TAS3004_REG_TREBLE ||
+ reg_num==TAS3004_REG_ANALOG_CTRL) {
return tas_sync_byte_register(&self->super,
(uint)reg_num,
register_width(reg_num));
SOUND_MASK_ALTPCM |
SOUND_MASK_IMIX |
SOUND_MASK_TREBLE |
- SOUND_MASK_BASS;
+ SOUND_MASK_BASS |
+ SOUND_MASK_MIC |
+ SOUND_MASK_LINE;
}
static int
shadow[TAS3004_REG_BASS][0]=temp&0xff;
rc = tas3004_sync_register(self,TAS3004_REG_BASS);
break;
+ case SOUND_MIXER_MIC:
+ if ((level&0xff)>0) {
+ software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff);
+ if (self->super.mixer[mixer] == 0) {
+ self->super.mixer[SOUND_MIXER_LINE] = 0;
+ shadow[TAS3004_REG_ANALOG_CTRL][0]=0xc2;
+ rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
+ } else rc=0;
+ } else {
+ self->super.mixer[SOUND_MIXER_LINE] = SW_INPUT_VOLUME_DEFAULT;
+ software_input_volume = SW_INPUT_VOLUME_SCALE *
+ (self->super.mixer[SOUND_MIXER_LINE]&0xff);
+ shadow[TAS3004_REG_ANALOG_CTRL][0]=0x00;
+ rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
+ }
+ break;
+ case SOUND_MIXER_LINE:
+ if (self->super.mixer[SOUND_MIXER_MIC] == 0) {
+ software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff);
+ rc=0;
+ }
+ break;
default:
rc = -1;
break;
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER);
(void)tas3004_sync_register(self,TAS3004_REG_TREBLE);
(void)tas3004_sync_register(self,TAS3004_REG_BASS);
+ (void)tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
return 0;
}
tas3004_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
+ tas3004_set_mixer_level(self, SOUND_MIXER_LINE,SW_INPUT_VOLUME_DEFAULT);
+
return 0;
}
tas3004_set_mixer_level(self, SOUND_MIXER_BASS, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_LINE, 0);
+
return 0;
}
printk(KERN_INFO "tas driver [%s])\n", driver_name);
+#ifndef CONFIG_I2C_KEYWEST
+ request_module("i2c-keywest");
+#endif
tas_node = find_devices("deq");
if (tas_node == NULL)
return -ENODEV;
unsigned int *mixer;
};
-typedef char tas_shadow_t[16];
+typedef char tas_shadow_t[0x45];
struct tas_data_t
{
*
* 08/02/2001 Iain Sandoe
* split from dmasound_awacs.c
+ * 11/29/2003 Renzo Davoli (King Enzo)
+ * - input resampling (for soft rate < hard rate)
+ * - software line in gain control
*/
#include <linux/soundcard.h>
/*** Translations ************************************************************/
-extern int expand_bal; /* Balance factor for expanding (not volume!) */
static int expand_data; /* Data for expanding */
static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
u_char data;
val = *p++;
+ val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
+ val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
u_char data;
val = *p++;
+ val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
+ val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
+ short *up = (short *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
- if (!stereo) {
- short *up = (short *) userPtr;
- while (count > 0) {
- short data;
+ while (count > 0) {
+ short data;
+
+ data = *fp++;
+ data = (data * software_input_volume) >> 7;
+ if (put_user(data, up++))
+ return -EFAULT;
+ if (stereo) {
data = *fp;
+ data = (data * software_input_volume) >> 7;
if (put_user(data, up++))
return -EFAULT;
- fp+=2;
- count--;
}
- } else {
- if (copy_to_user((u_char *)userPtr, fp, count * 4))
- return -EFAULT;
- }
+ fp++;
+ count--;
+ }
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
int data;
data = *fp++;
+ data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
+ data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
return stereo? used * 4: used * 2;
}
+/* data in routines (reducing speed)... */
+
+static ssize_t pmac_ctx_s8_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ short *p = (short *) &frame[*frameUsed];
+ int bal = expand_read_bal;
+ int vall,valr, stereo = dmasound.soft.stereo;
+ int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+ int utotal, ftotal;
+
+ frameLeft >>= 2;
+ if (stereo)
+ userCount >>= 1;
+ ftotal = frameLeft;
+ utotal = userCount;
+ while (frameLeft) {
+ u_char data;
+
+ if (bal<0 && userCount == 0)
+ break;
+ vall = *p++;
+ vall = (vall * software_input_volume) >> 7;
+ if (stereo) {
+ valr = *p;
+ valr = (valr * software_input_volume) >> 7;
+ }
+ p++;
+ if (bal < 0) {
+ data = vall >> 8;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ if (stereo) {
+ data = valr >> 8;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ }
+ userCount--;
+ bal += hSpeed;
+ }
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ expand_read_bal=bal;
+ *frameUsed += (ftotal - frameLeft) * 4;
+ utotal -= userCount;
+ return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_u8_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ short *p = (short *) &frame[*frameUsed];
+ int bal = expand_read_bal;
+ int vall,valr, stereo = dmasound.soft.stereo;
+ int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+ int utotal, ftotal;
+
+ frameLeft >>= 2;
+ if (stereo)
+ userCount >>= 1;
+ ftotal = frameLeft;
+ utotal = userCount;
+ while (frameLeft) {
+ u_char data;
+
+ if (bal<0 && userCount == 0)
+ break;
+
+ vall = *p++;
+ vall = (vall * software_input_volume) >> 7;
+ if (stereo) {
+ valr = *p;
+ valr = (valr * software_input_volume) >> 7;
+ }
+ p++;
+ if (bal < 0) {
+ data = (vall >> 8) ^ 0x80;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ if (stereo) {
+ data = (valr >> 8) ^ 0x80;
+ if (put_user(data, (u_char *)userPtr++))
+ return -EFAULT;
+ }
+ userCount--;
+ bal += hSpeed;
+ }
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ expand_read_bal=bal;
+ *frameUsed += (ftotal - frameLeft) * 4;
+ utotal -= userCount;
+ return stereo? utotal * 2: utotal;
+}
+
+static ssize_t pmac_ctx_s16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ int bal = expand_read_bal;
+ short *fp = (short *) &frame[*frameUsed];
+ short *up = (short *) userPtr;
+ int stereo = dmasound.soft.stereo;
+ int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+ int utotal, ftotal;
+
+ frameLeft >>= 2;
+ userCount >>= (stereo? 2: 1);
+ ftotal = frameLeft;
+ utotal = userCount;
+ while (frameLeft) {
+ int datal,datar;
+
+ if (bal<0 && userCount == 0)
+ break;
+
+ datal = *fp++;
+ datal = (datal * software_input_volume) >> 7;
+ if (stereo) {
+ datar = *fp;
+ datar = (datar * software_input_volume) >> 7;
+ }
+ fp++;
+ if (bal < 0) {
+ if (put_user(datal, up++))
+ return -EFAULT;
+ if (stereo) {
+ if (put_user(datar, up++))
+ return -EFAULT;
+ }
+ userCount--;
+ bal += hSpeed;
+ }
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ expand_read_bal=bal;
+ *frameUsed += (ftotal - frameLeft) * 4;
+ utotal -= userCount;
+ return stereo? utotal * 4: utotal * 2;
+}
+
+static ssize_t pmac_ctx_u16_read(const u_char *userPtr, size_t userCount,
+ u_char frame[], ssize_t *frameUsed,
+ ssize_t frameLeft)
+{
+ int bal = expand_read_bal;
+ int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+ short *fp = (short *) &frame[*frameUsed];
+ short *up = (short *) userPtr;
+ int stereo = dmasound.soft.stereo;
+ int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+ int utotal, ftotal;
+
+ frameLeft >>= 2;
+ userCount >>= (stereo? 2: 1);
+ ftotal = frameLeft;
+ utotal = userCount;
+ while (frameLeft) {
+ int datal,datar;
+
+ if (bal<0 && userCount == 0)
+ break;
+
+ datal = *fp++;
+ datal = (datal * software_input_volume) >> 7;
+ datal ^= mask;
+ if (stereo) {
+ datar = *fp;
+ datar = (datar * software_input_volume) >> 7;
+ datar ^= mask;
+ }
+ fp++;
+ if (bal < 0) {
+ if (put_user(datal, up++))
+ return -EFAULT;
+ if (stereo) {
+ if (put_user(datar, up++))
+ return -EFAULT;
+ }
+ userCount--;
+ bal += hSpeed;
+ }
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ expand_read_bal=bal;
+ *frameUsed += (ftotal - frameLeft) * 4;
+ utotal -= userCount;
+ return stereo? utotal * 4: utotal * 2;
+}
+
+
TRANS transAwacsNormal = {
.ct_ulaw= pmac_ct_law,
.ct_alaw= pmac_ct_law,
.ct_u16le= pmac_ct_u16_read,
};
+TRANS transAwacsExpandRead = {
+ .ct_s8= pmac_ctx_s8_read,
+ .ct_u8= pmac_ctx_u8_read,
+ .ct_s16be= pmac_ctx_s16_read,
+ .ct_u16be= pmac_ctx_u16_read,
+ .ct_s16le= pmac_ctx_s16_read,
+ .ct_u16le= pmac_ctx_u16_read,
+};
+
/* translation tables */
/* 16 bit mu-law */