コード例 #1
0
ファイル: via82xx_modem.c プロジェクト: 33d/linux-2.6.21-hh20
/*
 * allocate and initialize the descriptor buffers
 * periods = number of periods
 * fragsize = period size in bytes
 */
static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream,
			   struct pci_dev *pci,
			   unsigned int periods, unsigned int fragsize)
{
	unsigned int i, idx, ofs, rest;
	struct via82xx_modem *chip = snd_pcm_substream_chip(substream);

	if (dev->table.area == NULL) {
		/* the start of each lists must be aligned to 8 bytes,
		 * but the kernel pages are much bigger, so we don't care
		 */
		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
					PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
					&dev->table) < 0)
			return -ENOMEM;
	}
	if (! dev->idx_table) {
		dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
		if (! dev->idx_table)
			return -ENOMEM;
	}

	/* fill the entries */
	idx = 0;
	ofs = 0;
	for (i = 0; i < periods; i++) {
		rest = fragsize;
		/* fill descriptors for a period.
		 * a period can be split to several descriptors if it's
		 * over page boundary.
		 */
		do {
			unsigned int r;
			unsigned int flag;
			unsigned int addr;

			if (idx >= VIA_TABLE_SIZE) {
				snd_printk(KERN_ERR "via82xx: too much table size!\n");
				return -EINVAL;
			}
			addr = snd_pcm_sgbuf_get_addr(substream, ofs);
			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
			r = PAGE_SIZE - (ofs % PAGE_SIZE);
			if (rest < r)
				r = rest;
			rest -= r;
			if (! rest) {
				if (i == periods - 1)
					flag = VIA_TBL_BIT_EOL; /* buffer boundary */
				else
					flag = VIA_TBL_BIT_FLAG; /* period boundary */
			} else
				flag = 0; /* period continues to the next */
			/*
			printk(KERN_DEBUG "via: tbl %d: at %d  size %d "
			       "(rest %d)\n", idx, ofs, r, rest);
			*/
			((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
			dev->idx_table[idx].offset = ofs;
			dev->idx_table[idx].size = r;
			ofs += r;
			idx++;
		} while (rest > 0);
	}
	dev->tbl_entries = idx;
	dev->bufsize = periods * fragsize;
	dev->bufsize2 = dev->bufsize / 2;
	return 0;
}
コード例 #2
0
ファイル: oxygen_pcm.c プロジェクト: 383530895/linux
int oxygen_pcm_init(struct oxygen *chip)
{
	struct snd_pcm *pcm;
	int outs, ins;
	int err;

	outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S);
	ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 |
					      CAPTURE_0_FROM_I2S_2));
	if (outs | ins) {
		err = snd_pcm_new(chip->card, "Multichannel",
				  0, outs, ins, &pcm);
		if (err < 0)
			return err;
		if (outs)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
					&oxygen_multich_ops);
		if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
					&oxygen_rec_a_ops);
		else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
					&oxygen_rec_b_ops);
		pcm->private_data = chip;
		pcm->private_free = oxygen_pcm_free;
		strcpy(pcm->name, "Multichannel");
		if (outs)
			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
						      SNDRV_DMA_TYPE_DEV,
						      snd_dma_pci_data(chip->pci),
						      DEFAULT_BUFFER_BYTES_MULTICH,
						      BUFFER_BYTES_MAX_MULTICH);
		if (ins)
			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
						      SNDRV_DMA_TYPE_DEV,
						      snd_dma_pci_data(chip->pci),
						      DEFAULT_BUFFER_BYTES,
						      BUFFER_BYTES_MAX);
	}

	outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
	ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);
	if (outs | ins) {
		err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
		if (err < 0)
			return err;
		if (outs)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
					&oxygen_spdif_ops);
		if (ins)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
					&oxygen_rec_c_ops);
		pcm->private_data = chip;
		pcm->private_free = oxygen_pcm_free;
		strcpy(pcm->name, "Digital");
		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
						      snd_dma_pci_data(chip->pci),
						      DEFAULT_BUFFER_BYTES,
						      BUFFER_BYTES_MAX);
	}

	if (chip->has_ac97_1) {
		outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1);
		ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
	} else {
		outs = 0;
		ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);
	}
	if (outs | ins) {
		err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
				  2, outs, ins, &pcm);
		if (err < 0)
			return err;
		if (outs) {
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
					&oxygen_ac97_ops);
			oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
					     OXYGEN_REC_B_ROUTE_AC97_1,
					     OXYGEN_REC_B_ROUTE_MASK);
		}
		if (ins)
			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
					&oxygen_rec_b_ops);
		pcm->private_data = chip;
		pcm->private_free = oxygen_pcm_free;
		strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
						      snd_dma_pci_data(chip->pci),
						      DEFAULT_BUFFER_BYTES,
						      BUFFER_BYTES_MAX);
	}
	return 0;
}
コード例 #3
0
ファイル: lola.c プロジェクト: 303750856/linux-3.1
static int setup_corb_rirb(struct lola *chip)
{
	int err;
	unsigned char tmp;
	unsigned long end_time;

	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
				  snd_dma_pci_data(chip->pci),
				  PAGE_SIZE, &chip->rb);
	if (err < 0)
		return err;

	chip->corb.addr = chip->rb.addr;
	chip->corb.buf = (u32 *)chip->rb.area;
	chip->rirb.addr = chip->rb.addr + 2048;
	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);

	/* disable ringbuffer DMAs */
	lola_writeb(chip, BAR0, RIRBCTL, 0);
	lola_writeb(chip, BAR0, CORBCTL, 0);

	end_time = jiffies + msecs_to_jiffies(200);
	do {
		if (!lola_readb(chip, BAR0, RIRBCTL) &&
		    !lola_readb(chip, BAR0, CORBCTL))
			break;
		msleep(1);
	} while (time_before(jiffies, end_time));

	/* CORB set up */
	lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
	lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
	/* set the corb size to 256 entries */
	lola_writeb(chip, BAR0, CORBSIZE, 0x02);
	/* set the corb write pointer to 0 */
	lola_writew(chip, BAR0, CORBWP, 0);
	/* reset the corb hw read pointer */
	lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
	/* enable corb dma */
	lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
	/* clear flags if set */
	tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
	if (tmp)
		lola_writeb(chip, BAR0, CORBSTS, tmp);
	chip->corb.wp = 0;

	/* RIRB set up */
	lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
	lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
	/* set the rirb size to 256 entries */
	lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
	/* reset the rirb hw write pointer */
	lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
	/* set N=1, get RIRB response interrupt for new entry */
	lola_writew(chip, BAR0, RINTCNT, 1);
	/* enable rirb dma and response irq */
	lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
	/* clear flags if set */
	tmp =  lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
	if (tmp)
		lola_writeb(chip, BAR0, RIRBSTS, tmp);
	chip->rirb.rp = chip->rirb.cmds = 0;

	return 0;
}
コード例 #4
0
static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream,
			   struct pci_dev *pci,
			   unsigned int periods, unsigned int fragsize)
{
	unsigned int i, idx, ofs, rest;
	struct via82xx_modem *chip = snd_pcm_substream_chip(substream);

	if (dev->table.area == NULL) {
		/*                                                    
                                                           
   */
		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
					PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
					&dev->table) < 0)
			return -ENOMEM;
	}
	if (! dev->idx_table) {
		dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
		if (! dev->idx_table)
			return -ENOMEM;
	}

	/*                  */
	idx = 0;
	ofs = 0;
	for (i = 0; i < periods; i++) {
		rest = fragsize;
		/*                               
                                                         
                        
   */
		do {
			unsigned int r;
			unsigned int flag;
			unsigned int addr;

			if (idx >= VIA_TABLE_SIZE) {
				snd_printk(KERN_ERR "via82xx: too much table size!\n");
				return -EINVAL;
			}
			addr = snd_pcm_sgbuf_get_addr(substream, ofs);
			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
			r = PAGE_SIZE - (ofs % PAGE_SIZE);
			if (rest < r)
				r = rest;
			rest -= r;
			if (! rest) {
				if (i == periods - 1)
					flag = VIA_TBL_BIT_EOL; /*                 */
				else
					flag = VIA_TBL_BIT_FLAG; /*                 */
			} else
				flag = 0; /*                              */
			/*
                                                   
                                            
   */
			((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
			dev->idx_table[idx].offset = ofs;
			dev->idx_table[idx].size = r;
			ofs += r;
			idx++;
		} while (rest > 0);
	}
	dev->tbl_entries = idx;
	dev->bufsize = periods * fragsize;
	dev->bufsize2 = dev->bufsize / 2;
	return 0;
}
コード例 #5
0
ファイル: atiixp.c プロジェクト: 3null/linux
static int snd_atiixp_pcm_new(struct atiixp *chip)
{
	struct snd_pcm *pcm;
	struct snd_pcm_chmap *chmap;
	struct snd_ac97_bus *pbus = chip->ac97_bus;
	int err, i, num_pcms;

	/* initialize constants */
	chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops;
	chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops;
	if (! chip->spdif_over_aclink)
		chip->dmas[ATI_DMA_SPDIF].ops = &snd_atiixp_spdif_dma_ops;

	/* assign AC97 pcm */
	if (chip->spdif_over_aclink)
		num_pcms = 3;
	else
		num_pcms = 2;
	err = snd_ac97_pcm_assign(pbus, num_pcms, atiixp_pcm_defs);
	if (err < 0)
		return err;
	for (i = 0; i < num_pcms; i++)
		chip->pcms[i] = &pbus->pcms[i];

	chip->max_channels = 2;
	if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
		if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_LFE))
			chip->max_channels = 6;
		else
			chip->max_channels = 4;
	}

	/* PCM #0: analog I/O */
	err = snd_pcm_new(chip->card, "ATI IXP AC97",
			  ATI_PCMDEV_ANALOG, 1, 1, &pcm);
	if (err < 0)
		return err;
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
	pcm->private_data = chip;
	strcpy(pcm->name, "ATI IXP AC97");
	chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;

	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
					      snd_dma_pci_data(chip->pci),
					      64*1024, 128*1024);

	err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
				     snd_pcm_alt_chmaps, chip->max_channels, 0,
				     &chmap);
	if (err < 0)
		return err;
	chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
	chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;

	/* no SPDIF support on codec? */
	if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
		return 0;
		
	/* FIXME: non-48k sample rate doesn't work on my test machine with AD1888 */
	if (chip->pcms[ATI_PCM_SPDIF])
		chip->pcms[ATI_PCM_SPDIF]->rates = SNDRV_PCM_RATE_48000;

	/* PCM #1: spdif playback */
	err = snd_pcm_new(chip->card, "ATI IXP IEC958",
			  ATI_PCMDEV_DIGITAL, 1, 0, &pcm);
	if (err < 0)
		return err;
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops);
	pcm->private_data = chip;
	if (chip->spdif_over_aclink)
		strcpy(pcm->name, "ATI IXP IEC958 (AC97)");
	else
		strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
	chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;

	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
					      snd_dma_pci_data(chip->pci),
					      64*1024, 128*1024);

	/* pre-select AC97 SPDIF slots 10/11 */
	for (i = 0; i < NUM_ATI_CODECS; i++) {
		if (chip->ac97[i])
			snd_ac97_update_bits(chip->ac97[i],
					     AC97_EXTENDED_STATUS,
					     0x03 << 4, 0x03 << 4);
	}

	return 0;
}
コード例 #6
0
ファイル: emu10k1.c プロジェクト: nos1609/Chrono_Kernel-1
static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
					    const struct pci_device_id *pci_id)
{
	static int dev;
	struct snd_card *card;
	struct snd_emu10k1 *emu;
#ifdef ENABLE_SYNTH
	struct snd_seq_device *wave = NULL;
#endif
	int err;

	if (dev >= SNDRV_CARDS)
        	return -ENODEV;
	if (!enable[dev]) {
		dev++;
		return -ENOENT;
	}

	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
	if (err < 0)
		return err;
	if (max_buffer_size[dev] < 32)
		max_buffer_size[dev] = 32;
	else if (max_buffer_size[dev] > 1024)
		max_buffer_size[dev] = 1024;
	if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
				      (long)max_buffer_size[dev] * 1024 * 1024,
				      enable_ir[dev], subsystem[dev],
				      &emu)) < 0)
		goto error;
	card->private_data = emu;
	emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
	if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
		goto error;
	if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
		goto error;
	if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0)
		goto error;
	/* This stores the periods table. */
	if (emu->card_capabilities->ca0151_chip) { /* P16V */	
		if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
					       1024, &emu->p16v_buffer)) < 0)
			goto error;
	}

	if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
		goto error;
	
	if ((err = snd_emu10k1_timer(emu, 0)) < 0)
		goto error;

	if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0)
		goto error;
	if (emu->card_capabilities->ca0151_chip) { /* P16V */
		if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0)
			goto error;
	}
	if (emu->audigy) {
		if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
			goto error;
	} else {
		if ((err = snd_emu10k1_midi(emu)) < 0)
			goto error;
	}
	if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0)
		goto error;
#ifdef ENABLE_SYNTH
	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
			       sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
	    wave == NULL) {
#ifdef CONFIG_DEBUG_PRINTK
		snd_printk(KERN_WARNING "can't initialize Emu10k1 wavetable synth\n");
#else
		;
#endif
	} else {
		struct snd_emu10k1_synth_arg *arg;
		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
		strcpy(wave->name, "Emu-10k1 Synth");
		arg->hwptr = emu;
		arg->index = 1;
		arg->seq_ports = seq_ports[dev];
		arg->max_voices = max_synth_voices[dev];
	}
#endif
 
	strcpy(card->driver, emu->card_capabilities->driver);
	strcpy(card->shortname, emu->card_capabilities->name);
	snprintf(card->longname, sizeof(card->longname),
		 "%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
		 card->shortname, emu->revision, emu->serial, emu->port, emu->irq);

	if ((err = snd_card_register(card)) < 0)
		goto error;

	pci_set_drvdata(pci, card);
	dev++;
	return 0;

 error:
	snd_card_free(card);
	return err;
}
コード例 #7
0
ファイル: mixart.c プロジェクト: 274914765/C
int snd_mixart_kill_ref_pipe(struct mixart_mgr *mgr,
                 struct mixart_pipe *pipe, int monitoring)
{
    int err = 0;

    if(pipe->status == PIPE_UNDEFINED)
        return 0;

    if(monitoring)
        pipe->monitoring = 0;
    else
        pipe->references--;

    if((pipe->references <= 0) && (pipe->monitoring == 0)) {

        struct mixart_msg request;
        struct mixart_delete_group_resp delete_resp;

        /* release the clock */
        err = mixart_set_clock( mgr, pipe, 0);
        if( err < 0 ) {
            snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
        }

        /* stop the pipe */
        err = mixart_set_pipe_state(mgr, pipe, 0);
        if( err < 0 ) {
            snd_printk(KERN_ERR "error stopping pipe!\n");
        }

        request.message_id = MSG_STREAM_DELETE_GROUP;
        request.uid = (struct mixart_uid){0,0};
        request.data = &pipe->group_uid;            /* the streaming group ! */
        request.size = sizeof(pipe->group_uid);

        /* delete the pipe */
        err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
        if ((err < 0) || (delete_resp.status != 0)) {
            snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
        }

        pipe->group_uid = (struct mixart_uid){0,0};
        pipe->stream_count = 0;
        pipe->status = PIPE_UNDEFINED;
    }

    return err;
}

static int mixart_set_stream_state(struct mixart_stream *stream, int start)
{
    struct snd_mixart *chip;
    struct mixart_stream_state_req stream_state_req;
    struct mixart_msg request;

    if(!stream->substream)
        return -EINVAL;

    memset(&stream_state_req, 0, sizeof(stream_state_req));
    stream_state_req.stream_count = 1;
    stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
    stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;

    if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
        request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
    else
        request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;

    request.uid = (struct mixart_uid){0,0};
    request.data = &stream_state_req;
    request.size = sizeof(stream_state_req);

    stream->abs_period_elapsed = 0;            /* reset stream pos      */
    stream->buf_periods = 0;
    stream->buf_period_frag = 0;

    chip = snd_pcm_substream_chip(stream->substream);

    return snd_mixart_send_msg_nonblock(chip->mgr, &request);
}

/*
 *  Trigger callback
 */

static int snd_mixart_trigger(struct snd_pcm_substream *subs, int cmd)
{
    struct mixart_stream *stream = subs->runtime->private_data;

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:

        snd_printdd("SNDRV_PCM_TRIGGER_START\n");

        /* START_STREAM */
        if( mixart_set_stream_state(stream, 1) )
            return -EINVAL;

        stream->status = MIXART_STREAM_STATUS_RUNNING;

        break;
    case SNDRV_PCM_TRIGGER_STOP:

        /* STOP_STREAM */
        if( mixart_set_stream_state(stream, 0) )
            return -EINVAL;

        stream->status = MIXART_STREAM_STATUS_OPEN;

        snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");

        break;

    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        /* TODO */
        stream->status = MIXART_STREAM_STATUS_PAUSE;
        snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
        break;
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        /* TODO */
        stream->status = MIXART_STREAM_STATUS_RUNNING;
        snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static int mixart_sync_nonblock_events(struct mixart_mgr *mgr)
{
    unsigned long timeout = jiffies + HZ;
    while (atomic_read(&mgr->msg_processed) > 0) {
        if (time_after(jiffies, timeout)) {
            snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n");
            return -EBUSY;
        }
        schedule_timeout_uninterruptible(1);
    }
    return 0;
}

/*
 *  prepare callback for all pcms
 */
static int snd_mixart_prepare(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_stream *stream = subs->runtime->private_data;

    /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */

    snd_printdd("snd_mixart_prepare\n");

    mixart_sync_nonblock_events(chip->mgr);

    /* only the first stream can choose the sample rate */
    /* the further opened streams will be limited to its frequency (see open) */
    if(chip->mgr->ref_count_rate == 1)
        chip->mgr->sample_rate = subs->runtime->rate;

    /* set the clock only once (first stream) on the same pipe */
    if(stream->pipe->references == 1) {
        if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
            return -EINVAL;
    }

    return 0;
}


static int mixart_set_format(struct mixart_stream *stream, snd_pcm_format_t format)
{
    int err;
    struct snd_mixart *chip;
    struct mixart_msg request;
    struct mixart_stream_param_desc stream_param;
    struct mixart_return_uid resp;

    chip = snd_pcm_substream_chip(stream->substream);

    memset(&stream_param, 0, sizeof(stream_param));

    stream_param.coding_type = CT_LINEAR;
    stream_param.number_of_channel = stream->channels;

    stream_param.sampling_freq = chip->mgr->sample_rate;
    if(stream_param.sampling_freq == 0)
        stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */

    switch(format){
    case SNDRV_PCM_FORMAT_U8:
        stream_param.sample_type = ST_INTEGER_8;
        stream_param.sample_size = 8;
        break;
    case SNDRV_PCM_FORMAT_S16_LE:
        stream_param.sample_type = ST_INTEGER_16LE;
        stream_param.sample_size = 16;
        break;
    case SNDRV_PCM_FORMAT_S16_BE:
        stream_param.sample_type = ST_INTEGER_16BE;
        stream_param.sample_size = 16;
        break;
    case SNDRV_PCM_FORMAT_S24_3LE:
        stream_param.sample_type = ST_INTEGER_24LE;
        stream_param.sample_size = 24;
        break;
    case SNDRV_PCM_FORMAT_S24_3BE:
        stream_param.sample_type = ST_INTEGER_24BE;
        stream_param.sample_size = 24;
        break;
    case SNDRV_PCM_FORMAT_FLOAT_LE:
        stream_param.sample_type = ST_FLOATING_POINT_32LE;
        stream_param.sample_size = 32;
        break;
    case  SNDRV_PCM_FORMAT_FLOAT_BE:
        stream_param.sample_type = ST_FLOATING_POINT_32BE;
        stream_param.sample_size = 32;
        break;
    default:
        snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n");
        return -EINVAL;
    }

    snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
           stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);

    /* TODO: what else to configure ? */
    /* stream_param.samples_per_frame = 2; */
    /* stream_param.bytes_per_frame = 4; */
    /* stream_param.bytes_per_sample = 2; */

    stream_param.pipe_count = 1;      /* set to 1 */
    stream_param.stream_count = 1;    /* set to 1 */
    stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
    stream_param.stream_desc[0].stream_idx = stream->substream->number;

    request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
    request.uid = (struct mixart_uid){0,0};
    request.data = &stream_param;
    request.size = sizeof(stream_param);

    err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
    if((err < 0) || resp.error_code) {
        snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
        return -EINVAL;
    }
    return 0;
}


/*
 *  HW_PARAMS callback for all pcms
 */
static int snd_mixart_hw_params(struct snd_pcm_substream *subs,
                                struct snd_pcm_hw_params *hw)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr *mgr = chip->mgr;
    struct mixart_stream *stream = subs->runtime->private_data;
    snd_pcm_format_t format;
    int err;
    int channels;

    /* set up channels */
    channels = params_channels(hw);

    /*  set up format for the stream */
    format = params_format(hw);

    mutex_lock(&mgr->setup_mutex);

    /* update the stream levels */
    if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
        int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
        if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
            mixart_update_playback_stream_level(chip, is_aes, subs->number);
        else
            mixart_update_capture_stream_level( chip, is_aes);
    }

    stream->channels = channels;

    /* set the format to the board */
    err = mixart_set_format(stream, format);
    if(err < 0) {
        return err;
    }

    /* allocate buffer */
    err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));

    if (err > 0) {
        struct mixart_bufferinfo *bufferinfo;
        int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
        if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
            i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
        }
        
        bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area;
        bufferinfo[i].buffer_address = subs->runtime->dma_addr;
        bufferinfo[i].available_length = subs->runtime->dma_bytes;
        /* bufferinfo[i].buffer_id  is already defined */

        snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i,
                bufferinfo[i].buffer_address,
                bufferinfo[i].available_length,
                subs->number);
    }
    mutex_unlock(&mgr->setup_mutex);

    return err;
}

static int snd_mixart_hw_free(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    snd_pcm_lib_free_pages(subs);
    mixart_sync_nonblock_events(chip->mgr);
    return 0;
}



/*
 *  TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
 */
static struct snd_pcm_hardware snd_mixart_analog_caps =
{
    .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                  SNDRV_PCM_INFO_MMAP_VALID |
                  SNDRV_PCM_INFO_PAUSE),
    .formats      = ( SNDRV_PCM_FMTBIT_U8 |
                  SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
                  SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
                  SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
    .rates            = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
    .rate_min         = 8000,
    .rate_max         = 48000,
    .channels_min     = 1,
    .channels_max     = 2,
    .buffer_bytes_max = (32*1024),
    .period_bytes_min = 256,                  /* 256 frames U8 mono*/
    .period_bytes_max = (16*1024),
    .periods_min      = 2,
    .periods_max      = (32*1024/256),
};

static struct snd_pcm_hardware snd_mixart_digital_caps =
{
    .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                  SNDRV_PCM_INFO_MMAP_VALID |
                  SNDRV_PCM_INFO_PAUSE),
    .formats      = ( SNDRV_PCM_FMTBIT_U8 |
                  SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
                  SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
                  SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
    .rates            = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
    .rate_min         = 32000,
    .rate_max         = 48000,
    .channels_min     = 1,
    .channels_max     = 2,
    .buffer_bytes_max = (32*1024),
    .period_bytes_min = 256,                  /* 256 frames U8 mono*/
    .period_bytes_max = (16*1024),
    .periods_min      = 2,
    .periods_max      = (32*1024/256),
};


static int snd_mixart_playback_open(struct snd_pcm_substream *subs)
{
    struct snd_mixart            *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr        *mgr = chip->mgr;
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct snd_pcm *pcm = subs->pcm;
    struct mixart_stream     *stream;
    struct mixart_pipe       *pipe;
    int err = 0;
    int pcm_number;

    mutex_lock(&mgr->setup_mutex);

    if ( pcm == chip->pcm ) {
        pcm_number = MIXART_PCM_ANALOG;
        runtime->hw = snd_mixart_analog_caps;
    } else {
        snd_assert ( pcm == chip->pcm_dig ); 
        pcm_number = MIXART_PCM_DIGITAL;
        runtime->hw = snd_mixart_digital_caps;
    }
    snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);

    /* get stream info */
    stream = &(chip->playback_stream[pcm_number][subs->number]);

    if (stream->status != MIXART_STREAM_STATUS_FREE){
        /* streams in use */
        snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
        err = -EBUSY;
        goto _exit_open;
    }

    /* get pipe pointer (out pipe) */
    pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);

    if (pipe == NULL) {
        err = -EINVAL;
        goto _exit_open;
    }

    /* start the pipe if necessary */
    err = mixart_set_pipe_state(chip->mgr, pipe, 1);
    if( err < 0 ) {
        snd_printk(KERN_ERR "error starting pipe!\n");
        snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
        err = -EINVAL;
        goto _exit_open;
    }

    stream->pipe        = pipe;
    stream->pcm_number  = pcm_number;
    stream->status      = MIXART_STREAM_STATUS_OPEN;
    stream->substream   = subs;
    stream->channels    = 0; /* not configured yet */

    runtime->private_data = stream;

    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);

    /* if a sample rate is already used, another stream cannot change */
    if(mgr->ref_count_rate++) {
        if(mgr->sample_rate) {
            runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
        }
    }

 _exit_open:
    mutex_unlock(&mgr->setup_mutex);

    return err;
}


static int snd_mixart_capture_open(struct snd_pcm_substream *subs)
{
    struct snd_mixart            *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr        *mgr = chip->mgr;
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct snd_pcm *pcm = subs->pcm;
    struct mixart_stream     *stream;
    struct mixart_pipe       *pipe;
    int err = 0;
    int pcm_number;

    mutex_lock(&mgr->setup_mutex);

    if ( pcm == chip->pcm ) {
        pcm_number = MIXART_PCM_ANALOG;
        runtime->hw = snd_mixart_analog_caps;
    } else {
        snd_assert ( pcm == chip->pcm_dig ); 
        pcm_number = MIXART_PCM_DIGITAL;
        runtime->hw = snd_mixart_digital_caps;
    }

    runtime->hw.channels_min = 2; /* for instance, no mono */

    snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);

    /* get stream info */
    stream = &(chip->capture_stream[pcm_number]);

    if (stream->status != MIXART_STREAM_STATUS_FREE){
        /* streams in use */
        snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
        err = -EBUSY;
        goto _exit_open;
    }

    /* get pipe pointer (in pipe) */
    pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);

    if (pipe == NULL) {
        err = -EINVAL;
        goto _exit_open;
    }

    /* start the pipe if necessary */
    err = mixart_set_pipe_state(chip->mgr, pipe, 1);
    if( err < 0 ) {
        snd_printk(KERN_ERR "error starting pipe!\n");
        snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
        err = -EINVAL;
        goto _exit_open;
    }

    stream->pipe        = pipe;
    stream->pcm_number  = pcm_number;
    stream->status      = MIXART_STREAM_STATUS_OPEN;
    stream->substream   = subs;
    stream->channels    = 0; /* not configured yet */

    runtime->private_data = stream;

    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);

    /* if a sample rate is already used, another stream cannot change */
    if(mgr->ref_count_rate++) {
        if(mgr->sample_rate) {
            runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
        }
    }

 _exit_open:
    mutex_unlock(&mgr->setup_mutex);

    return err;
}



static int snd_mixart_close(struct snd_pcm_substream *subs)
{
    struct snd_mixart *chip = snd_pcm_substream_chip(subs);
    struct mixart_mgr *mgr = chip->mgr;
    struct mixart_stream *stream = subs->runtime->private_data;

    mutex_lock(&mgr->setup_mutex);

    snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);

    /* sample rate released */
    if(--mgr->ref_count_rate == 0) {
        mgr->sample_rate = 0;
    }

    /* delete pipe */
    if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {

        snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
    }

    stream->pipe      = NULL;
    stream->status    = MIXART_STREAM_STATUS_FREE;
    stream->substream = NULL;

    mutex_unlock(&mgr->setup_mutex);
    return 0;
}


static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *subs)
{
    struct snd_pcm_runtime *runtime = subs->runtime;
    struct mixart_stream   *stream  = runtime->private_data;

    return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
}



static struct snd_pcm_ops snd_mixart_playback_ops = {
    .open      = snd_mixart_playback_open,
    .close     = snd_mixart_close,
    .ioctl     = snd_pcm_lib_ioctl,
    .prepare   = snd_mixart_prepare,
    .hw_params = snd_mixart_hw_params,
    .hw_free   = snd_mixart_hw_free,
    .trigger   = snd_mixart_trigger,
    .pointer   = snd_mixart_stream_pointer,
};

static struct snd_pcm_ops snd_mixart_capture_ops = {
    .open      = snd_mixart_capture_open,
    .close     = snd_mixart_close,
    .ioctl     = snd_pcm_lib_ioctl,
    .prepare   = snd_mixart_prepare,
    .hw_params = snd_mixart_hw_params,
    .hw_free   = snd_mixart_hw_free,
    .trigger   = snd_mixart_trigger,
    .pointer   = snd_mixart_stream_pointer,
};

static void preallocate_buffers(struct snd_mixart *chip, struct snd_pcm *pcm)
{
#if 0
    struct snd_pcm_substream *subs;
    int stream;

    for (stream = 0; stream < 2; stream++) {
        int idx = 0;
        for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
            /* set up the unique device id with the chip index */
            subs->dma_device.id = subs->pcm->device << 16 |
                subs->stream << 8 | (subs->number + 1) |
                (chip->chip_idx + 1) << 24;
    }
#endif
    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                          snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024);
}

/*
 */
static int snd_mixart_pcm_analog(struct snd_mixart *chip)
{
    int err;
    struct snd_pcm *pcm;
    char name[32];

    sprintf(name, "miXart analog %d", chip->chip_idx);
    if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
                   MIXART_PLAYBACK_STREAMS,
                   MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
        snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
        return err;
    }

    pcm->private_data = chip;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);

    pcm->info_flags = 0;
    strcpy(pcm->name, name);

    preallocate_buffers(chip, pcm);

    chip->pcm = pcm;
    return 0;
}


/*
 */
static int snd_mixart_pcm_digital(struct snd_mixart *chip)
{
    int err;
    struct snd_pcm *pcm;
    char name[32];

    sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
    if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
                   MIXART_PLAYBACK_STREAMS,
                   MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
        snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
        return err;
    }

    pcm->private_data = chip;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);

    pcm->info_flags = 0;
    strcpy(pcm->name, name);

    preallocate_buffers(chip, pcm);

    chip->pcm_dig = pcm;
    return 0;
}

static int snd_mixart_chip_free(struct snd_mixart *chip)
{
    kfree(chip);
    return 0;
}

static int snd_mixart_chip_dev_free(struct snd_device *device)
{
    struct snd_mixart *chip = device->device_data;
    return snd_mixart_chip_free(chip);
}


/*
 */
static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *card, int idx)
{
    int err;
    struct snd_mixart *chip;
    static struct snd_device_ops ops = {
        .dev_free = snd_mixart_chip_dev_free,
    };

    mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
    if (! chip) {
        snd_printk(KERN_ERR "cannot allocate chip\n");
        return -ENOMEM;
    }

    chip->card = card;
    chip->chip_idx = idx;
    chip->mgr = mgr;

    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
        snd_mixart_chip_free(chip);
        return err;
    }

    snd_card_set_dev(card, &mgr->pci->dev);

    return 0;
}
コード例 #8
0
ファイル: mixart.c プロジェクト: 274914765/C
/*
 *    probe function - creates the card manager
 */
static int __devinit snd_mixart_probe(struct pci_dev *pci,
                      const struct pci_device_id *pci_id)
{
    static int dev;
    struct mixart_mgr *mgr;
    unsigned int i;
    int err;
    size_t size;

    /*
     */
    if (dev >= SNDRV_CARDS)
        return -ENODEV;
    if (! enable[dev]) {
        dev++;
        return -ENOENT;
    }

    /* enable PCI device */
    if ((err = pci_enable_device(pci)) < 0)
        return err;
    pci_set_master(pci);

    /* check if we can restrict PCI DMA transfers to 32 bits */
    if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) {
        snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
        pci_disable_device(pci);
        return -ENXIO;
    }

    /*
     */
    mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
    if (! mgr) {
        pci_disable_device(pci);
        return -ENOMEM;
    }

    mgr->pci = pci;
    mgr->irq = -1;

    /* resource assignment */
    if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
        kfree(mgr);
        pci_disable_device(pci);
        return err;
    }
    for (i = 0; i < 2; i++) {
        mgr->mem[i].phys = pci_resource_start(pci, i);
        mgr->mem[i].virt = ioremap_nocache(mgr->mem[i].phys,
                           pci_resource_len(pci, i));
        if (!mgr->mem[i].virt) {
                printk(KERN_ERR "unable to remap resource 0x%lx\n",
                   mgr->mem[i].phys);
            snd_mixart_free(mgr);
            return -EBUSY;
        }
    }

    if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED,
            CARD_NAME, mgr)) {
        snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
        snd_mixart_free(mgr);
        return -EBUSY;
    }
    mgr->irq = pci->irq;

    sprintf(mgr->shortname, "Digigram miXart");
    sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);

    /* ISR spinlock  */
    spin_lock_init(&mgr->lock);

    /* init mailbox  */
    mgr->msg_fifo_readptr = 0;
    mgr->msg_fifo_writeptr = 0;

    spin_lock_init(&mgr->msg_lock);
    mutex_init(&mgr->msg_mutex);
    init_waitqueue_head(&mgr->msg_sleep);
    atomic_set(&mgr->msg_processed, 0);

    /* init setup mutex*/
    mutex_init(&mgr->setup_mutex);

    /* init message taslket */
    tasklet_init(&mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);

    /* card assignment */
    mgr->num_cards = MIXART_MAX_CARDS; /* 4  FIXME: configurable? */
    for (i = 0; i < mgr->num_cards; i++) {
        struct snd_card *card;
        char tmpid[16];
        int idx;

        if (index[dev] < 0)
            idx = index[dev];
        else
            idx = index[dev] + i;
        snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
        card = snd_card_new(idx, tmpid, THIS_MODULE, 0);

        if (! card) {
            snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
            snd_mixart_free(mgr);
            return -ENOMEM;
        }

        strcpy(card->driver, CARD_NAME);
        sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
        sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);

        if ((err = snd_mixart_create(mgr, card, i)) < 0) {
            snd_mixart_free(mgr);
            return err;
        }

        if(i==0) {
            /* init proc interface only for chip0 */
            snd_mixart_proc_init(mgr->chip[i]);
        }

        if ((err = snd_card_register(card)) < 0) {
            snd_mixart_free(mgr);
            return err;
        }
    }

    /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */
    mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;

    /* create array of streaminfo */
    size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
                sizeof(struct mixart_flowinfo)) );
    if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
                size, &mgr->flowinfo) < 0) {
        snd_mixart_free(mgr);
        return -ENOMEM;
    }
    /* init streaminfo_array */
    memset(mgr->flowinfo.area, 0, size);

    /* create array of bufferinfo */
    size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS *
                sizeof(struct mixart_bufferinfo)) );
    if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
                size, &mgr->bufferinfo) < 0) {
        snd_mixart_free(mgr);
        return -ENOMEM;
    }
    /* init bufferinfo_array */
    memset(mgr->bufferinfo.area, 0, size);

    /* set up firmware */
    err = snd_mixart_setup_firmware(mgr);
    if (err < 0) {
        snd_mixart_free(mgr);
        return err;
    }

    pci_set_drvdata(pci, mgr);
    dev++;
    return 0;
}
コード例 #9
0
ファイル: atiixp_modem.c プロジェクト: 3sOx/asuswrt-merlin
static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
			       struct atiixp_dma *dma, int pcm_type)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	int err;
	static unsigned int rates[] = { 8000,  9600, 12000, 16000 };
	static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
		.count = ARRAY_SIZE(rates),
		.list = rates,
		.mask = 0,
	};

	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);

	if (dma->opened)
		return -EBUSY;
	dma->substream = substream;
	runtime->hw = snd_atiixp_pcm_hw;
	dma->ac97_pcm_type = pcm_type;
	if ((err = snd_pcm_hw_constraint_list(runtime, 0,
					      SNDRV_PCM_HW_PARAM_RATE,
					      &hw_constraints_rates)) < 0)
		return err;
	if ((err = snd_pcm_hw_constraint_integer(runtime,
						 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
		return err;
	runtime->private_data = dma;

	/* enable DMA bits */
	spin_lock_irq(&chip->reg_lock);
	dma->ops->enable_dma(chip, 1);
	spin_unlock_irq(&chip->reg_lock);
	dma->opened = 1;

	return 0;
}

static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
				struct atiixp_dma *dma)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	/* disable DMA bits */
	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
	spin_lock_irq(&chip->reg_lock);
	dma->ops->enable_dma(chip, 0);
	spin_unlock_irq(&chip->reg_lock);
	dma->substream = NULL;
	dma->opened = 0;
	return 0;
}

/*
 */
static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	int err;

	mutex_lock(&chip->open_mutex);
	err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
	mutex_unlock(&chip->open_mutex);
	if (err < 0)
		return err;
	return 0;
}

static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	int err;
	mutex_lock(&chip->open_mutex);
	err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
	mutex_unlock(&chip->open_mutex);
	return err;
}

static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1);
}

static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
{
	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
	return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]);
}


/* AC97 playback */
static struct snd_pcm_ops snd_atiixp_playback_ops = {
	.open =		snd_atiixp_playback_open,
	.close =	snd_atiixp_playback_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_atiixp_pcm_hw_params,
	.hw_free =	snd_atiixp_pcm_hw_free,
	.prepare =	snd_atiixp_playback_prepare,
	.trigger =	snd_atiixp_pcm_trigger,
	.pointer =	snd_atiixp_pcm_pointer,
};

/* AC97 capture */
static struct snd_pcm_ops snd_atiixp_capture_ops = {
	.open =		snd_atiixp_capture_open,
	.close =	snd_atiixp_capture_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_atiixp_pcm_hw_params,
	.hw_free =	snd_atiixp_pcm_hw_free,
	.prepare =	snd_atiixp_capture_prepare,
	.trigger =	snd_atiixp_pcm_trigger,
	.pointer =	snd_atiixp_pcm_pointer,
};

static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
	.type = ATI_DMA_PLAYBACK,
	.llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR,
	.dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR,
	.enable_dma = atiixp_out_enable_dma,
	.enable_transfer = atiixp_out_enable_transfer,
	.flush_dma = atiixp_out_flush_dma,
};
	
static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
	.type = ATI_DMA_CAPTURE,
	.llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR,
	.dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR,
	.enable_dma = atiixp_in_enable_dma,
	.enable_transfer = atiixp_in_enable_transfer,
	.flush_dma = atiixp_in_flush_dma,
};

static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip)
{
	struct snd_pcm *pcm;
	int err;

	/* initialize constants */
	chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops;
	chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops;

	/* PCM #0: analog I/O */
	err = snd_pcm_new(chip->card, "ATI IXP MC97", ATI_PCMDEV_ANALOG, 1, 1, &pcm);
	if (err < 0)
		return err;
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
	pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
	pcm->private_data = chip;
	strcpy(pcm->name, "ATI IXP MC97");
	chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;

	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
					      snd_dma_pci_data(chip->pci),
					      64*1024, 128*1024);

	return 0;
}



/*
 * interrupt handler
 */
static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
{
	struct atiixp_modem *chip = dev_id;
	unsigned int status;

	status = atiixp_read(chip, ISR);

	if (! status)
		return IRQ_NONE;

	/* process audio DMA */
	if (status & ATI_REG_ISR_MODEM_OUT1_XRUN)
		snd_atiixp_xrun_dma(chip,  &chip->dmas[ATI_DMA_PLAYBACK]);
	else if (status & ATI_REG_ISR_MODEM_OUT1_STATUS)
		snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
	if (status & ATI_REG_ISR_MODEM_IN_XRUN)
		snd_atiixp_xrun_dma(chip,  &chip->dmas[ATI_DMA_CAPTURE]);
	else if (status & ATI_REG_ISR_MODEM_IN_STATUS)
		snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);

	/* for codec detection */
	if (status & CODEC_CHECK_BITS) {
		unsigned int detected;
		detected = status & CODEC_CHECK_BITS;
		spin_lock(&chip->reg_lock);
		chip->codec_not_ready_bits |= detected;
		atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
		spin_unlock(&chip->reg_lock);
	}

	/* ack */
	atiixp_write(chip, ISR, status);

	return IRQ_HANDLED;
}


/*
 * ac97 mixer section
 */

static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
{
	struct snd_ac97_bus *pbus;
	struct snd_ac97_template ac97;
	int i, err;
	int codec_count;
	static struct snd_ac97_bus_ops ops = {
		.write = snd_atiixp_ac97_write,
		.read = snd_atiixp_ac97_read,
	};
	static unsigned int codec_skip[NUM_ATI_CODECS] = {
		ATI_REG_ISR_CODEC0_NOT_READY,
		ATI_REG_ISR_CODEC1_NOT_READY,
		ATI_REG_ISR_CODEC2_NOT_READY,
	};

	if (snd_atiixp_codec_detect(chip) < 0)
		return -ENXIO;

	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
		return err;
	pbus->clock = clock;
	chip->ac97_bus = pbus;

	codec_count = 0;
	for (i = 0; i < NUM_ATI_CODECS; i++) {
		if (chip->codec_not_ready_bits & codec_skip[i])
			continue;
		memset(&ac97, 0, sizeof(ac97));
		ac97.private_data = chip;
		ac97.pci = chip->pci;
		ac97.num = i;
		ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
		if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
			chip->ac97[i] = NULL; /* to be sure */
			snd_printdd("atiixp-modem: codec %d not available for modem\n", i);
			continue;
		}
		codec_count++;
	}

	if (! codec_count) {
		snd_printk(KERN_ERR "atiixp-modem: no codec available\n");
		return -ENODEV;
	}

	/* snd_ac97_tune_hardware(chip->ac97, ac97_quirks); */

	return 0;
}


#ifdef CONFIG_PM
/*
 * power management
 */
static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct atiixp_modem *chip = card->private_data;
	int i;

	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	for (i = 0; i < NUM_ATI_PCMDEVS; i++)
		snd_pcm_suspend_all(chip->pcmdevs[i]);
	for (i = 0; i < NUM_ATI_CODECS; i++)
		snd_ac97_suspend(chip->ac97[i]);
	snd_atiixp_aclink_down(chip);
	snd_atiixp_chip_stop(chip);

	pci_disable_device(pci);
	pci_save_state(pci);
	pci_set_power_state(pci, pci_choose_state(pci, state));
	return 0;
}

static int snd_atiixp_resume(struct pci_dev *pci)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct atiixp_modem *chip = card->private_data;
	int i;

	pci_set_power_state(pci, PCI_D0);
	pci_restore_state(pci);
	if (pci_enable_device(pci) < 0) {
		printk(KERN_ERR "atiixp-modem: pci_enable_device failed, "
		       "disabling device\n");
		snd_card_disconnect(card);
		return -EIO;
	}
	pci_set_master(pci);

	snd_atiixp_aclink_reset(chip);
	snd_atiixp_chip_start(chip);

	for (i = 0; i < NUM_ATI_CODECS; i++)
		snd_ac97_resume(chip->ac97[i]);

	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
}
#endif /* CONFIG_PM */


#ifdef CONFIG_PROC_FS
/*
 * proc interface for register dump
 */

static void snd_atiixp_proc_read(struct snd_info_entry *entry,
				 struct snd_info_buffer *buffer)
{
	struct atiixp_modem *chip = entry->private_data;
	int i;

	for (i = 0; i < 256; i += 4)
		snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
}

static void __devinit snd_atiixp_proc_init(struct atiixp_modem *chip)
{
	struct snd_info_entry *entry;

	if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry))
		snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
}
#else
#define snd_atiixp_proc_init(chip)
#endif


/*
 * destructor
 */

static int snd_atiixp_free(struct atiixp_modem *chip)
{
	if (chip->irq < 0)
		goto __hw_end;
	snd_atiixp_chip_stop(chip);
	synchronize_irq(chip->irq);
      __hw_end:
	if (chip->irq >= 0)
		free_irq(chip->irq, chip);
	if (chip->remap_addr)
		iounmap(chip->remap_addr);
	pci_release_regions(chip->pci);
	pci_disable_device(chip->pci);
	kfree(chip);
	return 0;
}

static int snd_atiixp_dev_free(struct snd_device *device)
{
	struct atiixp_modem *chip = device->device_data;
	return snd_atiixp_free(chip);
}

/*
 * constructor for chip instance
 */
static int __devinit snd_atiixp_create(struct snd_card *card,
				       struct pci_dev *pci,
				       struct atiixp_modem **r_chip)
{
	static struct snd_device_ops ops = {
		.dev_free =	snd_atiixp_dev_free,
	};
	struct atiixp_modem *chip;
	int err;

	if ((err = pci_enable_device(pci)) < 0)
		return err;

	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (chip == NULL) {
		pci_disable_device(pci);
		return -ENOMEM;
	}

	spin_lock_init(&chip->reg_lock);
	mutex_init(&chip->open_mutex);
	chip->card = card;
	chip->pci = pci;
	chip->irq = -1;
	if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) {
		kfree(chip);
		pci_disable_device(pci);
		return err;
	}
	chip->addr = pci_resource_start(pci, 0);
	chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci, 0));
	if (chip->remap_addr == NULL) {
		snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
		snd_atiixp_free(chip);
		return -EIO;
	}

	if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
			card->shortname, chip)) {
		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
		snd_atiixp_free(chip);
		return -EBUSY;
	}
	chip->irq = pci->irq;
	pci_set_master(pci);
	synchronize_irq(chip->irq);

	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
		snd_atiixp_free(chip);
		return err;
	}

	snd_card_set_dev(card, &pci->dev);

	*r_chip = chip;
	return 0;
}


static int __devinit snd_atiixp_probe(struct pci_dev *pci,
				      const struct pci_device_id *pci_id)
{
	struct snd_card *card;
	struct atiixp_modem *chip;
	unsigned char revision;
	int err;

	card = snd_card_new(index, id, THIS_MODULE, 0);
	if (card == NULL)
		return -ENOMEM;

	pci_read_config_byte(pci, PCI_REVISION_ID, &revision);

	strcpy(card->driver, "ATIIXP-MODEM");
	strcpy(card->shortname, "ATI IXP Modem");
	if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
		goto __error;
	card->private_data = chip;

	if ((err = snd_atiixp_aclink_reset(chip)) < 0)
		goto __error;

	if ((err = snd_atiixp_mixer_new(chip, ac97_clock)) < 0)
		goto __error;

	if ((err = snd_atiixp_pcm_new(chip)) < 0)
		goto __error;
	
	snd_atiixp_proc_init(chip);

	snd_atiixp_chip_start(chip);

	sprintf(card->longname, "%s rev %x at 0x%lx, irq %i",
		card->shortname, revision, chip->addr, chip->irq);

	if ((err = snd_card_register(card)) < 0)
		goto __error;

	pci_set_drvdata(pci, card);
	return 0;

 __error:
	snd_card_free(card);
	return err;
}

static void __devexit snd_atiixp_remove(struct pci_dev *pci)
{
	snd_card_free(pci_get_drvdata(pci));
	pci_set_drvdata(pci, NULL);
}
コード例 #10
0
static int setup_corb_rirb(struct lola *chip)
{
	int err;
	unsigned char tmp;
	unsigned long end_time;

	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
				  snd_dma_pci_data(chip->pci),
				  PAGE_SIZE, &chip->rb);
	if (err < 0)
		return err;

	chip->corb.addr = chip->rb.addr;
	chip->corb.buf = (u32 *)chip->rb.area;
	chip->rirb.addr = chip->rb.addr + 2048;
	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);

	
	lola_writeb(chip, BAR0, RIRBCTL, 0);
	lola_writeb(chip, BAR0, CORBCTL, 0);

	end_time = jiffies + msecs_to_jiffies(200);
	do {
		if (!lola_readb(chip, BAR0, RIRBCTL) &&
		    !lola_readb(chip, BAR0, CORBCTL))
			break;
		msleep(1);
	} while (time_before(jiffies, end_time));

	
	lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
	lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
	
	lola_writeb(chip, BAR0, CORBSIZE, 0x02);
	
	lola_writew(chip, BAR0, CORBWP, 0);
	
	lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
	
	lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
	
	tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
	if (tmp)
		lola_writeb(chip, BAR0, CORBSTS, tmp);
	chip->corb.wp = 0;

	
	lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
	lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
	
	lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
	
	lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
	
	lola_writew(chip, BAR0, RINTCNT, 1);
	
	lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
	
	tmp =  lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
	if (tmp)
		lola_writeb(chip, BAR0, RIRBSTS, tmp);
	chip->rirb.rp = chip->rirb.cmds = 0;

	return 0;
}