aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/host1x/bus.c
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2013-10-14 08:43:22 -0400
committerThierry Reding <treding@nvidia.com>2013-10-31 04:55:33 -0400
commit776dc38403676f499a73d32e2e7c61eb5b42f736 (patch)
tree5c9f8d670b51d743c5bbab45ec401e30f22579c1 /drivers/gpu/host1x/bus.c
parent35d747a81d7eb824bd0c3476cd0c564b52ad5353 (diff)
drm/tegra: Move subdevice infrastructure to host1x
The Tegra DRM driver currently uses some infrastructure to defer the DRM core initialization until all required devices have registered. The same infrastructure can potentially be used by any other driver that requires more than a single sub-device of the host1x module. Make the infrastructure more generic and keep only the DRM specific code in the DRM part of the driver. Eventually this will make it easy to move the DRM driver part back to the DRM subsystem. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/host1x/bus.c')
-rw-r--r--drivers/gpu/host1x/bus.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
new file mode 100644
index 000000000000..509383f8be03
--- /dev/null
+++ b/drivers/gpu/host1x/bus.c
@@ -0,0 +1,550 @@
1/*
2 * Copyright (C) 2012 Avionic Design GmbH
3 * Copyright (C) 2012-2013, NVIDIA Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/host1x.h>
19#include <linux/of.h>
20#include <linux/slab.h>
21
22#include "dev.h"
23
24static DEFINE_MUTEX(clients_lock);
25static LIST_HEAD(clients);
26
27static DEFINE_MUTEX(drivers_lock);
28static LIST_HEAD(drivers);
29
30static DEFINE_MUTEX(devices_lock);
31static LIST_HEAD(devices);
32
33struct host1x_subdev {
34 struct host1x_client *client;
35 struct device_node *np;
36 struct list_head list;
37};
38
39/**
40 * host1x_subdev_add() - add a new subdevice with an associated device node
41 */
42static int host1x_subdev_add(struct host1x_device *device,
43 struct device_node *np)
44{
45 struct host1x_subdev *subdev;
46
47 subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
48 if (!subdev)
49 return -ENOMEM;
50
51 INIT_LIST_HEAD(&subdev->list);
52 subdev->np = of_node_get(np);
53
54 mutex_lock(&device->subdevs_lock);
55 list_add_tail(&subdev->list, &device->subdevs);
56 mutex_unlock(&device->subdevs_lock);
57
58 return 0;
59}
60
61/**
62 * host1x_subdev_del() - remove subdevice
63 */
64static void host1x_subdev_del(struct host1x_subdev *subdev)
65{
66 list_del(&subdev->list);
67 of_node_put(subdev->np);
68 kfree(subdev);
69}
70
71/**
72 * host1x_device_parse_dt() - scan device tree and add matching subdevices
73 */
74static int host1x_device_parse_dt(struct host1x_device *device)
75{
76 struct device_node *np;
77 int err;
78
79 for_each_child_of_node(device->dev.parent->of_node, np) {
80 if (of_match_node(device->driver->subdevs, np) &&
81 of_device_is_available(np)) {
82 err = host1x_subdev_add(device, np);
83 if (err < 0)
84 return err;
85 }
86 }
87
88 return 0;
89}
90
91static void host1x_subdev_register(struct host1x_device *device,
92 struct host1x_subdev *subdev,
93 struct host1x_client *client)
94{
95 int err;
96
97 /*
98 * Move the subdevice to the list of active (registered) subdevices
99 * and associate it with a client. At the same time, associate the
100 * client with its parent device.
101 */
102 mutex_lock(&device->subdevs_lock);
103 mutex_lock(&device->clients_lock);
104 list_move_tail(&client->list, &device->clients);
105 list_move_tail(&subdev->list, &device->active);
106 client->parent = &device->dev;
107 subdev->client = client;
108 mutex_unlock(&device->clients_lock);
109 mutex_unlock(&device->subdevs_lock);
110
111 /*
112 * When all subdevices have been registered, the composite device is
113 * ready to be probed.
114 */
115 if (list_empty(&device->subdevs)) {
116 err = device->driver->probe(device);
117 if (err < 0)
118 dev_err(&device->dev, "probe failed: %d\n", err);
119 }
120}
121
122static void __host1x_subdev_unregister(struct host1x_device *device,
123 struct host1x_subdev *subdev)
124{
125 struct host1x_client *client = subdev->client;
126 int err;
127
128 /*
129 * If all subdevices have been activated, we're about to remove the
130 * first active subdevice, so unload the driver first.
131 */
132 if (list_empty(&device->subdevs)) {
133 err = device->driver->remove(device);
134 if (err < 0)
135 dev_err(&device->dev, "remove failed: %d\n", err);
136 }
137
138 /*
139 * Move the subdevice back to the list of idle subdevices and remove
140 * it from list of clients.
141 */
142 mutex_lock(&device->clients_lock);
143 subdev->client = NULL;
144 client->parent = NULL;
145 list_move_tail(&subdev->list, &device->subdevs);
146 /*
147 * XXX: Perhaps don't do this here, but rather explicitly remove it
148 * when the device is about to be deleted.
149 *
150 * This is somewhat complicated by the fact that this function is
151 * used to remove the subdevice when a client is unregistered but
152 * also when the composite device is about to be removed.
153 */
154 list_del_init(&client->list);
155 mutex_unlock(&device->clients_lock);
156}
157
158static void host1x_subdev_unregister(struct host1x_device *device,
159 struct host1x_subdev *subdev)
160{
161 mutex_lock(&device->subdevs_lock);
162 __host1x_subdev_unregister(device, subdev);
163 mutex_unlock(&device->subdevs_lock);
164}
165
166int host1x_device_init(struct host1x_device *device)
167{
168 struct host1x_client *client;
169 int err;
170
171 mutex_lock(&device->clients_lock);
172
173 list_for_each_entry(client, &device->clients, list) {
174 if (client->ops && client->ops->init) {
175 err = client->ops->init(client);
176 if (err < 0) {
177 dev_err(&device->dev,
178 "failed to initialize %s: %d\n",
179 dev_name(client->dev), err);
180 mutex_unlock(&device->clients_lock);
181 return err;
182 }
183 }
184 }
185
186 mutex_unlock(&device->clients_lock);
187
188 return 0;
189}
190
191int host1x_device_exit(struct host1x_device *device)
192{
193 struct host1x_client *client;
194 int err;
195
196 mutex_lock(&device->clients_lock);
197
198 list_for_each_entry_reverse(client, &device->clients, list) {
199 if (client->ops && client->ops->exit) {
200 err = client->ops->exit(client);
201 if (err < 0) {
202 dev_err(&device->dev,
203 "failed to cleanup %s: %d\n",
204 dev_name(client->dev), err);
205 mutex_unlock(&device->clients_lock);
206 return err;
207 }
208 }
209 }
210
211 mutex_unlock(&device->clients_lock);
212
213 return 0;
214}
215
216static int host1x_register_client(struct host1x *host1x,
217 struct host1x_client *client)
218{
219 struct host1x_device *device;
220 struct host1x_subdev *subdev;
221
222 mutex_lock(&host1x->devices_lock);
223
224 list_for_each_entry(device, &host1x->devices, list) {
225 list_for_each_entry(subdev, &device->subdevs, list) {
226 if (subdev->np == client->dev->of_node) {
227 host1x_subdev_register(device, subdev, client);
228 mutex_unlock(&host1x->devices_lock);
229 return 0;
230 }
231 }
232 }
233
234 mutex_unlock(&host1x->devices_lock);
235 return -ENODEV;
236}
237
238static int host1x_unregister_client(struct host1x *host1x,
239 struct host1x_client *client)
240{
241 struct host1x_device *device, *dt;
242 struct host1x_subdev *subdev;
243
244 mutex_lock(&host1x->devices_lock);
245
246 list_for_each_entry_safe(device, dt, &host1x->devices, list) {
247 list_for_each_entry(subdev, &device->active, list) {
248 if (subdev->client == client) {
249 host1x_subdev_unregister(device, subdev);
250 mutex_unlock(&host1x->devices_lock);
251 return 0;
252 }
253 }
254 }
255
256 mutex_unlock(&host1x->devices_lock);
257 return -ENODEV;
258}
259
260struct bus_type host1x_bus_type = {
261 .name = "host1x",
262};
263
264int host1x_bus_init(void)
265{
266 return bus_register(&host1x_bus_type);
267}
268
269void host1x_bus_exit(void)
270{
271 bus_unregister(&host1x_bus_type);
272}
273
274static void host1x_device_release(struct device *dev)
275{
276 struct host1x_device *device = to_host1x_device(dev);
277
278 kfree(device);
279}
280
281static int host1x_device_add(struct host1x *host1x,
282 struct host1x_driver *driver)
283{
284 struct host1x_client *client, *tmp;
285 struct host1x_subdev *subdev;
286 struct host1x_device *device;
287 int err;
288
289 device = kzalloc(sizeof(*device), GFP_KERNEL);
290 if (!device)
291 return -ENOMEM;
292
293 mutex_init(&device->subdevs_lock);
294 INIT_LIST_HEAD(&device->subdevs);
295 INIT_LIST_HEAD(&device->active);
296 mutex_init(&device->clients_lock);
297 INIT_LIST_HEAD(&device->clients);
298 INIT_LIST_HEAD(&device->list);
299 device->driver = driver;
300
301 device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
302 device->dev.dma_mask = &device->dev.coherent_dma_mask;
303 device->dev.release = host1x_device_release;
304 dev_set_name(&device->dev, driver->name);
305 device->dev.bus = &host1x_bus_type;
306 device->dev.parent = host1x->dev;
307
308 err = device_register(&device->dev);
309 if (err < 0)
310 return err;
311
312 err = host1x_device_parse_dt(device);
313 if (err < 0) {
314 device_unregister(&device->dev);
315 return err;
316 }
317
318 mutex_lock(&host1x->devices_lock);
319 list_add_tail(&device->list, &host1x->devices);
320 mutex_unlock(&host1x->devices_lock);
321
322 mutex_lock(&clients_lock);
323
324 list_for_each_entry_safe(client, tmp, &clients, list) {
325 list_for_each_entry(subdev, &device->subdevs, list) {
326 if (subdev->np == client->dev->of_node) {
327 host1x_subdev_register(device, subdev, client);
328 break;
329 }
330 }
331 }
332
333 mutex_unlock(&clients_lock);
334
335 return 0;
336}
337
338/*
339 * Removes a device by first unregistering any subdevices and then removing
340 * itself from the list of devices.
341 *
342 * This function must be called with the host1x->devices_lock held.
343 */
344static void host1x_device_del(struct host1x *host1x,
345 struct host1x_device *device)
346{
347 struct host1x_subdev *subdev, *sd;
348 struct host1x_client *client, *cl;
349
350 mutex_lock(&device->subdevs_lock);
351
352 /* unregister subdevices */
353 list_for_each_entry_safe(subdev, sd, &device->active, list) {
354 /*
355 * host1x_subdev_unregister() will remove the client from
356 * any lists, so we'll need to manually add it back to the
357 * list of idle clients.
358 *
359 * XXX: Alternatively, perhaps don't remove the client from
360 * any lists in host1x_subdev_unregister() and instead do
361 * that explicitly from host1x_unregister_client()?
362 */
363 client = subdev->client;
364
365 __host1x_subdev_unregister(device, subdev);
366
367 /* add the client to the list of idle clients */
368 mutex_lock(&clients_lock);
369 list_add_tail(&client->list, &clients);
370 mutex_unlock(&clients_lock);
371 }
372
373 /* remove subdevices */
374 list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
375 host1x_subdev_del(subdev);
376
377 mutex_unlock(&device->subdevs_lock);
378
379 /* move clients to idle list */
380 mutex_lock(&clients_lock);
381 mutex_lock(&device->clients_lock);
382
383 list_for_each_entry_safe(client, cl, &device->clients, list)
384 list_move_tail(&client->list, &clients);
385
386 mutex_unlock(&device->clients_lock);
387 mutex_unlock(&clients_lock);
388
389 /* finally remove the device */
390 list_del_init(&device->list);
391 device_unregister(&device->dev);
392}
393
394static void host1x_attach_driver(struct host1x *host1x,
395 struct host1x_driver *driver)
396{
397 struct host1x_device *device;
398 int err;
399
400 mutex_lock(&host1x->devices_lock);
401
402 list_for_each_entry(device, &host1x->devices, list) {
403 if (device->driver == driver) {
404 mutex_unlock(&host1x->devices_lock);
405 return;
406 }
407 }
408
409 mutex_unlock(&host1x->devices_lock);
410
411 err = host1x_device_add(host1x, driver);
412 if (err < 0)
413 dev_err(host1x->dev, "failed to allocate device: %d\n", err);
414}
415
416static void host1x_detach_driver(struct host1x *host1x,
417 struct host1x_driver *driver)
418{
419 struct host1x_device *device, *tmp;
420
421 mutex_lock(&host1x->devices_lock);
422
423 list_for_each_entry_safe(device, tmp, &host1x->devices, list)
424 if (device->driver == driver)
425 host1x_device_del(host1x, device);
426
427 mutex_unlock(&host1x->devices_lock);
428}
429
430int host1x_register(struct host1x *host1x)
431{
432 struct host1x_driver *driver;
433
434 mutex_lock(&devices_lock);
435 list_add_tail(&host1x->list, &devices);
436 mutex_unlock(&devices_lock);
437
438 mutex_lock(&drivers_lock);
439
440 list_for_each_entry(driver, &drivers, list)
441 host1x_attach_driver(host1x, driver);
442
443 mutex_unlock(&drivers_lock);
444
445 return 0;
446}
447
448int host1x_unregister(struct host1x *host1x)
449{
450 struct host1x_driver *driver;
451
452 mutex_lock(&drivers_lock);
453
454 list_for_each_entry(driver, &drivers, list)
455 host1x_detach_driver(host1x, driver);
456
457 mutex_unlock(&drivers_lock);
458
459 mutex_lock(&devices_lock);
460 list_del_init(&host1x->list);
461 mutex_unlock(&devices_lock);
462
463 return 0;
464}
465
466int host1x_driver_register(struct host1x_driver *driver)
467{
468 struct host1x *host1x;
469
470 INIT_LIST_HEAD(&driver->list);
471
472 mutex_lock(&drivers_lock);
473 list_add_tail(&driver->list, &drivers);
474 mutex_unlock(&drivers_lock);
475
476 mutex_lock(&devices_lock);
477
478 list_for_each_entry(host1x, &devices, list)
479 host1x_attach_driver(host1x, driver);
480
481 mutex_unlock(&devices_lock);
482
483 return 0;
484}
485EXPORT_SYMBOL(host1x_driver_register);
486
487void host1x_driver_unregister(struct host1x_driver *driver)
488{
489 mutex_lock(&drivers_lock);
490 list_del_init(&driver->list);
491 mutex_unlock(&drivers_lock);
492}
493EXPORT_SYMBOL(host1x_driver_unregister);
494
495int host1x_client_register(struct host1x_client *client)
496{
497 struct host1x *host1x;
498 int err;
499
500 mutex_lock(&devices_lock);
501
502 list_for_each_entry(host1x, &devices, list) {
503 err = host1x_register_client(host1x, client);
504 if (!err) {
505 mutex_unlock(&devices_lock);
506 return 0;
507 }
508 }
509
510 mutex_unlock(&devices_lock);
511
512 mutex_lock(&clients_lock);
513 list_add_tail(&client->list, &clients);
514 mutex_unlock(&clients_lock);
515
516 return 0;
517}
518EXPORT_SYMBOL(host1x_client_register);
519
520int host1x_client_unregister(struct host1x_client *client)
521{
522 struct host1x_client *c;
523 struct host1x *host1x;
524 int err;
525
526 mutex_lock(&devices_lock);
527
528 list_for_each_entry(host1x, &devices, list) {
529 err = host1x_unregister_client(host1x, client);
530 if (!err) {
531 mutex_unlock(&devices_lock);
532 return 0;
533 }
534 }
535
536 mutex_unlock(&devices_lock);
537 mutex_lock(&clients_lock);
538
539 list_for_each_entry(c, &clients, list) {
540 if (c == client) {
541 list_del_init(&c->list);
542 break;
543 }
544 }
545
546 mutex_unlock(&clients_lock);
547
548 return 0;
549}
550EXPORT_SYMBOL(host1x_client_unregister);