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