diff options
Diffstat (limited to 'drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c')
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 273 |
1 files changed, 105 insertions, 168 deletions
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 31a2c6331a1d..a11f1b80c488 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -24,145 +25,11 @@ static const char *iommu_ports[] = { "mdp_0", }; -static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev); - -const struct mdp5_config *mdp5_cfg; - -static const struct mdp5_config msm8x74_config = { - .name = "msm8x74", - .ctl = { - .count = 5, - .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, - }, - .pipe_vig = { - .count = 3, - .base = { 0x01200, 0x01600, 0x01a00 }, - }, - .pipe_rgb = { - .count = 3, - .base = { 0x01e00, 0x02200, 0x02600 }, - }, - .pipe_dma = { - .count = 2, - .base = { 0x02a00, 0x02e00 }, - }, - .lm = { - .count = 5, - .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 }, - }, - .dspp = { - .count = 3, - .base = { 0x04600, 0x04a00, 0x04e00 }, - }, - .ad = { - .count = 2, - .base = { 0x13100, 0x13300 }, /* NOTE: no ad in v1.0 */ - }, - .intf = { - .count = 4, - .base = { 0x12500, 0x12700, 0x12900, 0x12b00 }, - }, -}; - -static const struct mdp5_config apq8084_config = { - .name = "apq8084", - .ctl = { - .count = 5, - .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, - }, - .pipe_vig = { - .count = 4, - .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 }, - }, - .pipe_rgb = { - .count = 4, - .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 }, - }, - .pipe_dma = { - .count = 2, - .base = { 0x03200, 0x03600 }, - }, - .lm = { - .count = 6, - .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 }, - }, - .dspp = { - .count = 4, - .base = { 0x05200, 0x05600, 0x05a00, 0x05e00 }, - - }, - .ad = { - .count = 3, - .base = { 0x13500, 0x13700, 0x13900 }, - }, - .intf = { - .count = 5, - .base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 }, - }, -}; - -struct mdp5_config_entry { - int revision; - const struct mdp5_config *config; -}; - -static const struct mdp5_config_entry mdp5_configs[] = { - { .revision = 0, .config = &msm8x74_config }, - { .revision = 2, .config = &msm8x74_config }, - { .revision = 3, .config = &apq8084_config }, -}; - -static int mdp5_select_hw_cfg(struct msm_kms *kms) -{ - struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); - struct drm_device *dev = mdp5_kms->dev; - uint32_t version, major, minor; - int i, ret = 0; - - mdp5_enable(mdp5_kms); - version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION); - mdp5_disable(mdp5_kms); - - major = FIELD(version, MDP5_MDP_VERSION_MAJOR); - minor = FIELD(version, MDP5_MDP_VERSION_MINOR); - - DBG("found MDP5 version v%d.%d", major, minor); - - if (major != 1) { - dev_err(dev->dev, "unexpected MDP major version: v%d.%d\n", - major, minor); - ret = -ENXIO; - goto out; - } - - mdp5_kms->rev = minor; - - /* only after mdp5_cfg global pointer's init can we access the hw */ - for (i = 0; i < ARRAY_SIZE(mdp5_configs); i++) { - if (mdp5_configs[i].revision != minor) - continue; - mdp5_kms->hw_cfg = mdp5_cfg = mdp5_configs[i].config; - break; - } - if (unlikely(!mdp5_kms->hw_cfg)) { - dev_err(dev->dev, "unexpected MDP minor revision: v%d.%d\n", - major, minor); - ret = -ENXIO; - goto out; - } - - DBG("MDP5: %s config selected", mdp5_kms->hw_cfg->name); - - return 0; -out: - return ret; -} - static int mdp5_hw_init(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct drm_device *dev = mdp5_kms->dev; - int i; + unsigned long flags; pm_runtime_get_sync(dev->dev); @@ -190,10 +57,11 @@ static int mdp5_hw_init(struct msm_kms *kms) * care. */ + spin_lock_irqsave(&mdp5_kms->resource_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0); + spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); - for (i = 0; i < mdp5_kms->hw_cfg->ctl.count; i++) - mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(i), 0); + mdp5_ctlm_hw_reset(mdp5_kms->ctlm); pm_runtime_put_sync(dev->dev); @@ -221,10 +89,20 @@ static void mdp5_destroy(struct msm_kms *kms) struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct msm_mmu *mmu = mdp5_kms->mmu; + mdp5_irq_domain_fini(mdp5_kms); + if (mmu) { mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); mmu->funcs->destroy(mmu); } + + if (mdp5_kms->ctlm) + mdp5_ctlm_destroy(mdp5_kms->ctlm); + if (mdp5_kms->smp) + mdp5_smp_destroy(mdp5_kms->smp); + if (mdp5_kms->cfg) + mdp5_cfg_destroy(mdp5_kms->cfg); + kfree(mdp5_kms); } @@ -274,17 +152,31 @@ 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; struct drm_encoder *encoder; + const struct mdp5_cfg_hw *hw_cfg; int i, ret; - /* construct CRTCs: */ - for (i = 0; i < mdp5_kms->hw_cfg->pipe_rgb.count; i++) { + 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); + 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", @@ -302,6 +194,20 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) 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 encoder for HDMI: */ encoder = mdp5_encoder_init(dev, 3, INTF_HDMI); if (IS_ERR(encoder)) { @@ -324,11 +230,12 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) priv->encoders[priv->num_encoders++] = encoder; /* Construct bridge/connector for HDMI: */ - mdp5_kms->hdmi = hdmi_init(dev, encoder); - if (IS_ERR(mdp5_kms->hdmi)) { - ret = PTR_ERR(mdp5_kms->hdmi); - dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); - goto fail; + if (priv->hdmi) { + ret = hdmi_modeset_init(priv->hdmi, dev, encoder); + if (ret) { + dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); + goto fail; + } } return 0; @@ -337,6 +244,21 @@ fail: return ret; } +static void read_hw_revision(struct mdp5_kms *mdp5_kms, + uint32_t *major, uint32_t *minor) +{ + uint32_t version; + + mdp5_enable(mdp5_kms); + version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION); + mdp5_disable(mdp5_kms); + + *major = FIELD(version, MDP5_MDP_VERSION_MAJOR); + *minor = FIELD(version, MDP5_MDP_VERSION_MINOR); + + DBG("MDP5 version v%d.%d", *major, *minor); +} + static int get_clk(struct platform_device *pdev, struct clk **clkp, const char *name) { @@ -353,10 +275,11 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp, struct msm_kms *mdp5_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; - struct mdp5_platform_config *config = mdp5_get_config(pdev); + struct mdp5_cfg *config; struct mdp5_kms *mdp5_kms; struct msm_kms *kms = NULL; struct msm_mmu *mmu; + uint32_t major, minor; int i, ret; mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); @@ -366,12 +289,13 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) goto fail; } + spin_lock_init(&mdp5_kms->resource_lock); + mdp_kms_init(&mdp5_kms->base, &kms_funcs); kms = &mdp5_kms->base.base; mdp5_kms->dev = dev; - mdp5_kms->smp_blk_cnt = config->smp_blk_cnt; mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); if (IS_ERR(mdp5_kms->mmio)) { @@ -416,24 +340,52 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) if (ret) goto fail; - ret = clk_set_rate(mdp5_kms->src_clk, config->max_clk); + /* 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->src_clk, 200000000); + + read_hw_revision(mdp5_kms, &major, &minor); - ret = mdp5_select_hw_cfg(kms); - if (ret) + mdp5_kms->cfg = mdp5_cfg_init(mdp5_kms, major, minor); + if (IS_ERR(mdp5_kms->cfg)) { + ret = PTR_ERR(mdp5_kms->cfg); + mdp5_kms->cfg = NULL; goto fail; + } + + config = mdp5_cfg_get_config(mdp5_kms->cfg); + + /* TODO: compute core clock rate at runtime */ + clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk); + + mdp5_kms->smp = mdp5_smp_init(mdp5_kms->dev, &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, config->hw); + if (IS_ERR(mdp5_kms->ctlm)) { + ret = PTR_ERR(mdp5_kms->ctlm); + mdp5_kms->ctlm = NULL; + goto fail; + } /* 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): */ mdp5_enable(mdp5_kms); - for (i = 0; i < mdp5_kms->hw_cfg->intf.count; i++) + for (i = 0; i < config->hw->intf.count; i++) mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); mdp5_disable(mdp5_kms); mdelay(16); - if (config->iommu) { - mmu = msm_iommu_new(&pdev->dev, config->iommu); + if (config->platform.iommu) { + mmu = msm_iommu_new(&pdev->dev, config->platform.iommu); if (IS_ERR(mmu)) { ret = PTR_ERR(mmu); dev_err(dev->dev, "failed to init iommu: %d\n", ret); @@ -474,18 +426,3 @@ fail: mdp5_destroy(kms); return ERR_PTR(ret); } - -static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev) -{ - static struct mdp5_platform_config config = {}; -#ifdef CONFIG_OF - /* TODO */ -#endif - config.iommu = iommu_domain_alloc(&platform_bus_type); - /* TODO hard-coded in downstream mdss, but should it be? */ - config.max_clk = 200000000; - /* TODO get from DT: */ - config.smp_blk_cnt = 22; - - return &config; -} |