📄 cs4231_lib.c
字号:
if (status & CS4231_PLAYBACK_IRQ) { if (chip->playback_substream) snd_pcm_period_elapsed(chip->playback_substream); } if (status & CS4231_RECORD_IRQ) { if (chip->capture_substream) { snd_cs4231_overrange(chip); snd_pcm_period_elapsed(chip->capture_substream); } } } spin_lock(&chip->reg_lock); snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); spin_unlock(&chip->reg_lock); return IRQ_HANDLED;}#ifdef LEGACY_SUPPORTstatic snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); size_t ptr; if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) return 0; ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); return bytes_to_frames(substream->runtime, ptr);}static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); size_t ptr; if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) return 0; ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); return bytes_to_frames(substream->runtime, ptr);}#endif /* LEGACY_SUPPORT *//* */static int snd_cs4231_probe(cs4231_t *chip){ unsigned long flags; int i, id, rev; unsigned char *ptr; unsigned int hw;#if 0 snd_cs4231_debug(chip);#endif id = 0; for (i = 0; i < 50; i++) { mb(); if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) udelay(2000); else { spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; spin_unlock_irqrestore(&chip->reg_lock, flags); if (id == 0x0a) break; /* this is valid value */ } } snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); if (id != 0x0a) return -ENODEV; /* no valid device found */ if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); if (rev == 0x80) { unsigned char tmp = snd_cs4231_in(chip, 23); snd_cs4231_out(chip, 23, ~tmp); if (snd_cs4231_in(chip, 23) != tmp) chip->hardware = CS4231_HW_AD1845; else chip->hardware = CS4231_HW_CS4231; } else if (rev == 0xa0) { chip->hardware = CS4231_HW_CS4231A; } else if (rev == 0xa2) { chip->hardware = CS4231_HW_CS4232; } else if (rev == 0xb2) { chip->hardware = CS4231_HW_CS4232A; } else if (rev == 0x83) { chip->hardware = CS4231_HW_CS4236; } else if (rev == 0x03) { chip->hardware = CS4231_HW_CS4236B; } else { snd_printk("unknown CS chip with version 0x%x\n", rev); return -ENODEV; /* unknown CS4231 chip? */ } } spin_lock_irqsave(&chip->reg_lock, flags); cs4231_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */ cs4231_outb(chip, CS4231P(STATUS), 0); mb(); spin_unlock_irqrestore(&chip->reg_lock, flags); chip->image[CS4231_MISC_INFO] = CS4231_MODE2; switch (chip->hardware) { case CS4231_HW_INTERWAVE: chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; break; case CS4231_HW_CS4235: case CS4231_HW_CS4236B: case CS4231_HW_CS4237B: case CS4231_HW_CS4238B: case CS4231_HW_CS4239: if (hw == CS4231_HW_DETECT3) chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; else chip->hardware = CS4231_HW_CS4236; break; } chip->image[CS4231_IFACE_CTRL] = (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | (chip->single_dma ? CS4231_SINGLE_DMA : 0); chip->image[CS4231_ALT_FEATURE_1] = 0x80; chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; ptr = (unsigned char *) &chip->image; snd_cs4231_mce_down(chip); spin_lock_irqsave(&chip->reg_lock, flags); for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ snd_cs4231_out(chip, i, *ptr++); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_up(chip); snd_cs4231_mce_down(chip); mdelay(2); /* ok.. try check hardware version for CS4236+ chips */ if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { if (chip->hardware == CS4231_HW_CS4236B) { rev = snd_cs4236_ext_in(chip, CS4236_VERSION); snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); id = snd_cs4236_ext_in(chip, CS4236_VERSION); snd_cs4236_ext_out(chip, CS4236_VERSION, rev); snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); if ((id & 0x1f) == 0x1d) { /* CS4235 */ chip->hardware = CS4231_HW_CS4235; switch (id >> 5) { case 4: case 5: case 6: break; default: snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); } } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */ switch (id >> 5) { case 4: case 5: case 6: case 7: chip->hardware = CS4231_HW_CS4236B; break; default: snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); } } else if ((id & 0x1f) == 0x08) { /* CS4237B */ chip->hardware = CS4231_HW_CS4237B; switch (id >> 5) { case 4: case 5: case 6: case 7: break; default: snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); } } else if ((id & 0x1f) == 0x09) { /* CS4238B */ chip->hardware = CS4231_HW_CS4238B; switch (id >> 5) { case 5: case 6: case 7: break; default: snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); } } else if ((id & 0x1f) == 0x1e) { /* CS4239 */ chip->hardware = CS4231_HW_CS4239; switch (id >> 5) { case 4: case 5: case 6: break; default: snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); } } else { snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); } } } return 0; /* all things are ok.. */}/* */static snd_pcm_hardware_t snd_cs4231_playback ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_SYNC_START), .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5510, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0,};static snd_pcm_hardware_t snd_cs4231_capture ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_SYNC_START), .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, .rate_min = 5510, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0,};/* */static int snd_cs4231_playback_open(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; runtime->hw = snd_cs4231_playback; /* hardware bug in InterWave chipset */ if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; /* hardware limitation of cheap chips */ if (chip->hardware == CS4231_HW_CS4235 || chip->hardware == CS4231_HW_CS4239) runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;#ifdef LEGACY_SUPPORT snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); if (chip->claim_dma) { if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) return err; }#endif if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {#ifdef LEGACY_SUPPORT if (chip->release_dma) chip->release_dma(chip, chip->dma_private_data, chip->dma1);#endif snd_free_pages(runtime->dma_area, runtime->dma_bytes); return err; } chip->playback_substream = substream;#if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) chip->p_periods_sent = 0;#endif snd_pcm_set_sync(substream); chip->rate_constraint(runtime); return 0;}static int snd_cs4231_capture_open(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; runtime->hw = snd_cs4231_capture; /* hardware limitation of cheap chips */ if (chip->hardware == CS4231_HW_CS4235 || chip->hardware == CS4231_HW_CS4239) runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;#ifdef LEGACY_SUPPORT snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); if (chip->claim_dma) { if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) return err; }#endif if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {#ifdef LEGACY_SUPPORT if (chip->release_dma) chip->release_dma(chip, chip->dma_private_data, chip->dma2);#endif snd_free_pages(runtime->dma_area, runtime->dma_bytes); return err; } chip->capture_substream = substream;#if defined(SBUS_SUPPORT) || defined(EBUS_SUPPORT) chip->c_periods_sent = 0;#endif snd_pcm_set_sync(substream); chip->rate_constraint(runtime); return 0;}static int snd_cs4231_playback_close(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); chip->playback_substream = NULL; snd_cs4231_close(chip, CS4231_MODE_PLAY); return 0;}static int snd_cs4231_capture_close(snd_pcm_substream_t * substream){ cs4231_t *chip = snd_pcm_substream_chip(substream); chip->capture_substream = NULL; snd_cs4231_close(chip, CS4231_MODE_RECORD); return 0;}#ifdef CONFIG_PM/* lowlevel suspend callback for CS4231 */static void snd_cs4231_suspend(cs4231_t *chip){ int reg; unsigned long flags; if (chip->pcm) snd_pcm_suspend_all(chip->pcm); spin_lock_irqsave(&chip->reg_lock, flags); for (reg = 0; reg < 32; reg++) chip->image[reg] = snd_cs4231_in(chip, reg); spin_unlock_irqrestore(&chip->reg_lock, flags);}/* lowlevel resume callback for CS4231 */static void snd_cs4231_resume(cs4231_t *chip){ int reg; unsigned long flags; int timeout; snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); for (reg = 0; reg < 32; reg++) { switch (reg) { case CS4231_VERSION: break; default: snd_cs4231_out(chip, reg, chip->image[reg]); break; } } spin_unlock_irqrestore(&chip->reg_lock, flags);#if 0 snd_cs4231_mce_down(chip);#else /* The following is a workaround to avoid freeze after resume on TP600E. This is the first half of copy of snd_cs4231_mce_down(), but doesn't include rescheduling. -- iwai */ snd_cs4231_busy_wait(chip); spin_lock_irqsave(&chip->reg_lock, flags); chip->mce_bit &= ~CS4231_MCE; timeout = cs4231_inb(chip, CS4231P(REGSEL)); cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); spin_unlock_irqrestore(&chip->reg_lock, flags); if (timeout == 0x80) snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { return; } snd_cs4231_busy_wait(chip);#endif}static int snd_cs4231_pm_suspend(snd_card_t *card, pm_message_t state){ cs4231_t *chip = card->pm_private_data; if (chip->suspend) chip->suspend(chip); return 0;}static int snd_cs4231_pm_resume(snd_card_t *card){ cs4231_t *chip = card->pm_private_data; if (chip->resume) chip->resume(chip); return 0;}#endif /* CONFIG_PM */#ifdef LEGACY_SUPPORTstatic int snd_cs4231_free(cs4231_t *chip){ release_and_free_resource(chip->res_port); release_and_free_resource(chip->res_cport); if (chip->irq >= 0) { disable_irq(chip->irq); if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) free_irq(chip->irq, (void *) chip); } if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { snd_dma_disable(chip->dma1); free_dma(chip->dma1); } if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { snd_dma_disable(chip->dma2); free_dma(chip->dma2); } if (chip->timer) snd_device_free(chip->card, chip->timer); kfree(chip); return 0;}static int snd_cs4231_dev_free(snd_device_t *device){ cs4231_t *chip = device->device_data; return snd_cs4231_free(chip); }#endif /* LEGACY_SUPPORT */const char *snd_cs4231_chip_id(cs4231_t *chip){ switch (chip->hardware) { case CS4231_HW_CS4231: return "CS4231"; case CS4231_HW_CS4231A: return "CS4231A"; case CS4231_HW_CS4232: return "CS4232"; case CS4231_HW_CS4232A: return "CS4232A"; case CS4231_HW_CS4235: return "CS4235"; case CS4231_HW_CS4236: return "CS4236"; case CS4231_HW_CS4236B: return "CS4236B"; case CS4231_HW_CS4237B: return "CS4237B"; case CS4231_HW_CS4238B: return "CS4238B"; case CS4231_HW_CS4239: return "CS4239"; case CS4231_HW_INTERWAVE: return "AMD InterWave"; case CS4231_HW_OPL3SA2: return chip->card->shortname; case CS4231_HW_AD1845: return "AD1845"; default: return "???"; }}static int snd_cs4231_new(snd_card_t * card, unsigned short hardware, unsigned short hwshare, cs4231_t ** rchip){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -