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

Quelle  mcde_clk_div.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/regulator/consumer.h>

#include "mcde_drm.h"
#include "mcde_display_regs.h"

/* The MCDE internal clock dividers for FIFO A and B */
struct mcde_clk_div {
 struct clk_hw hw;
 struct mcde *mcde;
 u32 cr;
 u32 cr_div;
};

static int mcde_clk_div_enable(struct clk_hw *hw)
{
 struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 struct mcde *mcde = cdiv->mcde;
 u32 val;

 spin_lock(&mcde->fifo_crx1_lock);
 val = readl(mcde->regs + cdiv->cr);
 /*
 * Select the PLL72 (LCD) clock as parent
 * FIXME: implement other parents.
 */

 val &= ~MCDE_CRX1_CLKSEL_MASK;
 val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
 /* Internal clock */
 val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;

 /* Clear then set the divider */
 val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
 val |= cdiv->cr_div;

 writel(val, mcde->regs + cdiv->cr);
 spin_unlock(&mcde->fifo_crx1_lock);

 return 0;
}

static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
       unsigned long *prate, bool set_parent)
{
 int best_div = 1, div;
 struct clk_hw *parent = clk_hw_get_parent(hw);
 unsigned long best_prate = 0;
 unsigned long best_diff = ~0ul;
 int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;

 for (div = 1; div < max_div; div++) {
  unsigned long this_prate, div_rate, diff;

  if (set_parent)
   this_prate = clk_hw_round_rate(parent, rate * div);
  else
   this_prate = *prate;
  div_rate = DIV_ROUND_UP_ULL(this_prate, div);
  diff = abs(rate - div_rate);

  if (diff < best_diff) {
   best_div = div;
   best_diff = diff;
   best_prate = this_prate;
  }
 }

 *prate = best_prate;
 return best_div;
}

static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
         unsigned long *prate)
{
 int div = mcde_clk_div_choose_div(hw, rate, prate, true);

 return DIV_ROUND_UP_ULL(*prate, div);
}

static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
            unsigned long prate)
{
 struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 struct mcde *mcde = cdiv->mcde;
 u32 cr;
 int div;

 /*
 * If the MCDE is not powered we can't access registers.
 * It will come up with 0 in the divider register bits, which
 * means "divide by 2".
 */

 if (!regulator_is_enabled(mcde->epod))
  return DIV_ROUND_UP_ULL(prate, 2);

 cr = readl(mcde->regs + cdiv->cr);
 if (cr & MCDE_CRX1_BCD)
  return prate;

 /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
 div = cr & MCDE_CRX1_PCD_MASK;
 div += 2;

 return DIV_ROUND_UP_ULL(prate, div);
}

static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
      unsigned long prate)
{
 struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
 int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
 u32 cr = 0;

 /*
 * We cache the CR bits to set the divide in the state so that
 * we can call this before we can even write to the hardware.
 */

 if (div == 1) {
  /* Bypass clock divider */
  cr |= MCDE_CRX1_BCD;
 } else {
  div -= 2;
  cr |= div & MCDE_CRX1_PCD_MASK;
 }
 cdiv->cr_div = cr;

 return 0;
}

static const struct clk_ops mcde_clk_div_ops = {
 .enable = mcde_clk_div_enable,
 .recalc_rate = mcde_clk_div_recalc_rate,
 .round_rate = mcde_clk_div_round_rate,
 .set_rate = mcde_clk_div_set_rate,
};

int mcde_init_clock_divider(struct mcde *mcde)
{
 struct device *dev = mcde->dev;
 struct mcde_clk_div *fifoa;
 struct mcde_clk_div *fifob;
 const char *parent_name;
 struct clk_init_data fifoa_init = {
  .name = "fifoa",
  .ops = &mcde_clk_div_ops,
  .parent_names = &parent_name,
  .num_parents = 1,
  .flags = CLK_SET_RATE_PARENT,
 };
 struct clk_init_data fifob_init = {
  .name = "fifob",
  .ops = &mcde_clk_div_ops,
  .parent_names = &parent_name,
  .num_parents = 1,
  .flags = CLK_SET_RATE_PARENT,
 };
 int ret;

 spin_lock_init(&mcde->fifo_crx1_lock);
 parent_name = __clk_get_name(mcde->lcd_clk);

 /* Allocate 2 clocks */
 fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
 if (!fifoa)
  return -ENOMEM;
 fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
 if (!fifob)
  return -ENOMEM;

 fifoa->mcde = mcde;
 fifoa->cr = MCDE_CRA1;
 fifoa->hw.init = &fifoa_init;
 ret = devm_clk_hw_register(dev, &fifoa->hw);
 if (ret) {
  dev_err(dev, "error registering FIFO A clock divider\n");
  return ret;
 }
 mcde->fifoa_clk = fifoa->hw.clk;

 fifob->mcde = mcde;
 fifob->cr = MCDE_CRB1;
 fifob->hw.init = &fifob_init;
 ret = devm_clk_hw_register(dev, &fifob->hw);
 if (ret) {
  dev_err(dev, "error registering FIFO B clock divider\n");
  return ret;
 }
 mcde->fifob_clk = fifob->hw.clk;

 return 0;
}

Messung V0.5
C=92 H=75 G=83

¤ Dauer der Verarbeitung: 0.3 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.