static int hwmixer_init(struct mdp5_kms *mdp5_kms) { struct drm_device *dev = mdp5_kms->dev; const struct mdp5_cfg_hw *hw_cfg; int i, ret; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); for (i = 0; i < hw_cfg->lm.count; i++) { struct mdp5_hw_mixer *mixer; mixer = mdp5_mixer_init(&hw_cfg->lm.instances[i]); if (IS_ERR(mixer)) { ret = PTR_ERR(mixer); dev_err(dev->dev, "failed to construct LM%d (%d)\n", i, ret); return ret; } mixer->idx = mdp5_kms->num_hwmixers; mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer; } return 0; }
static int interface_init(struct mdp5_kms *mdp5_kms) { struct drm_device *dev = mdp5_kms->dev; const struct mdp5_cfg_hw *hw_cfg; const enum mdp5_intf_type *intf_types; int i; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); intf_types = hw_cfg->intf.connect; for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) { struct mdp5_interface *intf; if (intf_types[i] == INTF_DISABLED) continue; intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) { dev_err(dev->dev, "failed to construct INTF%d\n", i); return -ENOMEM; } intf->num = i; intf->type = intf_types[i]; intf->mode = MDP5_INTF_MODE_NONE; intf->idx = mdp5_kms->num_intfs; mdp5_kms->intfs[mdp5_kms->num_intfs++] = intf; } return 0; }
static int hwpipe_init(struct mdp5_kms *mdp5_kms) { static const enum mdp5_pipe rgb_planes[] = { SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3, }; static const enum mdp5_pipe vig_planes[] = { SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3, }; static const enum mdp5_pipe dma_planes[] = { SSPP_DMA0, SSPP_DMA1, }; static const enum mdp5_pipe cursor_planes[] = { SSPP_CURSOR0, SSPP_CURSOR1, }; const struct mdp5_cfg_hw *hw_cfg; int ret; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); /* Construct RGB pipes: */ ret = construct_pipes(mdp5_kms, hw_cfg->pipe_rgb.count, rgb_planes, hw_cfg->pipe_rgb.base, hw_cfg->pipe_rgb.caps); if (ret) return ret; /* Construct video (VIG) pipes: */ ret = construct_pipes(mdp5_kms, hw_cfg->pipe_vig.count, vig_planes, hw_cfg->pipe_vig.base, hw_cfg->pipe_vig.caps); if (ret) return ret; /* Construct DMA pipes: */ ret = construct_pipes(mdp5_kms, hw_cfg->pipe_dma.count, dma_planes, hw_cfg->pipe_dma.base, hw_cfg->pipe_dma.caps); if (ret) return ret; /* Construct cursor pipes: */ ret = construct_pipes(mdp5_kms, hw_cfg->pipe_cursor.count, cursor_planes, hw_cfg->pipe_cursor.base, hw_cfg->pipe_cursor.caps); if (ret) return ret; return 0; }
struct mdp5_ctl_manager *mdp5_ctlm_init(struct drm_device *dev, void __iomem *mmio_base, struct mdp5_cfg_handler *cfg_hnd) { struct mdp5_ctl_manager *ctl_mgr; const struct mdp5_cfg_hw *hw_cfg = mdp5_cfg_get_hw_config(cfg_hnd); int rev = mdp5_cfg_get_hw_rev(cfg_hnd); unsigned dsi_cnt = 0; const struct mdp5_ctl_block *ctl_cfg = &hw_cfg->ctl; unsigned long flags; int c, ret; ctl_mgr = kzalloc(sizeof(*ctl_mgr), GFP_KERNEL); if (!ctl_mgr) { DRM_DEV_ERROR(dev->dev, "failed to allocate CTL manager\n"); ret = -ENOMEM; goto fail; } if (unlikely(WARN_ON(ctl_cfg->count > MAX_CTL))) { DRM_DEV_ERROR(dev->dev, "Increase static pool size to at least %d\n", ctl_cfg->count); ret = -ENOSPC; goto fail; } /* initialize the CTL manager: */ ctl_mgr->dev = dev; ctl_mgr->nlm = hw_cfg->lm.count; ctl_mgr->nctl = ctl_cfg->count; ctl_mgr->flush_hw_mask = ctl_cfg->flush_hw_mask; spin_lock_init(&ctl_mgr->pool_lock); /* initialize each CTL of the pool: */ spin_lock_irqsave(&ctl_mgr->pool_lock, flags); for (c = 0; c < ctl_mgr->nctl; c++) { struct mdp5_ctl *ctl = &ctl_mgr->ctls[c]; if (WARN_ON(!ctl_cfg->base[c])) { DRM_DEV_ERROR(dev->dev, "CTL_%d: base is null!\n", c); ret = -EINVAL; spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); goto fail; } ctl->ctlm = ctl_mgr; ctl->id = c; ctl->reg_offset = ctl_cfg->base[c]; ctl->status = 0; spin_lock_init(&ctl->hw_lock); } /* * In Dual DSI case, CTL0 and CTL1 are always assigned to two DSI * interfaces to support single FLUSH feature (Flush CTL0 and CTL1 when * only write into CTL0's FLUSH register) to keep two DSI pipes in sync. * Single FLUSH is supported from hw rev v3.0. */ for (c = 0; c < ARRAY_SIZE(hw_cfg->intf.connect); c++) if (hw_cfg->intf.connect[c] == INTF_DSI) dsi_cnt++; if ((rev >= 3) && (dsi_cnt > 1)) { ctl_mgr->single_flush_supported = true; /* Reserve CTL0/1 for INTF1/2 */ ctl_mgr->ctls[0].status |= CTL_STAT_BOOKED; ctl_mgr->ctls[1].status |= CTL_STAT_BOOKED; } spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags); DBG("Pool of %d CTLs created.", ctl_mgr->nctl); return ctl_mgr; fail: if (ctl_mgr) mdp5_ctlm_destroy(ctl_mgr); return ERR_PTR(ret); }
static int modeset_init(struct mdp5_kms *mdp5_kms) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg; unsigned int num_crtcs; int i, ret, pi = 0, ci = 0; struct drm_plane *primary[MAX_BASES] = { NULL }; struct drm_plane *cursor[MAX_BASES] = { NULL }; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); /* * 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(priv->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; else if (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); dev_err(dev->dev, "failed to construct plane %d (%d)\n", i, ret); goto fail; } priv->planes[priv->num_planes++] = plane; 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); dev_err(dev->dev, "failed to construct crtc %d (%d)\n", i, ret); goto fail; } priv->crtcs[priv->num_crtcs++] = crtc; } /* * Now that we know the number of crtcs we've created, set the possible * crtcs for the encoders */ for (i = 0; i < priv->num_encoders; i++) { struct drm_encoder *encoder = priv->encoders[i]; encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; } return 0; fail: return ret; }
static int modeset_init_intf(struct mdp5_kms *mdp5_kms, struct mdp5_interface *intf) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm; struct mdp5_ctl *ctl; struct drm_encoder *encoder; int ret = 0; switch (intf->type) { case INTF_eDP: if (!priv->edp) 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_edp_modeset_init(priv->edp, dev, encoder); break; case INTF_HDMI: if (!priv->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->hdmi, dev, encoder); break; case INTF_DSI: { const struct 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->dsi)) || (dsi_id < 0)) { dev_err(dev->dev, "failed to find dsi from intf %d\n", intf->num); ret = -EINVAL; break; } if (!priv->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->dsi[dsi_id], dev, encoder); break; } default: dev_err(dev->dev, "unknown intf: %d\n", intf->type); ret = -EINVAL; break; } return ret; }
static int modeset_init(struct mdp5_kms *mdp5_kms) { static const enum mdp5_pipe crtcs[] = { SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3, }; static const enum mdp5_pipe pub_planes[] = { SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3, }; struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg; int i, ret; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); /* register our interrupt-controller for hdmi/eDP/dsi/etc * to use for irqs routed through mdp: */ ret = mdp5_irq_domain_init(mdp5_kms); if (ret) goto fail; /* construct CRTCs and their private planes: */ for (i = 0; i < hw_cfg->pipe_rgb.count; i++) { struct drm_plane *plane; struct drm_crtc *crtc; plane = mdp5_plane_init(dev, crtcs[i], true, hw_cfg->pipe_rgb.base[i]); if (IS_ERR(plane)) { ret = PTR_ERR(plane); dev_err(dev->dev, "failed to construct plane for %s (%d)\n", pipe2name(crtcs[i]), ret); goto fail; } crtc = mdp5_crtc_init(dev, plane, i); if (IS_ERR(crtc)) { ret = PTR_ERR(crtc); dev_err(dev->dev, "failed to construct crtc for %s (%d)\n", pipe2name(crtcs[i]), ret); goto fail; } priv->crtcs[priv->num_crtcs++] = crtc; } /* Construct public planes: */ for (i = 0; i < hw_cfg->pipe_vig.count; i++) { struct drm_plane *plane; plane = mdp5_plane_init(dev, pub_planes[i], false, hw_cfg->pipe_vig.base[i]); if (IS_ERR(plane)) { ret = PTR_ERR(plane); dev_err(dev->dev, "failed to construct %s plane: %d\n", pipe2name(pub_planes[i]), ret); goto fail; } } /* Construct encoders and modeset initialize connector devices * for each external display interface. */ for (i = 0; i < ARRAY_SIZE(hw_cfg->intfs); i++) { ret = modeset_init_intf(mdp5_kms, i); if (ret) goto fail; } return 0; fail: return ret; }
static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); enum mdp5_intf_type intf_type = hw_cfg->intfs[intf_num]; struct drm_encoder *encoder; int ret = 0; switch (intf_type) { case INTF_DISABLED: break; case INTF_eDP: if (!priv->edp) break; encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, MDP5_INTF_MODE_NONE); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; } ret = msm_edp_modeset_init(priv->edp, dev, encoder); break; case INTF_HDMI: if (!priv->hdmi) break; encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, MDP5_INTF_MODE_NONE); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; } ret = hdmi_modeset_init(priv->hdmi, dev, encoder); break; case INTF_DSI: { int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num); struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM]; enum mdp5_intf_mode mode; int i; if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) { dev_err(dev->dev, "failed to find dsi from intf %d\n", intf_num); ret = -EINVAL; break; } if (!priv->dsi[dsi_id]) break; for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { mode = (i == MSM_DSI_CMD_ENCODER_ID) ? MDP5_INTF_DSI_MODE_COMMAND : MDP5_INTF_DSI_MODE_VIDEO; dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI, intf_num, mode); if (IS_ERR(dsi_encs)) { ret = PTR_ERR(dsi_encs); break; } } ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs); break; } default: dev_err(dev->dev, "unknown intf: %d\n", intf_type); ret = -EINVAL; break; } return ret; }