有人用了新的EasyARM-IMX283A时候,发现按照我说的去修改LCD结果得不到目标频率,一直在24MHz下,当然这肯定不是板子问题,就算外部什么都没做,他这里都是按照实际输出显示,外部够不够24MHz他也不知道~
看图吧,有这个问题:
然后发现mach-mx28下面实现实在有问题~
static unsigned long lcdif_get_rate(struct clk *clk) { long rate = clk->parent->get_rate(clk->parent); long div; div = __raw_readl(clk->scale_reg); if (!(div & BM_CLKCTRL_DIS_LCDIF_DIV_FRAC_EN)) { div = (div >> clk->scale_bits) & BM_CLKCTRL_DIS_LCDIF_DIV; return rate / (div ? div : 1); } div = (div >> clk->scale_bits) & BM_CLKCTRL_DIS_LCDIF_DIV; rate /= (BM_CLKCTRL_DIS_LCDIF_DIV >> clk->scale_bits) + 1; rate *= div; return rate; }
正确方法:
static int lcdif_set_rate(struct clk *clk, unsigned long rate) { int ret = 0; /* * ref_pix can be between 480e6*18/35=246.9MHz and 480e6*18/18=480MHz, * which is between 18/(18*480e6)=2.084ns and 35/(18*480e6)=4.050ns. * * ns_cycle >= 2*18e3/(18*480) = 25/6 * ns_cycle <= 2*35e3/(18*480) = 875/108 * * Multiply the ns_cycle by 'div' to lengthen it until it fits the * bounds. This is the divider we'll use after ref_pix. * * 6 * ns_cycle >= 25 * div * 108 * ns_cycle <= 875 * div */ u32 ns_cycle = 1000000000 / rate; u32 div, reg_val; u32 lowest_result = (u32) -1; u32 lowest_div = 0, lowest_fracdiv = 0; ns_cycle *= 2; for (div = 1; div < 256; ++div) { u32 fracdiv; u32 ps_result; int lower_bound = 6 * ns_cycle >= 25 * div; int upper_bound = 108 * ns_cycle <= 875 * div; if (!lower_bound) break; if (!upper_bound) continue; /* * Found a matching div. Calculate fractional divider needed, * rounded up. */ fracdiv = ((clk_get_rate(&pll_clk[0]) / 1000000 * 18 / 2) * ns_cycle + 1000 * div - 1) / (1000 * div); if (fracdiv < 18 || fracdiv > 35) { ret = -EINVAL; goto out; } /* Calculate the actual cycle time this results in */ ps_result = 6250 * div * fracdiv / 27; /* Use the fastest result that doesn't break ns_cycle */ if (ps_result <= lowest_result) { lowest_result = ps_result; lowest_div = div; lowest_fracdiv = fracdiv; } } if (div >= 256 || lowest_result == (u32) -1) { ret = -EINVAL; goto out; } /* set to XTAL source first */ reg_val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ); reg_val |= BM_CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF; __raw_writel(reg_val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ); /* Program ref_pix phase fractional divider */ reg_val = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1); reg_val &= ~BM_CLKCTRL_FRAC1_PIXFRAC; reg_val |= BF_CLKCTRL_FRAC1_PIXFRAC(lowest_fracdiv); __raw_writel(reg_val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1); /* Ungate PIX CLK*/ __raw_writel(BM_CLKCTRL_FRAC1_CLKGATEPIX, CLKCTRL_BASE_ADDR + HW_CLKCTRL_FRAC1_CLR); /* Program LCDIF divider */ reg_val = __raw_readl(clk->scale_reg); reg_val &= ~(BM_CLKCTRL_DIS_LCDIF_DIV | BM_CLKCTRL_DIS_LCDIF_CLKGATE); reg_val |= BF_CLKCTRL_DIS_LCDIF_DIV(lowest_div); __raw_writel(reg_val, clk->scale_reg); if (clk->busy_reg) { int i; for (i = 10000; i; i--) if (!clk_is_busy(clk)) break; if (!i) return -ETIMEDOUT; } /* Switch to ref_pix source */ clk_set_parent(clk, &ref_pix_clk); reg_val = BM_CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF; __raw_writel(reg_val, CLKCTRL_BASE_ADDR + HW_CLKCTRL_CLKSEQ_CLR); out: pr_debug("Programming PFD=%u,DIV=%u ref_pix=%uMHz " "PIXCLK=%uMHz cycle=%u.%03uns ", lowest_fracdiv, lowest_div, 480*18/lowest_fracdiv, 480*18/lowest_fracdiv/lowest_div, lowest_result / 1000, lowest_result % 1000); return ret; }