xref: /linux/drivers/gpu/drm/imx/dc/dc-pe.c (revision edbafe65eef2b58625db1e113fbbfb1fe10c0291)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2024 NXP
4  */
5 
6 #include <linux/clk.h>
7 #include <linux/component.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/of_platform.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm.h>
13 #include <linux/pm_runtime.h>
14 
15 #include "dc-drv.h"
16 #include "dc-fu.h"
17 #include "dc-pe.h"
18 
19 static int dc_pe_bind(struct device *dev, struct device *master, void *data)
20 {
21 	struct dc_drm_device *dc_drm = data;
22 	struct dc_pe *pe;
23 	int ret;
24 
25 	pe = devm_kzalloc(dev, sizeof(*pe), GFP_KERNEL);
26 	if (!pe)
27 		return -ENOMEM;
28 
29 	pe->clk_axi = devm_clk_get(dev, NULL);
30 	if (IS_ERR(pe->clk_axi))
31 		return dev_err_probe(dev, PTR_ERR(pe->clk_axi),
32 				     "failed to get AXI clock\n");
33 
34 	pe->dev = dev;
35 
36 	dev_set_drvdata(dev, pe);
37 
38 	ret = devm_pm_runtime_enable(dev);
39 	if (ret)
40 		return ret;
41 
42 	dc_drm->pe = pe;
43 
44 	return 0;
45 }
46 
47 /*
48  * It's possible to get the child device pointers from the child component
49  * bind callbacks, but it depends on the component helper behavior to bind
50  * the pixel engine component first.  To avoid the dependency, post bind to
51  * get the pointers from dc_drm in a safe manner.
52  */
53 void dc_pe_post_bind(struct dc_drm_device *dc_drm)
54 {
55 	struct dc_pe *pe = dc_drm->pe;
56 	int i;
57 
58 	for (i = 0; i < DC_DISPLAYS; i++) {
59 		pe->cf_safe[i] = dc_drm->cf_safe[i];
60 		pe->cf_cont[i] = dc_drm->cf_cont[i];
61 		pe->ed_safe[i] = dc_drm->ed_safe[i];
62 		pe->ed_cont[i] = dc_drm->ed_cont[i];
63 	}
64 
65 	for (i = 0; i < DC_DISP_FU_CNT; i++)
66 		pe->fu_disp[i] = dc_drm->fu_disp[i];
67 
68 	for (i = 0; i < DC_LB_CNT; i++)
69 		pe->lb[i] = dc_drm->lb[i];
70 }
71 
72 static const struct component_ops dc_pe_ops = {
73 	.bind = dc_pe_bind,
74 };
75 
76 static int dc_pe_probe(struct platform_device *pdev)
77 {
78 	int ret;
79 
80 	ret = devm_of_platform_populate(&pdev->dev);
81 	if (ret < 0)
82 		return ret;
83 
84 	ret = component_add(&pdev->dev, &dc_pe_ops);
85 	if (ret)
86 		return dev_err_probe(&pdev->dev, ret,
87 				     "failed to add component\n");
88 
89 	return 0;
90 }
91 
92 static void dc_pe_remove(struct platform_device *pdev)
93 {
94 	component_del(&pdev->dev, &dc_pe_ops);
95 }
96 
97 static int dc_pe_runtime_suspend(struct device *dev)
98 {
99 	struct dc_pe *pe = dev_get_drvdata(dev);
100 
101 	clk_disable_unprepare(pe->clk_axi);
102 
103 	return 0;
104 }
105 
106 static int dc_pe_runtime_resume(struct device *dev)
107 {
108 	struct dc_pe *pe = dev_get_drvdata(dev);
109 	int i, ret;
110 
111 	ret = clk_prepare_enable(pe->clk_axi);
112 	if (ret) {
113 		dev_err(dev, "failed to enable AXI clock: %d\n", ret);
114 		return ret;
115 	}
116 
117 	for (i = 0; i < ARRAY_SIZE(pe->cf_safe); i++)
118 		dc_cf_init(pe->cf_safe[i]);
119 
120 	for (i = 0; i < ARRAY_SIZE(pe->cf_cont); i++)
121 		dc_cf_init(pe->cf_cont[i]);
122 
123 	for (i = 0; i < ARRAY_SIZE(pe->ed_safe); i++)
124 		dc_ed_init(pe->ed_safe[i]);
125 
126 	for (i = 0; i < ARRAY_SIZE(pe->ed_cont); i++)
127 		dc_ed_init(pe->ed_cont[i]);
128 
129 	for (i = 0; i < ARRAY_SIZE(pe->fu_disp); i++)
130 		pe->fu_disp[i]->ops.init(pe->fu_disp[i]);
131 
132 	for (i = 0; i < ARRAY_SIZE(pe->lb); i++)
133 		dc_lb_init(pe->lb[i]);
134 
135 	return 0;
136 }
137 
138 static const struct dev_pm_ops dc_pe_pm_ops = {
139 	RUNTIME_PM_OPS(dc_pe_runtime_suspend, dc_pe_runtime_resume, NULL)
140 };
141 
142 static const struct of_device_id dc_pe_dt_ids[] = {
143 	{ .compatible = "fsl,imx8qxp-dc-pixel-engine", },
144 	{ /* sentinel */ }
145 };
146 MODULE_DEVICE_TABLE(of, dc_pe_dt_ids);
147 
148 struct platform_driver dc_pe_driver = {
149 	.probe = dc_pe_probe,
150 	.remove = dc_pe_remove,
151 	.driver = {
152 		.name = "imx8-dc-pixel-engine",
153 		.suppress_bind_attrs = true,
154 		.of_match_table = dc_pe_dt_ids,
155 		.pm = pm_sleep_ptr(&dc_pe_pm_ops),
156 	},
157 };
158