📄 sscape.c
字号:
int err;#define CS4231_SHARE_HARDWARE (CS4231_HWSHARE_DMA1 | CS4231_HWSHARE_DMA2) /* * The AD1845 PCM device is only half-duplex, and so * we only give it one DMA channel ... */ if ((err = snd_cs4231_create(card, port, -1, irq, dma1, dma1, CS4231_HW_DETECT, CS4231_HWSHARE_DMA1, &chip)) == 0) { unsigned long flags; snd_pcm_t *pcm;#define AD1845_FREQ_SEL_ENABLE 0x08#define AD1845_PWR_DOWN_CTRL 0x1b#define AD1845_CRYS_CLOCK_SEL 0x1d/* * It turns out that the PLAYBACK_ENABLE bit is set * by the lowlevel driver ... *#define AD1845_IFACE_CONFIG \ (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE) snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); */ /* * The input clock frequency on the SoundScape must * be 14.31818 MHz, because we must set this register * to get the playback to sound correct ... */ snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip); /* * More custom configuration: * a) select "mode 2", and provide a current drive of 8 mA * b) enable frequency selection (for capture/playback) */ spin_lock_irqsave(&chip->reg_lock, flags); snd_cs4231_out(chip, CS4231_MISC_INFO, (CS4231_MODE2 | 0x10)); snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL, snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL) | AD1845_FREQ_SEL_ENABLE); spin_unlock_irqrestore(&chip->reg_lock, flags); if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { snd_printk(KERN_ERR "sscape: No PCM device for AD1845 chip\n"); goto _error; } if ((err = snd_cs4231_mixer(chip)) < 0) { snd_printk(KERN_ERR "sscape: No mixer device for AD1845 chip\n"); goto _error; } if ((err = snd_ctl_add(card, snd_ctl_new1(&midi_mixer_ctl, chip))) < 0) { snd_printk(KERN_ERR "sscape: Could not create MIDI mixer control\n"); goto _error; } strcpy(card->driver, "SoundScape"); strcpy(card->shortname, pcm->name); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, IRQ %d, DMA %d\n", pcm->name, chip->port, chip->irq, chip->dma1); chip->set_playback_format = ad1845_playback_format; chip->set_capture_format = ad1845_capture_format; sscape->chip = chip; } _error: return err;}struct params{ int index; const char *id; unsigned port; int irq; int mpu_irq; int dma1;};static inline struct params*init_params(struct params *params, int index, const char *id, unsigned port, int irq, int mpu_irq, int dma1){ params->index = index; params->id = id; params->port = port; params->irq = irq; params->mpu_irq = mpu_irq; params->dma1 = (dma1 & 0x03); return params;}/* * Create an ALSA soundcard entry for the SoundScape, using * the given list of port, IRQ and DMA resources. */static int __devinit create_sscape(const struct params *params, snd_card_t **rcardp){ snd_card_t *card; register struct soundscape *sscape; register unsigned dma_cfg; unsigned irq_cfg; unsigned mpu_irq_cfg; struct resource *io_res; unsigned long flags; int err; /* * Check that the user didn't pass us garbage data ... */ irq_cfg = get_irq_config(params->irq); if (irq_cfg == INVALID_IRQ) { snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->irq); return -ENXIO; } mpu_irq_cfg = get_irq_config(params->mpu_irq); if (mpu_irq_cfg == INVALID_IRQ) { printk(KERN_ERR "sscape: Invalid IRQ %d\n", params->mpu_irq); return -ENXIO; } /* * Grab IO ports that we will need to probe so that we * can detect and control this hardware ... */ if ((io_res = request_region(params->port, 8, "SoundScape")) == NULL) { snd_printk(KERN_ERR "sscape: can't grab port 0x%x\n", params->port); return -EBUSY; } /* * Grab both DMA channels (OK, only one for now) ... */ if ((err = request_dma(params->dma1, "SoundScape")) < 0) { snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", params->dma1); goto _release_region; } /* * Create a new ALSA sound card entry, in anticipation * of detecting our hardware ... */ if ((card = snd_card_new(params->index, params->id, THIS_MODULE, sizeof(struct soundscape))) == NULL) { err = -ENOMEM; goto _release_dma; } sscape = get_card_soundscape(card); spin_lock_init(&sscape->lock); spin_lock_init(&sscape->fwlock); sscape->io_res = io_res; sscape->io_base = params->port; if (!detect_sscape(sscape)) { printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base); err = -ENODEV; goto _release_card; } printk(KERN_INFO "sscape: hardware detected at 0x%x, using IRQ %d, DMA %d\n", sscape->io_base, params->irq, params->dma1); /* * Now create the hardware-specific device so that we can * load the microcode into the on-board processor. * We cannot use the MPU-401 MIDI system until this firmware * has been loaded into the card. */ if ((err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw))) < 0) { printk(KERN_ERR "sscape: Failed to create firmware device\n"); goto _release_card; } strlcpy(sscape->hw->name, "SoundScape M68K", sizeof(sscape->hw->name)); sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0'; sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE; sscape->hw->ops.open = sscape_hw_open; sscape->hw->ops.release = sscape_hw_release; sscape->hw->ops.ioctl = sscape_hw_ioctl; sscape->hw->private_data = sscape; /* * Tell the on-board devices where their resources are (I think - * I can't be sure without a datasheet ... So many magic values!) */ spin_lock_irqsave(&sscape->lock, flags); activate_ad1845_unsafe(sscape->io_base); sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x00); /* disable */ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e); sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00); /* * Enable and configure the DMA channels ... */ sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50); dma_cfg = (sscape->ic_type == IC_ODIE ? 0x70 : 0x40); sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg); sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20); sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | (mpu_irq_cfg << 2) | mpu_irq_cfg); sscape_write_unsafe(sscape->io_base, GA_CDCFG_REG, 0x09 | DMA_8BIT | (params->dma1 << 4) | (irq_cfg << 1)); spin_unlock_irqrestore(&sscape->lock, flags); /* * We have now enabled the codec chip, and so we should * detect the AD1845 device ... */ if ((err = create_ad1845(card, CODEC_IO(params->port), params->irq, params->dma1)) < 0) { printk(KERN_ERR "sscape: No AD1845 device at 0x%x, IRQ %d\n", CODEC_IO(params->port), params->irq); goto _release_card; }#define MIDI_DEVNUM 0 if ((err = create_mpu401(card, MIDI_DEVNUM, MPU401_IO(params->port), params->mpu_irq)) < 0) { printk(KERN_ERR "sscape: Failed to create MPU-401 device at 0x%x\n", MPU401_IO(params->port)); goto _release_card; } /* * Enable the master IRQ ... */ sscape_write(sscape, GA_INTENA_REG, 0x80); if ((err = snd_card_register(card)) < 0) { printk(KERN_ERR "sscape: Failed to register sound card\n"); goto _release_card; } /* * Initialize mixer */ sscape->midi_vol = 0; host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100); host_write_ctrl_unsafe(sscape->io_base, 0, 100); host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100); /* * Now that we have successfully created this sound card, * it is safe to store the pointer. * NOTE: we only register the sound card's "destructor" * function now that our "constructor" has completed. */ card->private_free = soundscape_free; *rcardp = card; return 0; _release_card: snd_card_free(card); _release_dma: free_dma(params->dma1); _release_region: release_resource(io_res); kfree_nocheck(io_res); return err;}static int sscape_cards __devinitdata;static struct params sscape_params[SNDRV_CARDS] __devinitdata;#ifdef CONFIG_PNPstatic inline int __devinit get_next_autoindex(int i){ while ((i < SNDRV_CARDS) && (port[i] != SNDRV_AUTO_PORT)) { ++i; } /* while */ return i;}static inline int __devinit is_port_known(unsigned io, struct params *params, int cards){ while (--cards >= 0) { if (params[cards].port == io) return 1; } /* while */ return 0;}static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard, const struct pnp_card_device_id *pid){ struct pnp_dev *dev; static int idx = 0; int ret; /* * Allow this function to fail *quietly* if all the ISA PnP * devices were configured using module parameters instead. */ if ((idx = get_next_autoindex(idx)) >= SNDRV_CARDS) { return -ENOSPC; } /* * We have found a candidate ISA PnP card. Now we * have to check that it has the devices that we * expect it to have. * * We will NOT try and autoconfigure all of the resources * needed and then activate the card as we are assuming that * has already been done at boot-time using /proc/isapnp. * We shall simply try to give each active card the resources * that it wants. This is a sensible strategy for a modular * system where unused modules are unloaded regularly. * * This strategy is utterly useless if we compile the driver * into the kernel, of course. */ // printk(KERN_INFO "sscape: %s\n", card->name); /* * Check that we still have room for another sound card ... */ if (sscape_cards >= SNDRV_CARDS) { printk(KERN_ERR "sscape: No room for another ALSA device\n"); return -ENOSPC; } ret = -ENODEV; dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL); if (dev) { struct params *this; if (!pnp_is_active(dev)) { if (pnp_activate_dev(dev) < 0) { printk(KERN_INFO "sscape: device is inactive\n"); return -EBUSY; } } /* * Read the correct parameters off the ISA PnP bus ... */ this = init_params(&sscape_params[sscape_cards], index[idx], id[idx], pnp_port_start(dev, 0), pnp_irq(dev, 0), pnp_irq(dev, 1), pnp_dma(dev, 0)); /* * Do we know about this sound card already? */ if ( !is_port_known(this->port, sscape_params, sscape_cards) ) { snd_card_t *card; ret = create_sscape(this, &card); if (ret < 0) return ret; snd_card_set_dev(card, &pcard->card->dev); pnp_set_card_drvdata(pcard, card); ++sscape_cards; ++idx; } } return ret;}static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard){ snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); pnp_set_card_drvdata(pcard, NULL); snd_card_disconnect(card); snd_card_free_in_thread(card);}static struct pnp_card_driver sscape_pnpc_driver = { .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, .name = "sscape", .id_table = sscape_pnpids, .probe = sscape_pnp_detect, .remove = __devexit_p(sscape_pnp_remove),};#endif /* CONFIG_PNP */static int __init sscape_manual_probe(struct params *params){ int ret; unsigned i; snd_card_t *card; for (i = 0; i < SNDRV_CARDS; ++i) { /* * We do NOT probe for ports. * If we're not given a port number for this * card then we completely ignore this line * of parameters. */ if (port[i] == SNDRV_AUTO_PORT) continue; /* * Make sure we were given ALL of the other parameters. */ if ( (irq[i] == SNDRV_AUTO_IRQ) || (mpu_irq[i] == SNDRV_AUTO_IRQ) || (dma[i] == SNDRV_AUTO_DMA) ) { printk(KERN_INFO "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n"); return -ENXIO; } /* * This cards looks OK ... */ init_params(params, index[i], id[i], port[i], irq[i], mpu_irq[i], dma[i]); ret = create_sscape(params, &card); if (ret < 0) return ret; sscape_card[sscape_cards] = card; params++; sscape_cards++; } /* for */ return 0;}static void sscape_exit(void){ unsigned i;#ifdef CONFIG_PNP pnp_unregister_card_driver(&sscape_pnpc_driver);#endif for (i = 0; i < ARRAY_SIZE(sscape_card); ++i) { snd_card_free(sscape_card[i]); } /* for */}static int __init sscape_init(void){ int ret; /* * First check whether we were passed any parameters. * These MUST take precedence over ANY automatic way * of allocating cards, because the operator is * S-P-E-L-L-I-N-G it out for us... */ ret = sscape_manual_probe(sscape_params); if (ret < 0) { int i; for (i = 0; i < sscape_cards; ++i) snd_card_free(sscape_card[i]); return ret; }#ifdef CONFIG_PNP if (sscape_cards < SNDRV_CARDS) { ret = pnp_register_card_driver(&sscape_pnpc_driver); if (ret < 0) { sscape_exit(); return ret; } }#endif return 0;}module_init(sscape_init);module_exit(sscape_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -