brcmsmac: rework of mac80211 .flush() callback operation
[pandora-kernel.git] / drivers / gpu / drm / tegra / host1x.c
1 /*
2  * Copyright (C) 2012 Avionic Design GmbH
3  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9
10 #include <linux/clk.h>
11 #include <linux/err.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15
16 #include "drm.h"
17
18 struct host1x_drm_client {
19         struct host1x_client *client;
20         struct device_node *np;
21         struct list_head list;
22 };
23
24 static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np)
25 {
26         struct host1x_drm_client *client;
27
28         client = kzalloc(sizeof(*client), GFP_KERNEL);
29         if (!client)
30                 return -ENOMEM;
31
32         INIT_LIST_HEAD(&client->list);
33         client->np = of_node_get(np);
34
35         list_add_tail(&client->list, &host1x->drm_clients);
36
37         return 0;
38 }
39
40 static int host1x_activate_drm_client(struct host1x *host1x,
41                                       struct host1x_drm_client *drm,
42                                       struct host1x_client *client)
43 {
44         mutex_lock(&host1x->drm_clients_lock);
45         list_del_init(&drm->list);
46         list_add_tail(&drm->list, &host1x->drm_active);
47         drm->client = client;
48         mutex_unlock(&host1x->drm_clients_lock);
49
50         return 0;
51 }
52
53 static int host1x_remove_drm_client(struct host1x *host1x,
54                                     struct host1x_drm_client *client)
55 {
56         mutex_lock(&host1x->drm_clients_lock);
57         list_del_init(&client->list);
58         mutex_unlock(&host1x->drm_clients_lock);
59
60         of_node_put(client->np);
61         kfree(client);
62
63         return 0;
64 }
65
66 static int host1x_parse_dt(struct host1x *host1x)
67 {
68         static const char * const compat[] = {
69                 "nvidia,tegra20-dc",
70                 "nvidia,tegra20-hdmi",
71                 "nvidia,tegra30-dc",
72                 "nvidia,tegra30-hdmi",
73         };
74         unsigned int i;
75         int err;
76
77         for (i = 0; i < ARRAY_SIZE(compat); i++) {
78                 struct device_node *np;
79
80                 for_each_child_of_node(host1x->dev->of_node, np) {
81                         if (of_device_is_compatible(np, compat[i]) &&
82                             of_device_is_available(np)) {
83                                 err = host1x_add_drm_client(host1x, np);
84                                 if (err < 0)
85                                         return err;
86                         }
87                 }
88         }
89
90         return 0;
91 }
92
93 static int tegra_host1x_probe(struct platform_device *pdev)
94 {
95         struct host1x *host1x;
96         struct resource *regs;
97         int err;
98
99         host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
100         if (!host1x)
101                 return -ENOMEM;
102
103         mutex_init(&host1x->drm_clients_lock);
104         INIT_LIST_HEAD(&host1x->drm_clients);
105         INIT_LIST_HEAD(&host1x->drm_active);
106         mutex_init(&host1x->clients_lock);
107         INIT_LIST_HEAD(&host1x->clients);
108         host1x->dev = &pdev->dev;
109
110         err = host1x_parse_dt(host1x);
111         if (err < 0) {
112                 dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
113                 return err;
114         }
115
116         host1x->clk = devm_clk_get(&pdev->dev, NULL);
117         if (IS_ERR(host1x->clk))
118                 return PTR_ERR(host1x->clk);
119
120         err = clk_prepare_enable(host1x->clk);
121         if (err < 0)
122                 return err;
123
124         regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
125         if (!regs) {
126                 err = -ENXIO;
127                 goto err;
128         }
129
130         err = platform_get_irq(pdev, 0);
131         if (err < 0)
132                 goto err;
133
134         host1x->syncpt = err;
135
136         err = platform_get_irq(pdev, 1);
137         if (err < 0)
138                 goto err;
139
140         host1x->irq = err;
141
142         host1x->regs = devm_request_and_ioremap(&pdev->dev, regs);
143         if (!host1x->regs) {
144                 err = -EADDRNOTAVAIL;
145                 goto err;
146         }
147
148         platform_set_drvdata(pdev, host1x);
149
150         return 0;
151
152 err:
153         clk_disable_unprepare(host1x->clk);
154         return err;
155 }
156
157 static int tegra_host1x_remove(struct platform_device *pdev)
158 {
159         struct host1x *host1x = platform_get_drvdata(pdev);
160
161         clk_disable_unprepare(host1x->clk);
162
163         return 0;
164 }
165
166 int host1x_drm_init(struct host1x *host1x, struct drm_device *drm)
167 {
168         struct host1x_client *client;
169
170         mutex_lock(&host1x->clients_lock);
171
172         list_for_each_entry(client, &host1x->clients, list) {
173                 if (client->ops && client->ops->drm_init) {
174                         int err = client->ops->drm_init(client, drm);
175                         if (err < 0) {
176                                 dev_err(host1x->dev,
177                                         "DRM setup failed for %s: %d\n",
178                                         dev_name(client->dev), err);
179                                 return err;
180                         }
181                 }
182         }
183
184         mutex_unlock(&host1x->clients_lock);
185
186         return 0;
187 }
188
189 int host1x_drm_exit(struct host1x *host1x)
190 {
191         struct platform_device *pdev = to_platform_device(host1x->dev);
192         struct host1x_client *client;
193
194         if (!host1x->drm)
195                 return 0;
196
197         mutex_lock(&host1x->clients_lock);
198
199         list_for_each_entry_reverse(client, &host1x->clients, list) {
200                 if (client->ops && client->ops->drm_exit) {
201                         int err = client->ops->drm_exit(client);
202                         if (err < 0) {
203                                 dev_err(host1x->dev,
204                                         "DRM cleanup failed for %s: %d\n",
205                                         dev_name(client->dev), err);
206                                 return err;
207                         }
208                 }
209         }
210
211         mutex_unlock(&host1x->clients_lock);
212
213         drm_platform_exit(&tegra_drm_driver, pdev);
214         host1x->drm = NULL;
215
216         return 0;
217 }
218
219 int host1x_register_client(struct host1x *host1x, struct host1x_client *client)
220 {
221         struct host1x_drm_client *drm, *tmp;
222         int err;
223
224         mutex_lock(&host1x->clients_lock);
225         list_add_tail(&client->list, &host1x->clients);
226         mutex_unlock(&host1x->clients_lock);
227
228         list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
229                 if (drm->np == client->dev->of_node)
230                         host1x_activate_drm_client(host1x, drm, client);
231
232         if (list_empty(&host1x->drm_clients)) {
233                 struct platform_device *pdev = to_platform_device(host1x->dev);
234
235                 err = drm_platform_init(&tegra_drm_driver, pdev);
236                 if (err < 0) {
237                         dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
238                         return err;
239                 }
240         }
241
242         return 0;
243 }
244
245 int host1x_unregister_client(struct host1x *host1x,
246                              struct host1x_client *client)
247 {
248         struct host1x_drm_client *drm, *tmp;
249         int err;
250
251         list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
252                 if (drm->client == client) {
253                         err = host1x_drm_exit(host1x);
254                         if (err < 0) {
255                                 dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
256                                         err);
257                                 return err;
258                         }
259
260                         host1x_remove_drm_client(host1x, drm);
261                         break;
262                 }
263         }
264
265         mutex_lock(&host1x->clients_lock);
266         list_del_init(&client->list);
267         mutex_unlock(&host1x->clients_lock);
268
269         return 0;
270 }
271
272 static struct of_device_id tegra_host1x_of_match[] = {
273         { .compatible = "nvidia,tegra30-host1x", },
274         { .compatible = "nvidia,tegra20-host1x", },
275         { },
276 };
277 MODULE_DEVICE_TABLE(of, tegra_host1x_of_match);
278
279 struct platform_driver tegra_host1x_driver = {
280         .driver = {
281                 .name = "tegra-host1x",
282                 .owner = THIS_MODULE,
283                 .of_match_table = tegra_host1x_of_match,
284         },
285         .probe = tegra_host1x_probe,
286         .remove = tegra_host1x_remove,
287 };
288
289 static int __init tegra_host1x_init(void)
290 {
291         int err;
292
293         err = platform_driver_register(&tegra_host1x_driver);
294         if (err < 0)
295                 return err;
296
297         err = platform_driver_register(&tegra_dc_driver);
298         if (err < 0)
299                 goto unregister_host1x;
300
301         err = platform_driver_register(&tegra_hdmi_driver);
302         if (err < 0)
303                 goto unregister_dc;
304
305         return 0;
306
307 unregister_dc:
308         platform_driver_unregister(&tegra_dc_driver);
309 unregister_host1x:
310         platform_driver_unregister(&tegra_host1x_driver);
311         return err;
312 }
313 module_init(tegra_host1x_init);
314
315 static void __exit tegra_host1x_exit(void)
316 {
317         platform_driver_unregister(&tegra_hdmi_driver);
318         platform_driver_unregister(&tegra_dc_driver);
319         platform_driver_unregister(&tegra_host1x_driver);
320 }
321 module_exit(tegra_host1x_exit);
322
323 MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
324 MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
325 MODULE_LICENSE("GPL");