static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev) { int i, ret; u64 lpar_addr, lpar_size; BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); the_card.ps3_dev = dev; ret = ps3_open_hv_device(dev); if (ret) return -ENXIO; ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); if (ret) { pr_info("%s: device map 2 failed %d\n", __func__, ret); goto clean_open; } ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, PAGE_SHIFT); ret = snd_ps3_map_mmio(); if (ret) goto clean_dev_map; ps3_dma_region_init(dev, dev->d_region, PAGE_SHIFT, 0, NULL, _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); dev->d_region->ioid = PS3_AUDIO_IOID; ret = ps3_dma_region_create(dev->d_region); if (ret) { pr_info("%s: region_create\n", __func__); goto clean_mmio; } snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); the_card.start_delay = snd_ps3_start_delay; if (snd_ps3_allocate_irq()) { ret = -ENXIO; goto clean_dma_region; } ret = snd_card_create(index, id, THIS_MODULE, 0, &the_card.card); if (ret < 0) goto clean_irq; strcpy(the_card.card->driver, "PS3"); strcpy(the_card.card->shortname, "PS3"); strcpy(the_card.card->longname, "PS3 sound"); for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { ret = snd_ctl_add(the_card.card, snd_ctl_new1(&spdif_ctls[i], &the_card)); if (ret < 0) goto clean_card; } ret = snd_pcm_new(the_card.card, "SPDIF", 0, 1, 0, &(the_card.pcm)); if (ret) goto clean_card; the_card.pcm->private_data = &the_card; strcpy(the_card.pcm->name, "SPDIF"); snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ps3_pcm_spdif_ops); the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, SNDRV_DMA_TYPE_DEV, &dev->core, SND_PS3_PCM_PREALLOC_SIZE, SND_PS3_PCM_PREALLOC_SIZE); if (ret < 0) { pr_info("%s: prealloc failed\n", __func__); goto clean_card; } the_card.null_buffer_start_vaddr = dma_alloc_coherent(&the_card.ps3_dev->core, PAGE_SIZE, &the_card.null_buffer_start_dma_addr, GFP_KERNEL); if (!the_card.null_buffer_start_vaddr) { pr_info("%s: nullbuffer alloc failed\n", __func__); goto clean_preallocate; } pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__, the_card.null_buffer_start_vaddr, the_card.null_buffer_start_dma_addr); snd_ps3_init_avsetting(&the_card); snd_card_set_dev(the_card.card, &dev->core); ret = snd_card_register(the_card.card); if (ret < 0) goto clean_dma_map; pr_info("%s started. start_delay=%dms\n", the_card.card->longname, the_card.start_delay); return 0; clean_dma_map: dma_free_coherent(&the_card.ps3_dev->core, PAGE_SIZE, the_card.null_buffer_start_vaddr, the_card.null_buffer_start_dma_addr); clean_preallocate: snd_pcm_lib_preallocate_free_for_all(the_card.pcm); clean_card: snd_card_free(the_card.card); clean_irq: snd_ps3_free_irq(); clean_dma_region: ps3_dma_region_free(dev->d_region); clean_mmio: snd_ps3_unmap_mmio(); clean_dev_map: lv1_gpu_device_unmap(2); clean_open: ps3_close_hv_device(dev); return ret; };
/* * Alsa Constructor - Component probe */ int tm6000_audio_init(struct tm6000_core *dev) { struct snd_card *card; struct snd_tm6000_card *chip; int rc; static int devnr; char component[14]; struct snd_pcm *pcm; if (!dev) return 0; if (devnr >= SNDRV_CARDS) return -ENODEV; if (!enable[devnr]) return -ENOENT; rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); if (rc < 0) { snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); return rc; } strcpy(card->driver, "tm6000-alsa"); strcpy(card->shortname, "TM5600/60x0"); sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", dev->udev->bus->busnum, dev->udev->devnum); sprintf(component, "USB%04x:%04x", le16_to_cpu(dev->udev->descriptor.idVendor), le16_to_cpu(dev->udev->descriptor.idProduct)); snd_component_add(card, component); snd_card_set_dev(card, &dev->udev->dev); chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); if (!chip) { rc = -ENOMEM; goto error; } chip->core = dev; chip->card = card; dev->adev = chip; spin_lock_init(&chip->reg_lock); rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); if (rc < 0) goto error; pcm->info_flags = 0; pcm->private_data = chip; strcpy(pcm->name, "Trident TM5600/60x0"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); rc = snd_card_register(card); if (rc < 0) goto error; dprintk(1,"Registered audio driver for %s\n", card->longname); return 0; error: snd_card_free(card); return rc; }
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; }
static int snd_stm_pcm_player_probe(struct platform_device *pdev) { int result = 0; struct snd_stm_pcm_player *pcm_player; struct snd_card *card = snd_stm_card_get(); int i; snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev)); BUG_ON(!card); pcm_player = kzalloc(sizeof(*pcm_player), GFP_KERNEL); if (!pcm_player) { snd_stm_printe("Can't allocate memory " "for a device description!\n"); result = -ENOMEM; goto error_alloc; } snd_stm_magic_set(pcm_player); pcm_player->info = pdev->dev.platform_data; BUG_ON(!pcm_player->info); pcm_player->ver = pcm_player->info->ver; BUG_ON(pcm_player->ver <= 0); pcm_player->device = &pdev->dev; /* Get resources */ result = snd_stm_memory_request(pdev, &pcm_player->mem_region, &pcm_player->base); if (result < 0) { snd_stm_printe("Memory region request failed!\n"); goto error_memory_request; } pcm_player->fifo_phys_address = pcm_player->mem_region->start + offset__AUD_PCMOUT_DATA(pcm_player); snd_stm_printd(0, "FIFO physical address: 0x%lx.\n", pcm_player->fifo_phys_address); result = snd_stm_irq_request(pdev, &pcm_player->irq, snd_stm_pcm_player_irq_handler, pcm_player); if (result < 0) { snd_stm_printe("IRQ request failed!\n"); goto error_irq_request; } result = snd_stm_fdma_request(pdev, &pcm_player->fdma_channel); if (result < 0) { snd_stm_printe("FDMA request failed!\n"); goto error_fdma_request; } /* FDMA transfer size depends (among others ;-) on FIFO length, * which is: * - 30 cells (120 bytes) in STx7100/9 and STx7200 cut 1.0 * - 70 cells (280 bytes) in STx7111 and STx7200 cut 2.0. */ if (pcm_player->ver < 5) pcm_player->fdma_max_transfer_size = 2; else if (pcm_player->ver == 5) pcm_player->fdma_max_transfer_size = 20; else pcm_player->fdma_max_transfer_size = 30; /* Get player capabilities */ snd_stm_printd(0, "Player's name is '%s'\n", pcm_player->info->name); BUG_ON(pcm_player->info->channels <= 0); BUG_ON(pcm_player->info->channels > 10); BUG_ON(pcm_player->info->channels % 2 != 0); if (pcm_player->ver > 1) { static unsigned int channels_2_10[] = { 2, 4, 6, 8, 10 }; pcm_player->channels_constraint.list = channels_2_10; pcm_player->channels_constraint.count = pcm_player->info->channels / 2; } else { /* In STx7100 cut < 3.0 PCM player ignored NUM_CH setting in * AUD_PCMOUT_FMT register (and it was always in 10 channels * mode...) */ static unsigned int channels_10[] = { 10 }; pcm_player->channels_constraint.list = channels_10; pcm_player->channels_constraint.count = 1; } pcm_player->channels_constraint.mask = 0; for (i = 0; i < pcm_player->channels_constraint.count; i++) snd_stm_printd(0, "Player capable of playing %u-channels PCM." "\n", pcm_player->channels_constraint.list[i]); /* STx7100 has a problem with 16/16 bits FIFO organization, * so we disable the 16 bits samples capability... */ if (pcm_player->ver <= 2) snd_stm_pcm_player_hw.formats &= ~SNDRV_PCM_FMTBIT_S16_LE; /* Create ALSA lowlevel device */ result = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pcm_player, &snd_stm_pcm_player_snd_device_ops); if (result < 0) { snd_stm_printe("ALSA low level device creation failed!\n"); goto error_device; } /* Create ALSA PCM device */ result = snd_pcm_new(card, NULL, pcm_player->info->card_device, 1, 0, &pcm_player->pcm); if (result < 0) { snd_stm_printe("ALSA PCM instance creation failed!\n"); goto error_pcm; } pcm_player->pcm->private_data = pcm_player; strcpy(pcm_player->pcm->name, pcm_player->info->name); snd_pcm_set_ops(pcm_player->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_stm_pcm_player_pcm_ops); /* Initialize buffer */ pcm_player->buffer = snd_stm_buffer_create(pcm_player->pcm, pcm_player->device, snd_stm_pcm_player_hw.buffer_bytes_max); if (!pcm_player->buffer) { snd_stm_printe("Cannot initialize buffer!\n"); result = -ENOMEM; goto error_buffer_init; } /* Register in converters router */ pcm_player->conv_source = snd_stm_conv_register_source( &platform_bus_type, dev_name(&pdev->dev), pcm_player->info->channels, card, pcm_player->info->card_device); if (!pcm_player->conv_source) { snd_stm_printe("Cannot register in converters router!\n"); result = -ENOMEM; goto error_conv_register_source; } /* Claim the pads */ if (pcm_player->info->pad_config) { pcm_player->pads = stm_pad_claim(pcm_player->info->pad_config, dev_name(&pdev->dev)); if (!pcm_player->pads) { snd_stm_printe("Failed to claimed pads for '%s'!\n", dev_name(&pdev->dev)); result = -EBUSY; goto error_pad_claim; } } /* Done now */ platform_set_drvdata(pdev, pcm_player); return 0; error_pad_claim: snd_stm_conv_unregister_source(pcm_player->conv_source); error_conv_register_source: snd_stm_buffer_dispose(pcm_player->buffer); error_buffer_init: /* snd_pcm_free() is not available - PCM device will be released * during card release */ error_pcm: snd_device_free(card, pcm_player); error_device: snd_stm_fdma_release(pcm_player->fdma_channel); error_fdma_request: snd_stm_irq_release(pcm_player->irq, pcm_player); error_irq_request: snd_stm_memory_release(pcm_player->mem_region, pcm_player->base); error_memory_request: snd_stm_magic_clear(pcm_player); kfree(pcm_player); error_alloc: return result; }
static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) { int ret; u64 lpar_addr, lpar_size; BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); the_card.ps3_dev = dev; ret = ps3_open_hv_device(dev); if (ret) return -ENXIO; /* setup MMIO */ ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); if (ret) { pr_info("%s: device map 2 failed %d\n", __func__, ret); goto clean_open; } ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, PAGE_SHIFT); ret = snd_ps3_map_mmio(); if (ret) goto clean_dev_map; /* setup DMA area */ ps3_dma_region_init(dev, dev->d_region, PAGE_SHIFT, /* use system page size */ 0, /* dma type; not used */ NULL, _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); dev->d_region->ioid = PS3_AUDIO_IOID; ret = ps3_dma_region_create(dev->d_region); if (ret) { pr_info("%s: region_create\n", __func__); goto clean_mmio; } snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ the_card.start_delay = snd_ps3_start_delay; /* irq */ if (snd_ps3_allocate_irq()) { ret = -ENXIO; goto clean_dma_region; } /* create card instance */ the_card.card = snd_card_new(index, id, THIS_MODULE, 0); if (!the_card.card) { ret = -ENXIO; goto clean_irq; } strcpy(the_card.card->driver, "PS3"); strcpy(the_card.card->shortname, "PS3"); strcpy(the_card.card->longname, "PS3 sound"); /* create PCM devices instance */ /* NOTE:this driver works assuming pcm:substream = 1:1 */ ret = snd_pcm_new(the_card.card, "SPDIF", 0, /* instance index, will be stored pcm.device*/ 1, /* output substream */ 0, /* input substream */ &(the_card.pcm)); if (ret) goto clean_card; the_card.pcm->private_data = &the_card; strcpy(the_card.pcm->name, "SPDIF"); /* set pcm ops */ snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ps3_pcm_spdif_ops); the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; /* pre-alloc PCM DMA buffer*/ ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, SNDRV_DMA_TYPE_DEV, &dev->core, SND_PS3_PCM_PREALLOC_SIZE, SND_PS3_PCM_PREALLOC_SIZE); if (ret < 0) { pr_info("%s: prealloc failed\n", __func__); goto clean_card; } /* * allocate null buffer * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 * PAGE_SIZE is enogh */ if (!(the_card.null_buffer_start_vaddr = dma_alloc_coherent(&the_card.ps3_dev->core, PAGE_SIZE, &the_card.null_buffer_start_dma_addr, GFP_KERNEL))) { pr_info("%s: nullbuffer alloc failed\n", __func__); goto clean_preallocate; } pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__, the_card.null_buffer_start_vaddr, the_card.null_buffer_start_dma_addr); /* set default sample rate/word width */ snd_ps3_init_avsetting(&the_card); /* register the card */ snd_card_set_dev(the_card.card, &dev->core); ret = snd_card_register(the_card.card); if (ret < 0) goto clean_dma_map; pr_info("%s started. start_delay=%dms\n", the_card.card->longname, the_card.start_delay); return 0; clean_dma_map: dma_free_coherent(&the_card.ps3_dev->core, PAGE_SIZE, the_card.null_buffer_start_vaddr, the_card.null_buffer_start_dma_addr); clean_preallocate: snd_pcm_lib_preallocate_free_for_all(the_card.pcm); clean_card: snd_card_free(the_card.card); clean_irq: snd_ps3_free_irq(); clean_dma_region: ps3_dma_region_free(dev->d_region); clean_mmio: snd_ps3_unmap_mmio(); clean_dev_map: lv1_gpu_device_unmap(2); clean_open: ps3_close_hv_device(dev); /* * there is no destructor function to pcm. * midlayer automatically releases if the card removed */ return ret; }; /* snd_ps3_probe */
int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) { int i, ret; dev->n_audio_in = max(dev->spec.num_analog_audio_in, dev->spec.num_digital_audio_in) / CHANNELS_PER_STREAM; dev->n_audio_out = max(dev->spec.num_analog_audio_out, dev->spec.num_digital_audio_out) / CHANNELS_PER_STREAM; dev->n_streams = max(dev->n_audio_in, dev->n_audio_out); debug("dev->n_audio_in = %d\n", dev->n_audio_in); debug("dev->n_audio_out = %d\n", dev->n_audio_out); debug("dev->n_streams = %d\n", dev->n_streams); if (dev->n_streams > MAX_STREAMS) { log("unable to initialize device, too many streams.\n"); return -EINVAL; } ret = snd_pcm_new(dev->chip.card, dev->product_name, 0, dev->n_audio_out, dev->n_audio_in, &dev->pcm); if (ret < 0) { log("snd_pcm_new() returned %d\n", ret); return ret; } dev->pcm->private_data = dev; strcpy(dev->pcm->name, dev->product_name); memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware, sizeof(snd_usb_caiaq_pcm_hardware)); /* setup samplerates */ dev->samplerates = dev->pcm_info.rates; switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE): dev->samplerates |= SNDRV_PCM_RATE_192000; /* fall thru */ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO2DJ): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): dev->samplerates |= SNDRV_PCM_RATE_88200; break; } snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_caiaq_ops); snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_caiaq_ops); snd_pcm_lib_preallocate_pages_for_all(dev->pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); dev->data_cb_info = kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, GFP_KERNEL); if (!dev->data_cb_info) return -ENOMEM; for (i = 0; i < N_URBS; i++) { dev->data_cb_info[i].dev = dev; dev->data_cb_info[i].index = i; } dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret); if (ret < 0) { kfree(dev->data_cb_info); free_urbs(dev->data_urbs_in); return ret; } dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret); if (ret < 0) { kfree(dev->data_cb_info); free_urbs(dev->data_urbs_in); free_urbs(dev->data_urbs_out); return ret; } return 0; }
static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { int err; static const struct snd_pcm_hardware hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_FIFO_IN_FRAMES | SNDRV_PCM_INFO_JOINT_DUPLEX | /* for Open Sound System compatibility */ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, /* set up later */ .rates = 0, .rate_min = UINT_MAX, .rate_max = 0, /* set up later */ .channels_min = UINT_MAX, .channels_max = 0, .buffer_bytes_max = 1024 * 1024 * 1024, .period_bytes_min = 256, .period_bytes_max = 1024 * 1024 * 1024 / 2, .periods_min = 2, .periods_max = 32, .fifo_size = 0, }; substream->runtime->hw = hw; substream->runtime->delay = substream->runtime->hw.fifo_size; /* add rule between channels and sampling rate */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { prepare_rates(&substream->runtime->hw, bebob->tx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->tx_stream_formations); substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_capture_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_capture_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } else { prepare_rates(&substream->runtime->hw, bebob->rx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->rx_stream_formations); substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_playback_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_playback_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } /* AM824 in IEC 61883-6 can deliver 24bit data */ err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); if (err < 0) goto end; /* * AMDTP functionality in firewire-lib require periods to be aligned to * 16 bit, or 24bit inner 32bit. */ err = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (err < 0) goto end; /* time for period constraint */ err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 500, UINT_MAX); if (err < 0) goto end; err = 0; end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; bool internal; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_check_internal_clock(bebob, &internal); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (!internal || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->tx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->tx_stream); end: return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->rx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->rx_stream); end: return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); end: return err; }
/* * * Probe/remove functions * */ static int __devinit minivosc_probe(struct platform_device *devptr) { struct snd_card *card; struct minivosc_device *mydev; int ret; int nr_subdevs; // how many capture substreams we want struct snd_pcm *pcm; int dev = devptr->id; // from aloop-kernel.c dbg("%s: probe", __func__); // no need to kzalloc minivosc_device separately, if the sizeof is included here ret = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(struct minivosc_device), &card); if (ret < 0) goto __nodev; mydev = card->private_data; mydev->card = card; // MUST have mutex_init here - else crash on mutex_lock!! mutex_init(&mydev->cable_lock); dbg2("-- mydev %p", mydev); sprintf(card->driver, "my_driver-%s", SND_MINIVOSC_DRIVER); sprintf(card->shortname, "MySoundCard Audio %s", SND_MINIVOSC_DRIVER); sprintf(card->longname, "%s", card->shortname); snd_card_set_dev(card, &devptr->dev); // present in dummy, not in aloop though ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, mydev, &dev_ops); if (ret < 0) goto __nodev; nr_subdevs = 1; // how many capture substreams we want // * we want 0 playback, and 1 capture substreams (4th and 5th arg) .. ret = snd_pcm_new(card, card->driver, 0, 0, nr_subdevs, &pcm); if (ret < 0) goto __nodev; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &minivosc_pcm_ops); // in both aloop-kernel.c and dummy.c, after snd_pcm_new... pcm->private_data = mydev; //here it should be dev/card struct (the one containing struct snd_card *card) - this DOES NOT end up in substream->private_data pcm->info_flags = 0; strcpy(pcm->name, card->shortname); /* trid to add this - but it crashes here: //mydev->substream->private_data = mydev; Well, first time real substream comes in, is in _open - so that has to be handled there.. That is: at this point, mydev->substream is null, and we first have a chance to set it ... in _open! */ ret = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), MAX_BUFFER, MAX_BUFFER); // in both aloop-kernel.c and dummy.c, after snd_pcm_set_ops... if (ret < 0) goto __nodev; // * will use the snd_card_register form from aloop-kernel.c/dummy.c here.. ret = snd_card_register(card); if (ret == 0) // or... (!ret) { platform_set_drvdata(devptr, card); return 0; // success } __nodev: // as in aloop/dummy... dbg("__nodev reached!!"); snd_card_free(card); // this will autocall .dev_free (= minivosc_pcm_dev_free) return ret; }
/** * hdmi_audio_probe - to create sound card instance for HDMI audio playabck * *@haddata: pointer to HAD private data *@card_id: card for which probe is called * * This function is called when the hdmi cable is plugged in. This function * creates and registers the sound card with ALSA */ static int __devinit hdmi_audio_probe(struct platform_device *devptr) { int retval; struct snd_pcm *pcm; struct snd_card *card; struct had_callback_ops ops_cb; struct snd_intelhad *intelhaddata; struct had_pvt_data *had_stream; pr_debug("Enter %s\n", __func__); /* allocate memory for saving internal context and working */ intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL); if (!intelhaddata) { pr_err("mem alloc failed\n"); return -ENOMEM; } had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL); if (!had_stream) { pr_err("mem alloc failed\n"); retval = -ENOMEM; goto free_haddata; } had_data = intelhaddata; ops_cb.intel_had_event_call_back = had_event_handler; /* registering with display driver to get access to display APIs */ retval = intel_hdmi_audio_query_capabilities( ops_cb.intel_had_event_call_back, &(intelhaddata->reg_ops), &(intelhaddata->query_ops)); if (retval) { pr_err("querying display driver APIs failed %#x\n", retval); goto free_hadstream; } mutex_lock(&had_mutex); spin_lock_init(&intelhaddata->had_spinlock); intelhaddata->drv_status = HAD_DRV_DISCONNECTED; /* create a card instance with ALSA framework */ retval = snd_card_create(hdmi_card_index, hdmi_card_id, THIS_MODULE, 0, &card); if (retval) goto unlock_mutex; intelhaddata->card = card; intelhaddata->card_id = hdmi_card_id; intelhaddata->card_index = card->number; intelhaddata->private_data = had_stream; intelhaddata->flag_underrun = 0; intelhaddata->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; strncpy(card->driver, INTEL_HAD, strlen(INTEL_HAD)); strncpy(card->shortname, INTEL_HAD, strlen(INTEL_HAD)); retval = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, MAX_CAP_STREAMS, &pcm); if (retval) goto err; /* setup private data which can be retrieved when required */ pcm->private_data = intelhaddata; pcm->private_free = snd_intelhad_pcm_free; pcm->info_flags = 0; strncpy(pcm->name, card->shortname, strlen(card->shortname)); /* setup the ops for palyabck */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intelhad_playback_ops); /* allocate dma pages for ALSA stream operations * memory allocated is based on size, not max value * thus using same argument for max & size */ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, HAD_MAX_BUFFER, HAD_MAX_BUFFER); if (retval) goto err; /* internal function call to register device with ALSA */ retval = snd_intelhad_create(intelhaddata, card); if (retval) goto err; card->private_data = &intelhaddata; retval = snd_card_register(card); if (retval) goto err; /* IEC958 controls */ retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask, intelhaddata)); if (retval < 0) goto err; retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958, intelhaddata)); if (retval < 0) goto err; /* Allocate memory for flat data */ intelhaddata->flat_data = kzalloc((MAX_SZ_ZERO_BUF), GFP_KERNEL); if (!intelhaddata->flat_data) { retval = -ENOMEM; goto err; } mutex_unlock(&had_mutex); retval = display_register(&had_interface, intelhaddata); if (retval) { pr_err("registering with display driver failed %#x\n", retval); snd_card_free(card); goto free_hadstream; } return retval; err: snd_card_free(card); unlock_mutex: mutex_unlock(&had_mutex); free_hadstream: kfree(had_stream); free_haddata: kfree(intelhaddata); intelhaddata = NULL; pr_err("Error returned from %s api %#x\n", __func__, retval); return retval; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, amdtp_rate_table[i]); t.max = max(t.max, amdtp_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(r, amdtp_rate_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { unsigned int rate, channels; int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, channels); hw->channels_max = max(hw->channels_max, channels); rate = amdtp_rate_table[i]; hw->rates |= snd_pcm_rate_to_rate_bit(rate); hw->rate_min = min(hw->rate_min, rate); hw->rate_max = max(hw->rate_max, rate); } } static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; const unsigned int *pcm_channels; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; pcm_channels = ff->spec->pcm_capture_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->rx_stream; pcm_channels = ff->spec->pcm_playback_channels; } limit_channels_and_rates(&runtime->hw, pcm_channels); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_ff_add_pcm_hw_constraints(s, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; unsigned int rate; enum snd_ff_clock_src src; int i, err; err = snd_ff_stream_lock_try(ff); if (err < 0) return err; err = pcm_init_hw_params(ff, substream); if (err < 0) goto release_lock; err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } /* * The unit is configured at sampling frequency which packet * streaming engine can't support. */ if (i >= CIP_SFC_COUNT) { err = -EIO; goto release_lock; } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { if (amdtp_stream_pcm_running(&ff->rx_stream) || amdtp_stream_pcm_running(&ff->tx_stream)) { rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } } snd_pcm_set_sync(substream); return 0; release_lock: snd_ff_stream_lock_release(ff); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; snd_ff_stream_lock_release(ff); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->tx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->rx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) { static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = ff; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++ * * Function Name: PcmDeviceNew * * Description: Create PCM playback and capture device * *------------------------------------------------------------ */ int __devinit PcmDeviceNew(struct snd_card *card) { struct snd_pcm *pcm; int err = 0; brcm_alsa_chip_t *pChip; struct snd_pcm_substream *substream; callMode = CALL_MODE_NONE; err = snd_pcm_new(card, "Broadcom CAPH", 0, NUM_PLAYBACK_SUBDEVICE, NUM_CAPTURE_SUBDEVICE, &pcm); if (err < 0) return err; pcm->private_data = card->private_data; strcpy(pcm->name, "Broadcom CAPH PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &brcm_alsa_omx_pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &brcm_alsa_omx_pcm_capture_ops); pcm->info_flags = 0; /*pre-allocate memory for playback device */ substream = pcm->streams[0].substream; for (; substream; substream = substream->next) { err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, 0, (IS_PCM_MEM_PREALLOCATED) ? PCM_MAX_PLAYBACK_BUF_BYTES : 0, PCM_MAX_PLAYBACK_BUF_BYTES); if (err) aError ("\n Error : Error when allocate memory for" " playback device err=%d\n", err); } /*pre-allocate memory for capture device */ substream = pcm->streams[1].substream; for (; substream; substream = substream->next) { err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, 0, (IS_PCM_MEM_PREALLOCATED) ? PCM_MAX_CAPTURE_BUF_BYTES : 0, PCM_MAX_CAPTURE_BUF_BYTES); if (err) aError ("\n Error : Error when allocate memory for" " capture device err=%d\n", err); } pChip = (brcm_alsa_chip_t *) card->private_data; /* Initialize the audio controller */ aTrace(LOG_ALSA_INTERFACE, "ALSA-CAPH PcmDeviceNew:call AUDIO_Init\n"); caph_audio_init(); #if defined(CONFIG_BCM_MODEM) DSPDRV_Init(); #endif AUDCTRL_Init(); #if defined(DYNAMIC_DMA_PLAYBACK) spin_lock_init(©_lock); #endif aTrace(LOG_ALSA_INTERFACE, "\n PcmDeviceNew : PcmDeviceNew err=%d\n", err); return err; }
/* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_pcm_ops *soc_pcm_ops = &rtd->ops; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; soc_pcm_ops->open = soc_pcm_open; soc_pcm_ops->close = soc_pcm_close; soc_pcm_ops->hw_params = soc_pcm_hw_params; soc_pcm_ops->hw_free = soc_pcm_hw_free; soc_pcm_ops->prepare = soc_pcm_prepare; soc_pcm_ops->trigger = soc_pcm_trigger; soc_pcm_ops->pointer = soc_pcm_pointer; /* check client and interface hw capabilities */ snprintf(new_name, sizeof(new_name), "%s %s-%d", rtd->dai_link->stream_name, codec_dai->name, num); if (codec_dai->driver->playback.channels_min) playback = 1; if (codec_dai->driver->capture.channels_min) capture = 1; dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm); if (ret < 0) { printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name); return ret; } /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); rtd->pcm = pcm; pcm->private_data = rtd; if (platform->driver->ops) { soc_pcm_ops->mmap = platform->driver->ops->mmap; soc_pcm_ops->pointer = platform->driver->ops->pointer; soc_pcm_ops->ioctl = platform->driver->ops->ioctl; soc_pcm_ops->copy = platform->driver->ops->copy; soc_pcm_ops->silence = platform->driver->ops->silence; soc_pcm_ops->ack = platform->driver->ops->ack; soc_pcm_ops->page = platform->driver->ops->page; } if (playback) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); if (platform->driver->pcm_new) { ret = platform->driver->pcm_new(rtd); if (ret < 0) { pr_err("asoc: platform pcm constructor failed\n"); return ret; } } pcm->private_free = platform->driver->pcm_free; printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; }
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; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(c, snd_dg00x_stream_pcm_channels[i])) continue; t.min = min(t.min, snd_dg00x_stream_rates[i]); t.max = max(t.max, snd_dg00x_stream_rates[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(r, snd_dg00x_stream_rates[i])) continue; t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); } return snd_interval_refine(c, &t); } static int pcm_init_hw_params(struct snd_dg00x *dg00x, struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 44100, .rate_max = 96000, .channels_min = 10, .channels_max = 18, .period_bytes_min = 4 * 18, .period_bytes_max = 4 * 18 * 2048, .buffer_bytes_max = 4 * 18 * 2048 * 2, .periods_min = 2, .periods_max = UINT_MAX, }; struct amdtp_stream *s; int err; substream->runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->tx_stream; } else { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; s = &dg00x->rx_stream; } err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; enum snd_dg00x_clock clock; bool detect; unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) goto end; err = pcm_init_hw_params(dg00x, substream); if (err < 0) goto err_locked; /* Check current clock source. */ err = snd_dg00x_stream_get_clock(dg00x, &clock); if (err < 0) goto err_locked; if (clock != SND_DG00X_CLOCK_INTERNAL) { err = snd_dg00x_stream_check_external_clock(dg00x, &detect); if (err < 0) goto err_locked; if (!detect) { err = -EBUSY; goto err_locked; } } if ((clock != SND_DG00X_CLOCK_INTERNAL) || amdtp_stream_pcm_running(&dg00x->rx_stream) || amdtp_stream_pcm_running(&dg00x->tx_stream)) { err = snd_dg00x_stream_get_external_rate(dg00x, &rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_dg00x_stream_lock_release(dg00x); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; snd_dg00x_stream_lock_release(dg00x); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dg00x->tx_stream); mutex_unlock(&dg00x->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) { amdtp_stream_pcm_prepare(&dg00x->rx_stream); amdtp_dot_reset(&dg00x->rx_stream); } mutex_unlock(&dg00x->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; }
/** * \brief Creates a new File PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param fname Output filename (or NULL if file descriptor fd is available) * \param fd Output file descriptor * \param ifname Input filename (or NULL if file descriptor ifd is available) * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input * redirection will be performed) * \param trunc Truncate the file if it already exists * \param fmt File format ("raw" or "wav" are available) * \param perm File permission * \param slave Slave PCM handle * \param close_slave When set, the slave PCM handle is closed with copy PCM * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, const char *fname, int fd, const char *ifname, int ifd, int trunc, const char *fmt, int perm, snd_pcm_t *slave, int close_slave) { snd_pcm_t *pcm; snd_pcm_file_t *file; snd_pcm_file_format_t format; struct timespec timespec; int err; assert(pcmp); if (fmt == NULL || strcmp(fmt, "raw") == 0) format = SND_PCM_FILE_FORMAT_RAW; else if (!strcmp(fmt, "wav")) format = SND_PCM_FILE_FORMAT_WAV; else { SNDERR("file format %s is unknown", fmt); return -EINVAL; } file = calloc(1, sizeof(snd_pcm_file_t)); if (!file) { return -ENOMEM; } /* opening output fname is delayed until writing, when PCM params are known */ if (fname) file->fname = strdup(fname); file->trunc = trunc; file->perm = perm; if (ifname) { ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */ if (ifd < 0) { SYSERR("open %s for reading failed", ifname); free(file); return -errno; } file->ifname = strdup(ifname); } file->fd = fd; file->ifd = ifd; file->format = format; file->gen.slave = slave; file->gen.close_slave = close_slave; err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode); if (err < 0) { free(file->fname); free(file); return err; } pcm->ops = &snd_pcm_file_ops; pcm->fast_ops = &snd_pcm_file_fast_ops; pcm->private_data = file; pcm->poll_fd = slave->poll_fd; pcm->poll_events = slave->poll_events; pcm->mmap_shadow = 1; #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) pcm->monotonic = clock_gettime(CLOCK_MONOTONIC, ×pec) == 0; #else pcm->monotonic = 0; #endif snd_pcm_link_hw_ptr(pcm, slave); snd_pcm_link_appl_ptr(pcm, slave); *pcmp = pcm; return 0; }
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, }; if (snd_BUG_ON(!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 */ if (snd_BUG_ON(!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); __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; int err; card = snd_card_new(index, id, THIS_MODULE, 0); if (card == NULL) return -ENOMEM; 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, pci->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); }
/* Create ALSA pcm device */ int ct_alsa_pcm_create(struct ct_atc *atc, enum CTALSADEVS device, const char *device_name) { struct snd_pcm *pcm; const struct snd_pcm_chmap_elem *map; int chs; int err; int playback_count, capture_count; playback_count = (IEC958 == device) ? 1 : 256; capture_count = (FRONT == device) ? 1 : 0; err = snd_pcm_new(atc->card, "ctxfi", device, playback_count, capture_count, &pcm); if (err < 0) { pr_err("ctxfi: snd_pcm_new failed!! Err=%d\n", err); return err; } pcm->private_data = atc; pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strlcpy(pcm->name, device_name, sizeof(pcm->name)); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops); if (FRONT == device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(atc->pci), 128*1024, 128*1024); chs = 2; switch (device) { case FRONT: chs = 8; map = snd_pcm_std_chmaps; break; case SURROUND: map = surround_map; break; case CLFE: map = clfe_map; break; case SIDE: map = side_map; break; default: map = snd_pcm_std_chmaps; break; } err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs, 0, NULL); if (err < 0) return err; #ifdef CONFIG_PM_SLEEP atc->pcms[device] = pcm; #endif return 0; }
/* create a pcm device */ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) { struct snd_pcm *pcm; struct snd_kcontrol *kctl; int i; int err, nr_capt; if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST) return -ENODEV; /* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the * same dma engine. WT uses it own separate dma engine which can't capture. */ if (idx == VORTEX_PCM_ADB) nr_capt = nr; else nr_capt = 0; err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, nr_capt, &pcm); if (err < 0) return err; snprintf(pcm->name, sizeof(pcm->name), "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]); chip->pcm[idx] = pcm; // This is an evil hack, but it saves a lot of duplicated code. VORTEX_PCM_TYPE(pcm) = idx; pcm->private_data = chip; /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vortex_playback_ops); if (idx == VORTEX_PCM_ADB) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vortex_playback_ops); /* pre-allocation of Scatter-Gather buffers */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci_dev), 0x10000, 0x10000); switch (VORTEX_PCM_TYPE(pcm)) { case VORTEX_PCM_ADB: err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_std_chmaps, VORTEX_IS_QUAD(chip) ? 4 : 2, 0, NULL); if (err < 0) return err; err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, snd_pcm_std_chmaps, 2, 0, NULL); if (err < 0) return err; break; #ifdef CHIP_AU8830 case VORTEX_PCM_A3D: err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_std_chmaps, 1, 0, NULL); if (err < 0) return err; break; #endif } if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip); if (!kctl) return -ENOMEM; if ((err = snd_ctl_add(chip->card, kctl)) < 0) return err; } } if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) { for (i = 0; i < NR_PCM; i++) { chip->pcm_vol[i].active = 0; chip->pcm_vol[i].dma = -1; kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip); if (!kctl) return -ENOMEM; chip->pcm_vol[i].kctl = kctl; kctl->id.device = 0; kctl->id.subdevice = i; err = snd_ctl_add(chip->card, kctl); if (err < 0) return err; } } return 0; }
int snd_dice_create_pcm(struct snd_dice *dice) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = capture_hw_params, .hw_free = capture_hw_free, .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, .ack = capture_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = playback_hw_params, .hw_free = playback_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, .ack = playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; __be32 reg; struct snd_pcm *pcm; unsigned int i, max_capture, max_playback, capture, playback; int err; /* Check whether PCM substreams are required. */ if (dice->force_two_pcms) { max_capture = max_playback = 2; } else { max_capture = max_playback = 0; err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, sizeof(reg)); if (err < 0) return err; max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); } for (i = 0; i < MAX_STREAMS; i++) { capture = playback = 0; if (i < max_capture) capture = 1; if (i < max_playback) playback = 1; if (capture == 0 && playback == 0) break; err = snd_pcm_new(dice->card, "DICE", i, playback, capture, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); if (playback > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); } return 0; }
static int firewave_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static const struct snd_interval all_channels = { .min = 6, .max = 6 }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* 32/44.1 kHz work only with all six channels */ if (snd_interval_max(rate) < 48000) return snd_interval_refine(channels, &all_channels); return 0; } static int firewave_constraints(struct snd_pcm_runtime *runtime) { static unsigned int channels_list[] = { 2, 6 }; static struct snd_pcm_hw_constraint_list channels_list_constraint = { .count = 2, .list = channels_list, }; int err; runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000; runtime->hw.channels_max = 6; err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &channels_list_constraint); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, firewave_rate_constraint, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, firewave_channels_constraint, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; return 0; } static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) { runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; return 0; } static int fwspk_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, .periods_min = 1, .periods_max = UINT_MAX, }; struct fwspk *fwspk = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; runtime->hw = hardware; err = fwspk->device_info->pcm_constraints(runtime); if (err < 0) return err; err = snd_pcm_limit_hw_rates(runtime); if (err < 0) return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; return 0; } static int fwspk_close(struct snd_pcm_substream *substream) { return 0; } static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } } static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) { u8 *buf; int err; buf = kmalloc(8, GFP_KERNEL); if (!buf) return -ENOMEM; buf[0] = 0x00; /* AV/C, CONTROL */ buf[1] = 0xff; /* unit */ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ buf[3] = 0x00; /* plug 0 */ buf[4] = 0x90; /* format: audio */ buf[5] = 0x00 | sfc; /* AM824, frequency */ buf[6] = 0xff; /* SYT (not used) */ buf[7] = 0xff; err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); if (err < 0) goto error; if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { dev_err(&fwspk->unit->device, "failed to set sample rate\n"); err = -EIO; goto error; } err = 0; error: kfree(buf); return err; } static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) goto error; amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) goto err_buffer; return 0; err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: return err; } static int fwspk_hw_free(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int fwspk_prepare(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); if (!fwspk->stream_running) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; err = amdtp_out_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) goto err_connection; fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); amdtp_out_stream_pcm_prepare(&fwspk->stream); return 0; err_connection: cmp_connection_break(&fwspk->connection); err_mutex: mutex_unlock(&fwspk->mutex); return err; } static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) { struct fwspk *fwspk = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { case SNDRV_PCM_TRIGGER_START: pcm = substream; break; case SNDRV_PCM_TRIGGER_STOP: pcm = NULL; break; default: return -EINVAL; } amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; return amdtp_out_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) { static struct snd_pcm_ops ops = { .open = fwspk_open, .close = fwspk_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = fwspk_hw_params, .hw_free = fwspk_hw_free, .prepare = fwspk_prepare, .trigger = fwspk_trigger, .pointer = fwspk_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); if (err < 0) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; fwspk->pcm->ops = &ops; return 0; } enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, CTL_MAX = 0x03, CTL_CURRENT = 0x10, }; static int fwspk_mute_command(struct fwspk *fwspk, bool *value, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(11, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ buf[5] = 0x10; /* control attribute: current */ buf[6] = 0x02; /* selector length */ buf[7] = 0x00; /* audio channel number */ buf[8] = 0x01; /* control selector: mute */ buf[9] = 0x01; /* control data length */ if (action == CTL_READ) buf[10] = 0xff; else buf[10] = *value ? 0x70 : 0x60; err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); if (err < 0) goto error; if (err < 11) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "mute command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = buf[10] == 0x70; err = 0; error: kfree(buf); return err; }
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) { mutex_unlock(&mgr->setup_mutex); 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_BUG_ON(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_BUG_ON(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, }; 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; } mgr->chip[idx] = chip; snd_card_set_dev(card, &mgr->pci->dev); return 0; }
/** * \brief Creates a new File PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param fname Output filename (or NULL if file descriptor fd is available) * \param fd Output file descriptor * \param ifname Input filename (or NULL if file descriptor ifd is available) * \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input * redirection will be performed) * \param fmt File format ("raw" is supported only) * \param perm File permission * \param slave Slave PCM handle * \param close_slave When set, the slave PCM handle is closed with copy PCM * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name, const char *fname, int fd, const char *ifname, int ifd, const char *fmt, int perm, snd_pcm_t *slave, int close_slave) { snd_pcm_t *pcm; snd_pcm_file_t *file; snd_pcm_file_format_t format; int err; assert(pcmp); if (fmt == NULL || strcmp(fmt, "raw") == 0) format = SND_PCM_FILE_FORMAT_RAW; else { SNDERR("file format %s is unknown", fmt); return -EINVAL; } if (fname) { fd = open(fname, O_WRONLY|O_CREAT, perm); if (fd < 0) { SYSERR("open %s for writing failed", fname); return -errno; } } file = calloc(1, sizeof(snd_pcm_file_t)); if (!file) { if (fname) close(fd); return -ENOMEM; } if (ifname) { ifd = open(ifname, O_RDONLY); /* TODO: mind blocking mode */ if (ifd < 0) { SYSERR("open %s for reading failed", ifname); if (fname) close(fd); return -errno; } } if (fname) file->fname = strdup(fname); if (ifname) file->ifname = strdup(ifname); file->fd = fd; file->ifd = ifd; file->format = format; file->gen.slave = slave; file->gen.close_slave = close_slave; err = snd_pcm_new(&pcm, SND_PCM_TYPE_FILE, name, slave->stream, slave->mode); if (err < 0) { free(file->fname); free(file); return err; } pcm->ops = &snd_pcm_file_ops; pcm->fast_ops = &snd_pcm_file_fast_ops; pcm->private_data = file; pcm->poll_fd = slave->poll_fd; pcm->poll_events = slave->poll_events; pcm->mmap_shadow = 1; snd_pcm_link_hw_ptr(pcm, slave); snd_pcm_link_appl_ptr(pcm, slave); *pcmp = pcm; return 0; }