]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/gpu/host1x/bus.c
gpu: host1x: Factor out __host1x_device_del()
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / host1x / bus.c
CommitLineData
776dc384
TR
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
d24b2898 22#include "bus.h"
776dc384
TR
23#include "dev.h"
24
25static DEFINE_MUTEX(clients_lock);
26static LIST_HEAD(clients);
27
28static DEFINE_MUTEX(drivers_lock);
29static LIST_HEAD(drivers);
30
31static DEFINE_MUTEX(devices_lock);
32static LIST_HEAD(devices);
33
34struct host1x_subdev {
35 struct host1x_client *client;
36 struct device_node *np;
37 struct list_head list;
38};
39
40/**
41 * host1x_subdev_add() - add a new subdevice with an associated device node
42 */
43static int host1x_subdev_add(struct host1x_device *device,
44 struct device_node *np)
45{
46 struct host1x_subdev *subdev;
47
48 subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
49 if (!subdev)
50 return -ENOMEM;
51
52 INIT_LIST_HEAD(&subdev->list);
53 subdev->np = of_node_get(np);
54
55 mutex_lock(&device->subdevs_lock);
56 list_add_tail(&subdev->list, &device->subdevs);
57 mutex_unlock(&device->subdevs_lock);
58
59 return 0;
60}
61
62/**
63 * host1x_subdev_del() - remove subdevice
64 */
65static void host1x_subdev_del(struct host1x_subdev *subdev)
66{
67 list_del(&subdev->list);
68 of_node_put(subdev->np);
69 kfree(subdev);
70}
71
72/**
73 * host1x_device_parse_dt() - scan device tree and add matching subdevices
74 */
75static int host1x_device_parse_dt(struct host1x_device *device)
76{
77 struct device_node *np;
78 int err;
79
80 for_each_child_of_node(device->dev.parent->of_node, np) {
81 if (of_match_node(device->driver->subdevs, np) &&
82 of_device_is_available(np)) {
83 err = host1x_subdev_add(device, np);
84 if (err < 0)
85 return err;
86 }
87 }
88
89 return 0;
90}
91
92static void host1x_subdev_register(struct host1x_device *device,
93 struct host1x_subdev *subdev,
94 struct host1x_client *client)
95{
96 int err;
97
98 /*
99 * Move the subdevice to the list of active (registered) subdevices
100 * and associate it with a client. At the same time, associate the
101 * client with its parent device.
102 */
103 mutex_lock(&device->subdevs_lock);
104 mutex_lock(&device->clients_lock);
105 list_move_tail(&client->list, &device->clients);
106 list_move_tail(&subdev->list, &device->active);
107 client->parent = &device->dev;
108 subdev->client = client;
109 mutex_unlock(&device->clients_lock);
110 mutex_unlock(&device->subdevs_lock);
111
112 /*
113 * When all subdevices have been registered, the composite device is
114 * ready to be probed.
115 */
116 if (list_empty(&device->subdevs)) {
117 err = device->driver->probe(device);
118 if (err < 0)
536e1715
TR
119 dev_err(&device->dev, "probe failed for %ps: %d\n",
120 device->driver, err);
121 else
122 device->bound = true;
776dc384
TR
123 }
124}
125
126static void __host1x_subdev_unregister(struct host1x_device *device,
127 struct host1x_subdev *subdev)
128{
129 struct host1x_client *client = subdev->client;
130 int err;
131
132 /*
133 * If all subdevices have been activated, we're about to remove the
134 * first active subdevice, so unload the driver first.
135 */
536e1715 136 if (list_empty(&device->subdevs) && device->bound) {
776dc384
TR
137 err = device->driver->remove(device);
138 if (err < 0)
139 dev_err(&device->dev, "remove failed: %d\n", err);
536e1715
TR
140
141 device->bound = false;
776dc384
TR
142 }
143
144 /*
145 * Move the subdevice back to the list of idle subdevices and remove
146 * it from list of clients.
147 */
148 mutex_lock(&device->clients_lock);
149 subdev->client = NULL;
150 client->parent = NULL;
151 list_move_tail(&subdev->list, &device->subdevs);
152 /*
153 * XXX: Perhaps don't do this here, but rather explicitly remove it
154 * when the device is about to be deleted.
155 *
156 * This is somewhat complicated by the fact that this function is
157 * used to remove the subdevice when a client is unregistered but
158 * also when the composite device is about to be removed.
159 */
160 list_del_init(&client->list);
161 mutex_unlock(&device->clients_lock);
162}
163
164static void host1x_subdev_unregister(struct host1x_device *device,
165 struct host1x_subdev *subdev)
166{
167 mutex_lock(&device->subdevs_lock);
168 __host1x_subdev_unregister(device, subdev);
169 mutex_unlock(&device->subdevs_lock);
170}
171
172int host1x_device_init(struct host1x_device *device)
173{
174 struct host1x_client *client;
175 int err;
176
177 mutex_lock(&device->clients_lock);
178
179 list_for_each_entry(client, &device->clients, list) {
180 if (client->ops && client->ops->init) {
181 err = client->ops->init(client);
182 if (err < 0) {
183 dev_err(&device->dev,
184 "failed to initialize %s: %d\n",
185 dev_name(client->dev), err);
186 mutex_unlock(&device->clients_lock);
187 return err;
188 }
189 }
190 }
191
192 mutex_unlock(&device->clients_lock);
193
194 return 0;
195}
fae798a1 196EXPORT_SYMBOL(host1x_device_init);
776dc384
TR
197
198int host1x_device_exit(struct host1x_device *device)
199{
200 struct host1x_client *client;
201 int err;
202
203 mutex_lock(&device->clients_lock);
204
205 list_for_each_entry_reverse(client, &device->clients, list) {
206 if (client->ops && client->ops->exit) {
207 err = client->ops->exit(client);
208 if (err < 0) {
209 dev_err(&device->dev,
210 "failed to cleanup %s: %d\n",
211 dev_name(client->dev), err);
212 mutex_unlock(&device->clients_lock);
213 return err;
214 }
215 }
216 }
217
218 mutex_unlock(&device->clients_lock);
219
220 return 0;
221}
fae798a1 222EXPORT_SYMBOL(host1x_device_exit);
776dc384 223
0c7dfd36
TR
224static int host1x_add_client(struct host1x *host1x,
225 struct host1x_client *client)
776dc384
TR
226{
227 struct host1x_device *device;
228 struct host1x_subdev *subdev;
229
230 mutex_lock(&host1x->devices_lock);
231
232 list_for_each_entry(device, &host1x->devices, list) {
233 list_for_each_entry(subdev, &device->subdevs, list) {
234 if (subdev->np == client->dev->of_node) {
235 host1x_subdev_register(device, subdev, client);
236 mutex_unlock(&host1x->devices_lock);
237 return 0;
238 }
239 }
240 }
241
242 mutex_unlock(&host1x->devices_lock);
243 return -ENODEV;
244}
245
0c7dfd36
TR
246static int host1x_del_client(struct host1x *host1x,
247 struct host1x_client *client)
776dc384
TR
248{
249 struct host1x_device *device, *dt;
250 struct host1x_subdev *subdev;
251
252 mutex_lock(&host1x->devices_lock);
253
254 list_for_each_entry_safe(device, dt, &host1x->devices, list) {
255 list_for_each_entry(subdev, &device->active, list) {
256 if (subdev->client == client) {
257 host1x_subdev_unregister(device, subdev);
258 mutex_unlock(&host1x->devices_lock);
259 return 0;
260 }
261 }
262 }
263
264 mutex_unlock(&host1x->devices_lock);
265 return -ENODEV;
266}
267
d24b2898 268static struct bus_type host1x_bus_type = {
776dc384
TR
269 .name = "host1x",
270};
271
272int host1x_bus_init(void)
273{
274 return bus_register(&host1x_bus_type);
275}
276
277void host1x_bus_exit(void)
278{
279 bus_unregister(&host1x_bus_type);
280}
281
99d2cd81
TR
282static void __host1x_device_del(struct host1x_device *device)
283{
284 struct host1x_subdev *subdev, *sd;
285 struct host1x_client *client, *cl;
286
287 mutex_lock(&device->subdevs_lock);
288
289 /* unregister subdevices */
290 list_for_each_entry_safe(subdev, sd, &device->active, list) {
291 /*
292 * host1x_subdev_unregister() will remove the client from
293 * any lists, so we'll need to manually add it back to the
294 * list of idle clients.
295 *
296 * XXX: Alternatively, perhaps don't remove the client from
297 * any lists in host1x_subdev_unregister() and instead do
298 * that explicitly from host1x_unregister_client()?
299 */
300 client = subdev->client;
301
302 __host1x_subdev_unregister(device, subdev);
303
304 /* add the client to the list of idle clients */
305 mutex_lock(&clients_lock);
306 list_add_tail(&client->list, &clients);
307 mutex_unlock(&clients_lock);
308 }
309
310 /* remove subdevices */
311 list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
312 host1x_subdev_del(subdev);
313
314 mutex_unlock(&device->subdevs_lock);
315
316 /* move clients to idle list */
317 mutex_lock(&clients_lock);
318 mutex_lock(&device->clients_lock);
319
320 list_for_each_entry_safe(client, cl, &device->clients, list)
321 list_move_tail(&client->list, &clients);
322
323 mutex_unlock(&device->clients_lock);
324 mutex_unlock(&clients_lock);
325
326 /* finally remove the device */
327 list_del_init(&device->list);
328}
329
776dc384
TR
330static void host1x_device_release(struct device *dev)
331{
332 struct host1x_device *device = to_host1x_device(dev);
333
99d2cd81 334 __host1x_device_del(device);
776dc384
TR
335 kfree(device);
336}
337
338static int host1x_device_add(struct host1x *host1x,
339 struct host1x_driver *driver)
340{
341 struct host1x_client *client, *tmp;
342 struct host1x_subdev *subdev;
343 struct host1x_device *device;
344 int err;
345
346 device = kzalloc(sizeof(*device), GFP_KERNEL);
347 if (!device)
348 return -ENOMEM;
349
350 mutex_init(&device->subdevs_lock);
351 INIT_LIST_HEAD(&device->subdevs);
352 INIT_LIST_HEAD(&device->active);
353 mutex_init(&device->clients_lock);
354 INIT_LIST_HEAD(&device->clients);
355 INIT_LIST_HEAD(&device->list);
356 device->driver = driver;
357
358 device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
359 device->dev.dma_mask = &device->dev.coherent_dma_mask;
360 device->dev.release = host1x_device_release;
d24b2898 361 dev_set_name(&device->dev, "%s", driver->name);
776dc384
TR
362 device->dev.bus = &host1x_bus_type;
363 device->dev.parent = host1x->dev;
364
365 err = device_register(&device->dev);
366 if (err < 0)
367 return err;
368
369 err = host1x_device_parse_dt(device);
370 if (err < 0) {
371 device_unregister(&device->dev);
372 return err;
373 }
374
776dc384 375 list_add_tail(&device->list, &host1x->devices);
776dc384
TR
376
377 mutex_lock(&clients_lock);
378
379 list_for_each_entry_safe(client, tmp, &clients, list) {
380 list_for_each_entry(subdev, &device->subdevs, list) {
381 if (subdev->np == client->dev->of_node) {
382 host1x_subdev_register(device, subdev, client);
383 break;
384 }
385 }
386 }
387
388 mutex_unlock(&clients_lock);
389
390 return 0;
391}
392
393/*
394 * Removes a device by first unregistering any subdevices and then removing
395 * itself from the list of devices.
396 *
397 * This function must be called with the host1x->devices_lock held.
398 */
399static void host1x_device_del(struct host1x *host1x,
400 struct host1x_device *device)
401{
776dc384
TR
402 device_unregister(&device->dev);
403}
404
405static void host1x_attach_driver(struct host1x *host1x,
406 struct host1x_driver *driver)
407{
408 struct host1x_device *device;
409 int err;
410
411 mutex_lock(&host1x->devices_lock);
412
413 list_for_each_entry(device, &host1x->devices, list) {
414 if (device->driver == driver) {
415 mutex_unlock(&host1x->devices_lock);
416 return;
417 }
418 }
419
776dc384
TR
420 err = host1x_device_add(host1x, driver);
421 if (err < 0)
422 dev_err(host1x->dev, "failed to allocate device: %d\n", err);
38d98de4
TR
423
424 mutex_unlock(&host1x->devices_lock);
776dc384
TR
425}
426
427static void host1x_detach_driver(struct host1x *host1x,
428 struct host1x_driver *driver)
429{
430 struct host1x_device *device, *tmp;
431
432 mutex_lock(&host1x->devices_lock);
433
434 list_for_each_entry_safe(device, tmp, &host1x->devices, list)
435 if (device->driver == driver)
436 host1x_device_del(host1x, device);
437
438 mutex_unlock(&host1x->devices_lock);
439}
440
441int host1x_register(struct host1x *host1x)
442{
443 struct host1x_driver *driver;
444
445 mutex_lock(&devices_lock);
446 list_add_tail(&host1x->list, &devices);
447 mutex_unlock(&devices_lock);
448
449 mutex_lock(&drivers_lock);
450
451 list_for_each_entry(driver, &drivers, list)
452 host1x_attach_driver(host1x, driver);
453
454 mutex_unlock(&drivers_lock);
455
456 return 0;
457}
458
459int host1x_unregister(struct host1x *host1x)
460{
461 struct host1x_driver *driver;
462
463 mutex_lock(&drivers_lock);
464
465 list_for_each_entry(driver, &drivers, list)
466 host1x_detach_driver(host1x, driver);
467
468 mutex_unlock(&drivers_lock);
469
470 mutex_lock(&devices_lock);
471 list_del_init(&host1x->list);
472 mutex_unlock(&devices_lock);
473
474 return 0;
475}
476
477int host1x_driver_register(struct host1x_driver *driver)
478{
479 struct host1x *host1x;
480
481 INIT_LIST_HEAD(&driver->list);
482
483 mutex_lock(&drivers_lock);
484 list_add_tail(&driver->list, &drivers);
485 mutex_unlock(&drivers_lock);
486
487 mutex_lock(&devices_lock);
488
489 list_for_each_entry(host1x, &devices, list)
490 host1x_attach_driver(host1x, driver);
491
492 mutex_unlock(&devices_lock);
493
494 return 0;
495}
496EXPORT_SYMBOL(host1x_driver_register);
497
498void host1x_driver_unregister(struct host1x_driver *driver)
499{
500 mutex_lock(&drivers_lock);
501 list_del_init(&driver->list);
502 mutex_unlock(&drivers_lock);
503}
504EXPORT_SYMBOL(host1x_driver_unregister);
505
506int host1x_client_register(struct host1x_client *client)
507{
508 struct host1x *host1x;
509 int err;
510
511 mutex_lock(&devices_lock);
512
513 list_for_each_entry(host1x, &devices, list) {
0c7dfd36 514 err = host1x_add_client(host1x, client);
776dc384
TR
515 if (!err) {
516 mutex_unlock(&devices_lock);
517 return 0;
518 }
519 }
520
521 mutex_unlock(&devices_lock);
522
523 mutex_lock(&clients_lock);
524 list_add_tail(&client->list, &clients);
525 mutex_unlock(&clients_lock);
526
527 return 0;
528}
529EXPORT_SYMBOL(host1x_client_register);
530
531int host1x_client_unregister(struct host1x_client *client)
532{
533 struct host1x_client *c;
534 struct host1x *host1x;
535 int err;
536
537 mutex_lock(&devices_lock);
538
539 list_for_each_entry(host1x, &devices, list) {
0c7dfd36 540 err = host1x_del_client(host1x, client);
776dc384
TR
541 if (!err) {
542 mutex_unlock(&devices_lock);
543 return 0;
544 }
545 }
546
547 mutex_unlock(&devices_lock);
548 mutex_lock(&clients_lock);
549
550 list_for_each_entry(c, &clients, list) {
551 if (c == client) {
552 list_del_init(&c->list);
553 break;
554 }
555 }
556
557 mutex_unlock(&clients_lock);
558
559 return 0;
560}
561EXPORT_SYMBOL(host1x_client_unregister);