]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/exynos/exynos_drm_core.c
drm/nvc0/fb: fix crash when different mutex is used to protect same list
[karo-tx-linux.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  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #include <drm/drmP.h>
30 #include "exynos_drm_drv.h"
31 #include "exynos_drm_encoder.h"
32 #include "exynos_drm_connector.h"
33 #include "exynos_drm_fbdev.h"
34
35 static LIST_HEAD(exynos_drm_subdrv_list);
36
37 static int exynos_drm_create_enc_conn(struct drm_device *dev,
38                                         struct exynos_drm_subdrv *subdrv)
39 {
40         struct drm_encoder *encoder;
41         struct drm_connector *connector;
42         int ret;
43
44         DRM_DEBUG_DRIVER("%s\n", __FILE__);
45
46         subdrv->manager->dev = subdrv->dev;
47
48         /* create and initialize a encoder for this sub driver. */
49         encoder = exynos_drm_encoder_create(dev, subdrv->manager,
50                         (1 << MAX_CRTC) - 1);
51         if (!encoder) {
52                 DRM_ERROR("failed to create encoder\n");
53                 return -EFAULT;
54         }
55
56         /*
57          * create and initialize a connector for this sub driver and
58          * attach the encoder created above to the connector.
59          */
60         connector = exynos_drm_connector_create(dev, encoder);
61         if (!connector) {
62                 DRM_ERROR("failed to create connector\n");
63                 ret = -EFAULT;
64                 goto err_destroy_encoder;
65         }
66
67         subdrv->encoder = encoder;
68         subdrv->connector = connector;
69
70         return 0;
71
72 err_destroy_encoder:
73         encoder->funcs->destroy(encoder);
74         return ret;
75 }
76
77 static void exynos_drm_destroy_enc_conn(struct exynos_drm_subdrv *subdrv)
78 {
79         if (subdrv->encoder) {
80                 struct drm_encoder *encoder = subdrv->encoder;
81                 encoder->funcs->destroy(encoder);
82                 subdrv->encoder = NULL;
83         }
84
85         if (subdrv->connector) {
86                 struct drm_connector *connector = subdrv->connector;
87                 connector->funcs->destroy(connector);
88                 subdrv->connector = NULL;
89         }
90 }
91
92 static int exynos_drm_subdrv_probe(struct drm_device *dev,
93                                         struct exynos_drm_subdrv *subdrv)
94 {
95         if (subdrv->probe) {
96                 int ret;
97
98                 subdrv->drm_dev = dev;
99
100                 /*
101                  * this probe callback would be called by sub driver
102                  * after setting of all resources to this sub driver,
103                  * such as clock, irq and register map are done or by load()
104                  * of exynos drm driver.
105                  *
106                  * P.S. note that this driver is considered for modularization.
107                  */
108                 ret = subdrv->probe(dev, subdrv->dev);
109                 if (ret)
110                         return ret;
111         }
112
113         return 0;
114 }
115
116 static void exynos_drm_subdrv_remove(struct drm_device *dev,
117                                       struct exynos_drm_subdrv *subdrv)
118 {
119         DRM_DEBUG_DRIVER("%s\n", __FILE__);
120
121         if (subdrv->remove)
122                 subdrv->remove(dev, subdrv->dev);
123 }
124
125 int exynos_drm_device_register(struct drm_device *dev)
126 {
127         struct exynos_drm_subdrv *subdrv, *n;
128         unsigned int fine_cnt = 0;
129         int err;
130
131         DRM_DEBUG_DRIVER("%s\n", __FILE__);
132
133         if (!dev)
134                 return -EINVAL;
135
136         list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
137                 err = exynos_drm_subdrv_probe(dev, subdrv);
138                 if (err) {
139                         DRM_DEBUG("exynos drm subdrv probe failed.\n");
140                         list_del(&subdrv->list);
141                         continue;
142                 }
143
144                 /*
145                  * if manager is null then it means that this sub driver
146                  * doesn't need encoder and connector.
147                  */
148                 if (!subdrv->manager) {
149                         fine_cnt++;
150                         continue;
151                 }
152
153                 err = exynos_drm_create_enc_conn(dev, subdrv);
154                 if (err) {
155                         DRM_DEBUG("failed to create encoder and connector.\n");
156                         exynos_drm_subdrv_remove(dev, subdrv);
157                         list_del(&subdrv->list);
158                         continue;
159                 }
160
161                 fine_cnt++;
162         }
163
164         if (!fine_cnt)
165                 return -EINVAL;
166
167         return 0;
168 }
169 EXPORT_SYMBOL_GPL(exynos_drm_device_register);
170
171 int exynos_drm_device_unregister(struct drm_device *dev)
172 {
173         struct exynos_drm_subdrv *subdrv;
174
175         DRM_DEBUG_DRIVER("%s\n", __FILE__);
176
177         if (!dev) {
178                 WARN(1, "Unexpected drm device unregister!\n");
179                 return -EINVAL;
180         }
181
182         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
183                 exynos_drm_subdrv_remove(dev, subdrv);
184                 exynos_drm_destroy_enc_conn(subdrv);
185         }
186
187         return 0;
188 }
189 EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
190
191 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
192 {
193         DRM_DEBUG_DRIVER("%s\n", __FILE__);
194
195         if (!subdrv)
196                 return -EINVAL;
197
198         list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
199
200         return 0;
201 }
202 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
203
204 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
205 {
206         DRM_DEBUG_DRIVER("%s\n", __FILE__);
207
208         if (!subdrv)
209                 return -EINVAL;
210
211         list_del(&subdrv->list);
212
213         return 0;
214 }
215 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
216
217 int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
218 {
219         struct exynos_drm_subdrv *subdrv;
220         int ret;
221
222         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
223                 if (subdrv->open) {
224                         ret = subdrv->open(dev, subdrv->dev, file);
225                         if (ret)
226                                 goto err;
227                 }
228         }
229
230         return 0;
231
232 err:
233         list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
234                 if (subdrv->close)
235                         subdrv->close(dev, subdrv->dev, file);
236         }
237         return ret;
238 }
239 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
240
241 void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
242 {
243         struct exynos_drm_subdrv *subdrv;
244
245         list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
246                 if (subdrv->close)
247                         subdrv->close(dev, subdrv->dev, file);
248         }
249 }
250 EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);