summaryrefslogtreecommitdiffstats
path: root/sound/isa/wss
diff options
context:
space:
mode:
Diffstat (limited to 'sound/isa/wss')
-rw-r--r--sound/isa/wss/wss_lib.c150
1 files changed, 120 insertions, 30 deletions
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 57d1e8ee6bb..a5602f515f4 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1073,7 +1073,11 @@ irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
struct snd_wss *chip = dev_id;
unsigned char status;
- status = snd_wss_in(chip, CS4231_IRQ_STATUS);
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ /* pretend it was the only possible irq for AD1848 */
+ status = CS4231_PLAYBACK_IRQ;
+ else
+ status = snd_wss_in(chip, CS4231_IRQ_STATUS);
if (status & CS4231_TIMER_IRQ) {
if (chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
@@ -1105,7 +1109,11 @@ irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
}
spin_lock(&chip->reg_lock);
- snd_wss_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
+ status = ~CS4231_ALL_IRQS | ~status;
+ if (chip->hardware & WSS_HW_AD1848_MASK)
+ wss_outb(chip, CS4231P(STATUS), 0);
+ else
+ snd_wss_outm(chip, CS4231_IRQ_STATUS, status, 0);
spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -1137,36 +1145,112 @@ static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *subst
*/
-static int snd_wss_probe(struct snd_wss *chip)
+static int snd_ad1848_probe(struct snd_wss *chip)
{
unsigned long flags;
- int i, id, rev;
- unsigned char *ptr;
- unsigned int hw;
+ int i, id, rev, ad1847;
-#if 0
- snd_wss_debug(chip);
-#endif
id = 0;
- for (i = 0; i < 50; i++) {
+ ad1847 = 0;
+ for (i = 0; i < 1000; i++) {
mb();
- if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- udelay(2000);
+ if (inb(chip->port + CS4231P(REGSEL)) & CS4231_INIT)
+ msleep(1);
else {
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
- id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
+ snd_wss_out(chip, CS4231_MISC_INFO, 0x00);
+ snd_wss_out(chip, CS4231_LEFT_INPUT, 0xaa);
+ snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x45);
+ rev = snd_wss_in(chip, CS4231_RIGHT_INPUT);
+ if (rev == 0x65) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ id = 1;
+ ad1847 = 1;
+ break;
+ }
+ if (snd_wss_in(chip, CS4231_LEFT_INPUT) == 0xaa &&
+ rev == 0x45) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ id = 1;
+ break;
+ }
spin_unlock_irqrestore(&chip->reg_lock, flags);
- if (id == 0x0a)
- break; /* this is valid value */
}
}
- snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
- if (id != 0x0a)
+ if (id != 1)
return -ENODEV; /* no valid device found */
+ id = 0;
+ if (chip->hardware == WSS_HW_DETECT)
+ id = ad1847 ? WSS_HW_AD1847 : WSS_HW_AD1848;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ inb(chip->port + CS4231P(STATUS)); /* clear any pendings IRQ */
+ outb(0, chip->port + CS4231P(STATUS));
+ mb();
+ if (id == WSS_HW_AD1848) {
+ /* check if there are more than 16 registers */
+ rev = snd_wss_in(chip, CS4231_MISC_INFO);
+ snd_wss_out(chip, CS4231_MISC_INFO, 0x40);
+ for (i = 0; i < 16; ++i) {
+ if (snd_wss_in(chip, i) != snd_wss_in(chip, i + 16)) {
+ id = WSS_HW_CMI8330;
+ break;
+ }
+ }
+ snd_wss_out(chip, CS4231_MISC_INFO, 0x00);
+ if (id != WSS_HW_CMI8330 && (rev & 0x80))
+ id = WSS_HW_CS4248;
+ if (id == WSS_HW_CMI8330 && (rev & 0x0f) != 0x0a)
+ id = 0;
+ }
+ if (id == WSS_HW_CMI8330) {
+ /* verify it is not CS4231 by changing the version register */
+ /* on CMI8330 it is volume control register and can be set 0 */
+ snd_wss_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
+ snd_wss_dout(chip, CS4231_VERSION, 0x00);
+ rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
+ if (rev)
+ id = 0;
+ snd_wss_out(chip, CS4231_MISC_INFO, 0);
+ }
+ if (id)
+ chip->hardware = id;
+
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0; /* all things are ok.. */
+}
+
+static int snd_wss_probe(struct snd_wss *chip)
+{
+ unsigned long flags;
+ int i, id, rev, regnum;
+ unsigned char *ptr;
+ unsigned int hw;
+
+ id = snd_ad1848_probe(chip);
+ if (id < 0)
+ return id;
hw = chip->hardware;
if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
+ for (i = 0; i < 50; i++) {
+ mb();
+ if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+ msleep(2);
+ else {
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_MISC_INFO,
+ CS4231_MODE2);
+ id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (id == 0x0a)
+ break; /* this is valid value */
+ }
+ }
+ snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
+ if (id != 0x0a)
+ return -ENODEV; /* no valid device found */
+
rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
if (rev == 0x80) {
@@ -1197,7 +1281,8 @@ static int snd_wss_probe(struct snd_wss *chip)
mb();
spin_unlock_irqrestore(&chip->reg_lock, flags);
- chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
switch (chip->hardware) {
case WSS_HW_INTERWAVE:
chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
@@ -1223,9 +1308,10 @@ static int snd_wss_probe(struct snd_wss *chip)
chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01;
}
ptr = (unsigned char *) &chip->image;
+ regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
snd_wss_mce_down(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
- for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */
+ for (i = 0; i < regnum; i++) /* ok.. fill all registers */
snd_wss_out(chip, i, *ptr++);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_up(chip);
@@ -1635,6 +1721,10 @@ static int snd_wss_new(struct snd_card *card,
else
memcpy(&chip->image, &snd_wss_original_image,
sizeof(snd_wss_original_image));
+ if (chip->hardware & WSS_HW_AD1848_MASK) {
+ chip->image[CS4231_PIN_CTRL] = 0;
+ chip->image[CS4231_TEST_INIT] = 0;
+ }
*rchip = chip;
return 0;
@@ -1662,7 +1752,7 @@ int snd_wss_create(struct snd_card *card,
chip->dma1 = -1;
chip->dma2 = -1;
- chip->res_port = request_region(port, 4, "CS4231");
+ chip->res_port = request_region(port, 4, "WSS");
if (!chip->res_port) {
snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port);
snd_wss_free(chip);
@@ -1681,20 +1771,20 @@ int snd_wss_create(struct snd_card *card,
chip->cport = cport;
if (!(hwshare & WSS_HWSHARE_IRQ))
if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED,
- "CS4231", (void *) chip)) {
+ "WSS", (void *) chip)) {
snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
snd_wss_free(chip);
return -EBUSY;
}
chip->irq = irq;
- if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
+ if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) {
snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
snd_wss_free(chip);
return -EBUSY;
}
chip->dma1 = dma1;
if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 &&
- dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
+ dma2 >= 0 && request_dma(dma2, "WSS - 2")) {
snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
snd_wss_free(chip);
return -EBUSY;
@@ -1705,6 +1795,12 @@ int snd_wss_create(struct snd_card *card,
} else
chip->dma2 = dma2;
+ if (hardware == WSS_HW_THINKPAD) {
+ chip->thinkpad_flag = 1;
+ chip->hardware = WSS_HW_DETECT; /* reset */
+ snd_wss_thinkpad_twiddle(chip, 1);
+ }
+
/* global setup */
if (snd_wss_probe(chip) < 0) {
snd_wss_free(chip);
@@ -1775,12 +1871,6 @@ int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wss_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wss_capture_ops);
- /* temporary */
- if (chip->hardware & WSS_HW_AD1848_MASK) {
- chip->rate_constraint = snd_wss_xrate;
- chip->set_playback_format = snd_wss_playback_format;
- chip->set_capture_format = snd_wss_capture_format;
- }
/* global setup */
pcm->private_data = chip;
pcm->info_flags = 0;