Merge tag 'v3.15' into next
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / exynos / exynos_drm_core.c
1 /* exynos_drm_core.c
2  *
3  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4  * Author:
5  *      Inki Dae <inki.dae@samsung.com>
6  *      Joonyoung Shim <jy0922.shim@samsung.com>
7  *      Seung-Woo Kim <sw0312.kim@samsung.com>
8  *
9  * This program is free software; you can redistribute  it and/or modify it
10  * under  the terms of  the GNU General  Public License as published by the
11  * Free Software Foundation;  either version 2 of the  License, or (at your
12  * option) any later version.
13  */
14
15 #include <drm/drmP.h>
16 #include "exynos_drm_drv.h"
17 #include "exynos_drm_crtc.h"
18 #include "exynos_drm_encoder.h"
19 #include "exynos_drm_fbdev.h"
20
21 static LIST_HEAD(exynos_drm_subdrv_list);
22 static LIST_HEAD(exynos_drm_manager_list);
23 static LIST_HEAD(exynos_drm_display_list);
24
25 static int exynos_drm_create_enc_conn(struct drm_device *dev,
26                                         struct exynos_drm_display *display)
27 {
28         struct drm_encoder *encoder;
29         struct exynos_drm_manager *manager;
30         int ret;
31         unsigned long possible_crtcs = 0;
32
33         /* Find possible crtcs for this display */
34         list_for_each_entry(manager, &exynos_drm_manager_list, list)
35                 if (manager->type == display->type)
36                         possible_crtcs |= 1 << manager->pipe;
37
38         /* create and initialize a encoder for this sub driver. */
39         encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
40         if (!encoder) {
41                 DRM_ERROR("failed to create encoder\n");
42                 return -EFAULT;
43         }
44
45         display->encoder = encoder;
46
47         ret = display->ops->create_connector(display, encoder);
48         if (ret) {
49                 DRM_ERROR("failed to create connector ret = %d\n", ret);
50                 goto err_destroy_encoder;
51         }
52
53         return 0;
54
55 err_destroy_encoder:
56         encoder->funcs->destroy(encoder);
57         return ret;
58 }
59
60 static int exynos_drm_subdrv_probe(struct drm_device *dev,
61                                         struct exynos_drm_subdrv *subdrv)
62 {
63         if (subdrv->probe) {
64                 int ret;
65
66                 subdrv->drm_dev = dev;
67
68                 /*
69                  * this probe callback would be called by sub driver
70                  * after setting of all resources to this sub driver,
71                  * such as clock, irq and register map are done or by load()
72                  * of exynos drm driver.
73                  *
74                  * P.S. note that this driver is considered for modularization.
75                  */
76                 ret = subdrv->probe(dev, subdrv->dev);
77                 if (ret)
78                         return ret;
79         }
80
81         return 0;
82 }
83
84 static void exynos_drm_subdrv_remove(struct drm_device *dev,
85                                       struct exynos_drm_subdrv *subdrv)
86 {
87         if (subdrv->remove)
88                 subdrv->remove(dev, subdrv->dev);
89 }
90
91 int exynos_drm_initialize_managers(struct drm_device *dev)
92 {
93         struct exynos_drm_manager *manager, *n;
94         int ret, pipe = 0;
95
96         list_for_each_entry(manager, &exynos_drm_manager_list, list) {
97                 if (manager->ops->initialize) {
98                         ret = manager->ops->initialize(manager, dev, pipe);
99                         if (ret) {
100                                 DRM_ERROR("Mgr init [%d] failed with %d\n",
101                                                 manager->type, ret);
102                                 goto err;
103                         }
104                 }
105
106                 manager->drm_dev = dev;
107                 manager->pipe = pipe++;
108
109                 ret = exynos_drm_crtc_create(manager);
110                 if (ret) {
111                         DRM_ERROR("CRTC create [%d] failed with %d\n",
112                                         manager->type, ret);
113                         goto err;
114                 }
115         }
116         return 0;
117
118 err:
119         list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
120                 if (pipe-- > 0)
121                         exynos_drm_manager_unregister(manager);
122                 else
123                         list_del(&manager->list);
124         }
125         return ret;
126 }
127
128 void exynos_drm_remove_managers(struct drm_device *dev)
129 {
130         struct exynos_drm_manager *manager, *n;
131
132         list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
133                 exynos_drm_manager_unregister(manager);
134 }
135
136 int exynos_drm_initialize_displays(struct drm_device *dev)
137 {
138         struct exynos_drm_display *display, *n;
139         int ret, initialized = 0;
140
141         list_for_each_entry(display, &exynos_drm_display_list, list) {
142                 if (display->ops->initialize) {
143                         ret = display->ops->initialize(display, dev);
144                         if (ret) {
145                                 DRM_ERROR("Display init [%d] failed with %d\n",
146                                                 display->type, ret);
147                                 goto err;
148                         }
149                 }
150
151                 initialized++;
152
153                 ret = exynos_drm_create_enc_conn(dev, display);
154                 if (ret) {
155                         DRM_ERROR("Encoder create [%d] failed with %d\n",
156                                         display->type, ret);
157                         goto err;
158                 }
159         }
160         return 0;
161
162 err:
163         list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
164                 if (initialized-- > 0)
165                         exynos_drm_display_unregister(display);
166                 else
167                         list_del(&display->list);
168         }
169         return ret;
170 }
171
172 void exynos_drm_remove_displays(struct drm_device *dev)
173 {
174         struct exynos_drm_display *display, *n;
175
176         list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
177                 exynos_drm_display_unregister(display);
178 }
179
180 int exynos_drm_device_register(struct drm_device *dev)
181 {
182         struct exynos_drm_subdrv *subdrv, *n;
183         int err;
184
185         if (!dev)
186                 return -EINVAL;
187
188         list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
189                 err = exynos_drm_subdrv_probe(dev, subdrv);
190                 if (err) {
191                         DRM_DEBUG("exynos drm subdrv probe failed.\n");
192                         list_del(&subdrv->list);
193                         continue;
194                 }
195         }
196
197         return 0;
198 }
199 EXPORT_SYMBOL_GPL(exynos_drm_device_register);
200
201 int exynos_drm_device_unregister(struct drm_device *dev)
202 {
203         struct exynos_drm_subdrv *subdrv;
204
205         if (!dev) {
206                 WARN(1, "Unexpected drm device unregister!\n");
207                 return -EINVAL;
208         }
209
210         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
211                 exynos_drm_subdrv_remove(dev, subdrv);
212         }
213
214         return 0;
215 }
216 EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
217
218 int exynos_drm_manager_register(struct exynos_drm_manager *manager)
219 {
220         BUG_ON(!manager->ops);
221         list_add_tail(&manager->list, &exynos_drm_manager_list);
222         return 0;
223 }
224
225 int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
226 {
227         if (manager->ops->remove)
228                 manager->ops->remove(manager);
229
230         list_del(&manager->list);
231         return 0;
232 }
233
234 int exynos_drm_display_register(struct exynos_drm_display *display)
235 {
236         BUG_ON(!display->ops);
237         list_add_tail(&display->list, &exynos_drm_display_list);
238         return 0;
239 }
240
241 int exynos_drm_display_unregister(struct exynos_drm_display *display)
242 {
243         if (display->ops->remove)
244                 display->ops->remove(display);
245
246         list_del(&display->list);
247         return 0;
248 }
249
250 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
251 {
252         if (!subdrv)
253                 return -EINVAL;
254
255         list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
256
257         return 0;
258 }
259 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
260
261 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
262 {
263         if (!subdrv)
264                 return -EINVAL;
265
266         list_del(&subdrv->list);
267
268         return 0;
269 }
270 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
271
272 int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
273 {
274         struct exynos_drm_subdrv *subdrv;
275         int ret;
276
277         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
278                 if (subdrv->open) {
279                         ret = subdrv->open(dev, subdrv->dev, file);
280                         if (ret)
281                                 goto err;
282                 }
283         }
284
285         return 0;
286
287 err:
288         list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
289                 if (subdrv->close)
290                         subdrv->close(dev, subdrv->dev, file);
291         }
292         return ret;
293 }
294 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
295
296 void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
297 {
298         struct exynos_drm_subdrv *subdrv;
299
300         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
301                 if (subdrv->close)
302                         subdrv->close(dev, subdrv->dev, file);
303         }
304 }
305 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);