summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/host1x/bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/host1x/bus.c')
-rw-r--r--drivers/gpu/host1x/bus.c201
1 files changed, 125 insertions, 76 deletions
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index aaf54859adb0..4a99c6416e6a 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -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,14 +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: %d\n", err);
+ dev_err(&device->dev, "failed to add: %d\n", err);
+ else
+ device->registered = true;
}
}
@@ -124,16 +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)) {
- err = device->driver->remove(device);
- if (err < 0)
- dev_err(&device->dev, "remove failed: %d\n", err);
+ if (device->registered) {
+ device->registered = false;
+ device_del(&device->dev);
+ }
}
/*
@@ -260,24 +259,113 @@ 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;
+}
+
+static int host1x_device_probe(struct device *dev)
+{
+ 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;
+}
-int host1x_bus_init(void)
+static int host1x_device_remove(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->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);
}
-void host1x_bus_exit(void)
+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)
{
- bus_unregister(&host1x_bus_type);
+ struct host1x_subdev *subdev, *sd;
+ struct host1x_client *client, *cl;
+
+ mutex_lock(&device->subdevs_lock);
+
+ /* unregister subdevices */
+ list_for_each_entry_safe(subdev, sd, &device->active, list) {
+ /*
+ * host1x_subdev_unregister() will remove the client from
+ * any lists, so we'll need to manually add it back to the
+ * list of idle clients.
+ *
+ * XXX: Alternatively, perhaps don't remove the client from
+ * any lists in host1x_subdev_unregister() and instead do
+ * that explicitly from host1x_unregister_client()?
+ */
+ client = subdev->client;
+
+ __host1x_subdev_unregister(device, subdev);
+
+ /* add the client to the list of idle clients */
+ mutex_lock(&clients_lock);
+ list_add_tail(&client->list, &clients);
+ mutex_unlock(&clients_lock);
+ }
+
+ /* remove subdevices */
+ list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
+ host1x_subdev_del(subdev);
+
+ mutex_unlock(&device->subdevs_lock);
+
+ /* move clients to idle list */
+ mutex_lock(&clients_lock);
+ mutex_lock(&device->clients_lock);
+
+ list_for_each_entry_safe(client, cl, &device->clients, list)
+ list_move_tail(&client->list, &clients);
+
+ mutex_unlock(&device->clients_lock);
+ mutex_unlock(&clients_lock);
+
+ /* finally remove the device */
+ list_del_init(&device->list);
}
static void host1x_device_release(struct device *dev)
{
struct host1x_device *device = to_host1x_device(dev);
+ __host1x_device_del(device);
kfree(device);
}
@@ -293,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);
@@ -303,24 +393,18 @@ 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;
}
- mutex_lock(&host1x->devices_lock);
list_add_tail(&device->list, &host1x->devices);
- mutex_unlock(&host1x->devices_lock);
mutex_lock(&clients_lock);
@@ -347,51 +431,12 @@ static int host1x_device_add(struct host1x *host1x,
static void host1x_device_del(struct host1x *host1x,
struct host1x_device *device)
{
- struct host1x_subdev *subdev, *sd;
- struct host1x_client *client, *cl;
-
- mutex_lock(&device->subdevs_lock);
-
- /* unregister subdevices */
- list_for_each_entry_safe(subdev, sd, &device->active, list) {
- /*
- * host1x_subdev_unregister() will remove the client from
- * any lists, so we'll need to manually add it back to the
- * list of idle clients.
- *
- * XXX: Alternatively, perhaps don't remove the client from
- * any lists in host1x_subdev_unregister() and instead do
- * that explicitly from host1x_unregister_client()?
- */
- client = subdev->client;
-
- __host1x_subdev_unregister(device, subdev);
-
- /* add the client to the list of idle clients */
- mutex_lock(&clients_lock);
- list_add_tail(&client->list, &clients);
- mutex_unlock(&clients_lock);
+ if (device->registered) {
+ device->registered = false;
+ device_del(&device->dev);
}
- /* remove subdevices */
- list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
- host1x_subdev_del(subdev);
-
- mutex_unlock(&device->subdevs_lock);
-
- /* move clients to idle list */
- mutex_lock(&clients_lock);
- mutex_lock(&device->clients_lock);
-
- list_for_each_entry_safe(client, cl, &device->clients, list)
- list_move_tail(&client->list, &clients);
-
- mutex_unlock(&device->clients_lock);
- mutex_unlock(&clients_lock);
-
- /* finally remove the device */
- list_del_init(&device->list);
- device_unregister(&device->dev);
+ put_device(&device->dev);
}
static void host1x_attach_driver(struct host1x *host1x,
@@ -409,11 +454,11 @@ static void host1x_attach_driver(struct host1x *host1x,
}
}
- mutex_unlock(&host1x->devices_lock);
-
err = host1x_device_add(host1x, driver);
if (err < 0)
dev_err(host1x->dev, "failed to allocate device: %d\n", err);
+
+ mutex_unlock(&host1x->devices_lock);
}
static void host1x_detach_driver(struct host1x *host1x,
@@ -466,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;
@@ -483,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)
{