// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com>
*/
/* Magic unknown register writes: * * W VBIF:0x004 00000001 (mdss_mdp.c:839) * W MDP5:0x2e0 0xe9 (mdss_mdp.c:839) * W MDP5:0x2e4 0x55 (mdss_mdp.c:839) * W MDP5:0x3ac 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3b4 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3bc 0xcccccc (mdss_mdp.c:839) * W MDP5:0x4a8 0xcccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b0 0xccccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b8 0xccccc000 (mdss_mdp.c:839) * * Downstream fbdev driver gets these register offsets/values * from DT.. not really sure what these registers are or if * different values for different boards/SoC's, etc. I guess * they are the golden registers. * * Not setting these does not seem to cause any problem. But * we may be getting lucky with the bootloader initializing * them for us. OTOH, if we can always count on the bootloader * setting the golden registers, then perhaps we don't need to * care.
*/
/* * This is a helper that returns the private state currently in operation. * Note that this would return the "old_state" if called in the atomic check * path, and the "new_state" after the atomic swap has been done.
*/ struct mdp5_global_state *
mdp5_get_existing_global_state(struct mdp5_kms *mdp5_kms)
{ return to_mdp5_global_state(mdp5_kms->glob_state.state);
}
/* * This acquires the modeset lock set aside for global state, creates * a new duplicated private object state.
*/ struct mdp5_global_state *mdp5_get_global_state(struct drm_atomic_state *s)
{ struct msm_drm_private *priv = s->dev->dev_private; struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct drm_private_state *priv_state;
priv_state = drm_atomic_get_private_obj_state(s, &mdp5_kms->glob_state); if (IS_ERR(priv_state)) return ERR_CAST(priv_state);
switch (intf->type) { case INTF_eDP:
DRM_DEV_INFO(dev->dev, "Skipping eDP interface %d\n", intf->num); break; case INTF_HDMI: if (!priv->kms->hdmi) break;
ctl = mdp5_ctlm_request(ctlm, intf->num); if (!ctl) {
ret = -EINVAL; break;
}
encoder = construct_encoder(mdp5_kms, intf, ctl); if (IS_ERR(encoder)) {
ret = PTR_ERR(encoder); break;
}
ret = msm_hdmi_modeset_init(priv->kms->hdmi, dev, encoder); break; case INTF_DSI:
{ conststruct mdp5_cfg_hw *hw_cfg =
mdp5_cfg_get_hw_config(mdp5_kms->cfg); int dsi_id = get_dsi_id_from_intf(hw_cfg, intf->num);
if ((dsi_id >= ARRAY_SIZE(priv->kms->dsi)) || (dsi_id < 0)) {
DRM_DEV_ERROR(dev->dev, "failed to find dsi from intf %d\n",
intf->num);
ret = -EINVAL; break;
}
if (!priv->kms->dsi[dsi_id]) break;
ctl = mdp5_ctlm_request(ctlm, intf->num); if (!ctl) {
ret = -EINVAL; break;
}
encoder = construct_encoder(mdp5_kms, intf, ctl); if (IS_ERR(encoder)) {
ret = PTR_ERR(encoder); break;
}
ret = msm_dsi_modeset_init(priv->kms->dsi[dsi_id], dev, encoder); if (!ret)
mdp5_encoder_set_intf_mode(encoder,
msm_dsi_is_cmd_mode(priv->kms->dsi[dsi_id]));
staticint modeset_init(struct mdp5_kms *mdp5_kms)
{ struct drm_device *dev = mdp5_kms->dev; unsignedint num_crtcs; int i, ret, pi = 0, ci = 0; struct drm_plane *primary[MAX_BASES] = { NULL }; struct drm_plane *cursor[MAX_BASES] = { NULL }; struct drm_encoder *encoder; unsignedint num_encoders;
/* * Construct encoders and modeset initialize connector devices * for each external display interface.
*/ for (i = 0; i < mdp5_kms->num_intfs; i++) {
ret = modeset_init_intf(mdp5_kms, mdp5_kms->intfs[i]); if (ret) goto fail;
}
/* * We should ideally have less number of encoders (set up by parsing * the MDP5 interfaces) than the number of layer mixers present in HW, * but let's be safe here anyway
*/
num_crtcs = min(num_encoders, mdp5_kms->num_hwmixers);
/* * Construct planes equaling the number of hw pipes, and CRTCs for the * N encoders set up by the driver. The first N planes become primary * planes for the CRTCs, with the remainder as overlay planes:
*/ for (i = 0; i < mdp5_kms->num_hwpipes; i++) { struct mdp5_hw_pipe *hwpipe = mdp5_kms->hwpipes[i]; struct drm_plane *plane; enum drm_plane_type type;
if (i < num_crtcs)
type = DRM_PLANE_TYPE_PRIMARY; elseif (hwpipe->caps & MDP_PIPE_CAP_CURSOR)
type = DRM_PLANE_TYPE_CURSOR; else
type = DRM_PLANE_TYPE_OVERLAY;
plane = mdp5_plane_init(dev, type); if (IS_ERR(plane)) {
ret = PTR_ERR(plane);
DRM_DEV_ERROR(dev->dev, "failed to construct plane %d (%d)\n", i, ret); goto fail;
}
if (type == DRM_PLANE_TYPE_PRIMARY)
primary[pi++] = plane; if (type == DRM_PLANE_TYPE_CURSOR)
cursor[ci++] = plane;
}
for (i = 0; i < num_crtcs; i++) { struct drm_crtc *crtc;
crtc = mdp5_crtc_init(dev, primary[i], cursor[i], i); if (IS_ERR(crtc)) {
ret = PTR_ERR(crtc);
DRM_DEV_ERROR(dev->dev, "failed to construct crtc %d (%d)\n", i, ret); goto fail;
}
}
/* * Now that we know the number of crtcs we've created, set the possible * crtcs for the encoders
*/
drm_for_each_encoder(encoder, dev)
encoder->possible_crtcs = (1 << dev->mode_config.num_crtc) - 1;
ret = mdp5_init(to_platform_device(dev->dev), dev); if (ret) return ret;
mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
pdev = mdp5_kms->pdev;
ret = mdp_kms_init(&mdp5_kms->base, &kms_funcs); if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to init kms\n"); goto fail;
}
config = mdp5_cfg_get_config(mdp5_kms->cfg);
/* make sure things are off before attaching iommu (bootloader could * have left things on, in which case we'll start getting faults if * we don't disable):
*/
pm_runtime_get_sync(&pdev->dev); for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) ||
!config->hw->intf.base[i]) continue;
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
ret = mdp5_global_obj_init(mdp5_kms); if (ret) goto fail;
/* we need to set a default rate before enabling. Set a safe * rate first, then figure out hw revision, and then set a * more optimal rate:
*/
clk_set_rate(mdp5_kms->core_clk, 200000000);
/* * Some chipsets have a Shared Memory Pool (SMP), while others * have dedicated latency buffering per source pipe instead; * this section initializes the SMP:
*/ if (mdp5_kms->caps & MDP_CAP_SMP) {
mdp5_kms->smp = mdp5_smp_init(mdp5_kms, &config->hw->smp); if (IS_ERR(mdp5_kms->smp)) {
ret = PTR_ERR(mdp5_kms->smp);
mdp5_kms->smp = NULL; goto fail;
}
}
mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, mdp5_kms->cfg); if (IS_ERR(mdp5_kms->ctlm)) {
ret = PTR_ERR(mdp5_kms->ctlm);
mdp5_kms->ctlm = NULL; goto fail;
}
ret = hwpipe_init(mdp5_kms); if (ret) goto fail;
ret = hwmixer_init(mdp5_kms); if (ret) goto fail;
ret = interface_init(mdp5_kms); if (ret) goto fail;
if (!path0) { /* no interconnect support is not necessarily a fatal * condition, the platform may simply not have an * interconnect driver yet. But warn about it in case * bootloader didn't setup bus clocks high enough for * scanout.
*/
dev_warn(&pdev->dev, "No interconnect support may cause display underflows!\n"); return 0;
}
icc_set_bw(path0, 0, MBps_to_icc(6400));
if (!IS_ERR_OR_NULL(path1))
icc_set_bw(path1, 0, MBps_to_icc(6400)); if (!IS_ERR_OR_NULL(path_rot))
icc_set_bw(path_rot, 0, MBps_to_icc(6400));
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.