Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/imx/dcss/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  dcss-ctxld.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 NXP.
 */


#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include "dcss-dev.h"

#define DCSS_CTXLD_CONTROL_STATUS 0x0
#define   CTXLD_ENABLE   BIT(0)
#define   ARB_SEL   BIT(1)
#define   RD_ERR_EN   BIT(2)
#define   DB_COMP_EN   BIT(3)
#define   SB_HP_COMP_EN   BIT(4)
#define   SB_LP_COMP_EN   BIT(5)
#define   DB_PEND_SB_REC_EN  BIT(6)
#define   SB_PEND_DISP_ACTIVE_EN BIT(7)
#define   AHB_ERR_EN   BIT(8)
#define   RD_ERR   BIT(16)
#define   DB_COMP   BIT(17)
#define   SB_HP_COMP   BIT(18)
#define   SB_LP_COMP   BIT(19)
#define   DB_PEND_SB_REC  BIT(20)
#define   SB_PEND_DISP_ACTIVE  BIT(21)
#define   AHB_ERR   BIT(22)
#define DCSS_CTXLD_DB_BASE_ADDR  0x10
#define DCSS_CTXLD_DB_COUNT  0x14
#define DCSS_CTXLD_SB_BASE_ADDR  0x18
#define DCSS_CTXLD_SB_COUNT  0x1C
#define   SB_HP_COUNT_POS  0
#define   SB_HP_COUNT_MASK  0xffff
#define   SB_LP_COUNT_POS  16
#define   SB_LP_COUNT_MASK  0xffff0000
#define DCSS_AHB_ERR_ADDR  0x20

#define CTXLD_IRQ_COMPLETION  (DB_COMP | SB_HP_COMP | SB_LP_COMP)
#define CTXLD_IRQ_ERROR   (RD_ERR | DB_PEND_SB_REC | AHB_ERR)

/* The following sizes are in context loader entries, 8 bytes each. */
#define CTXLD_DB_CTX_ENTRIES  1024 /* max 65536 */
#define CTXLD_SB_LP_CTX_ENTRIES  10240 /* max 65536 */
#define CTXLD_SB_HP_CTX_ENTRIES  20000 /* max 65536 */
#define CTXLD_SB_CTX_ENTRIES  (CTXLD_SB_LP_CTX_ENTRIES + \
      CTXLD_SB_HP_CTX_ENTRIES)

/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */
static u16 dcss_ctxld_ctx_size[3] = {
 CTXLD_DB_CTX_ENTRIES,
 CTXLD_SB_HP_CTX_ENTRIES,
 CTXLD_SB_LP_CTX_ENTRIES
};

/* this represents an entry in the context loader map */
struct dcss_ctxld_item {
 u32 val;
 u32 ofs;
};

#define CTX_ITEM_SIZE   sizeof(struct dcss_ctxld_item)

struct dcss_ctxld {
 struct device *dev;
 void __iomem *ctxld_reg;
 int irq;
 bool irq_en;

 struct dcss_ctxld_item *db[2];
 struct dcss_ctxld_item *sb_hp[2];
 struct dcss_ctxld_item *sb_lp[2];

 dma_addr_t db_paddr[2];
 dma_addr_t sb_paddr[2];

 u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */
 u8 current_ctx;

 bool in_use;
 bool armed;

 spinlock_t lock; /* protects concurent access to private data */
};

static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
{
 struct dcss_ctxld *ctxld = data;
 struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
 u32 irq_status;

 irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);

 if (irq_status & CTXLD_IRQ_COMPLETION &&
     !(irq_status & CTXLD_ENABLE) && ctxld->in_use) {
  ctxld->in_use = false;

  if (dcss && dcss->disable_callback)
   dcss->disable_callback(dcss);
 } else if (irq_status & CTXLD_IRQ_ERROR) {
  /*
 * Except for throwing an error message and clearing the status
 * register, there's not much we can do here.
 */

  dev_err(ctxld->dev, "ctxld: error encountered: %08x\n",
   irq_status);
  dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n",
   ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB],
   ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP],
   ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]);
 }

 dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
   ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);

 return IRQ_HANDLED;
}

static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld,
     struct platform_device *pdev)
{
 int ret;

 ctxld->irq = platform_get_irq_byname(pdev, "ctxld");
 if (ctxld->irq < 0)
  return ctxld->irq;

 ret = request_irq(ctxld->irq, dcss_ctxld_irq_handler,
     0, "dcss_ctxld", ctxld);
 if (ret) {
  dev_err(ctxld->dev, "ctxld: irq request failed.\n");
  return ret;
 }

 ctxld->irq_en = true;

 return 0;
}

static void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld)
{
 dcss_writel(RD_ERR_EN | SB_HP_COMP_EN |
      DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR,
      ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
}

static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld)
{
 struct dcss_ctxld_item *ctx;
 int i;

 for (i = 0; i < 2; i++) {
  if (ctxld->db[i]) {
   dma_free_coherent(ctxld->dev,
       CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
       ctxld->db[i], ctxld->db_paddr[i]);
   ctxld->db[i] = NULL;
   ctxld->db_paddr[i] = 0;
  }

  if (ctxld->sb_hp[i]) {
   dma_free_coherent(ctxld->dev,
       CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
       ctxld->sb_hp[i], ctxld->sb_paddr[i]);
   ctxld->sb_hp[i] = NULL;
   ctxld->sb_paddr[i] = 0;
  }
 }
}

static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld)
{
 struct dcss_ctxld_item *ctx;
 int i;

 for (i = 0; i < 2; i++) {
  ctx = dma_alloc_coherent(ctxld->dev,
      CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
      &ctxld->db_paddr[i], GFP_KERNEL);
  if (!ctx)
   return -ENOMEM;

  ctxld->db[i] = ctx;

  ctx = dma_alloc_coherent(ctxld->dev,
      CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
      &ctxld->sb_paddr[i], GFP_KERNEL);
  if (!ctx)
   return -ENOMEM;

  ctxld->sb_hp[i] = ctx;
  ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES;
 }

 return 0;
}

int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base)
{
 struct dcss_ctxld *ctxld;
 int ret;

 ctxld = devm_kzalloc(dcss->dev, sizeof(*ctxld), GFP_KERNEL);
 if (!ctxld)
  return -ENOMEM;

 dcss->ctxld = ctxld;
 ctxld->dev = dcss->dev;

 spin_lock_init(&ctxld->lock);

 ret = dcss_ctxld_alloc_ctx(ctxld);
 if (ret) {
  dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n");
  goto err;
 }

 ctxld->ctxld_reg = devm_ioremap(dcss->dev, ctxld_base, SZ_4K);
 if (!ctxld->ctxld_reg) {
  dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n");
  ret = -ENOMEM;
  goto err;
 }

 ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev));
 if (ret)
  goto err;

 dcss_ctxld_hw_cfg(ctxld);

 return 0;

err:
 dcss_ctxld_free_ctx(ctxld);

 return ret;
}

void dcss_ctxld_exit(struct dcss_ctxld *ctxld)
{
 free_irq(ctxld->irq, ctxld);

 dcss_ctxld_free_ctx(ctxld);
}

static int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld)
{
 int curr_ctx = ctxld->current_ctx;
 u32 db_base, sb_base, sb_count;
 u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
 struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);

 if (!dcss)
  return 0;

 dcss_dpr_write_sysctrl(dcss->dpr);

 dcss_scaler_write_sclctrl(dcss->scaler);

 sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
 sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP];
 db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB];

 /* make sure SB_LP context area comes after SB_HP */
 if (sb_lp_cnt &&
     ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) {
  struct dcss_ctxld_item *sb_lp_adjusted;

  sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt;

  memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx],
         sb_lp_cnt * CTX_ITEM_SIZE);
 }

 db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0;

 dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR);
 dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT);

 if (sb_hp_cnt)
  sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) |
      ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK);
 else
  sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK;

 sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0;

 dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR);
 dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT);

 /* enable the context loader */
 dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);

 ctxld->in_use = true;

 /*
 * Toggle the current context to the alternate one so that any updates
 * in the modules' settings take place there.
 */

 ctxld->current_ctx ^= 1;

 ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0;
 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0;
 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0;

 return 0;
}

int dcss_ctxld_enable(struct dcss_ctxld *ctxld)
{
 spin_lock_irq(&ctxld->lock);
 ctxld->armed = true;
 spin_unlock_irq(&ctxld->lock);

 return 0;
}

void dcss_ctxld_kick(struct dcss_ctxld *ctxld)
{
 unsigned long flags;

 spin_lock_irqsave(&ctxld->lock, flags);
 if (ctxld->armed && !ctxld->in_use) {
  ctxld->armed = false;
  dcss_ctxld_enable_locked(ctxld);
 }
 spin_unlock_irqrestore(&ctxld->lock, flags);
}

void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val,
         u32 reg_ofs)
{
 int curr_ctx = ctxld->current_ctx;
 struct dcss_ctxld_item *ctx[] = {
  [CTX_DB] = ctxld->db[curr_ctx],
  [CTX_SB_HP] = ctxld->sb_hp[curr_ctx],
  [CTX_SB_LP] = ctxld->sb_lp[curr_ctx]
 };
 int item_idx = ctxld->ctx_size[curr_ctx][ctx_id];

 if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) {
  WARN_ON(1);
  return;
 }

 ctx[ctx_id][item_idx].val = val;
 ctx[ctx_id][item_idx].ofs = reg_ofs;
 ctxld->ctx_size[curr_ctx][ctx_id] += 1;
}

void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
        u32 val, u32 reg_ofs)
{
 spin_lock_irq(&ctxld->lock);
 dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs);
 spin_unlock_irq(&ctxld->lock);
}

bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld)
{
 return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
  ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
  ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
}

int dcss_ctxld_resume(struct dcss_ctxld *ctxld)
{
 dcss_ctxld_hw_cfg(ctxld);

 if (!ctxld->irq_en) {
  enable_irq(ctxld->irq);
  ctxld->irq_en = true;
 }

 return 0;
}

int dcss_ctxld_suspend(struct dcss_ctxld *ctxld)
{
 int ret = 0;
 unsigned long timeout = jiffies + msecs_to_jiffies(500);

 if (!dcss_ctxld_is_flushed(ctxld)) {
  dcss_ctxld_kick(ctxld);

  while (!time_after(jiffies, timeout) && ctxld->in_use)
   msleep(20);

  if (time_after(jiffies, timeout))
   return -ETIMEDOUT;
 }

 spin_lock_irq(&ctxld->lock);

 if (ctxld->irq_en) {
  disable_irq_nosync(ctxld->irq);
  ctxld->irq_en = false;
 }

 /* reset context region and sizes */
 ctxld->current_ctx = 0;
 ctxld->ctx_size[0][CTX_DB] = 0;
 ctxld->ctx_size[0][CTX_SB_HP] = 0;
 ctxld->ctx_size[0][CTX_SB_LP] = 0;

 spin_unlock_irq(&ctxld->lock);

 return ret;
}

void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld)
{
 lockdep_assert_held(&ctxld->lock);
}

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.