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