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
dc_pe_bind(struct device * dev,struct device * master,void * data)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 */
dc_pe_post_bind(struct dc_drm_device * dc_drm)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
dc_pe_probe(struct platform_device * pdev)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
dc_pe_remove(struct platform_device * pdev)92 static void dc_pe_remove(struct platform_device *pdev)
93 {
94 component_del(&pdev->dev, &dc_pe_ops);
95 }
96
dc_pe_runtime_suspend(struct device * dev)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
dc_pe_runtime_resume(struct device * dev)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