struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn) { struct nhlt_fmt *fmt; struct nhlt_endpoint *epnt; struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; u16 bps = (s_fmt == 16) ? 16 : 32; u8 j; dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); epnt = (struct nhlt_endpoint *)nhlt->desc; dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); for (j = 0; j < nhlt->endpoint_count; j++) { if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps, link_type); if (sp_config) return sp_config; } epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); } return NULL; }
/** * snd_hdac_ext_device_init - initialize the HDA extended codec base device * @ebus: hdac extended bus to attach to * @addr: codec address * * Returns zero for success or a negative error code. */ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) { struct hdac_device *hdev = NULL; struct hdac_bus *bus = ebus_to_hbus(ebus); char name[15]; int ret; hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); if (!hdev) return -ENOMEM; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); ret = snd_hdac_device_init(hdev, bus, name, addr); if (ret < 0) { dev_err(bus->dev, "device init failed for hdac device\n"); return ret; } hdev->type = HDA_DEV_ASOC; hdev->dev.release = default_release; ret = snd_hdac_device_register(hdev); if (ret) { dev_err(bus->dev, "failed to register hdac device\n"); snd_hdac_ext_bus_device_exit(hdev); return ret; } return 0; }
static int skl_dsp_prepare(struct device *dev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_ext_stream *estream; struct hdac_stream *stream; struct snd_pcm_substream substream; int ret; if (!bus) return -ENODEV; memset(&substream, 0, sizeof(substream)); substream.stream = SNDRV_PCM_STREAM_PLAYBACK; estream = snd_hdac_ext_stream_assign(ebus, &substream, HDAC_EXT_STREAM_TYPE_HOST); if (!estream) return -ENODEV; stream = hdac_stream(estream); /* assign decouple host dma channel */ ret = snd_hdac_dsp_prepare(stream, format, size, dmab); if (ret < 0) return ret; skl_dsp_setup_spib(dev, size, stream->stream_tag, true); return stream->stream_tag; }
int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); int irq = bus->irq; struct skl_dsp_loader_ops loader_ops; int ret; loader_ops.alloc_dma_buf = skl_alloc_dma_buf; loader_ops.free_dma_buf = skl_free_dma_buf; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); /* read the BAR of the ADSP MMIO */ mmio_base = pci_ioremap_bar(skl->pci, 4); if (mmio_base == NULL) { dev_err(bus->dev, "ioremap error\n"); return -ENXIO; } ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, &skl->skl_sst); if (ret < 0) return ret; skl_dsp_enable_notification(skl->skl_sst, false); dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return ret; }
static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, size, dmab); }
static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; bus->io_ops->dma_free_pages(bus, dmab); return 0; }
static int skl_pcm_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; return skl_substream_free_pages(ebus_to_hbus(ebus), substream); }
void skl_free_dsp(struct skl *skl) { struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_sst *ctx = skl->skl_sst; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); skl_sst_dsp_cleanup(bus->dev, ctx); if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); }
static int skl_decoupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_ext_stream *stream; int start; unsigned long cookie; struct hdac_stream *hstr; stream = get_hdac_ext_stream(substream); hstr = hdac_stream(stream); if (!hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = 0; break; default: return -EINVAL; } spin_lock_irqsave(&bus->reg_lock, cookie); if (start) { snd_hdac_stream_start(hdac_stream(stream), true); snd_hdac_stream_timecounter_init(hstr, 0); } else { snd_hdac_stream_stop(hdac_stream(stream)); } spin_unlock_irqrestore(&bus->reg_lock, cookie); return 0; }
static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_stream *stream; struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; stream = snd_hdac_get_stream(bus, SNDRV_PCM_STREAM_PLAYBACK, stream_tag); if (!stream) return -EINVAL; snd_hdac_dsp_trigger(stream, start); return 0; }
int skl_nhlt_update_topology_bin(struct skl *skl) { struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct device *dev = bus->dev; dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", nhlt->header.oem_id, nhlt->header.oem_table_id, nhlt->header.oem_revision); snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, nhlt->header.oem_revision, "-tplg.bin"); skl_nhlt_trim_space(skl->tplg_name); return 0; }
int skl_free_dsp(struct skl *skl) { struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_sst *ctx = skl->skl_sst; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); ctx->dsp_ops->cleanup(bus->dev, ctx); kfree(ctx->cores.state); kfree(ctx->cores.usage_count); if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); return 0; }
static int skl_dsp_setup_spib(struct device *dev, unsigned int size, int stream_tag, int enable) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_stream *stream = snd_hdac_get_stream(bus, SNDRV_PCM_STREAM_PLAYBACK, stream_tag); struct hdac_ext_stream *estream; if (!stream) return -EINVAL; estream = stream_to_hdac_ext_stream(stream); /* enable/disable SPIB for this hdac stream */ snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index); /* set the spib value */ snd_hdac_ext_stream_set_spib(ebus, estream, size); return 0; }
int skl_free_dsp(struct skl *skl) { struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_sst *ctx = skl->skl_sst; int index; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); index = skl_get_dsp_ops(skl->pci->device); if (index < 0) return -EIO; dsp_ops[index].cleanup(bus->dev, ctx); if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); return 0; }
static int skl_dsp_cleanup(struct device *dev, struct snd_dma_buffer *dmab, int stream_tag) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct hdac_stream *stream; struct hdac_ext_stream *estream; struct hdac_bus *bus = ebus_to_hbus(ebus); if (!bus) return -ENODEV; stream = snd_hdac_get_stream(bus, SNDRV_PCM_STREAM_PLAYBACK, stream_tag); if (!stream) return -EINVAL; estream = stream_to_hdac_ext_stream(stream); skl_dsp_setup_spib(dev, 0, stream_tag, false); snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); snd_hdac_dsp_cleanup(stream, dmab); return 0; }
int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; struct skl_dsp_cores *cores; int ret; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true); /* read the BAR of the ADSP MMIO */ mmio_base = pci_ioremap_bar(skl->pci, 4); if (mmio_base == NULL) { dev_err(bus->dev, "ioremap error\n"); return -ENXIO; } ops = skl_get_dsp_ops(skl->pci->device); if (!ops) { ret = -EIO; goto unmap_mmio; } loader_ops = ops->loader_ops(); ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, &skl->skl_sst); if (ret < 0) goto unmap_mmio; skl->skl_sst->dsp_ops = ops; cores = &skl->skl_sst->cores; cores->count = ops->num_cores; cores->state = kcalloc(cores->count, sizeof(*cores->state), GFP_KERNEL); if (!cores->state) { ret = -ENOMEM; goto unmap_mmio; } cores->usage_count = kcalloc(cores->count, sizeof(*cores->usage_count), GFP_KERNEL); if (!cores->usage_count) { ret = -ENOMEM; goto free_core_state; } dev_dbg(bus->dev, "dsp registration status=%d\n", ret); return 0; free_core_state: kfree(cores->state); unmap_mmio: iounmap(mmio_base); return ret; }
static int skl_coupled_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_bus *bus = ebus_to_hbus(ebus); struct hdac_ext_stream *stream; struct snd_pcm_substream *s; bool start; int sbits = 0; unsigned long cookie; struct hdac_stream *hstr; stream = get_hdac_ext_stream(substream); hstr = hdac_stream(stream); dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd); if (!hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: start = false; break; default: return -EINVAL; } snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; stream = get_hdac_ext_stream(s); sbits |= 1 << hdac_stream(stream)->index; snd_pcm_trigger_done(s, substream); } spin_lock_irqsave(&bus->reg_lock, cookie); /* first, set SYNC bits of corresponding streams */ snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; stream = get_hdac_ext_stream(s); if (start) snd_hdac_stream_start(hdac_stream(stream), true); else snd_hdac_stream_stop(hdac_stream(stream)); } spin_unlock_irqrestore(&bus->reg_lock, cookie); snd_hdac_stream_sync(hstr, start, sbits); spin_lock_irqsave(&bus->reg_lock, cookie); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC); if (start) snd_hdac_stream_timecounter_init(hstr, sbits); spin_unlock_irqrestore(&bus->reg_lock, cookie); return 0; }