.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Avionic Design GmbH |
---|
3 | 4 | * 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 | 5 | */ |
---|
17 | 6 | |
---|
| 7 | +#include <linux/debugfs.h> |
---|
18 | 8 | #include <linux/host1x.h> |
---|
19 | 9 | #include <linux/of.h> |
---|
| 10 | +#include <linux/seq_file.h> |
---|
20 | 11 | #include <linux/slab.h> |
---|
21 | 12 | #include <linux/of_device.h> |
---|
22 | 13 | |
---|
.. | .. |
---|
129 | 120 | mutex_lock(&device->clients_lock); |
---|
130 | 121 | list_move_tail(&client->list, &device->clients); |
---|
131 | 122 | list_move_tail(&subdev->list, &device->active); |
---|
132 | | - client->parent = &device->dev; |
---|
| 123 | + client->host = &device->dev; |
---|
133 | 124 | subdev->client = client; |
---|
134 | 125 | mutex_unlock(&device->clients_lock); |
---|
135 | 126 | mutex_unlock(&device->subdevs_lock); |
---|
.. | .. |
---|
165 | 156 | */ |
---|
166 | 157 | mutex_lock(&device->clients_lock); |
---|
167 | 158 | subdev->client = NULL; |
---|
168 | | - client->parent = NULL; |
---|
| 159 | + client->host = NULL; |
---|
169 | 160 | list_move_tail(&subdev->list, &device->subdevs); |
---|
170 | 161 | /* |
---|
171 | 162 | * XXX: Perhaps don't do this here, but rather explicitly remove it |
---|
.. | .. |
---|
314 | 305 | return strcmp(dev_name(dev), drv->name) == 0; |
---|
315 | 306 | } |
---|
316 | 307 | |
---|
| 308 | +static int host1x_device_uevent(struct device *dev, |
---|
| 309 | + struct kobj_uevent_env *env) |
---|
| 310 | +{ |
---|
| 311 | + struct device_node *np = dev->parent->of_node; |
---|
| 312 | + unsigned int count = 0; |
---|
| 313 | + struct property *p; |
---|
| 314 | + const char *compat; |
---|
| 315 | + |
---|
| 316 | + /* |
---|
| 317 | + * This duplicates most of of_device_uevent(), but the latter cannot |
---|
| 318 | + * be called from modules and operates on dev->of_node, which is not |
---|
| 319 | + * available in this case. |
---|
| 320 | + * |
---|
| 321 | + * Note that this is really only needed for backwards compatibility |
---|
| 322 | + * with libdrm, which parses this information from sysfs and will |
---|
| 323 | + * fail if it can't find the OF_FULLNAME, specifically. |
---|
| 324 | + */ |
---|
| 325 | + add_uevent_var(env, "OF_NAME=%pOFn", np); |
---|
| 326 | + add_uevent_var(env, "OF_FULLNAME=%pOF", np); |
---|
| 327 | + |
---|
| 328 | + of_property_for_each_string(np, "compatible", p, compat) { |
---|
| 329 | + add_uevent_var(env, "OF_COMPATIBLE_%u=%s", count, compat); |
---|
| 330 | + count++; |
---|
| 331 | + } |
---|
| 332 | + |
---|
| 333 | + add_uevent_var(env, "OF_COMPATIBLE_N=%u", count); |
---|
| 334 | + |
---|
| 335 | + return 0; |
---|
| 336 | +} |
---|
| 337 | + |
---|
317 | 338 | static int host1x_dma_configure(struct device *dev) |
---|
318 | 339 | { |
---|
319 | 340 | return of_dma_configure(dev, dev->of_node, true); |
---|
.. | .. |
---|
331 | 352 | struct bus_type host1x_bus_type = { |
---|
332 | 353 | .name = "host1x", |
---|
333 | 354 | .match = host1x_device_match, |
---|
334 | | - .dma_configure = host1x_dma_configure, |
---|
| 355 | + .uevent = host1x_device_uevent, |
---|
| 356 | + .dma_configure = host1x_dma_configure, |
---|
335 | 357 | .pm = &host1x_device_pm_ops, |
---|
336 | 358 | }; |
---|
337 | 359 | |
---|
.. | .. |
---|
417 | 439 | device->dev.dma_mask = &device->dev.coherent_dma_mask; |
---|
418 | 440 | dev_set_name(&device->dev, "%s", driver->driver.name); |
---|
419 | 441 | device->dev.release = host1x_device_release; |
---|
420 | | - device->dev.of_node = host1x->dev->of_node; |
---|
421 | 442 | device->dev.bus = &host1x_bus_type; |
---|
422 | 443 | device->dev.parent = host1x->dev; |
---|
423 | 444 | |
---|
424 | 445 | of_dma_configure(&device->dev, host1x->dev->of_node, true); |
---|
425 | 446 | |
---|
426 | 447 | device->dev.dma_parms = &device->dma_parms; |
---|
427 | | - dma_set_max_seg_size(&device->dev, SZ_4M); |
---|
| 448 | + dma_set_max_seg_size(&device->dev, UINT_MAX); |
---|
428 | 449 | |
---|
429 | 450 | err = host1x_device_parse_dt(device, driver); |
---|
430 | 451 | if (err < 0) { |
---|
.. | .. |
---|
503 | 524 | mutex_unlock(&host1x->devices_lock); |
---|
504 | 525 | } |
---|
505 | 526 | |
---|
| 527 | +static int host1x_devices_show(struct seq_file *s, void *data) |
---|
| 528 | +{ |
---|
| 529 | + struct host1x *host1x = s->private; |
---|
| 530 | + struct host1x_device *device; |
---|
| 531 | + |
---|
| 532 | + mutex_lock(&host1x->devices_lock); |
---|
| 533 | + |
---|
| 534 | + list_for_each_entry(device, &host1x->devices, list) { |
---|
| 535 | + struct host1x_subdev *subdev; |
---|
| 536 | + |
---|
| 537 | + seq_printf(s, "%s\n", dev_name(&device->dev)); |
---|
| 538 | + |
---|
| 539 | + mutex_lock(&device->subdevs_lock); |
---|
| 540 | + |
---|
| 541 | + list_for_each_entry(subdev, &device->active, list) |
---|
| 542 | + seq_printf(s, " %pOFf: %s\n", subdev->np, |
---|
| 543 | + dev_name(subdev->client->dev)); |
---|
| 544 | + |
---|
| 545 | + list_for_each_entry(subdev, &device->subdevs, list) |
---|
| 546 | + seq_printf(s, " %pOFf:\n", subdev->np); |
---|
| 547 | + |
---|
| 548 | + mutex_unlock(&device->subdevs_lock); |
---|
| 549 | + } |
---|
| 550 | + |
---|
| 551 | + mutex_unlock(&host1x->devices_lock); |
---|
| 552 | + |
---|
| 553 | + return 0; |
---|
| 554 | +} |
---|
| 555 | +DEFINE_SHOW_ATTRIBUTE(host1x_devices); |
---|
| 556 | + |
---|
506 | 557 | /** |
---|
507 | 558 | * host1x_register() - register a host1x controller |
---|
508 | 559 | * @host1x: host1x controller |
---|
.. | .. |
---|
525 | 576 | host1x_attach_driver(host1x, driver); |
---|
526 | 577 | |
---|
527 | 578 | mutex_unlock(&drivers_lock); |
---|
| 579 | + |
---|
| 580 | + debugfs_create_file("devices", S_IRUGO, host1x->debugfs, host1x, |
---|
| 581 | + &host1x_devices_fops); |
---|
528 | 582 | |
---|
529 | 583 | return 0; |
---|
530 | 584 | } |
---|
.. | .. |
---|
650 | 704 | EXPORT_SYMBOL(host1x_driver_unregister); |
---|
651 | 705 | |
---|
652 | 706 | /** |
---|
653 | | - * host1x_client_register() - register a host1x client |
---|
| 707 | + * __host1x_client_init() - initialize a host1x client |
---|
654 | 708 | * @client: host1x client |
---|
| 709 | + * @key: lock class key for the client-specific mutex |
---|
| 710 | + */ |
---|
| 711 | +void __host1x_client_init(struct host1x_client *client, struct lock_class_key *key) |
---|
| 712 | +{ |
---|
| 713 | + INIT_LIST_HEAD(&client->list); |
---|
| 714 | + __mutex_init(&client->lock, "host1x client lock", key); |
---|
| 715 | + client->usecount = 0; |
---|
| 716 | +} |
---|
| 717 | +EXPORT_SYMBOL(__host1x_client_init); |
---|
| 718 | + |
---|
| 719 | +/** |
---|
| 720 | + * host1x_client_exit() - uninitialize a host1x client |
---|
| 721 | + * @client: host1x client |
---|
| 722 | + */ |
---|
| 723 | +void host1x_client_exit(struct host1x_client *client) |
---|
| 724 | +{ |
---|
| 725 | + mutex_destroy(&client->lock); |
---|
| 726 | +} |
---|
| 727 | +EXPORT_SYMBOL(host1x_client_exit); |
---|
| 728 | + |
---|
| 729 | +/** |
---|
| 730 | + * __host1x_client_register() - register a host1x client |
---|
| 731 | + * @client: host1x client |
---|
| 732 | + * @key: lock class key for the client-specific mutex |
---|
655 | 733 | * |
---|
656 | 734 | * Registers a host1x client with each host1x controller instance. Note that |
---|
657 | 735 | * each client will only match their parent host1x controller and will only be |
---|
.. | .. |
---|
660 | 738 | * device and call host1x_device_init(), which will in turn call each client's |
---|
661 | 739 | * &host1x_client_ops.init implementation. |
---|
662 | 740 | */ |
---|
663 | | -int host1x_client_register(struct host1x_client *client) |
---|
| 741 | +int __host1x_client_register(struct host1x_client *client) |
---|
664 | 742 | { |
---|
665 | 743 | struct host1x *host1x; |
---|
666 | 744 | int err; |
---|
.. | .. |
---|
683 | 761 | |
---|
684 | 762 | return 0; |
---|
685 | 763 | } |
---|
686 | | -EXPORT_SYMBOL(host1x_client_register); |
---|
| 764 | +EXPORT_SYMBOL(__host1x_client_register); |
---|
687 | 765 | |
---|
688 | 766 | /** |
---|
689 | 767 | * host1x_client_unregister() - unregister a host1x client |
---|
.. | .. |
---|
723 | 801 | return 0; |
---|
724 | 802 | } |
---|
725 | 803 | EXPORT_SYMBOL(host1x_client_unregister); |
---|
| 804 | + |
---|
| 805 | +int host1x_client_suspend(struct host1x_client *client) |
---|
| 806 | +{ |
---|
| 807 | + int err = 0; |
---|
| 808 | + |
---|
| 809 | + mutex_lock(&client->lock); |
---|
| 810 | + |
---|
| 811 | + if (client->usecount == 1) { |
---|
| 812 | + if (client->ops && client->ops->suspend) { |
---|
| 813 | + err = client->ops->suspend(client); |
---|
| 814 | + if (err < 0) |
---|
| 815 | + goto unlock; |
---|
| 816 | + } |
---|
| 817 | + } |
---|
| 818 | + |
---|
| 819 | + client->usecount--; |
---|
| 820 | + dev_dbg(client->dev, "use count: %u\n", client->usecount); |
---|
| 821 | + |
---|
| 822 | + if (client->parent) { |
---|
| 823 | + err = host1x_client_suspend(client->parent); |
---|
| 824 | + if (err < 0) |
---|
| 825 | + goto resume; |
---|
| 826 | + } |
---|
| 827 | + |
---|
| 828 | + goto unlock; |
---|
| 829 | + |
---|
| 830 | +resume: |
---|
| 831 | + if (client->usecount == 0) |
---|
| 832 | + if (client->ops && client->ops->resume) |
---|
| 833 | + client->ops->resume(client); |
---|
| 834 | + |
---|
| 835 | + client->usecount++; |
---|
| 836 | +unlock: |
---|
| 837 | + mutex_unlock(&client->lock); |
---|
| 838 | + return err; |
---|
| 839 | +} |
---|
| 840 | +EXPORT_SYMBOL(host1x_client_suspend); |
---|
| 841 | + |
---|
| 842 | +int host1x_client_resume(struct host1x_client *client) |
---|
| 843 | +{ |
---|
| 844 | + int err = 0; |
---|
| 845 | + |
---|
| 846 | + mutex_lock(&client->lock); |
---|
| 847 | + |
---|
| 848 | + if (client->parent) { |
---|
| 849 | + err = host1x_client_resume(client->parent); |
---|
| 850 | + if (err < 0) |
---|
| 851 | + goto unlock; |
---|
| 852 | + } |
---|
| 853 | + |
---|
| 854 | + if (client->usecount == 0) { |
---|
| 855 | + if (client->ops && client->ops->resume) { |
---|
| 856 | + err = client->ops->resume(client); |
---|
| 857 | + if (err < 0) |
---|
| 858 | + goto suspend; |
---|
| 859 | + } |
---|
| 860 | + } |
---|
| 861 | + |
---|
| 862 | + client->usecount++; |
---|
| 863 | + dev_dbg(client->dev, "use count: %u\n", client->usecount); |
---|
| 864 | + |
---|
| 865 | + goto unlock; |
---|
| 866 | + |
---|
| 867 | +suspend: |
---|
| 868 | + if (client->parent) |
---|
| 869 | + host1x_client_suspend(client->parent); |
---|
| 870 | +unlock: |
---|
| 871 | + mutex_unlock(&client->lock); |
---|
| 872 | + return err; |
---|
| 873 | +} |
---|
| 874 | +EXPORT_SYMBOL(host1x_client_resume); |
---|