// SPDX-License-Identifier: GPL-2.0-only /* * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. * * Parts of this file were based on sources as follows: * * Copyright (c) 2006-2008 Intel Corporation * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> * Copyright (C) 2011 Texas Instruments
*/
/* * We use the pixelclock to also account for interlaced modes, the * resulting bandwidth is in bytes per second.
*/
bw = mode->clock * 1000ULL; /* In Hz */
bw = bw * mode->hdisplay * mode->vdisplay * cpp;
bw = div_u64(bw, mode->htotal * mode->vtotal);
/* * If no bandwidth constraints, anything goes, else * check if we are too fast.
*/ if (priv->memory_bw && (bw > priv->memory_bw)) {
DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu too fast\n",
mode->hdisplay, mode->vdisplay,
mode->clock * 1000, cpp, bw);
if (fb) {
u32 offset = drm_fb_dma_get_gem_addr(fb, pstate, 0);
/* FB base address must be dword aligned. */ if (offset & 3) return -EINVAL;
/* There's no pitch register -- the mode's hdisplay * controls it.
*/ if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) return -EINVAL;
/* We can't change the FB format in a flicker-free * manner (and only update it during CRTC enable).
*/ if (old_fb && old_fb->format != fb->format)
cstate->mode_changed = true;
}
ret = clk_set_rate(priv->clk, mode->clock * 1000); if (ret) {
dev_err(drm->dev, "Failed to set pixel clock rate to %d: %d\n",
mode->clock * 1000, ret);
}
/* * The AC pin bias frequency is set to max count when using * grayscale so at least once in a while we will reverse * polarity and get rid of any DC built up that could * damage the display.
*/ if (grayscale)
tim2 |= TIM2_ACB_MASK;
}
if (bridge) { conststruct drm_bridge_timings *btimings = bridge->timings;
/* * Here is when things get really fun. Sometimes the bridge * timings are such that the signal out from PL11x is not * stable before the receiving bridge (such as a dumb VGA DAC * or similar) samples it. If that happens, we compensate by * the only method we have: output the data on the opposite * edge of the clock so it is for sure stable when it gets * sampled. * * The PL111 manual does not contain proper timining diagrams * or data for these details, but we know from experiments * that the setup time is more than 3000 picoseconds (3 ns). * If we have a bridge that requires the signal to be stable * earlier than 3000 ps before the clock pulse, we have to * output the data on the opposite edge to avoid flicker.
*/ if (btimings && btimings->setup_time_ps >= 3000)
tim2 ^= TIM2_IPC;
}
/* * Detect grayscale bus format. We do not support a grayscale mode * toward userspace, instead we expose an RGB24 buffer and then the * hardware will activate its grayscaler to convert to the grayscale * format.
*/ if (grayscale)
cntl = CNTL_LCDEN | CNTL_LCDMONO8; else /* Else we assume TFT display */
cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1);
/* On the ST Micro variant, assume all 24 bits are connected */ if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_CDWID_24;
/* * Note that the ARM hardware's format reader takes 'r' from * the low bit, while DRM formats list channels from high bit * to low bit as you read left to right. The ST Micro version of * the PL110 (LCDC) however uses the standard DRM format.
*/ switch (fb->format->format) { case DRM_FORMAT_BGR888: /* Only supported on the ST Micro variant */ if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_LCDBPP24_PACKED | CNTL_BGR; break; case DRM_FORMAT_RGB888: /* Only supported on the ST Micro variant */ if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_LCDBPP24_PACKED; break; case DRM_FORMAT_ABGR8888: case DRM_FORMAT_XBGR8888: if (priv->variant->st_bitmux_control)
cntl |= CNTL_LCDBPP24 | CNTL_BGR; else
cntl |= CNTL_LCDBPP24; break; case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XRGB8888: if (priv->variant->st_bitmux_control)
cntl |= CNTL_LCDBPP24; else
cntl |= CNTL_LCDBPP24 | CNTL_BGR; break; case DRM_FORMAT_BGR565: if (priv->variant->is_pl110)
cntl |= CNTL_LCDBPP16; elseif (priv->variant->st_bitmux_control)
cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565 | CNTL_BGR; else
cntl |= CNTL_LCDBPP16_565; break; case DRM_FORMAT_RGB565: if (priv->variant->is_pl110)
cntl |= CNTL_LCDBPP16 | CNTL_BGR; elseif (priv->variant->st_bitmux_control)
cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565; else
cntl |= CNTL_LCDBPP16_565 | CNTL_BGR; break; case DRM_FORMAT_ABGR1555: case DRM_FORMAT_XBGR1555:
cntl |= CNTL_LCDBPP16; if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_1XBPP_5551 | CNTL_BGR; break; case DRM_FORMAT_ARGB1555: case DRM_FORMAT_XRGB1555:
cntl |= CNTL_LCDBPP16; if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_1XBPP_5551; else
cntl |= CNTL_BGR; break; case DRM_FORMAT_ABGR4444: case DRM_FORMAT_XBGR4444:
cntl |= CNTL_LCDBPP16_444; if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_1XBPP_444 | CNTL_BGR; break; case DRM_FORMAT_ARGB4444: case DRM_FORMAT_XRGB4444:
cntl |= CNTL_LCDBPP16_444; if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_1XBPP_444; else
cntl |= CNTL_BGR; break; default:
WARN_ONCE(true, "Unknown FB format 0x%08x\n",
fb->format->format); break;
}
/* The PL110 in Integrator/Versatile does the BGR routing externally */ if (priv->variant->external_bgr)
cntl &= ~CNTL_BGR;
/* Power sequence: first enable and chill */
writel(cntl, priv->regs + priv->ctrl);
/* * We expect this delay to stabilize the contrast * voltage Vee as stipulated by the manual
*/
msleep(20);
if (priv->variant_display_enable)
priv->variant_display_enable(drm, fb->format->format);
/* Power Up */
cntl |= CNTL_LCDPWR;
writel(cntl, priv->regs + priv->ctrl);
if (!priv->variant->broken_vblank)
drm_crtc_vblank_on(crtc);
}
if (IS_ERR(parent)) {
dev_err(drm->dev, "CLCD: unable to get clcdclk.\n"); return PTR_ERR(parent);
}
spin_lock_init(&priv->tim2_lock);
/* If the clock divider is broken, use the parent directly */ if (priv->variant->broken_clockdivider) {
priv->clk = parent; return 0;
}
parent_name = __clk_get_name(parent);
div->init = &init;
ret = devm_clk_hw_register(drm->dev, div);
priv->clk = div->clk; return ret;
}
int pl111_display_init(struct drm_device *drm)
{ struct pl111_drm_dev_private *priv = drm->dev_private; int ret;
ret = pl111_init_clock_divider(drm); if (ret) return ret;
if (!priv->variant->broken_vblank) {
pl111_display_funcs.enable_vblank = pl111_display_enable_vblank;
pl111_display_funcs.disable_vblank = pl111_display_disable_vblank;
}
ret = drm_simple_display_pipe_init(drm, &priv->pipe,
&pl111_display_funcs,
priv->variant->formats,
priv->variant->nformats,
NULL,
priv->connector); if (ret) return ret;
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.