Merge tag 'drm-misc-next-2020-01-02' of git://anongit.freedesktop.org/drm/drm-misc...
[platform/kernel/linux-rpi.git] / drivers / gpu / drm / arm / display / komeda / komeda_drv.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4  * Author: James.Qian.Wang <james.qian.wang@arm.com>
5  *
6  */
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/platform_device.h>
10 #include <linux/component.h>
11 #include <linux/pm_runtime.h>
12 #include <drm/drm_of.h>
13 #include "komeda_dev.h"
14 #include "komeda_kms.h"
15
16 struct komeda_drv {
17         struct komeda_dev *mdev;
18         struct komeda_kms_dev *kms;
19 };
20
21 struct komeda_dev *dev_to_mdev(struct device *dev)
22 {
23         struct komeda_drv *mdrv = dev_get_drvdata(dev);
24
25         return mdrv ? mdrv->mdev : NULL;
26 }
27
28 static void komeda_unbind(struct device *dev)
29 {
30         struct komeda_drv *mdrv = dev_get_drvdata(dev);
31
32         if (!mdrv)
33                 return;
34
35         komeda_kms_detach(mdrv->kms);
36
37         if (pm_runtime_enabled(dev))
38                 pm_runtime_disable(dev);
39         else
40                 komeda_dev_suspend(mdrv->mdev);
41
42         komeda_dev_destroy(mdrv->mdev);
43
44         dev_set_drvdata(dev, NULL);
45         devm_kfree(dev, mdrv);
46 }
47
48 static int komeda_bind(struct device *dev)
49 {
50         struct komeda_drv *mdrv;
51         int err;
52
53         mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL);
54         if (!mdrv)
55                 return -ENOMEM;
56
57         mdrv->mdev = komeda_dev_create(dev);
58         if (IS_ERR(mdrv->mdev)) {
59                 err = PTR_ERR(mdrv->mdev);
60                 goto free_mdrv;
61         }
62
63         pm_runtime_enable(dev);
64         if (!pm_runtime_enabled(dev))
65                 komeda_dev_resume(mdrv->mdev);
66
67         mdrv->kms = komeda_kms_attach(mdrv->mdev);
68         if (IS_ERR(mdrv->kms)) {
69                 err = PTR_ERR(mdrv->kms);
70                 goto destroy_mdev;
71         }
72
73         dev_set_drvdata(dev, mdrv);
74
75         return 0;
76
77 destroy_mdev:
78         if (pm_runtime_enabled(dev))
79                 pm_runtime_disable(dev);
80         else
81                 komeda_dev_suspend(mdrv->mdev);
82
83         komeda_dev_destroy(mdrv->mdev);
84
85 free_mdrv:
86         devm_kfree(dev, mdrv);
87         return err;
88 }
89
90 static const struct component_master_ops komeda_master_ops = {
91         .bind   = komeda_bind,
92         .unbind = komeda_unbind,
93 };
94
95 static int compare_of(struct device *dev, void *data)
96 {
97         return dev->of_node == data;
98 }
99
100 static void komeda_add_slave(struct device *master,
101                              struct component_match **match,
102                              struct device_node *np,
103                              u32 port, u32 endpoint)
104 {
105         struct device_node *remote;
106
107         remote = of_graph_get_remote_node(np, port, endpoint);
108         if (remote) {
109                 drm_of_component_match_add(master, match, compare_of, remote);
110                 of_node_put(remote);
111         }
112 }
113
114 static int komeda_platform_probe(struct platform_device *pdev)
115 {
116         struct device *dev = &pdev->dev;
117         struct component_match *match = NULL;
118         struct device_node *child;
119
120         if (!dev->of_node)
121                 return -ENODEV;
122
123         for_each_available_child_of_node(dev->of_node, child) {
124                 if (of_node_cmp(child->name, "pipeline") != 0)
125                         continue;
126
127                 /* add connector */
128                 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 0);
129                 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 1);
130         }
131
132         return component_master_add_with_match(dev, &komeda_master_ops, match);
133 }
134
135 static int komeda_platform_remove(struct platform_device *pdev)
136 {
137         component_master_del(&pdev->dev, &komeda_master_ops);
138         return 0;
139 }
140
141 static const struct of_device_id komeda_of_match[] = {
142         { .compatible = "arm,mali-d71", .data = d71_identify, },
143         { .compatible = "arm,mali-d32", .data = d71_identify, },
144         {},
145 };
146
147 MODULE_DEVICE_TABLE(of, komeda_of_match);
148
149 static int komeda_rt_pm_suspend(struct device *dev)
150 {
151         struct komeda_drv *mdrv = dev_get_drvdata(dev);
152
153         return komeda_dev_suspend(mdrv->mdev);
154 }
155
156 static int komeda_rt_pm_resume(struct device *dev)
157 {
158         struct komeda_drv *mdrv = dev_get_drvdata(dev);
159
160         return komeda_dev_resume(mdrv->mdev);
161 }
162
163 static int __maybe_unused komeda_pm_suspend(struct device *dev)
164 {
165         struct komeda_drv *mdrv = dev_get_drvdata(dev);
166         int res;
167
168         res = drm_mode_config_helper_suspend(&mdrv->kms->base);
169
170         if (!pm_runtime_status_suspended(dev))
171                 komeda_dev_suspend(mdrv->mdev);
172
173         return res;
174 }
175
176 static int __maybe_unused komeda_pm_resume(struct device *dev)
177 {
178         struct komeda_drv *mdrv = dev_get_drvdata(dev);
179
180         if (!pm_runtime_status_suspended(dev))
181                 komeda_dev_resume(mdrv->mdev);
182
183         return drm_mode_config_helper_resume(&mdrv->kms->base);
184 }
185
186 static const struct dev_pm_ops komeda_pm_ops = {
187         SET_SYSTEM_SLEEP_PM_OPS(komeda_pm_suspend, komeda_pm_resume)
188         SET_RUNTIME_PM_OPS(komeda_rt_pm_suspend, komeda_rt_pm_resume, NULL)
189 };
190
191 static struct platform_driver komeda_platform_driver = {
192         .probe  = komeda_platform_probe,
193         .remove = komeda_platform_remove,
194         .driver = {
195                 .name = "komeda",
196                 .of_match_table = komeda_of_match,
197                 .pm = &komeda_pm_ops,
198         },
199 };
200
201 module_platform_driver(komeda_platform_driver);
202
203 MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>");
204 MODULE_DESCRIPTION("Komeda KMS driver");
205 MODULE_LICENSE("GPL v2");