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