Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/sound/soc/intel/avs/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  cldma.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021-2022 Intel Corporation
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//

#include <linux/pci.h>
#include <sound/hda_register.h>
#include <sound/hdaudio_ext.h>
#include "cldma.h"
#include "registers.h"

/* Stream Registers */
#define AZX_CL_SD_BASE   0x80
#define AZX_SD_CTL_STRM_MASK  GENMASK(23, 20)
#define AZX_SD_CTL_STRM(s)  (((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK)
#define AZX_SD_BDLPL_BDLPLBA_MASK GENMASK(31, 7)
#define AZX_SD_BDLPL_BDLPLBA(lb) ((lb) & AZX_SD_BDLPL_BDLPLBA_MASK)

/* Software Position Based FIFO Capability Registers */
#define AZX_CL_SPBFCS   0x20
#define AZX_REG_CL_SPBFCTL  (AZX_CL_SPBFCS + 0x4)
#define AZX_REG_CL_SD_SPIB  (AZX_CL_SPBFCS + 0x8)

#define AVS_CL_OP_INTERVAL_US  3
#define AVS_CL_OP_TIMEOUT_US  300
#define AVS_CL_IOC_TIMEOUT_MS  300
#define AVS_CL_STREAM_INDEX  0

struct hda_cldma {
 struct device *dev;
 struct hdac_bus *bus;
 void __iomem *dsp_ba;

 unsigned int buffer_size;
 unsigned int num_periods;
 unsigned char stream_tag;
 void __iomem *sd_addr;

 struct snd_dma_buffer dmab_data;
 struct snd_dma_buffer dmab_bdl;
 struct delayed_work memcpy_work;
 struct completion completion;

 /* runtime */
 void *position;
 unsigned int remaining;
 unsigned int sd_status;
};

static void cldma_memcpy_work(struct work_struct *work);

struct hda_cldma code_loader = {
 .stream_tag = AVS_CL_STREAM_INDEX + 1,
 .memcpy_work = __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0),
 .completion = COMPLETION_INITIALIZER(code_loader.completion),
};

void hda_cldma_fill(struct hda_cldma *cl)
{
 unsigned int size, offset;

 if (cl->remaining > cl->buffer_size)
  size = cl->buffer_size;
 else
  size = cl->remaining;

 offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);
 if (offset + size > cl->buffer_size) {
  unsigned int ss;

  ss = cl->buffer_size - offset;
  memcpy(cl->dmab_data.area + offset, cl->position, ss);
  offset = 0;
  size -= ss;
  cl->position += ss;
  cl->remaining -= ss;
 }

 memcpy(cl->dmab_data.area + offset, cl->position, size);
 cl->position += size;
 cl->remaining -= size;

 snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);
}

static void cldma_memcpy_work(struct work_struct *work)
{
 struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work);
 int ret;

 ret = hda_cldma_start(cl);
 if (ret < 0) {
  dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);
  return;
 }

 while (true) {
  ret = wait_for_completion_timeout(&cl->completion,
        msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));
  if (!ret) {
   dev_err(cl->dev, "cldma IOC timeout\n");
   break;
  }

  if (!(cl->sd_status & SD_INT_COMPLETE)) {
   dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",
    cl->sd_status);
   break;
  }

  if (!cl->remaining)
   break;

  reinit_completion(&cl->completion);
  hda_cldma_fill(cl);
  /* enable CLDMA interrupt */
  snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
          AVS_ADSP_ADSPIC_CLDMA);
 }
}

void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay)
{
 if (!cl->remaining)
  return;

 reinit_completion(&cl->completion);
 /* fill buffer with the first chunk before scheduling run */
 hda_cldma_fill(cl);

 schedule_delayed_work(&cl->memcpy_work, start_delay);
}

int hda_cldma_start(struct hda_cldma *cl)
{
 unsigned int reg;

 /* enable interrupts */
 snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
         AVS_ADSP_ADSPIC_CLDMA);
 snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START,
    SD_INT_MASK | SD_CTL_DMA_START);

 /* await DMA engine start */
 return snd_hdac_stream_readb_poll(cl, SD_CTL, reg, reg & SD_CTL_DMA_START,
       AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
}

int hda_cldma_stop(struct hda_cldma *cl)
{
 unsigned int reg;
 int ret;

 /* disable interrupts */
 snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);
 snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);

 /* await DMA engine stop */
 ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_DMA_START),
      AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
 cancel_delayed_work_sync(&cl->memcpy_work);

 return ret;
}

int hda_cldma_reset(struct hda_cldma *cl)
{
 unsigned int reg;
 int ret;

 ret = hda_cldma_stop(cl);
 if (ret < 0) {
  dev_err(cl->dev, "cldma stop failed: %d\n", ret);
  return ret;
 }

 snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, SD_CTL_STREAM_RESET);
 ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & SD_CTL_STREAM_RESET),
      AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
 if (ret < 0) {
  dev_err(cl->dev, "cldma set SRST failed: %d\n", ret);
  return ret;
 }

 snd_hdac_stream_updateb(cl, SD_CTL, SD_CTL_STREAM_RESET, 0);
 ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & SD_CTL_STREAM_RESET),
      AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
 if (ret < 0) {
  dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret);
  return ret;
 }

 return 0;
}

void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size)
{
 /* setup runtime */
 cl->position = data;
 cl->remaining = size;
}

static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size)
{
 struct snd_dma_buffer *dmab = &cl->dmab_data;
 __le32 *bdl = (__le32 *)cl->dmab_bdl.area;
 int remaining = cl->buffer_size;
 int offset = 0;

 cl->num_periods = 0;

 while (remaining > 0) {
  phys_addr_t addr;
  int chunk;

  addr = snd_sgbuf_get_addr(dmab, offset);
  bdl[0] = cpu_to_le32(lower_32_bits(addr));
  bdl[1] = cpu_to_le32(upper_32_bits(addr));
  chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size);
  bdl[2] = cpu_to_le32(chunk);

  remaining -= chunk;
  /* set IOC only for the last entry */
  bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);

  bdl += 4;
  offset += chunk;
  cl->num_periods++;
 }
}

void hda_cldma_setup(struct hda_cldma *cl)
{
 dma_addr_t bdl_addr = cl->dmab_bdl.addr;

 cldma_setup_bdle(cl, cl->buffer_size / 2);

 snd_hdac_stream_writel(cl, SD_BDLPL, AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr)));
 snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr));

 snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size);
 snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1);

 snd_hdac_stream_updatel(cl, SD_CTL, AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl));
 /* enable spib */
 snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);
}

void hda_cldma_interrupt(struct hda_cldma *cl)
{
 /* disable CLDMA interrupt */
 snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);

 cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
 dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);

 complete(&cl->completion);
}

int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
     unsigned int buffer_size)
{
 int ret;

 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data);
 if (ret < 0)
  return ret;

 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl);
 if (ret < 0) {
  snd_dma_free_pages(&cl->dmab_data);
  return ret;
 }

 cl->dev = bus->dev;
 cl->bus = bus;
 cl->dsp_ba = dsp_ba;
 cl->buffer_size = buffer_size;
 cl->sd_addr = dsp_ba + AZX_CL_SD_BASE;

 return 0;
}

void hda_cldma_free(struct hda_cldma *cl)
{
 snd_dma_free_pages(&cl->dmab_data);
 snd_dma_free_pages(&cl->dmab_bdl);
}

Messung V0.5
C=97 H=98 G=97

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.