int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) { struct snd_pcm *sp; struct snd_card *sc = cxsc->sc; struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); int ret; ret = snd_pcm_new(sc, "CX23418 PCM", 0, /* PCM device 0, the only one for this card */ 0, /* 0 playback substreams */ 1, /* 1 capture substream */ &sp); if (ret) { CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", __func__, ret); goto err_exit; } spin_lock_init(&cxsc->slock); snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, &snd_cx18_pcm_capture_ops); sp->info_flags = 0; sp->private_data = cxsc; strlcpy(sp->name, cx->card_name, sizeof(sp->name)); return 0; err_exit: return ret; }
static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); struct cx18 *cx = to_cx18(cxsc->v4l2_dev); struct v4l2_control vctrl; int ret; vctrl.id = V4L2_CID_AUDIO_VOLUME; vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); snd_cx18_lock(cxsc); /* Fetch current state */ ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl); if (ret || (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { /* Set, if needed */ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl); if (!ret) ret = 1; /* Indicate control was changed w/o error */ } snd_cx18_unlock(cxsc); return ret; }
static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) { struct cx18 *cx = to_cx18(cxsc->v4l2_dev); /* */ snd_card_free(cxsc->sc); cx->alsa = NULL; }
static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) { struct cx18 *cx = to_cx18(cxsc->v4l2_dev); /* FIXME - pointer checks & shutdown cxsc */ snd_card_free(cxsc->sc); cx->alsa = NULL; }
static void cx18_remove(struct pci_dev *pci_dev) { struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx18 *cx = to_cx18(v4l2_dev); int i; CX18_DEBUG_INFO("Removing Card\n"); /* Stop all captures */ CX18_DEBUG_INFO("Stopping all streams\n"); if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); /* Stop interrupts that cause incoming work to be queued */ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); /* Incoming work can cause outgoing work, so clean up incoming first */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) cx18_cancel_in_work_orders(cx); cx18_cancel_out_work_orders(cx); #else flush_workqueue(cx->in_work_queue); flush_workqueue(cx->out_work_queue); #endif /* Stop ack interrupts that may have been needed for work to finish */ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); destroy_workqueue(cx->in_work_queue); destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->pci_dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); pci_disable_device(cx->pci_dev); if (cx->vbi.sliced_mpeg_data[0] != NULL) for (i = 0; i < CX18_VBI_FRAMES; i++) kfree(cx->vbi.sliced_mpeg_data[i]); CX18_INFO("Removed %s\n", cx->card_name); v4l2_device_unregister(v4l2_dev); kfree(cx); }
static void snd_cx18_card_free(struct snd_cx18_card *cxsc) { if (cxsc == NULL) return; if (cxsc->v4l2_dev != NULL) to_cx18(cxsc->v4l2_dev)->alsa = NULL; /* */ kfree(cxsc); }
void cx18_reset_ir_gpio(void *data) { struct cx18 *cx = to_cx18((struct v4l2_device *)data); if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) return; CX18_DEBUG_INFO("Resetting IR microcontroller\n"); v4l2_subdev_call(&cx->sd_resetctrl, core, reset, CX18_GPIO_RESET_Z8F0811); }
static void snd_cx18_card_free(struct snd_cx18_card *cxsc) { if (cxsc == NULL) return; if (cxsc->v4l2_dev != NULL) to_cx18(cxsc->v4l2_dev)->alsa = NULL; /* FIXME - take any other stopping actions needed */ kfree(cxsc); }
static void cx18_remove(struct pci_dev *pci_dev) { struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx18 *cx = to_cx18(v4l2_dev); int i; CX18_DEBUG_INFO("Removing Card\n"); CX18_DEBUG_INFO("Stopping all streams\n"); if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); cx18_cancel_in_work_orders(cx); cx18_cancel_out_work_orders(cx); cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); destroy_workqueue(cx->in_work_queue); destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); free_irq(cx->pci_dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); pci_disable_device(cx->pci_dev); if (cx->vbi.sliced_mpeg_data[0] != NULL) for (i = 0; i < CX18_VBI_FRAMES; i++) kfree(cx->vbi.sliced_mpeg_data[i]); CX18_INFO("Removed %s\n", cx->card_name); v4l2_device_unregister(v4l2_dev); kfree(cx); }
static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) { struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; struct cx18_open_id item; int ret; /* Instruct the cx18 to start sending packets */ snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; item.cx = cx; item.type = s->type; item.open_id = cx->open_id++; /* See if the stream is available */ if (cx18_claim_stream(&item, item.type)) { /* No, it's already in use */ snd_cx18_unlock(cxsc); return -EBUSY; } if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { /* We're already streaming. No additional action required */ snd_cx18_unlock(cxsc); return 0; } runtime->hw = snd_cx18_hw_capture; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); cxsc->capture_pcm_substream = substream; runtime->private_data = cx; cx->pcm_announce_callback = cx18_alsa_announce_pcm_data; /* Not currently streaming, so start it up */ set_bit(CX18_F_S_STREAMING, &s->s_flags); ret = cx18_start_v4l2_encode_stream(s); snd_cx18_unlock(cxsc); return ret; }
static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc) { struct cx18 *cx = to_cx18(cxsc->v4l2_dev); struct snd_card *sc = cxsc->sc; /* */ strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); /* */ snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", cx->instance); /* */ snprintf(sc->longname, sizeof(sc->longname), "CX23418 #%d %s TV/FM Radio/Line-In Capture", cx->instance, cx->card_name); return 0; }
static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc) { struct cx18 *cx = to_cx18(cxsc->v4l2_dev); struct snd_card *sc = cxsc->sc; /* sc->driver is used by alsa-lib's configurator: simple, unique */ strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */ snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", cx->instance); /* sc->longname is read from /proc/asound/cards */ snprintf(sc->longname, sizeof(sc->longname), "CX23418 #%d %s TV/FM Radio/Line-In Capture", cx->instance, cx->card_name); return 0; }
static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); struct cx18 *cx = to_cx18(cxsc->v4l2_dev); struct v4l2_control vctrl; int ret; vctrl.id = V4L2_CID_AUDIO_VOLUME; vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); snd_cx18_lock(cxsc); ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl); snd_cx18_unlock(cxsc); if (!ret) uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); return ret; }
static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) { struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; /* Instruct the cx18 to stop sending packets */ snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; cx18_stop_v4l2_encode_stream(s, 0); clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_release_stream(s); cx->pcm_announce_callback = NULL; snd_cx18_unlock(cxsc); return 0; }
int cx18_alsa_load(struct cx18 *cx) { struct v4l2_device *v4l2_dev = &cx->v4l2_dev; struct cx18_stream *s; if (v4l2_dev == NULL) { printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", __func__); return 0; } cx = to_cx18(v4l2_dev); if (cx == NULL) { printk(KERN_ERR "cx18-alsa cx is NULL\n"); return 0; } s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; if (s->video_dev == NULL) { CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " "skipping\n", __func__); return 0; } if (cx->alsa != NULL) { CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n", __func__); return 0; } if (snd_cx18_init(v4l2_dev)) { CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n", __func__); } else { CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " "\n", __func__); } return 0; }
static inline struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev) { return to_cx18(v4l2_dev)->alsa; }
static int snd_cx18_init(struct v4l2_device *v4l2_dev) { struct cx18 *cx = to_cx18(v4l2_dev); struct snd_card *sc = NULL; struct snd_cx18_card *cxsc; int ret; /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ /* (1) Check and increment the device index */ /* This is a no-op for us. We'll use the cx->instance */ /* (2) Create a card instance */ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ THIS_MODULE, 0, &sc); if (ret) { CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", __func__, ret); goto err_exit; } /* (3) Create a main component */ ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); if (ret) { CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", __func__, ret); goto err_exit_free; } /* (4) Set the driver ID and name strings */ snd_cx18_card_set_names(cxsc); ret = snd_cx18_pcm_create(cxsc); if (ret) { CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", __func__, ret); goto err_exit_free; } /* FIXME - proc files */ /* (7) Set the driver data and return 0 */ /* We do this out of normal order for PCI drivers to avoid races */ cx->alsa = cxsc; /* (6) Register the card instance */ ret = snd_card_register(sc); if (ret) { cx->alsa = NULL; CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", __func__, ret); goto err_exit_free; } return 0; err_exit_free: if (sc != NULL) snd_card_free(sc); kfree(cxsc); err_exit: return ret; }
static int snd_cx18_init(struct v4l2_device *v4l2_dev) { struct cx18 *cx = to_cx18(v4l2_dev); struct snd_card *sc = NULL; struct snd_cx18_card *cxsc; int ret; /* */ /* */ /* */ /* */ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* */ SNDRV_DEFAULT_STR1, /* */ THIS_MODULE, 0, &sc); if (ret) { CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", __func__, ret); goto err_exit; } /* */ ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); if (ret) { CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", __func__, ret); goto err_exit_free; } /* */ snd_cx18_card_set_names(cxsc); ret = snd_cx18_pcm_create(cxsc); if (ret) { CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", __func__, ret); goto err_exit_free; } /* */ /* */ /* */ cx->alsa = cxsc; /* */ ret = snd_card_register(sc); if (ret) { cx->alsa = NULL; CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", __func__, ret); goto err_exit_free; } return 0; err_exit_free: if (sc != NULL) snd_card_free(sc); kfree(cxsc); err_exit: return ret; }