gpu: host1x: Provide a proper struct bus_type
authorThierry Reding <treding@nvidia.com>
Thu, 18 Dec 2014 14:29:14 +0000 (15:29 +0100)
committerThierry Reding <treding@nvidia.com>
Tue, 27 Jan 2015 09:09:14 +0000 (10:09 +0100)
Previously the struct bus_type exported by the host1x infrastructure was
only a very basic skeleton. Turn that implementation into a more full-
fledged bus to support proper probe ordering and power management.

Note that the bus infrastructure needs to be available before any of the
drivers can be registered. This is automatically ensured if all drivers
are built as loadable modules (via symbol dependencies). If all drivers
are built-in there are no such guarantees and the link order determines
the initcall ordering. Adjust drivers/gpu/Makefile to make sure that the
host1x bus infrastructure is initialized prior to any of its users (only
drm/tegra currently).

v2: Fix building host1x and tegra-drm as modules
Reported-by: Dave Airlie <airlied@gmail.com>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reviewed-by: Mark Zhang <markz@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/Makefile
drivers/gpu/drm/tegra/drm.c
drivers/gpu/host1x/bus.c
drivers/gpu/host1x/bus.h
drivers/gpu/host1x/dev.c
include/linux/host1x.h

index 70da9eb52a42cb623c887ca91e31fa250f8fd33f..e9ed439a5b65329c33f450a1c82e78290581783a 100644 (file)
@@ -1,3 +1,6 @@
-obj-y                  += drm/ vga/
+# drm/tegra depends on host1x, so if both drivers are built-in care must be
+# taken to initialize them in the correct order. Link order is the only way
+# to ensure this currently.
 obj-$(CONFIG_TEGRA_HOST1X)     += host1x/
+obj-y                  += drm/ vga/
 obj-$(CONFIG_IMX_IPUV3_CORE)   += ipu-v3/
index d4f827593dfa2041a386dba733f9436cbed1d111..a0c18ae79029102d0d8a0ba7a56d876a9c003790 100644 (file)
@@ -912,7 +912,9 @@ static const struct of_device_id host1x_drm_subdevs[] = {
 };
 
 static struct host1x_driver host1x_drm_driver = {
-       .name = "drm",
+       .driver = {
+               .name = "drm",
+       },
        .probe = host1x_drm_probe,
        .remove = host1x_drm_remove,
        .subdevs = host1x_drm_subdevs,
index 0b52f0ea88717477f68bdcf576b439fd77f4246c..4a99c6416e6a3e3c254cb1e46cc000b79a68e785 100644 (file)
@@ -72,13 +72,14 @@ static void host1x_subdev_del(struct host1x_subdev *subdev)
 /**
  * host1x_device_parse_dt() - scan device tree and add matching subdevices
  */
-static int host1x_device_parse_dt(struct host1x_device *device)
+static int host1x_device_parse_dt(struct host1x_device *device,
+                                 struct host1x_driver *driver)
 {
        struct device_node *np;
        int err;
 
        for_each_child_of_node(device->dev.parent->of_node, np) {
-               if (of_match_node(device->driver->subdevs, np) &&
+               if (of_match_node(driver->subdevs, np) &&
                    of_device_is_available(np)) {
                        err = host1x_subdev_add(device, np);
                        if (err < 0)
@@ -109,17 +110,12 @@ static void host1x_subdev_register(struct host1x_device *device,
        mutex_unlock(&device->clients_lock);
        mutex_unlock(&device->subdevs_lock);
 
-       /*
-        * When all subdevices have been registered, the composite device is
-        * ready to be probed.
-        */
        if (list_empty(&device->subdevs)) {
-               err = device->driver->probe(device);
+               err = device_add(&device->dev);
                if (err < 0)
-                       dev_err(&device->dev, "probe failed for %ps: %d\n",
-                               device->driver, err);
+                       dev_err(&device->dev, "failed to add: %d\n", err);
                else
-                       device->bound = true;
+                       device->registered = true;
        }
 }
 
@@ -127,18 +123,16 @@ static void __host1x_subdev_unregister(struct host1x_device *device,
                                       struct host1x_subdev *subdev)
 {
        struct host1x_client *client = subdev->client;
-       int err;
 
        /*
         * If all subdevices have been activated, we're about to remove the
         * first active subdevice, so unload the driver first.
         */
-       if (list_empty(&device->subdevs) && device->bound) {
-               err = device->driver->remove(device);
-               if (err < 0)
-                       dev_err(&device->dev, "remove failed: %d\n", err);
-
-               device->bound = false;
+       if (list_empty(&device->subdevs)) {
+               if (device->registered) {
+                       device->registered = false;
+                       device_del(&device->dev);
+               }
        }
 
        /*
@@ -265,20 +259,60 @@ static int host1x_del_client(struct host1x *host1x,
        return -ENODEV;
 }
 
-static struct bus_type host1x_bus_type = {
-       .name = "host1x",
-};
+static int host1x_device_match(struct device *dev, struct device_driver *drv)
+{
+       return strcmp(dev_name(dev), drv->name) == 0;
+}
 
-int host1x_bus_init(void)
+static int host1x_device_probe(struct device *dev)
 {
-       return bus_register(&host1x_bus_type);
+       struct host1x_driver *driver = to_host1x_driver(dev->driver);
+       struct host1x_device *device = to_host1x_device(dev);
+
+       if (driver->probe)
+               return driver->probe(device);
+
+       return 0;
 }
 
-void host1x_bus_exit(void)
+static int host1x_device_remove(struct device *dev)
 {
-       bus_unregister(&host1x_bus_type);
+       struct host1x_driver *driver = to_host1x_driver(dev->driver);
+       struct host1x_device *device = to_host1x_device(dev);
+
+       if (driver->remove)
+               return driver->remove(device);
+
+       return 0;
 }
 
+static void host1x_device_shutdown(struct device *dev)
+{
+       struct host1x_driver *driver = to_host1x_driver(dev->driver);
+       struct host1x_device *device = to_host1x_device(dev);
+
+       if (driver->shutdown)
+               driver->shutdown(device);
+}
+
+static const struct dev_pm_ops host1x_device_pm_ops = {
+       .suspend = pm_generic_suspend,
+       .resume = pm_generic_resume,
+       .freeze = pm_generic_freeze,
+       .thaw = pm_generic_thaw,
+       .poweroff = pm_generic_poweroff,
+       .restore = pm_generic_restore,
+};
+
+struct bus_type host1x_bus_type = {
+       .name = "host1x",
+       .match = host1x_device_match,
+       .probe = host1x_device_probe,
+       .remove = host1x_device_remove,
+       .shutdown = host1x_device_shutdown,
+       .pm = &host1x_device_pm_ops,
+};
+
 static void __host1x_device_del(struct host1x_device *device)
 {
        struct host1x_subdev *subdev, *sd;
@@ -347,6 +381,8 @@ static int host1x_device_add(struct host1x *host1x,
        if (!device)
                return -ENOMEM;
 
+       device_initialize(&device->dev);
+
        mutex_init(&device->subdevs_lock);
        INIT_LIST_HEAD(&device->subdevs);
        INIT_LIST_HEAD(&device->active);
@@ -357,18 +393,14 @@ static int host1x_device_add(struct host1x *host1x,
 
        device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
        device->dev.dma_mask = &device->dev.coherent_dma_mask;
+       dev_set_name(&device->dev, "%s", driver->driver.name);
        device->dev.release = host1x_device_release;
-       dev_set_name(&device->dev, "%s", driver->name);
        device->dev.bus = &host1x_bus_type;
        device->dev.parent = host1x->dev;
 
-       err = device_register(&device->dev);
-       if (err < 0)
-               return err;
-
-       err = host1x_device_parse_dt(device);
+       err = host1x_device_parse_dt(device, driver);
        if (err < 0) {
-               device_unregister(&device->dev);
+               kfree(device);
                return err;
        }
 
@@ -399,7 +431,12 @@ static int host1x_device_add(struct host1x *host1x,
 static void host1x_device_del(struct host1x *host1x,
                              struct host1x_device *device)
 {
-       device_unregister(&device->dev);
+       if (device->registered) {
+               device->registered = false;
+               device_del(&device->dev);
+       }
+
+       put_device(&device->dev);
 }
 
 static void host1x_attach_driver(struct host1x *host1x,
@@ -474,7 +511,8 @@ int host1x_unregister(struct host1x *host1x)
        return 0;
 }
 
-int host1x_driver_register(struct host1x_driver *driver)
+int host1x_driver_register_full(struct host1x_driver *driver,
+                               struct module *owner)
 {
        struct host1x *host1x;
 
@@ -491,9 +529,12 @@ int host1x_driver_register(struct host1x_driver *driver)
 
        mutex_unlock(&devices_lock);
 
-       return 0;
+       driver->driver.bus = &host1x_bus_type;
+       driver->driver.owner = owner;
+
+       return driver_register(&driver->driver);
 }
-EXPORT_SYMBOL(host1x_driver_register);
+EXPORT_SYMBOL(host1x_driver_register_full);
 
 void host1x_driver_unregister(struct host1x_driver *driver)
 {
index 4099e99212c87354377d9a851d5409573792611b..88fb1c4aac685c26e652594394f117c5241fe59f 100644 (file)
 #ifndef HOST1X_BUS_H
 #define HOST1X_BUS_H
 
+struct bus_type;
 struct host1x;
 
-int host1x_bus_init(void);
-void host1x_bus_exit(void);
+extern struct bus_type host1x_bus_type;
 
 int host1x_register(struct host1x *host1x);
 int host1x_unregister(struct host1x *host1x);
index 2529908d304bd851fdd9c2d0b9d71471e5d69b20..53d3d1d45b48ecdf1758e4ddf58076c840f6fd02 100644 (file)
@@ -216,7 +216,7 @@ static int __init tegra_host1x_init(void)
 {
        int err;
 
-       err = host1x_bus_init();
+       err = bus_register(&host1x_bus_type);
        if (err < 0)
                return err;
 
@@ -233,7 +233,7 @@ static int __init tegra_host1x_init(void)
 unregister_host1x:
        platform_driver_unregister(&tegra_host1x_driver);
 unregister_bus:
-       host1x_bus_exit();
+       bus_unregister(&host1x_bus_type);
        return err;
 }
 module_init(tegra_host1x_init);
@@ -242,7 +242,7 @@ static void __exit tegra_host1x_exit(void)
 {
        platform_driver_unregister(&tegra_mipi_driver);
        platform_driver_unregister(&tegra_host1x_driver);
-       host1x_bus_exit();
+       bus_unregister(&host1x_bus_type);
 }
 module_exit(tegra_host1x_exit);
 
index 7890b553d12ec074421ded87a6722bec730fe5ee..464f33814a94d0ccf82f20b69041188fedb96b59 100644 (file)
@@ -250,17 +250,29 @@ void host1x_job_unpin(struct host1x_job *job);
 struct host1x_device;
 
 struct host1x_driver {
+       struct device_driver driver;
+
        const struct of_device_id *subdevs;
        struct list_head list;
-       const char *name;
 
        int (*probe)(struct host1x_device *device);
        int (*remove)(struct host1x_device *device);
+       void (*shutdown)(struct host1x_device *device);
 };
 
-int host1x_driver_register(struct host1x_driver *driver);
+static inline struct host1x_driver *
+to_host1x_driver(struct device_driver *driver)
+{
+       return container_of(driver, struct host1x_driver, driver);
+}
+
+int host1x_driver_register_full(struct host1x_driver *driver,
+                               struct module *owner);
 void host1x_driver_unregister(struct host1x_driver *driver);
 
+#define host1x_driver_register(driver) \
+       host1x_driver_register_full(driver, THIS_MODULE)
+
 struct host1x_device {
        struct host1x_driver *driver;
        struct list_head list;
@@ -273,7 +285,7 @@ struct host1x_device {
        struct mutex clients_lock;
        struct list_head clients;
 
-       bool bound;
+       bool registered;
 };
 
 static inline struct host1x_device *to_host1x_device(struct device *dev)