]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/gpu/drm/exynos/exynos_drm_core.c
Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[mirror_ubuntu-artful-kernel.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);