/* * The address of some registers depends on the HW version: such registers have * an extra offset specified with layer_ofs.
*/ #define LAY_OFS_0 0x80 #define LAY_OFS_1 0x100 #define LAY_OFS (ldev->caps.layer_ofs)
/* Global register offsets */ #define LTDC_IDR 0x0000 /* IDentification */ #define LTDC_LCR 0x0004 /* Layer Count */ #define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */ #define LTDC_BPCR 0x000C /* Back Porch Configuration */ #define LTDC_AWCR 0x0010 /* Active Width Configuration */ #define LTDC_TWCR 0x0014 /* Total Width Configuration */ #define LTDC_GCR 0x0018 /* Global Control */ #define LTDC_GC1R 0x001C /* Global Configuration 1 */ #define LTDC_GC2R 0x0020 /* Global Configuration 2 */ #define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */ #define LTDC_GACR 0x0028 /* GAmma Correction */ #define LTDC_BCCR 0x002C /* Background Color Configuration */ #define LTDC_IER 0x0034 /* Interrupt Enable */ #define LTDC_ISR 0x0038 /* Interrupt Status */ #define LTDC_ICR 0x003C /* Interrupt Clear */ #define LTDC_LIPCR 0x0040 /* Line Interrupt Position Conf. */ #define LTDC_CPSR 0x0044 /* Current Position Status */ #define LTDC_CDSR 0x0048 /* Current Display Status */ #define LTDC_EDCR 0x0060 /* External Display Control */ #define LTDC_CCRCR 0x007C /* Computed CRC value */ #define LTDC_FUT 0x0090 /* Fifo underrun Threshold */
#define BCCR_BCBLACK 0x00 /* Background Color BLACK */ #define BCCR_BCBLUE GENMASK(7, 0) /* Background Color BLUE */ #define BCCR_BCGREEN GENMASK(15, 8) /* Background Color GREEN */ #define BCCR_BCRED GENMASK(23, 16) /* Background Color RED */ #define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
#define FUT_DFT 128 /* Default value of fifo underrun threshold */
/* * Skip the first value and the second in case CRC was enabled during * the thread irq. This is to be sure CRC value is relevant for the * frame.
*/ #define CRC_SKIP_FRAMES 2
/* Layer register offsets */ staticconst u32 ltdc_layer_regs_a0[] = {
0x80, /* L1 configuration 0 */
0x00, /* not available */
0x00, /* not available */
0x84, /* L1 control register */
0x88, /* L1 window horizontal position configuration */
0x8c, /* L1 window vertical position configuration */
0x90, /* L1 color keying configuration */
0x94, /* L1 pixel format configuration */
0x98, /* L1 constant alpha configuration */
0x9c, /* L1 default color configuration */
0xa0, /* L1 blending factors configuration */
0x00, /* not available */
0x00, /* not available */
0xac, /* L1 color frame buffer address */
0xb0, /* L1 color frame buffer length */
0xb4, /* L1 color frame buffer line number */
0x00, /* not available */
0x00, /* not available */
0x00, /* not available */
0x00, /* not available */
0xc4, /* L1 CLUT write */
0x00, /* not available */
0x00, /* not available */
0x00, /* not available */
0x00 /* not available */
};
staticconst u32 ltdc_layer_regs_a1[] = {
0x80, /* L1 configuration 0 */
0x84, /* L1 configuration 1 */
0x00, /* L1 reload control */
0x88, /* L1 control register */
0x8c, /* L1 window horizontal position configuration */
0x90, /* L1 window vertical position configuration */
0x94, /* L1 color keying configuration */
0x98, /* L1 pixel format configuration */
0x9c, /* L1 constant alpha configuration */
0xa0, /* L1 default color configuration */
0xa4, /* L1 blending factors configuration */
0xa8, /* L1 burst length configuration */
0x00, /* not available */
0xac, /* L1 color frame buffer address */
0xb0, /* L1 color frame buffer length */
0xb4, /* L1 color frame buffer line number */
0xb8, /* L1 auxiliary frame buffer address 0 */
0xbc, /* L1 auxiliary frame buffer address 1 */
0xc0, /* L1 auxiliary frame buffer length */
0xc4, /* L1 auxiliary frame buffer line number */
0xc8, /* L1 CLUT write */
0x00, /* not available */
0x00, /* not available */
0x00, /* not available */
0x00 /* not available */
};
switch (drm_fmt) { case DRM_FORMAT_ARGB8888: case DRM_FORMAT_XRGB8888:
pf = PF_ARGB8888; break; case DRM_FORMAT_ABGR8888: case DRM_FORMAT_XBGR8888:
pf = PF_ABGR8888; break; case DRM_FORMAT_RGBA8888: case DRM_FORMAT_RGBX8888:
pf = PF_RGBA8888; break; case DRM_FORMAT_BGRA8888: case DRM_FORMAT_BGRX8888:
pf = PF_BGRA8888; break; case DRM_FORMAT_RGB888:
pf = PF_RGB888; break; case DRM_FORMAT_BGR888:
pf = PF_BGR888; break; case DRM_FORMAT_RGB565:
pf = PF_RGB565; break; case DRM_FORMAT_BGR565:
pf = PF_BGR565; break; case DRM_FORMAT_ARGB1555: case DRM_FORMAT_XRGB1555:
pf = PF_ARGB1555; break; case DRM_FORMAT_ARGB4444: case DRM_FORMAT_XRGB4444:
pf = PF_ARGB4444; break; case DRM_FORMAT_C8:
pf = PF_L8; break; default:
pf = PF_NONE; break; /* Note: There are no DRM_FORMAT for AL44 and AL88 */
}
/* * All non-alpha color formats derived from native alpha color formats are * either characterized by a FourCC format code
*/ staticinline u32 is_xrgb(u32 drm)
{ return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
}
switch (drm_pix_fmt) { case DRM_FORMAT_YUYV:
val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF; break; case DRM_FORMAT_YVYU:
val = (YCM_I << 4) | LxPCR_YF; break; case DRM_FORMAT_UYVY:
val = (YCM_I << 4) | LxPCR_CBF; break; case DRM_FORMAT_VYUY:
val = (YCM_I << 4); break; case DRM_FORMAT_NV12:
val = (YCM_SP << 4) | LxPCR_CBF; break; case DRM_FORMAT_NV21:
val = (YCM_SP << 4); break; case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420:
val = (YCM_FP << 4); break; default: /* RGB or not a YCbCr supported format */
DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt); return;
}
/* Enable limited range */ if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
val |= LxPCR_YREN;
if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc); /* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
enc = DRM_COLOR_YCBCR_BT601;
}
if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
DRM_ERROR("color range %d not supported, use limited range by default\n", ran); /* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
}
/* * Read & Clear the interrupt status * In order to write / read registers in this critical section * very quickly, the regmap functions are not used.
*/
ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
/* immediately commit disable of layers before switching off LTDC */ if (!ldev->caps.plane_reg_shadow)
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
staticenum drm_mode_status
ltdc_crtc_mode_valid(struct drm_crtc *crtc, conststruct drm_display_mode *mode)
{ struct ltdc_device *ldev = crtc_to_ltdc(crtc); int target = mode->clock * 1000; int target_min = target - CLK_TOLERANCE_HZ; int target_max = target + CLK_TOLERANCE_HZ; int result;
result = clk_round_rate(ldev->pixel_clk, target);
DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
/* Filter modes according to the max frequency supported by the pads */ if (result > ldev->caps.pad_max_freq_hz) return MODE_CLOCK_HIGH;
/* * Accept all "preferred" modes: * - this is important for panels because panel clock tolerances are * bigger than hdmi ones and there is no reason to not accept them * (the fps may vary a little but it is not a problem). * - the hdmi preferred mode will be accepted too, but userland will * be able to use others hdmi "valid" modes if necessary.
*/ if (mode->type & DRM_MODE_TYPE_PREFERRED) return MODE_OK;
/* * Filter modes according to the clock value, particularly useful for * hdmi modes that require precise pixel clocks.
*/ if (result < target_min || result > target_max) return MODE_CLOCK_RANGE;
/* get encoder from crtc */
drm_for_each_encoder(en_iter, ddev) if (en_iter->crtc == crtc) {
encoder = en_iter; break;
}
if (encoder) { /* get bridge from encoder */
list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node) if (br_iter->encoder == encoder) {
bridge = br_iter; break;
}
/* Get the connector from encoder */
drm_connector_list_iter_begin(ddev, &iter);
drm_for_each_connector_iter(connector, &iter) if (connector->encoder == encoder) break;
drm_connector_list_iter_end(&iter);
}
/* Configure the output format (hw version dependent) */ if (ldev->caps.ycbcr_output) { /* Input video dynamic_range & colorimetry */ int vic = drm_match_cea_mode(mode);
u32 val;
/* The active area starts after vsync + front porch and ends * at vsync + front porc + display size. * The total height also include back porch. * We have 3 possible cases to handle: * - line < vactive_start: vpos = line - vactive_start and will be * negative * - vactive_start < line < vactive_end: vpos = line - vactive_start * and will be positive * - line > vactive_end: vpos = line - vtotal - vactive_start * and will negative * * Computation for the two first cases are identical so we can * simplify the code and only test if line > vactive_end
*/ if (pm_runtime_active(ddev->dev)) {
regmap_read(ldev->regmap, LTDC_CPSR, &line);
line &= CPSR_CYPOS;
regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
vactive_start &= BPCR_AVBP;
regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
vactive_end &= AWCR_AAH;
regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
vtotal &= TWCR_TOTALH;
if (line > vactive_end)
*vpos = line - vtotal - vactive_start; else
*vpos = line - vactive_start;
} else {
*vpos = 0;
}
/* Configures the horizontal start and stop position */
val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
/* Configures the vertical start and stop position */
val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
/* Specifies the pixel format */
pf = to_ltdc_pixelformat(fb->format->format); for (val = 0; val < NB_PF; val++) if (ldev->caps.pix_fmt_hw[val] == pf) break;
/* Use the flexible color format feature if necessary and available */ if (ldev->caps.pix_fmt_flex && val == NB_PF)
val = ltdc_set_flexible_pixel_format(plane, pf);
if (val == NB_PF) {
DRM_ERROR("Pixel format %.4s not supported\n",
(char *)&fb->format->format);
val = 0; /* set by default ARGB 32 bits */
}
regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
/* Specifies the constant alpha value */
val = newstate->alpha >> 8;
regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
/* Specifies the blending factors */
val = BF1_PAXCA | BF2_1PAXCA; if (!fb->format->has_alpha)
val = BF1_CA | BF2_1CA;
/* Manage hw-specific capabilities */ if (ldev->caps.non_alpha_only_l1 &&
plane->type != DRM_PLANE_TYPE_PRIMARY)
val = BF1_PAXCA | BF2_1PAXCA;
/* Configures the color frame buffer pitch in bytes & line length */
line_length = fb->format->cpp[0] *
(x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
if (newstate->rotation & DRM_MODE_REFLECT_Y) /* Compute negative value (signed on 16 bits) for the picth */
pitch_in_bytes = 0x10000 - fb->pitches[0]; else
pitch_in_bytes = fb->pitches[0];
/* * Set the length and the number of lines of the auxiliary * buffers if the framebuffer contains more than one plane.
*/ if (fb->format->num_planes > 1) { if (newstate->rotation & DRM_MODE_REFLECT_Y) /* * Compute negative value (signed on 16 bits) * for the picth
*/
pitch_in_bytes = 0x10000 - fb->pitches[1]; else
pitch_in_bytes = fb->pitches[1];
if (ldev->caps.plane_rotation)
drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
supported_rotations);
/* Init CRTC according to its hardware features */ if (ldev->caps.crc)
ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL,
<dc_crtc_with_crc_support_funcs, NULL); else
ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL,
<dc_crtc_funcs, NULL); if (ret) {
DRM_ERROR("Can not initialize CRTC\n"); return ret;
}
/* Add planes. Note : the first layer is used by primary plane */ for (i = 1; i < ldev->caps.nb_layers; i++) {
overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i); if (!overlay) {
DRM_ERROR("Can not create overlay plane %d\n", i); return -ENOMEM;
} if (ldev->caps.dynamic_zorder)
drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1); else
drm_plane_create_zpos_immutable_property(overlay, i);
if (ldev->caps.plane_rotation)
drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
supported_rotations);
}
/* * Set to default state the pinctrl only with DPI type. * Others types like DSI, don't need pinctrl due to * internal bridge (the signals do not come out of the chipset).
*/ if (encoder->encoder_type == DRM_MODE_ENCODER_DPI)
pinctrl_pm_select_default_state(ddev->dev);
}
int ltdc_resume(struct drm_device *ddev)
{ struct ltdc_device *ldev = ddev->dev_private; int ret;
DRM_DEBUG_DRIVER("\n");
ret = clk_prepare_enable(ldev->pixel_clk); if (ret) {
DRM_ERROR("failed to enable pixel clock (%d)\n", ret); return ret;
}
return 0;
}
int ltdc_load(struct drm_device *ddev)
{ struct platform_device *pdev = to_platform_device(ddev->dev); struct ltdc_device *ldev = ddev->dev_private; struct device *dev = ddev->dev; struct device_node *np = dev->of_node; struct drm_bridge *bridge; struct drm_panel *panel; struct drm_crtc *crtc; struct reset_control *rstc; int irq, i, nb_endpoints; int ret = -ENODEV;
DRM_DEBUG_DRIVER("\n");
/* Get number of endpoints */
nb_endpoints = of_graph_get_endpoint_count(np); if (!nb_endpoints) return -ENODEV;
ldev->pixel_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(ldev->pixel_clk)) { if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
DRM_ERROR("Unable to get lcd clock\n"); return PTR_ERR(ldev->pixel_clk);
}
if (clk_prepare_enable(ldev->pixel_clk)) {
DRM_ERROR("Unable to prepare pixel clock\n"); return -ENODEV;
}
/* Get endpoints if any */ for (i = 0; i < nb_endpoints; i++) {
ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);
/* * If at least one endpoint is -ENODEV, continue probing, * else if at least one endpoint returned an error * (ie -EPROBE_DEFER) then stop probing.
*/ if (ret == -ENODEV) continue; elseif (ret) goto err;
if (panel) {
bridge = drmm_panel_bridge_add(ddev, panel); if (IS_ERR(bridge)) {
DRM_ERROR("panel-bridge endpoint %d\n", i);
ret = PTR_ERR(bridge); goto err;
}
}
if (bridge) {
ret = ltdc_encoder_init(ddev, bridge); if (ret) { if (ret != -EPROBE_DEFER)
DRM_ERROR("init encoder endpoint %d\n", i); goto err;
}
}
}
if (!IS_ERR(rstc)) {
reset_control_assert(rstc);
usleep_range(10, 20);
reset_control_deassert(rstc);
}
ldev->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ldev->regs)) {
DRM_ERROR("Unable to get ltdc registers\n");
ret = PTR_ERR(ldev->regs); goto err;
}
ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg); if (IS_ERR(ldev->regmap)) {
DRM_ERROR("Unable to regmap ltdc registers\n");
ret = PTR_ERR(ldev->regmap); goto err;
}
ret = ltdc_get_caps(ddev); if (ret) {
DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
ldev->caps.hw_version); goto err;
}
/* Disable all interrupts */
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_MASK);
DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
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.