2019-06-03 07:44:50 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2016-05-07 23:11:25 +05:30
|
|
|
/*
|
|
|
|
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/irqdomain.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
|
|
|
|
#include "msm_drv.h"
|
|
|
|
#include "mdp5_kms.h"
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
#define to_mdp5_mdss(x) container_of(x, struct mdp5_mdss, base)
|
|
|
|
|
|
|
|
struct mdp5_mdss {
|
|
|
|
struct msm_mdss base;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
void __iomem *mmio, *vbif;
|
|
|
|
|
|
|
|
struct regulator *vdd;
|
|
|
|
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
struct clk *ahb_clk;
|
|
|
|
struct clk *axi_clk;
|
|
|
|
struct clk *vsync_clk;
|
|
|
|
|
2016-05-07 23:11:25 +05:30
|
|
|
struct {
|
|
|
|
volatile unsigned long enabled_mask;
|
|
|
|
struct irq_domain *domain;
|
|
|
|
} irqcontroller;
|
|
|
|
};
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static inline void mdss_write(struct mdp5_mdss *mdp5_mdss, u32 reg, u32 data)
|
2016-05-07 23:11:25 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
msm_writel(data, mdp5_mdss->mmio + reg);
|
2016-05-07 23:11:25 +05:30
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static inline u32 mdss_read(struct mdp5_mdss *mdp5_mdss, u32 reg)
|
2016-05-07 23:11:25 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
return msm_readl(mdp5_mdss->mmio + reg);
|
2016-05-07 23:11:25 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t mdss_irq(int irq, void *arg)
|
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = arg;
|
2016-05-07 23:11:25 +05:30
|
|
|
u32 intr;
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
intr = mdss_read(mdp5_mdss, REG_MDSS_HW_INTR_STATUS);
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
VERB("intr=%08x", intr);
|
|
|
|
|
|
|
|
while (intr) {
|
|
|
|
irq_hw_number_t hwirq = fls(intr) - 1;
|
|
|
|
|
|
|
|
generic_handle_irq(irq_find_mapping(
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->irqcontroller.domain, hwirq));
|
2016-05-07 23:11:25 +05:30
|
|
|
intr &= ~(1 << hwirq);
|
|
|
|
}
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* interrupt-controller implementation, so sub-blocks (MDP/HDMI/eDP/DSI/etc)
|
|
|
|
* can register to get their irq's delivered
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define VALID_IRQS (MDSS_HW_INTR_STATUS_INTR_MDP | \
|
|
|
|
MDSS_HW_INTR_STATUS_INTR_DSI0 | \
|
|
|
|
MDSS_HW_INTR_STATUS_INTR_DSI1 | \
|
|
|
|
MDSS_HW_INTR_STATUS_INTR_HDMI | \
|
|
|
|
MDSS_HW_INTR_STATUS_INTR_EDP)
|
|
|
|
|
|
|
|
static void mdss_hw_mask_irq(struct irq_data *irqd)
|
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = irq_data_get_irq_chip_data(irqd);
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
smp_mb__before_atomic();
|
2018-06-21 16:06:10 -04:00
|
|
|
clear_bit(irqd->hwirq, &mdp5_mdss->irqcontroller.enabled_mask);
|
2016-05-07 23:11:25 +05:30
|
|
|
smp_mb__after_atomic();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mdss_hw_unmask_irq(struct irq_data *irqd)
|
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = irq_data_get_irq_chip_data(irqd);
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
smp_mb__before_atomic();
|
2018-06-21 16:06:10 -04:00
|
|
|
set_bit(irqd->hwirq, &mdp5_mdss->irqcontroller.enabled_mask);
|
2016-05-07 23:11:25 +05:30
|
|
|
smp_mb__after_atomic();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct irq_chip mdss_hw_irq_chip = {
|
|
|
|
.name = "mdss",
|
|
|
|
.irq_mask = mdss_hw_mask_irq,
|
|
|
|
.irq_unmask = mdss_hw_unmask_irq,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mdss_hw_irqdomain_map(struct irq_domain *d, unsigned int irq,
|
|
|
|
irq_hw_number_t hwirq)
|
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = d->host_data;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
if (!(VALID_IRQS & (1 << hwirq)))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
irq_set_chip_and_handler(irq, &mdss_hw_irq_chip, handle_level_irq);
|
2018-06-21 16:06:10 -04:00
|
|
|
irq_set_chip_data(irq, mdp5_mdss);
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-24 18:12:19 +02:00
|
|
|
static const struct irq_domain_ops mdss_hw_irqdomain_ops = {
|
2016-05-07 23:11:25 +05:30
|
|
|
.map = mdss_hw_irqdomain_map,
|
|
|
|
.xlate = irq_domain_xlate_onecell,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static int mdss_irq_domain_init(struct mdp5_mdss *mdp5_mdss)
|
2016-05-07 23:11:25 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct device *dev = mdp5_mdss->base.dev->dev;
|
2016-05-07 23:11:25 +05:30
|
|
|
struct irq_domain *d;
|
|
|
|
|
|
|
|
d = irq_domain_add_linear(dev->of_node, 32, &mdss_hw_irqdomain_ops,
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss);
|
2016-05-07 23:11:25 +05:30
|
|
|
if (!d) {
|
2018-10-20 23:19:26 +05:30
|
|
|
DRM_DEV_ERROR(dev, "mdss irq domain add failed\n");
|
2016-05-07 23:11:25 +05:30
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->irqcontroller.enabled_mask = 0;
|
|
|
|
mdp5_mdss->irqcontroller.domain = d;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static int mdp5_mdss_enable(struct msm_mdss *mdss)
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
DBG("");
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
clk_prepare_enable(mdp5_mdss->ahb_clk);
|
|
|
|
if (mdp5_mdss->axi_clk)
|
|
|
|
clk_prepare_enable(mdp5_mdss->axi_clk);
|
|
|
|
if (mdp5_mdss->vsync_clk)
|
|
|
|
clk_prepare_enable(mdp5_mdss->vsync_clk);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static int mdp5_mdss_disable(struct msm_mdss *mdss)
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
DBG("");
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
if (mdp5_mdss->vsync_clk)
|
|
|
|
clk_disable_unprepare(mdp5_mdss->vsync_clk);
|
|
|
|
if (mdp5_mdss->axi_clk)
|
|
|
|
clk_disable_unprepare(mdp5_mdss->axi_clk);
|
|
|
|
clk_disable_unprepare(mdp5_mdss->ahb_clk);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static int msm_mdss_get_clocks(struct mdp5_mdss *mdp5_mdss)
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
{
|
2018-06-21 16:06:10 -04:00
|
|
|
struct platform_device *pdev =
|
|
|
|
to_platform_device(mdp5_mdss->base.dev->dev);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->ahb_clk = msm_clk_get(pdev, "iface");
|
|
|
|
if (IS_ERR(mdp5_mdss->ahb_clk))
|
|
|
|
mdp5_mdss->ahb_clk = NULL;
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->axi_clk = msm_clk_get(pdev, "bus");
|
|
|
|
if (IS_ERR(mdp5_mdss->axi_clk))
|
|
|
|
mdp5_mdss->axi_clk = NULL;
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->vsync_clk = msm_clk_get(pdev, "vsync");
|
|
|
|
if (IS_ERR(mdp5_mdss->vsync_clk))
|
|
|
|
mdp5_mdss->vsync_clk = NULL;
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static void mdp5_mdss_destroy(struct drm_device *dev)
|
2016-05-07 23:11:25 +05:30
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(priv->mdss);
|
2016-05-07 23:11:25 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
if (!mdp5_mdss)
|
2016-05-07 23:11:25 +05:30
|
|
|
return;
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
irq_domain_remove(mdp5_mdss->irqcontroller.domain);
|
|
|
|
mdp5_mdss->irqcontroller.domain = NULL;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
regulator_disable(mdp5_mdss->vdd);
|
2016-06-15 18:04:31 +05:30
|
|
|
|
|
|
|
pm_runtime_disable(dev->dev);
|
2016-05-07 23:11:25 +05:30
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
static const struct msm_mdss_funcs mdss_funcs = {
|
|
|
|
.enable = mdp5_mdss_enable,
|
|
|
|
.disable = mdp5_mdss_disable,
|
|
|
|
.destroy = mdp5_mdss_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
int mdp5_mdss_init(struct drm_device *dev)
|
2016-05-07 23:11:25 +05:30
|
|
|
{
|
2016-12-18 00:01:19 +02:00
|
|
|
struct platform_device *pdev = to_platform_device(dev->dev);
|
2016-05-07 23:11:25 +05:30
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
2018-06-21 16:06:10 -04:00
|
|
|
struct mdp5_mdss *mdp5_mdss;
|
2016-05-07 23:11:25 +05:30
|
|
|
int ret;
|
|
|
|
|
|
|
|
DBG("");
|
|
|
|
|
|
|
|
if (!of_device_is_compatible(dev->dev->of_node, "qcom,mdss"))
|
|
|
|
return 0;
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss = devm_kzalloc(dev->dev, sizeof(*mdp5_mdss), GFP_KERNEL);
|
|
|
|
if (!mdp5_mdss) {
|
2016-05-07 23:11:25 +05:30
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->base.dev = dev;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->mmio = msm_ioremap(pdev, "mdss_phys", "MDSS");
|
|
|
|
if (IS_ERR(mdp5_mdss->mmio)) {
|
|
|
|
ret = PTR_ERR(mdp5_mdss->mmio);
|
2016-05-07 23:11:25 +05:30
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF");
|
|
|
|
if (IS_ERR(mdp5_mdss->vbif)) {
|
|
|
|
ret = PTR_ERR(mdp5_mdss->vbif);
|
2016-05-07 23:11:25 +05:30
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
ret = msm_mdss_get_clocks(mdp5_mdss);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
if (ret) {
|
2018-10-20 23:19:26 +05:30
|
|
|
DRM_DEV_ERROR(dev->dev, "failed to get clocks: %d\n", ret);
|
drm/msm/mdp5: Set up runtime PM for MDSS
MDSS represents the top level wrapper that contains MDP5, DSI, HDMI and
other sub-blocks. W.r.t device heirarchy, it's the parent of all these
devices. The power domain of this device is actually tied to the GDSC
hw. When any sub-device enables its PD, MDSS's PD is also enabled.
The suspend/resume ops enable the top level clocks that end at the MDSS
boundary. For now, we're letting them all be optional, since the child
devices anyway hold a ref to these clocks.
Until now, we'd called a runtime_get() during probe, which ensured that
the GDSC was always on. Now that we've set up runtime PM for the children
devices, we can get rid of this hack.
Note: that the MDSS device is the platform_device in msm_drv.c. The
msm_runtime_suspend/resume ops call the funcs that enable/disable
the top level MDSS clocks. This is different from MDP4, where the
platform device created in msm_drv.c represents MDP4 itself. It would
have been nicer to hide these differences by adding new kms funcs, but
runtime PM needs to be enabled before kms is set up (i.e, msm_kms_init
is called).
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2017-07-28 16:17:07 +05:30
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-05-07 23:11:25 +05:30
|
|
|
/* Regulator to enable GDSCs in downstream kernels */
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->vdd = devm_regulator_get(dev->dev, "vdd");
|
|
|
|
if (IS_ERR(mdp5_mdss->vdd)) {
|
|
|
|
ret = PTR_ERR(mdp5_mdss->vdd);
|
2016-05-07 23:11:25 +05:30
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
ret = regulator_enable(mdp5_mdss->vdd);
|
2016-05-07 23:11:25 +05:30
|
|
|
if (ret) {
|
2018-10-20 23:19:26 +05:30
|
|
|
DRM_DEV_ERROR(dev->dev, "failed to enable regulator vdd: %d\n",
|
2016-05-07 23:11:25 +05:30
|
|
|
ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = devm_request_irq(dev->dev, platform_get_irq(pdev, 0),
|
2018-06-21 16:06:10 -04:00
|
|
|
mdss_irq, 0, "mdss_isr", mdp5_mdss);
|
2016-05-07 23:11:25 +05:30
|
|
|
if (ret) {
|
2018-10-20 23:19:26 +05:30
|
|
|
DRM_DEV_ERROR(dev->dev, "failed to init irq: %d\n", ret);
|
2016-05-07 23:11:25 +05:30
|
|
|
goto fail_irq;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
ret = mdss_irq_domain_init(mdp5_mdss);
|
2016-05-07 23:11:25 +05:30
|
|
|
if (ret) {
|
2018-10-20 23:19:26 +05:30
|
|
|
DRM_DEV_ERROR(dev->dev, "failed to init sub-block irqs: %d\n", ret);
|
2016-05-07 23:11:25 +05:30
|
|
|
goto fail_irq;
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:10 -04:00
|
|
|
mdp5_mdss->base.funcs = &mdss_funcs;
|
|
|
|
priv->mdss = &mdp5_mdss->base;
|
2016-05-07 23:11:25 +05:30
|
|
|
|
2016-06-15 18:04:31 +05:30
|
|
|
pm_runtime_enable(dev->dev);
|
|
|
|
|
2016-05-07 23:11:25 +05:30
|
|
|
return 0;
|
|
|
|
fail_irq:
|
2018-06-21 16:06:10 -04:00
|
|
|
regulator_disable(mdp5_mdss->vdd);
|
2016-05-07 23:11:25 +05:30
|
|
|
fail:
|
|
|
|
return ret;
|
|
|
|
}
|