/* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */ staticvoid mpc512x_set_pixel_clock(unsignedint pixclock)
{ struct device_node *np; struct clk *clk_diu; unsignedlong epsilon, minpixclock, maxpixclock; unsignedlong offset, want, got, delta;
/* lookup and enable the DIU clock */
np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); if (!np) {
pr_err("Could not find DIU device tree node.\n"); return;
}
clk_diu = of_clk_get(np, 0); if (IS_ERR(clk_diu)) { /* backwards compat with device trees that lack clock specs */
clk_diu = clk_get_sys(np->name, "ipg");
}
of_node_put(np); if (IS_ERR(clk_diu)) {
pr_err("Could not lookup DIU clock.\n"); return;
} if (clk_prepare_enable(clk_diu)) {
pr_err("Could not enable DIU clock.\n"); return;
}
/* * convert the picoseconds spec into the desired clock rate, * determine the acceptable clock range for the monitor (+/- 5%), * do the calculation in steps to avoid integer overflow
*/
pr_debug("DIU pixclock in ps - %u\n", pixclock);
pixclock = (1000000000 / pixclock) * 1000;
pr_debug("DIU pixclock freq - %u\n", pixclock);
epsilon = pixclock / 20; /* pixclock * 0.05 */
pr_debug("DIU deviation - %lu\n", epsilon);
minpixclock = pixclock - epsilon;
maxpixclock = pixclock + epsilon;
pr_debug("DIU minpixclock - %lu\n", minpixclock);
pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
/* * check whether the DIU supports the desired pixel clock * * - simply request the desired clock and see what the * platform's clock driver will make of it, assuming that it * will setup the best approximation of the requested value * - try other candidate frequencies in the order of decreasing * preference (i.e. with increasing distance from the desired * pixel clock, and checking the lower frequency before the * higher frequency to not overload the hardware) until the * first match is found -- any potential subsequent match * would only be as good as the former match or typically * would be less preferrable * * the offset increment of pixelclock divided by 64 is an * arbitrary choice -- it's simple to calculate, in the typical * case we expect the first check to succeed already, in the * worst case seven frequencies get tested (the exact center and * three more values each to the left and to the right) before * the 5% tolerance window is exceeded, resulting in fast enough * execution yet high enough probability of finding a suitable * value, while the error rate will be in the order of single * percents
*/ for (offset = 0; offset <= epsilon; offset += pixclock / 64) {
want = pixclock - offset;
pr_debug("DIU checking clock - %lu\n", want);
clk_set_rate(clk_diu, want);
got = clk_get_rate(clk_diu);
delta = abs(pixclock - got); if (delta < epsilon) break; if (!offset) continue;
want = pixclock + offset;
pr_debug("DIU checking clock - %lu\n", want);
clk_set_rate(clk_diu, want);
got = clk_get_rate(clk_diu);
delta = abs(pixclock - got); if (delta < epsilon) break;
} if (offset <= epsilon) {
pr_debug("DIU clock accepted - %lu\n", want);
pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
pixclock, got, delta, epsilon); return;
}
pr_warn("DIU pixclock auto search unsuccessful\n");
/* * what is the most appropriate action to take when the search * for an available pixel clock which is acceptable to the * monitor has failed? disable the DIU (clock) or just provide * a "best effort"? we go with the latter
*/
pr_warn("DIU pixclock best effort fallback (backend's choice)\n");
clk_set_rate(clk_diu, pixclock);
got = clk_get_rate(clk_diu);
delta = abs(pixclock - got);
pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
pixclock, got, delta, epsilon);
}
/* * Check if DIU was pre-initialized. If so, perform steps * needed to continue displaying through the whole boot process. * Move area descriptor and gamma table elsewhere, they are * destroyed by bootmem allocator otherwise. The frame buffer * address range will be reserved in setup_arch() after bootmem * allocator is up.
*/ staticvoid __init mpc512x_init_diu(void)
{ struct device_node *np; struct diu __iomem *diu_reg;
phys_addr_t desc; void __iomem *vaddr; unsignedlong mode, pix_fmt, res, bpp; unsignedlong dst;
np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); if (!np) {
pr_err("No DIU node\n"); return;
}
staticvoid __init mpc512x_setup_diu(void)
{ int ret;
/* * We do not allocate and configure new area for bitmap buffer * because it would require copying bitmap data (splash image) * and so negatively affect boot time. Instead we reserve the * already configured frame buffer area so that it won't be * destroyed. The starting address of the area to reserve and * also its length is passed to memblock_reserve(). It will be * freed later on first open of fbdev, when splash image is not * needed any more.
*/ if (diu_shared_fb.in_use) {
ret = memblock_reserve(diu_shared_fb.fb_phys,
diu_shared_fb.fb_len); if (ret) {
pr_err("%s: reserve bootmem failed\n", __func__);
diu_shared_fb.in_use = false;
}
}
/* Init PSC FIFO space for TX and RX slices */ staticvoid __init mpc512x_psc_fifo_init(void)
{ struct device_node *np; void __iomem *psc; unsignedint tx_fifo_size; unsignedint rx_fifo_size; constchar *psc_compat; int fifobase = 0; /* current fifo address in 32 bit words */
psc_compat = mpc512x_select_psc_compat(); if (!psc_compat) {
pr_err("%s: no compatible devices found\n", __func__); return;
}
/* size in register is in 4 byte units */
tx_fifo_size /= 4;
rx_fifo_size /= 4; if (!tx_fifo_size)
tx_fifo_size = 1; if (!rx_fifo_size)
rx_fifo_size = 1;
/* FIFO space is 4KiB, check if requested size is available */ if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) {
pr_err("%s: no fifo space available for %pOF\n",
__func__, np);
iounmap(psc); /* * chances are that another device requests less * fifo space, so we continue.
*/ continue;
}
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.