xref: /openbmc/linux/drivers/gpu/drm/msm/msm_mdss.c (revision ecb23f2e3009082bb0b74a1cb1d918f90dc35778)
187729e2aSDmitry Baryshkov /*
287729e2aSDmitry Baryshkov  * SPDX-License-Identifier: GPL-2.0
387729e2aSDmitry Baryshkov  * Copyright (c) 2018, The Linux Foundation
487729e2aSDmitry Baryshkov  */
587729e2aSDmitry Baryshkov 
6e1072257SDmitry Baryshkov #include <linux/clk.h>
787729e2aSDmitry Baryshkov #include <linux/irq.h>
887729e2aSDmitry Baryshkov #include <linux/irqchip.h>
987729e2aSDmitry Baryshkov #include <linux/irqdesc.h>
1087729e2aSDmitry Baryshkov #include <linux/irqchip/chained_irq.h>
11e1072257SDmitry Baryshkov #include <linux/pm_runtime.h>
1287729e2aSDmitry Baryshkov 
13*ecb23f2eSDmitry Baryshkov #include "msm_drv.h"
14*ecb23f2eSDmitry Baryshkov #include "msm_kms.h"
15*ecb23f2eSDmitry Baryshkov 
1687729e2aSDmitry Baryshkov /* for DPU_HW_* defines */
1787729e2aSDmitry Baryshkov #include "disp/dpu1/dpu_hw_catalog.h"
1887729e2aSDmitry Baryshkov 
1987729e2aSDmitry Baryshkov #define HW_REV				0x0
2087729e2aSDmitry Baryshkov #define HW_INTR_STATUS			0x0010
2187729e2aSDmitry Baryshkov 
2287729e2aSDmitry Baryshkov #define UBWC_STATIC			0x144
2387729e2aSDmitry Baryshkov #define UBWC_CTRL_2			0x150
2487729e2aSDmitry Baryshkov #define UBWC_PREDICTION_MODE		0x154
2587729e2aSDmitry Baryshkov 
26e1072257SDmitry Baryshkov struct msm_mdss {
27e1072257SDmitry Baryshkov 	struct device *dev;
28e1072257SDmitry Baryshkov 
2987729e2aSDmitry Baryshkov 	void __iomem *mmio;
3087729e2aSDmitry Baryshkov 	struct clk_bulk_data *clocks;
3187729e2aSDmitry Baryshkov 	size_t num_clocks;
3287729e2aSDmitry Baryshkov 	bool is_mdp5;
3387729e2aSDmitry Baryshkov 	struct {
3487729e2aSDmitry Baryshkov 		unsigned long enabled_mask;
3587729e2aSDmitry Baryshkov 		struct irq_domain *domain;
3687729e2aSDmitry Baryshkov 	} irq_controller;
3787729e2aSDmitry Baryshkov };
3887729e2aSDmitry Baryshkov 
3987729e2aSDmitry Baryshkov static void msm_mdss_irq(struct irq_desc *desc)
4087729e2aSDmitry Baryshkov {
41e1072257SDmitry Baryshkov 	struct msm_mdss *msm_mdss = irq_desc_get_handler_data(desc);
4287729e2aSDmitry Baryshkov 	struct irq_chip *chip = irq_desc_get_chip(desc);
4387729e2aSDmitry Baryshkov 	u32 interrupts;
4487729e2aSDmitry Baryshkov 
4587729e2aSDmitry Baryshkov 	chained_irq_enter(chip, desc);
4687729e2aSDmitry Baryshkov 
47e1072257SDmitry Baryshkov 	interrupts = readl_relaxed(msm_mdss->mmio + HW_INTR_STATUS);
4887729e2aSDmitry Baryshkov 
4987729e2aSDmitry Baryshkov 	while (interrupts) {
5087729e2aSDmitry Baryshkov 		irq_hw_number_t hwirq = fls(interrupts) - 1;
5187729e2aSDmitry Baryshkov 		int rc;
5287729e2aSDmitry Baryshkov 
53e1072257SDmitry Baryshkov 		rc = generic_handle_domain_irq(msm_mdss->irq_controller.domain,
5487729e2aSDmitry Baryshkov 					       hwirq);
5587729e2aSDmitry Baryshkov 		if (rc < 0) {
56e1072257SDmitry Baryshkov 			dev_err(msm_mdss->dev, "handle irq fail: irq=%lu rc=%d\n",
5787729e2aSDmitry Baryshkov 				  hwirq, rc);
5887729e2aSDmitry Baryshkov 			break;
5987729e2aSDmitry Baryshkov 		}
6087729e2aSDmitry Baryshkov 
6187729e2aSDmitry Baryshkov 		interrupts &= ~(1 << hwirq);
6287729e2aSDmitry Baryshkov 	}
6387729e2aSDmitry Baryshkov 
6487729e2aSDmitry Baryshkov 	chained_irq_exit(chip, desc);
6587729e2aSDmitry Baryshkov }
6687729e2aSDmitry Baryshkov 
6787729e2aSDmitry Baryshkov static void msm_mdss_irq_mask(struct irq_data *irqd)
6887729e2aSDmitry Baryshkov {
69e1072257SDmitry Baryshkov 	struct msm_mdss *msm_mdss = irq_data_get_irq_chip_data(irqd);
7087729e2aSDmitry Baryshkov 
7187729e2aSDmitry Baryshkov 	/* memory barrier */
7287729e2aSDmitry Baryshkov 	smp_mb__before_atomic();
73e1072257SDmitry Baryshkov 	clear_bit(irqd->hwirq, &msm_mdss->irq_controller.enabled_mask);
7487729e2aSDmitry Baryshkov 	/* memory barrier */
7587729e2aSDmitry Baryshkov 	smp_mb__after_atomic();
7687729e2aSDmitry Baryshkov }
7787729e2aSDmitry Baryshkov 
7887729e2aSDmitry Baryshkov static void msm_mdss_irq_unmask(struct irq_data *irqd)
7987729e2aSDmitry Baryshkov {
80e1072257SDmitry Baryshkov 	struct msm_mdss *msm_mdss = irq_data_get_irq_chip_data(irqd);
8187729e2aSDmitry Baryshkov 
8287729e2aSDmitry Baryshkov 	/* memory barrier */
8387729e2aSDmitry Baryshkov 	smp_mb__before_atomic();
84e1072257SDmitry Baryshkov 	set_bit(irqd->hwirq, &msm_mdss->irq_controller.enabled_mask);
8587729e2aSDmitry Baryshkov 	/* memory barrier */
8687729e2aSDmitry Baryshkov 	smp_mb__after_atomic();
8787729e2aSDmitry Baryshkov }
8887729e2aSDmitry Baryshkov 
8987729e2aSDmitry Baryshkov static struct irq_chip msm_mdss_irq_chip = {
90e1072257SDmitry Baryshkov 	.name = "msm_mdss",
9187729e2aSDmitry Baryshkov 	.irq_mask = msm_mdss_irq_mask,
9287729e2aSDmitry Baryshkov 	.irq_unmask = msm_mdss_irq_unmask,
9387729e2aSDmitry Baryshkov };
9487729e2aSDmitry Baryshkov 
9587729e2aSDmitry Baryshkov static struct lock_class_key msm_mdss_lock_key, msm_mdss_request_key;
9687729e2aSDmitry Baryshkov 
9787729e2aSDmitry Baryshkov static int msm_mdss_irqdomain_map(struct irq_domain *domain,
9887729e2aSDmitry Baryshkov 		unsigned int irq, irq_hw_number_t hwirq)
9987729e2aSDmitry Baryshkov {
100e1072257SDmitry Baryshkov 	struct msm_mdss *msm_mdss = domain->host_data;
10187729e2aSDmitry Baryshkov 
10287729e2aSDmitry Baryshkov 	irq_set_lockdep_class(irq, &msm_mdss_lock_key, &msm_mdss_request_key);
10387729e2aSDmitry Baryshkov 	irq_set_chip_and_handler(irq, &msm_mdss_irq_chip, handle_level_irq);
10487729e2aSDmitry Baryshkov 
105e1072257SDmitry Baryshkov 	return irq_set_chip_data(irq, msm_mdss);
10687729e2aSDmitry Baryshkov }
10787729e2aSDmitry Baryshkov 
10887729e2aSDmitry Baryshkov static const struct irq_domain_ops msm_mdss_irqdomain_ops = {
10987729e2aSDmitry Baryshkov 	.map = msm_mdss_irqdomain_map,
11087729e2aSDmitry Baryshkov 	.xlate = irq_domain_xlate_onecell,
11187729e2aSDmitry Baryshkov };
11287729e2aSDmitry Baryshkov 
113e1072257SDmitry Baryshkov static int _msm_mdss_irq_domain_add(struct msm_mdss *msm_mdss)
11487729e2aSDmitry Baryshkov {
11587729e2aSDmitry Baryshkov 	struct device *dev;
11687729e2aSDmitry Baryshkov 	struct irq_domain *domain;
11787729e2aSDmitry Baryshkov 
118e1072257SDmitry Baryshkov 	dev = msm_mdss->dev;
11987729e2aSDmitry Baryshkov 
12087729e2aSDmitry Baryshkov 	domain = irq_domain_add_linear(dev->of_node, 32,
121e1072257SDmitry Baryshkov 			&msm_mdss_irqdomain_ops, msm_mdss);
12287729e2aSDmitry Baryshkov 	if (!domain) {
123e1072257SDmitry Baryshkov 		dev_err(dev, "failed to add irq_domain\n");
12487729e2aSDmitry Baryshkov 		return -EINVAL;
12587729e2aSDmitry Baryshkov 	}
12687729e2aSDmitry Baryshkov 
127e1072257SDmitry Baryshkov 	msm_mdss->irq_controller.enabled_mask = 0;
128e1072257SDmitry Baryshkov 	msm_mdss->irq_controller.domain = domain;
12987729e2aSDmitry Baryshkov 
13087729e2aSDmitry Baryshkov 	return 0;
13187729e2aSDmitry Baryshkov }
13287729e2aSDmitry Baryshkov 
133*ecb23f2eSDmitry Baryshkov static int msm_mdss_enable(struct msm_mdss *msm_mdss)
13487729e2aSDmitry Baryshkov {
13587729e2aSDmitry Baryshkov 	int ret;
13687729e2aSDmitry Baryshkov 
137e1072257SDmitry Baryshkov 	ret = clk_bulk_prepare_enable(msm_mdss->num_clocks, msm_mdss->clocks);
13887729e2aSDmitry Baryshkov 	if (ret) {
139e1072257SDmitry Baryshkov 		dev_err(msm_mdss->dev, "clock enable failed, ret:%d\n", ret);
14087729e2aSDmitry Baryshkov 		return ret;
14187729e2aSDmitry Baryshkov 	}
14287729e2aSDmitry Baryshkov 
14387729e2aSDmitry Baryshkov 	/*
14487729e2aSDmitry Baryshkov 	 * HW_REV requires MDSS_MDP_CLK, which is not enabled by the mdss on
14587729e2aSDmitry Baryshkov 	 * mdp5 hardware. Skip reading it for now.
14687729e2aSDmitry Baryshkov 	 */
147e1072257SDmitry Baryshkov 	if (msm_mdss->is_mdp5)
14887729e2aSDmitry Baryshkov 		return 0;
14987729e2aSDmitry Baryshkov 
15087729e2aSDmitry Baryshkov 	/*
15187729e2aSDmitry Baryshkov 	 * ubwc config is part of the "mdss" region which is not accessible
15287729e2aSDmitry Baryshkov 	 * from the rest of the driver. hardcode known configurations here
15387729e2aSDmitry Baryshkov 	 */
154e1072257SDmitry Baryshkov 	switch (readl_relaxed(msm_mdss->mmio + HW_REV)) {
15587729e2aSDmitry Baryshkov 	case DPU_HW_VER_500:
15687729e2aSDmitry Baryshkov 	case DPU_HW_VER_501:
157e1072257SDmitry Baryshkov 		writel_relaxed(0x420, msm_mdss->mmio + UBWC_STATIC);
15887729e2aSDmitry Baryshkov 		break;
15987729e2aSDmitry Baryshkov 	case DPU_HW_VER_600:
16087729e2aSDmitry Baryshkov 		/* TODO: 0x102e for LP_DDR4 */
161e1072257SDmitry Baryshkov 		writel_relaxed(0x103e, msm_mdss->mmio + UBWC_STATIC);
162e1072257SDmitry Baryshkov 		writel_relaxed(2, msm_mdss->mmio + UBWC_CTRL_2);
163e1072257SDmitry Baryshkov 		writel_relaxed(1, msm_mdss->mmio + UBWC_PREDICTION_MODE);
16487729e2aSDmitry Baryshkov 		break;
16587729e2aSDmitry Baryshkov 	case DPU_HW_VER_620:
166e1072257SDmitry Baryshkov 		writel_relaxed(0x1e, msm_mdss->mmio + UBWC_STATIC);
16787729e2aSDmitry Baryshkov 		break;
16887729e2aSDmitry Baryshkov 	case DPU_HW_VER_720:
169e1072257SDmitry Baryshkov 		writel_relaxed(0x101e, msm_mdss->mmio + UBWC_STATIC);
17087729e2aSDmitry Baryshkov 		break;
17187729e2aSDmitry Baryshkov 	}
17287729e2aSDmitry Baryshkov 
17387729e2aSDmitry Baryshkov 	return ret;
17487729e2aSDmitry Baryshkov }
17587729e2aSDmitry Baryshkov 
176*ecb23f2eSDmitry Baryshkov static int msm_mdss_disable(struct msm_mdss *msm_mdss)
17787729e2aSDmitry Baryshkov {
178e1072257SDmitry Baryshkov 	clk_bulk_disable_unprepare(msm_mdss->num_clocks, msm_mdss->clocks);
17987729e2aSDmitry Baryshkov 
18087729e2aSDmitry Baryshkov 	return 0;
18187729e2aSDmitry Baryshkov }
18287729e2aSDmitry Baryshkov 
183*ecb23f2eSDmitry Baryshkov static void msm_mdss_destroy(struct msm_mdss *msm_mdss)
18487729e2aSDmitry Baryshkov {
185e1072257SDmitry Baryshkov 	struct platform_device *pdev = to_platform_device(msm_mdss->dev);
18687729e2aSDmitry Baryshkov 	int irq;
18787729e2aSDmitry Baryshkov 
188e1072257SDmitry Baryshkov 	pm_runtime_suspend(msm_mdss->dev);
189e1072257SDmitry Baryshkov 	pm_runtime_disable(msm_mdss->dev);
190e1072257SDmitry Baryshkov 	irq_domain_remove(msm_mdss->irq_controller.domain);
191e1072257SDmitry Baryshkov 	msm_mdss->irq_controller.domain = NULL;
19287729e2aSDmitry Baryshkov 	irq = platform_get_irq(pdev, 0);
19387729e2aSDmitry Baryshkov 	irq_set_chained_handler_and_data(irq, NULL, NULL);
19487729e2aSDmitry Baryshkov }
19587729e2aSDmitry Baryshkov 
19687729e2aSDmitry Baryshkov /*
19787729e2aSDmitry Baryshkov  * MDP5 MDSS uses at most three specified clocks.
19887729e2aSDmitry Baryshkov  */
19987729e2aSDmitry Baryshkov #define MDP5_MDSS_NUM_CLOCKS 3
20087729e2aSDmitry Baryshkov static int mdp5_mdss_parse_clock(struct platform_device *pdev, struct clk_bulk_data **clocks)
20187729e2aSDmitry Baryshkov {
20287729e2aSDmitry Baryshkov 	struct clk_bulk_data *bulk;
20387729e2aSDmitry Baryshkov 	int num_clocks = 0;
20487729e2aSDmitry Baryshkov 	int ret;
20587729e2aSDmitry Baryshkov 
20687729e2aSDmitry Baryshkov 	if (!pdev)
20787729e2aSDmitry Baryshkov 		return -EINVAL;
20887729e2aSDmitry Baryshkov 
20987729e2aSDmitry Baryshkov 	bulk = devm_kcalloc(&pdev->dev, MDP5_MDSS_NUM_CLOCKS, sizeof(struct clk_bulk_data), GFP_KERNEL);
21087729e2aSDmitry Baryshkov 	if (!bulk)
21187729e2aSDmitry Baryshkov 		return -ENOMEM;
21287729e2aSDmitry Baryshkov 
21387729e2aSDmitry Baryshkov 	bulk[num_clocks++].id = "iface";
21487729e2aSDmitry Baryshkov 	bulk[num_clocks++].id = "bus";
21587729e2aSDmitry Baryshkov 	bulk[num_clocks++].id = "vsync";
21687729e2aSDmitry Baryshkov 
21787729e2aSDmitry Baryshkov 	ret = devm_clk_bulk_get_optional(&pdev->dev, num_clocks, bulk);
21887729e2aSDmitry Baryshkov 	if (ret)
21987729e2aSDmitry Baryshkov 		return ret;
22087729e2aSDmitry Baryshkov 
22187729e2aSDmitry Baryshkov 	*clocks = bulk;
22287729e2aSDmitry Baryshkov 
22387729e2aSDmitry Baryshkov 	return num_clocks;
22487729e2aSDmitry Baryshkov }
22587729e2aSDmitry Baryshkov 
226*ecb23f2eSDmitry Baryshkov static struct msm_mdss *msm_mdss_init(struct platform_device *pdev, bool is_mdp5)
22787729e2aSDmitry Baryshkov {
228e1072257SDmitry Baryshkov 	struct msm_mdss *msm_mdss;
22987729e2aSDmitry Baryshkov 	int ret;
23087729e2aSDmitry Baryshkov 	int irq;
23187729e2aSDmitry Baryshkov 
232e1072257SDmitry Baryshkov 	msm_mdss = devm_kzalloc(&pdev->dev, sizeof(*msm_mdss), GFP_KERNEL);
233e1072257SDmitry Baryshkov 	if (!msm_mdss)
234e1072257SDmitry Baryshkov 		return ERR_PTR(-ENOMEM);
23587729e2aSDmitry Baryshkov 
236e1072257SDmitry Baryshkov 	msm_mdss->mmio = devm_platform_ioremap_resource_byname(pdev, is_mdp5 ? "mdss_phys" : "mdss");
237e1072257SDmitry Baryshkov 	if (IS_ERR(msm_mdss->mmio))
238e1072257SDmitry Baryshkov 		return ERR_CAST(msm_mdss->mmio);
23987729e2aSDmitry Baryshkov 
240e1072257SDmitry Baryshkov 	dev_dbg(&pdev->dev, "mapped mdss address space @%pK\n", msm_mdss->mmio);
24187729e2aSDmitry Baryshkov 
24287729e2aSDmitry Baryshkov 	if (is_mdp5)
243e1072257SDmitry Baryshkov 		ret = mdp5_mdss_parse_clock(pdev, &msm_mdss->clocks);
24487729e2aSDmitry Baryshkov 	else
245e1072257SDmitry Baryshkov 		ret = devm_clk_bulk_get_all(&pdev->dev, &msm_mdss->clocks);
24687729e2aSDmitry Baryshkov 	if (ret < 0) {
247e1072257SDmitry Baryshkov 		dev_err(&pdev->dev, "failed to parse clocks, ret=%d\n", ret);
248e1072257SDmitry Baryshkov 		return ERR_PTR(ret);
24987729e2aSDmitry Baryshkov 	}
250e1072257SDmitry Baryshkov 	msm_mdss->num_clocks = ret;
251e1072257SDmitry Baryshkov 	msm_mdss->is_mdp5 = is_mdp5;
25287729e2aSDmitry Baryshkov 
253e1072257SDmitry Baryshkov 	msm_mdss->dev = &pdev->dev;
25487729e2aSDmitry Baryshkov 
25587729e2aSDmitry Baryshkov 	irq = platform_get_irq(pdev, 0);
25687729e2aSDmitry Baryshkov 	if (irq < 0)
257e1072257SDmitry Baryshkov 		return ERR_PTR(irq);
25887729e2aSDmitry Baryshkov 
259e1072257SDmitry Baryshkov 	ret = _msm_mdss_irq_domain_add(msm_mdss);
26087729e2aSDmitry Baryshkov 	if (ret)
261e1072257SDmitry Baryshkov 		return ERR_PTR(ret);
26287729e2aSDmitry Baryshkov 
26387729e2aSDmitry Baryshkov 	irq_set_chained_handler_and_data(irq, msm_mdss_irq,
264e1072257SDmitry Baryshkov 					 msm_mdss);
26587729e2aSDmitry Baryshkov 
26687729e2aSDmitry Baryshkov 	pm_runtime_enable(&pdev->dev);
26787729e2aSDmitry Baryshkov 
268e1072257SDmitry Baryshkov 	return msm_mdss;
26987729e2aSDmitry Baryshkov }
270*ecb23f2eSDmitry Baryshkov 
271*ecb23f2eSDmitry Baryshkov static int __maybe_unused mdss_runtime_suspend(struct device *dev)
272*ecb23f2eSDmitry Baryshkov {
273*ecb23f2eSDmitry Baryshkov 	struct msm_drm_private *priv = dev_get_drvdata(dev);
274*ecb23f2eSDmitry Baryshkov 
275*ecb23f2eSDmitry Baryshkov 	DBG("");
276*ecb23f2eSDmitry Baryshkov 
277*ecb23f2eSDmitry Baryshkov 	return msm_mdss_disable(priv->mdss);
278*ecb23f2eSDmitry Baryshkov }
279*ecb23f2eSDmitry Baryshkov 
280*ecb23f2eSDmitry Baryshkov static int __maybe_unused mdss_runtime_resume(struct device *dev)
281*ecb23f2eSDmitry Baryshkov {
282*ecb23f2eSDmitry Baryshkov 	struct msm_drm_private *priv = dev_get_drvdata(dev);
283*ecb23f2eSDmitry Baryshkov 
284*ecb23f2eSDmitry Baryshkov 	DBG("");
285*ecb23f2eSDmitry Baryshkov 
286*ecb23f2eSDmitry Baryshkov 	return msm_mdss_enable(priv->mdss);
287*ecb23f2eSDmitry Baryshkov }
288*ecb23f2eSDmitry Baryshkov 
289*ecb23f2eSDmitry Baryshkov static int __maybe_unused mdss_pm_suspend(struct device *dev)
290*ecb23f2eSDmitry Baryshkov {
291*ecb23f2eSDmitry Baryshkov 
292*ecb23f2eSDmitry Baryshkov 	if (pm_runtime_suspended(dev))
293*ecb23f2eSDmitry Baryshkov 		return 0;
294*ecb23f2eSDmitry Baryshkov 
295*ecb23f2eSDmitry Baryshkov 	return mdss_runtime_suspend(dev);
296*ecb23f2eSDmitry Baryshkov }
297*ecb23f2eSDmitry Baryshkov 
298*ecb23f2eSDmitry Baryshkov static int __maybe_unused mdss_pm_resume(struct device *dev)
299*ecb23f2eSDmitry Baryshkov {
300*ecb23f2eSDmitry Baryshkov 	if (pm_runtime_suspended(dev))
301*ecb23f2eSDmitry Baryshkov 		return 0;
302*ecb23f2eSDmitry Baryshkov 
303*ecb23f2eSDmitry Baryshkov 	return mdss_runtime_resume(dev);
304*ecb23f2eSDmitry Baryshkov }
305*ecb23f2eSDmitry Baryshkov 
306*ecb23f2eSDmitry Baryshkov static const struct dev_pm_ops mdss_pm_ops = {
307*ecb23f2eSDmitry Baryshkov 	SET_SYSTEM_SLEEP_PM_OPS(mdss_pm_suspend, mdss_pm_resume)
308*ecb23f2eSDmitry Baryshkov 	SET_RUNTIME_PM_OPS(mdss_runtime_suspend, mdss_runtime_resume, NULL)
309*ecb23f2eSDmitry Baryshkov 	.prepare = msm_pm_prepare,
310*ecb23f2eSDmitry Baryshkov 	.complete = msm_pm_complete,
311*ecb23f2eSDmitry Baryshkov };
312*ecb23f2eSDmitry Baryshkov 
313*ecb23f2eSDmitry Baryshkov static int get_mdp_ver(struct platform_device *pdev)
314*ecb23f2eSDmitry Baryshkov {
315*ecb23f2eSDmitry Baryshkov 	struct device *dev = &pdev->dev;
316*ecb23f2eSDmitry Baryshkov 
317*ecb23f2eSDmitry Baryshkov 	return (int) (unsigned long) of_device_get_match_data(dev);
318*ecb23f2eSDmitry Baryshkov }
319*ecb23f2eSDmitry Baryshkov 
320*ecb23f2eSDmitry Baryshkov static int find_mdp_node(struct device *dev, void *data)
321*ecb23f2eSDmitry Baryshkov {
322*ecb23f2eSDmitry Baryshkov 	return of_match_node(dpu_dt_match, dev->of_node) ||
323*ecb23f2eSDmitry Baryshkov 		of_match_node(mdp5_dt_match, dev->of_node);
324*ecb23f2eSDmitry Baryshkov }
325*ecb23f2eSDmitry Baryshkov 
326*ecb23f2eSDmitry Baryshkov static int mdss_probe(struct platform_device *pdev)
327*ecb23f2eSDmitry Baryshkov {
328*ecb23f2eSDmitry Baryshkov 	struct msm_mdss *mdss;
329*ecb23f2eSDmitry Baryshkov 	struct msm_drm_private *priv;
330*ecb23f2eSDmitry Baryshkov 	int mdp_ver = get_mdp_ver(pdev);
331*ecb23f2eSDmitry Baryshkov 	struct device *mdp_dev;
332*ecb23f2eSDmitry Baryshkov 	struct device *dev = &pdev->dev;
333*ecb23f2eSDmitry Baryshkov 	int ret;
334*ecb23f2eSDmitry Baryshkov 
335*ecb23f2eSDmitry Baryshkov 	mdss = msm_mdss_init(pdev, mdp_ver == KMS_MDP5);
336*ecb23f2eSDmitry Baryshkov 	if (IS_ERR(mdss))
337*ecb23f2eSDmitry Baryshkov 		return PTR_ERR(mdss);
338*ecb23f2eSDmitry Baryshkov 
339*ecb23f2eSDmitry Baryshkov 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
340*ecb23f2eSDmitry Baryshkov 	if (!priv) {
341*ecb23f2eSDmitry Baryshkov 		ret = -ENOMEM;
342*ecb23f2eSDmitry Baryshkov 		goto fail;
343*ecb23f2eSDmitry Baryshkov 	}
344*ecb23f2eSDmitry Baryshkov 
345*ecb23f2eSDmitry Baryshkov 	priv->mdss = mdss;
346*ecb23f2eSDmitry Baryshkov 	platform_set_drvdata(pdev, priv);
347*ecb23f2eSDmitry Baryshkov 
348*ecb23f2eSDmitry Baryshkov 	/*
349*ecb23f2eSDmitry Baryshkov 	 * MDP5/DPU based devices don't have a flat hierarchy. There is a top
350*ecb23f2eSDmitry Baryshkov 	 * level parent: MDSS, and children: MDP5/DPU, DSI, HDMI, eDP etc.
351*ecb23f2eSDmitry Baryshkov 	 * Populate the children devices, find the MDP5/DPU node, and then add
352*ecb23f2eSDmitry Baryshkov 	 * the interfaces to our components list.
353*ecb23f2eSDmitry Baryshkov 	 */
354*ecb23f2eSDmitry Baryshkov 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
355*ecb23f2eSDmitry Baryshkov 	if (ret) {
356*ecb23f2eSDmitry Baryshkov 		DRM_DEV_ERROR(dev, "failed to populate children devices\n");
357*ecb23f2eSDmitry Baryshkov 		goto fail;
358*ecb23f2eSDmitry Baryshkov 	}
359*ecb23f2eSDmitry Baryshkov 
360*ecb23f2eSDmitry Baryshkov 	mdp_dev = device_find_child(dev, NULL, find_mdp_node);
361*ecb23f2eSDmitry Baryshkov 	if (!mdp_dev) {
362*ecb23f2eSDmitry Baryshkov 		DRM_DEV_ERROR(dev, "failed to find MDSS MDP node\n");
363*ecb23f2eSDmitry Baryshkov 		of_platform_depopulate(dev);
364*ecb23f2eSDmitry Baryshkov 		ret = -ENODEV;
365*ecb23f2eSDmitry Baryshkov 		goto fail;
366*ecb23f2eSDmitry Baryshkov 	}
367*ecb23f2eSDmitry Baryshkov 
368*ecb23f2eSDmitry Baryshkov 	/*
369*ecb23f2eSDmitry Baryshkov 	 * on MDP5 based platforms, the MDSS platform device is the component
370*ecb23f2eSDmitry Baryshkov 	 * that adds MDP5 and other display interface components to
371*ecb23f2eSDmitry Baryshkov 	 * itself.
372*ecb23f2eSDmitry Baryshkov 	 */
373*ecb23f2eSDmitry Baryshkov 	ret = msm_drv_probe(dev, mdp_dev);
374*ecb23f2eSDmitry Baryshkov 	put_device(mdp_dev);
375*ecb23f2eSDmitry Baryshkov 	if (ret)
376*ecb23f2eSDmitry Baryshkov 		goto fail;
377*ecb23f2eSDmitry Baryshkov 
378*ecb23f2eSDmitry Baryshkov 	return 0;
379*ecb23f2eSDmitry Baryshkov 
380*ecb23f2eSDmitry Baryshkov fail:
381*ecb23f2eSDmitry Baryshkov 	of_platform_depopulate(dev);
382*ecb23f2eSDmitry Baryshkov 	msm_mdss_destroy(priv->mdss);
383*ecb23f2eSDmitry Baryshkov 
384*ecb23f2eSDmitry Baryshkov 	return ret;
385*ecb23f2eSDmitry Baryshkov }
386*ecb23f2eSDmitry Baryshkov 
387*ecb23f2eSDmitry Baryshkov static int mdss_remove(struct platform_device *pdev)
388*ecb23f2eSDmitry Baryshkov {
389*ecb23f2eSDmitry Baryshkov 	struct msm_drm_private *priv = platform_get_drvdata(pdev);
390*ecb23f2eSDmitry Baryshkov 	struct msm_mdss *mdss = priv->mdss;
391*ecb23f2eSDmitry Baryshkov 
392*ecb23f2eSDmitry Baryshkov 	component_master_del(&pdev->dev, &msm_drm_ops);
393*ecb23f2eSDmitry Baryshkov 	of_platform_depopulate(&pdev->dev);
394*ecb23f2eSDmitry Baryshkov 
395*ecb23f2eSDmitry Baryshkov 	msm_mdss_destroy(mdss);
396*ecb23f2eSDmitry Baryshkov 
397*ecb23f2eSDmitry Baryshkov 	return 0;
398*ecb23f2eSDmitry Baryshkov }
399*ecb23f2eSDmitry Baryshkov 
400*ecb23f2eSDmitry Baryshkov static const struct of_device_id mdss_dt_match[] = {
401*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,mdss", .data = (void *)KMS_MDP5 },
402*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,msm8998-mdss", .data = (void *)KMS_DPU },
403*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,qcm2290-mdss", .data = (void *)KMS_DPU },
404*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sdm845-mdss", .data = (void *)KMS_DPU },
405*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sc7180-mdss", .data = (void *)KMS_DPU },
406*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sc7280-mdss", .data = (void *)KMS_DPU },
407*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sc8180x-mdss", .data = (void *)KMS_DPU },
408*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sm8150-mdss", .data = (void *)KMS_DPU },
409*ecb23f2eSDmitry Baryshkov 	{ .compatible = "qcom,sm8250-mdss", .data = (void *)KMS_DPU },
410*ecb23f2eSDmitry Baryshkov 	{}
411*ecb23f2eSDmitry Baryshkov };
412*ecb23f2eSDmitry Baryshkov MODULE_DEVICE_TABLE(of, mdss_dt_match);
413*ecb23f2eSDmitry Baryshkov 
414*ecb23f2eSDmitry Baryshkov static struct platform_driver mdss_platform_driver = {
415*ecb23f2eSDmitry Baryshkov 	.probe      = mdss_probe,
416*ecb23f2eSDmitry Baryshkov 	.remove     = mdss_remove,
417*ecb23f2eSDmitry Baryshkov 	.shutdown   = msm_drv_shutdown,
418*ecb23f2eSDmitry Baryshkov 	.driver     = {
419*ecb23f2eSDmitry Baryshkov 		.name   = "msm-mdss",
420*ecb23f2eSDmitry Baryshkov 		.of_match_table = mdss_dt_match,
421*ecb23f2eSDmitry Baryshkov 		.pm     = &mdss_pm_ops,
422*ecb23f2eSDmitry Baryshkov 	},
423*ecb23f2eSDmitry Baryshkov };
424*ecb23f2eSDmitry Baryshkov 
425*ecb23f2eSDmitry Baryshkov void __init msm_mdss_register(void)
426*ecb23f2eSDmitry Baryshkov {
427*ecb23f2eSDmitry Baryshkov 	platform_driver_register(&mdss_platform_driver);
428*ecb23f2eSDmitry Baryshkov }
429*ecb23f2eSDmitry Baryshkov 
430*ecb23f2eSDmitry Baryshkov void __exit msm_mdss_unregister(void)
431*ecb23f2eSDmitry Baryshkov {
432*ecb23f2eSDmitry Baryshkov 	platform_driver_unregister(&mdss_platform_driver);
433*ecb23f2eSDmitry Baryshkov }
434