diff options
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/host1x/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpu/host1x/bus.c | 550 | ||||
| -rw-r--r-- | drivers/gpu/host1x/bus.h (renamed from drivers/gpu/host1x/host1x_client.h) | 24 | ||||
| -rw-r--r-- | drivers/gpu/host1x/dev.c | 60 | ||||
| -rw-r--r-- | drivers/gpu/host1x/dev.h | 9 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/bus.c | 76 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/dc.c | 30 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/drm.c | 347 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/drm.h | 31 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/gr2d.c | 60 | ||||
| -rw-r--r-- | drivers/gpu/host1x/drm/hdmi.c | 28 |
11 files changed, 853 insertions, 364 deletions
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 3b037b6e0298..7b781920c58a 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | ccflags-y = -Idrivers/gpu/host1x | 1 | ccflags-y = -Idrivers/gpu/host1x |
| 2 | 2 | ||
| 3 | host1x-y = \ | 3 | host1x-y = \ |
| 4 | bus.o \ | ||
| 4 | syncpt.o \ | 5 | syncpt.o \ |
| 5 | dev.o \ | 6 | dev.o \ |
| 6 | intr.o \ | 7 | intr.o \ |
| @@ -17,4 +18,5 @@ host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o | |||
| 17 | host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o | 18 | host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o |
| 18 | host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o | 19 | host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o |
| 19 | host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o | 20 | host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o |
| 21 | host1x-$(CONFIG_DRM_TEGRA) += drm/bus.o | ||
| 20 | obj-$(CONFIG_TEGRA_HOST1X) += host1x.o | 22 | obj-$(CONFIG_TEGRA_HOST1X) += host1x.o |
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 | |||
| 24 | static DEFINE_MUTEX(clients_lock); | ||
| 25 | static LIST_HEAD(clients); | ||
| 26 | |||
| 27 | static DEFINE_MUTEX(drivers_lock); | ||
| 28 | static LIST_HEAD(drivers); | ||
| 29 | |||
| 30 | static DEFINE_MUTEX(devices_lock); | ||
| 31 | static LIST_HEAD(devices); | ||
| 32 | |||
| 33 | struct 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 | */ | ||
| 42 | static 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 | */ | ||
| 64 | static 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 | */ | ||
| 74 | static 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 | |||
| 91 | static 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 | |||
| 122 | static 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 | |||
| 158 | static 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 | |||
| 166 | int 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 | |||
| 191 | int 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 | |||
| 216 | static 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 | |||
| 238 | static 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 | |||
| 260 | struct bus_type host1x_bus_type = { | ||
| 261 | .name = "host1x", | ||
| 262 | }; | ||
| 263 | |||
| 264 | int host1x_bus_init(void) | ||
| 265 | { | ||
| 266 | return bus_register(&host1x_bus_type); | ||
| 267 | } | ||
| 268 | |||
| 269 | void host1x_bus_exit(void) | ||
| 270 | { | ||
| 271 | bus_unregister(&host1x_bus_type); | ||
| 272 | } | ||
| 273 | |||
| 274 | static void host1x_device_release(struct device *dev) | ||
| 275 | { | ||
| 276 | struct host1x_device *device = to_host1x_device(dev); | ||
| 277 | |||
| 278 | kfree(device); | ||
| 279 | } | ||
| 280 | |||
| 281 | static 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 | */ | ||
| 344 | static 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 | |||
| 394 | static 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 | |||
| 416 | static 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 | |||
| 430 | int 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 | |||
| 448 | int 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 | |||
| 466 | int 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 | } | ||
| 485 | EXPORT_SYMBOL(host1x_driver_register); | ||
| 486 | |||
| 487 | void 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 | } | ||
| 493 | EXPORT_SYMBOL(host1x_driver_unregister); | ||
| 494 | |||
| 495 | int 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 | } | ||
| 518 | EXPORT_SYMBOL(host1x_client_register); | ||
| 519 | |||
| 520 | int 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 | } | ||
| 550 | EXPORT_SYMBOL(host1x_client_unregister); | ||
diff --git a/drivers/gpu/host1x/host1x_client.h b/drivers/gpu/host1x/bus.h index 6a0bd0268042..4099e99212c8 100644 --- a/drivers/gpu/host1x/host1x_client.h +++ b/drivers/gpu/host1x/bus.h | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2013, NVIDIA Corporation. | 2 | * Copyright (C) 2012 Avionic Design GmbH |
| 3 | * Copyright (C) 2012-2013, NVIDIA Corporation | ||
| 3 | * | 4 | * |
| 4 | * This program is free software; you can redistribute it and/or modify it | 5 | * This program is free software; you can redistribute it and/or modify it |
| 5 | * under the terms and conditions of the GNU General Public License, | 6 | * under the terms and conditions of the GNU General Public License, |
| @@ -14,22 +15,15 @@ | |||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 15 | */ | 16 | */ |
| 16 | 17 | ||
| 17 | #ifndef HOST1X_CLIENT_H | 18 | #ifndef HOST1X_BUS_H |
| 18 | #define HOST1X_CLIENT_H | 19 | #define HOST1X_BUS_H |
| 19 | 20 | ||
| 20 | struct device; | 21 | struct host1x; |
| 21 | struct platform_device; | ||
| 22 | 22 | ||
| 23 | #ifdef CONFIG_DRM_TEGRA | 23 | int host1x_bus_init(void); |
| 24 | int tegra_drm_alloc(struct platform_device *pdev); | 24 | void host1x_bus_exit(void); |
| 25 | #else | ||
| 26 | static inline int tegra_drm_alloc(struct platform_device *pdev) | ||
| 27 | { | ||
| 28 | return 0; | ||
| 29 | } | ||
| 30 | #endif | ||
| 31 | 25 | ||
| 32 | void host1x_set_drm_data(struct device *dev, void *data); | 26 | int host1x_register(struct host1x *host1x); |
| 33 | void *host1x_get_drm_data(struct device *dev); | 27 | int host1x_unregister(struct host1x *host1x); |
| 34 | 28 | ||
| 35 | #endif | 29 | #endif |
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 105aa4ed665a..de0fd552710d 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c | |||
| @@ -27,24 +27,12 @@ | |||
| 27 | #define CREATE_TRACE_POINTS | 27 | #define CREATE_TRACE_POINTS |
| 28 | #include <trace/events/host1x.h> | 28 | #include <trace/events/host1x.h> |
| 29 | 29 | ||
| 30 | #include "bus.h" | ||
| 30 | #include "dev.h" | 31 | #include "dev.h" |
| 31 | #include "intr.h" | 32 | #include "intr.h" |
| 32 | #include "channel.h" | 33 | #include "channel.h" |
| 33 | #include "debug.h" | 34 | #include "debug.h" |
| 34 | #include "hw/host1x01.h" | 35 | #include "hw/host1x01.h" |
| 35 | #include "host1x_client.h" | ||
| 36 | |||
| 37 | void host1x_set_drm_data(struct device *dev, void *data) | ||
| 38 | { | ||
| 39 | struct host1x *host1x = dev_get_drvdata(dev); | ||
| 40 | host1x->drm_data = data; | ||
| 41 | } | ||
| 42 | |||
| 43 | void *host1x_get_drm_data(struct device *dev) | ||
| 44 | { | ||
| 45 | struct host1x *host1x = dev_get_drvdata(dev); | ||
| 46 | return host1x ? host1x->drm_data : NULL; | ||
| 47 | } | ||
| 48 | 36 | ||
| 49 | void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) | 37 | void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) |
| 50 | { | 38 | { |
| @@ -114,6 +102,9 @@ static int host1x_probe(struct platform_device *pdev) | |||
| 114 | if (!host) | 102 | if (!host) |
| 115 | return -ENOMEM; | 103 | return -ENOMEM; |
| 116 | 104 | ||
| 105 | mutex_init(&host->devices_lock); | ||
| 106 | INIT_LIST_HEAD(&host->devices); | ||
| 107 | INIT_LIST_HEAD(&host->list); | ||
| 117 | host->dev = &pdev->dev; | 108 | host->dev = &pdev->dev; |
| 118 | host->info = id->data; | 109 | host->info = id->data; |
| 119 | 110 | ||
| @@ -163,10 +154,14 @@ static int host1x_probe(struct platform_device *pdev) | |||
| 163 | 154 | ||
| 164 | host1x_debug_init(host); | 155 | host1x_debug_init(host); |
| 165 | 156 | ||
| 166 | tegra_drm_alloc(pdev); | 157 | err = host1x_register(host); |
| 158 | if (err < 0) | ||
| 159 | goto fail_deinit_intr; | ||
| 167 | 160 | ||
| 168 | return 0; | 161 | return 0; |
| 169 | 162 | ||
| 163 | fail_deinit_intr: | ||
| 164 | host1x_intr_deinit(host); | ||
| 170 | fail_deinit_syncpt: | 165 | fail_deinit_syncpt: |
| 171 | host1x_syncpt_deinit(host); | 166 | host1x_syncpt_deinit(host); |
| 172 | return err; | 167 | return err; |
| @@ -176,6 +171,7 @@ static int host1x_remove(struct platform_device *pdev) | |||
| 176 | { | 171 | { |
| 177 | struct host1x *host = platform_get_drvdata(pdev); | 172 | struct host1x *host = platform_get_drvdata(pdev); |
| 178 | 173 | ||
| 174 | host1x_unregister(host); | ||
| 179 | host1x_intr_deinit(host); | 175 | host1x_intr_deinit(host); |
| 180 | host1x_syncpt_deinit(host); | 176 | host1x_syncpt_deinit(host); |
| 181 | clk_disable_unprepare(host->clk); | 177 | clk_disable_unprepare(host->clk); |
| @@ -196,46 +192,24 @@ static int __init tegra_host1x_init(void) | |||
| 196 | { | 192 | { |
| 197 | int err; | 193 | int err; |
| 198 | 194 | ||
| 199 | err = platform_driver_register(&tegra_host1x_driver); | 195 | err = host1x_bus_init(); |
| 200 | if (err < 0) | 196 | if (err < 0) |
| 201 | return err; | 197 | return err; |
| 202 | 198 | ||
| 203 | #ifdef CONFIG_DRM_TEGRA | 199 | err = platform_driver_register(&tegra_host1x_driver); |
| 204 | err = platform_driver_register(&tegra_dc_driver); | 200 | if (err < 0) { |
| 205 | if (err < 0) | 201 | host1x_bus_exit(); |
| 206 | goto unregister_host1x; | 202 | return err; |
| 207 | 203 | } | |
| 208 | err = platform_driver_register(&tegra_hdmi_driver); | ||
| 209 | if (err < 0) | ||
| 210 | goto unregister_dc; | ||
| 211 | |||
| 212 | err = platform_driver_register(&tegra_gr2d_driver); | ||
| 213 | if (err < 0) | ||
| 214 | goto unregister_hdmi; | ||
| 215 | #endif | ||
| 216 | 204 | ||
| 217 | return 0; | 205 | return 0; |
| 218 | |||
| 219 | #ifdef CONFIG_DRM_TEGRA | ||
| 220 | unregister_hdmi: | ||
| 221 | platform_driver_unregister(&tegra_hdmi_driver); | ||
| 222 | unregister_dc: | ||
| 223 | platform_driver_unregister(&tegra_dc_driver); | ||
| 224 | unregister_host1x: | ||
| 225 | platform_driver_unregister(&tegra_host1x_driver); | ||
| 226 | return err; | ||
| 227 | #endif | ||
| 228 | } | 206 | } |
| 229 | module_init(tegra_host1x_init); | 207 | module_init(tegra_host1x_init); |
| 230 | 208 | ||
| 231 | static void __exit tegra_host1x_exit(void) | 209 | static void __exit tegra_host1x_exit(void) |
| 232 | { | 210 | { |
| 233 | #ifdef CONFIG_DRM_TEGRA | ||
| 234 | platform_driver_unregister(&tegra_gr2d_driver); | ||
| 235 | platform_driver_unregister(&tegra_hdmi_driver); | ||
| 236 | platform_driver_unregister(&tegra_dc_driver); | ||
| 237 | #endif | ||
| 238 | platform_driver_unregister(&tegra_host1x_driver); | 211 | platform_driver_unregister(&tegra_host1x_driver); |
| 212 | host1x_bus_exit(); | ||
| 239 | } | 213 | } |
| 240 | module_exit(tegra_host1x_exit); | 214 | module_exit(tegra_host1x_exit); |
| 241 | 215 | ||
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index bed90a8131be..6cf689b9e17b 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h | |||
| @@ -125,7 +125,10 @@ struct host1x { | |||
| 125 | 125 | ||
| 126 | struct dentry *debugfs; | 126 | struct dentry *debugfs; |
| 127 | 127 | ||
| 128 | void *drm_data; | 128 | struct mutex devices_lock; |
| 129 | struct list_head devices; | ||
| 130 | |||
| 131 | struct list_head list; | ||
| 129 | }; | 132 | }; |
| 130 | 133 | ||
| 131 | void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); | 134 | void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); |
| @@ -301,8 +304,4 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o) | |||
| 301 | host->debug_op->show_mlocks(host, o); | 304 | host->debug_op->show_mlocks(host, o); |
| 302 | } | 305 | } |
| 303 | 306 | ||
| 304 | extern struct platform_driver tegra_dc_driver; | ||
| 305 | extern struct platform_driver tegra_hdmi_driver; | ||
| 306 | extern struct platform_driver tegra_gr2d_driver; | ||
| 307 | |||
| 308 | #endif | 307 | #endif |
diff --git a/drivers/gpu/host1x/drm/bus.c b/drivers/gpu/host1x/drm/bus.c new file mode 100644 index 000000000000..565f8f7b9a47 --- /dev/null +++ b/drivers/gpu/host1x/drm/bus.c | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2013 NVIDIA Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "drm.h" | ||
| 10 | |||
| 11 | static int drm_host1x_set_busid(struct drm_device *dev, | ||
| 12 | struct drm_master *master) | ||
| 13 | { | ||
| 14 | const char *device = dev_name(dev->dev); | ||
| 15 | const char *driver = dev->driver->name; | ||
| 16 | const char *bus = dev->dev->bus->name; | ||
| 17 | int length; | ||
| 18 | |||
| 19 | master->unique_len = strlen(bus) + 1 + strlen(device); | ||
| 20 | master->unique_size = master->unique_len; | ||
| 21 | |||
| 22 | master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); | ||
| 23 | if (!master->unique) | ||
| 24 | return -ENOMEM; | ||
| 25 | |||
| 26 | snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device); | ||
| 27 | |||
| 28 | length = strlen(driver) + 1 + master->unique_len; | ||
| 29 | |||
| 30 | dev->devname = kmalloc(length + 1, GFP_KERNEL); | ||
| 31 | if (!dev->devname) | ||
| 32 | return -ENOMEM; | ||
| 33 | |||
| 34 | snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique); | ||
| 35 | |||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static struct drm_bus drm_host1x_bus = { | ||
| 40 | .bus_type = DRIVER_BUS_HOST1X, | ||
| 41 | .set_busid = drm_host1x_set_busid, | ||
| 42 | }; | ||
| 43 | |||
| 44 | int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device) | ||
| 45 | { | ||
| 46 | struct drm_device *drm; | ||
| 47 | int ret; | ||
| 48 | |||
| 49 | INIT_LIST_HEAD(&driver->device_list); | ||
| 50 | driver->bus = &drm_host1x_bus; | ||
| 51 | |||
| 52 | drm = drm_dev_alloc(driver, &device->dev); | ||
| 53 | if (!drm) | ||
| 54 | return -ENOMEM; | ||
| 55 | |||
| 56 | ret = drm_dev_register(drm, 0); | ||
| 57 | if (ret) | ||
| 58 | goto err_free; | ||
| 59 | |||
| 60 | DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, | ||
| 61 | driver->major, driver->minor, driver->patchlevel, | ||
| 62 | driver->date, drm->primary->index); | ||
| 63 | |||
| 64 | return 0; | ||
| 65 | |||
| 66 | err_free: | ||
| 67 | drm_dev_free(drm); | ||
| 68 | return ret; | ||
| 69 | } | ||
| 70 | |||
| 71 | void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device) | ||
| 72 | { | ||
| 73 | struct tegra_drm *tegra = dev_get_drvdata(&device->dev); | ||
| 74 | |||
| 75 | drm_put_dev(tegra->drm); | ||
| 76 | } | ||
diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index 5106df08f046..588d4ba0d8cf 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include <linux/clk/tegra.h> | 11 | #include <linux/clk/tegra.h> |
| 12 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
| 13 | 13 | ||
| 14 | #include "host1x_client.h" | ||
| 15 | #include "dc.h" | 14 | #include "dc.h" |
| 16 | #include "drm.h" | 15 | #include "drm.h" |
| 17 | #include "gem.h" | 16 | #include "gem.h" |
| @@ -1040,28 +1039,28 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc) | |||
| 1040 | 1039 | ||
| 1041 | static int tegra_dc_init(struct host1x_client *client) | 1040 | static int tegra_dc_init(struct host1x_client *client) |
| 1042 | { | 1041 | { |
| 1043 | struct tegra_drm_client *drm = to_tegra_drm_client(client); | 1042 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); |
| 1044 | struct tegra_dc *dc = tegra_drm_client_to_dc(drm); | 1043 | struct tegra_dc *dc = host1x_client_to_dc(client); |
| 1045 | int err; | 1044 | int err; |
| 1046 | 1045 | ||
| 1047 | dc->pipe = drm->drm->mode_config.num_crtc; | 1046 | dc->pipe = tegra->drm->mode_config.num_crtc; |
| 1048 | 1047 | ||
| 1049 | drm_crtc_init(drm->drm, &dc->base, &tegra_crtc_funcs); | 1048 | drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs); |
| 1050 | drm_mode_crtc_set_gamma_size(&dc->base, 256); | 1049 | drm_mode_crtc_set_gamma_size(&dc->base, 256); |
| 1051 | drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); | 1050 | drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); |
| 1052 | 1051 | ||
| 1053 | err = tegra_dc_rgb_init(drm->drm, dc); | 1052 | err = tegra_dc_rgb_init(tegra->drm, dc); |
| 1054 | if (err < 0 && err != -ENODEV) { | 1053 | if (err < 0 && err != -ENODEV) { |
| 1055 | dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); | 1054 | dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); |
| 1056 | return err; | 1055 | return err; |
| 1057 | } | 1056 | } |
| 1058 | 1057 | ||
| 1059 | err = tegra_dc_add_planes(drm->drm, dc); | 1058 | err = tegra_dc_add_planes(tegra->drm, dc); |
| 1060 | if (err < 0) | 1059 | if (err < 0) |
| 1061 | return err; | 1060 | return err; |
| 1062 | 1061 | ||
| 1063 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | 1062 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
| 1064 | err = tegra_dc_debugfs_init(dc, drm->drm->primary); | 1063 | err = tegra_dc_debugfs_init(dc, tegra->drm->primary); |
| 1065 | if (err < 0) | 1064 | if (err < 0) |
| 1066 | dev_err(dc->dev, "debugfs setup failed: %d\n", err); | 1065 | dev_err(dc->dev, "debugfs setup failed: %d\n", err); |
| 1067 | } | 1066 | } |
| @@ -1079,8 +1078,7 @@ static int tegra_dc_init(struct host1x_client *client) | |||
| 1079 | 1078 | ||
| 1080 | static int tegra_dc_exit(struct host1x_client *client) | 1079 | static int tegra_dc_exit(struct host1x_client *client) |
| 1081 | { | 1080 | { |
| 1082 | struct tegra_drm_client *drm = to_tegra_drm_client(client); | 1081 | struct tegra_dc *dc = host1x_client_to_dc(client); |
| 1083 | struct tegra_dc *dc = tegra_drm_client_to_dc(drm); | ||
| 1084 | int err; | 1082 | int err; |
| 1085 | 1083 | ||
| 1086 | devm_free_irq(dc->dev, dc->irq, dc); | 1084 | devm_free_irq(dc->dev, dc->irq, dc); |
| @@ -1107,7 +1105,6 @@ static const struct host1x_client_ops dc_client_ops = { | |||
| 1107 | 1105 | ||
| 1108 | static int tegra_dc_probe(struct platform_device *pdev) | 1106 | static int tegra_dc_probe(struct platform_device *pdev) |
| 1109 | { | 1107 | { |
| 1110 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 1111 | struct resource *regs; | 1108 | struct resource *regs; |
| 1112 | struct tegra_dc *dc; | 1109 | struct tegra_dc *dc; |
| 1113 | int err; | 1110 | int err; |
| @@ -1141,9 +1138,9 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
| 1141 | return -ENXIO; | 1138 | return -ENXIO; |
| 1142 | } | 1139 | } |
| 1143 | 1140 | ||
| 1144 | INIT_LIST_HEAD(&dc->client.base.list); | 1141 | INIT_LIST_HEAD(&dc->client.list); |
| 1145 | dc->client.base.ops = &dc_client_ops; | 1142 | dc->client.ops = &dc_client_ops; |
| 1146 | dc->client.base.dev = &pdev->dev; | 1143 | dc->client.dev = &pdev->dev; |
| 1147 | 1144 | ||
| 1148 | err = tegra_dc_rgb_probe(dc); | 1145 | err = tegra_dc_rgb_probe(dc); |
| 1149 | if (err < 0 && err != -ENODEV) { | 1146 | if (err < 0 && err != -ENODEV) { |
| @@ -1151,7 +1148,7 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
| 1151 | return err; | 1148 | return err; |
| 1152 | } | 1149 | } |
| 1153 | 1150 | ||
| 1154 | err = host1x_register_client(tegra, &dc->client.base); | 1151 | err = host1x_client_register(&dc->client); |
| 1155 | if (err < 0) { | 1152 | if (err < 0) { |
| 1156 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | 1153 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", |
| 1157 | err); | 1154 | err); |
| @@ -1165,11 +1162,10 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
| 1165 | 1162 | ||
| 1166 | static int tegra_dc_remove(struct platform_device *pdev) | 1163 | static int tegra_dc_remove(struct platform_device *pdev) |
| 1167 | { | 1164 | { |
| 1168 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 1169 | struct tegra_dc *dc = platform_get_drvdata(pdev); | 1165 | struct tegra_dc *dc = platform_get_drvdata(pdev); |
| 1170 | int err; | 1166 | int err; |
| 1171 | 1167 | ||
| 1172 | err = host1x_unregister_client(tegra, &dc->client.base); | 1168 | err = host1x_client_unregister(&dc->client); |
| 1173 | if (err < 0) { | 1169 | if (err < 0) { |
| 1174 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | 1170 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", |
| 1175 | err); | 1171 | err); |
diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 1abcdbc7f4cd..c2db409bbd63 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c | |||
| @@ -7,7 +7,8 @@ | |||
| 7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include "host1x_client.h" | 10 | #include <linux/host1x.h> |
| 11 | |||
| 11 | #include "drm.h" | 12 | #include "drm.h" |
| 12 | #include "gem.h" | 13 | #include "gem.h" |
| 13 | 14 | ||
| @@ -22,239 +23,25 @@ struct tegra_drm_file { | |||
| 22 | struct list_head contexts; | 23 | struct list_head contexts; |
| 23 | }; | 24 | }; |
| 24 | 25 | ||
| 25 | struct host1x_subdev { | 26 | static int tegra_drm_load(struct drm_device *drm, unsigned long flags) |
| 26 | struct host1x_client *client; | ||
| 27 | struct device_node *np; | ||
| 28 | struct list_head list; | ||
| 29 | }; | ||
| 30 | |||
| 31 | static int host1x_subdev_add(struct tegra_drm *tegra, struct device_node *np) | ||
| 32 | { | ||
| 33 | struct host1x_subdev *subdev; | ||
| 34 | |||
| 35 | subdev = kzalloc(sizeof(*subdev), GFP_KERNEL); | ||
| 36 | if (!subdev) | ||
| 37 | return -ENOMEM; | ||
| 38 | |||
| 39 | INIT_LIST_HEAD(&subdev->list); | ||
| 40 | subdev->np = of_node_get(np); | ||
| 41 | |||
| 42 | list_add_tail(&subdev->list, &tegra->subdevs); | ||
| 43 | |||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 47 | static int host1x_subdev_register(struct tegra_drm *tegra, | ||
| 48 | struct host1x_subdev *subdev, | ||
| 49 | struct host1x_client *client) | ||
| 50 | { | ||
| 51 | mutex_lock(&tegra->subdevs_lock); | ||
| 52 | list_del_init(&subdev->list); | ||
| 53 | list_add_tail(&subdev->list, &tegra->active); | ||
| 54 | subdev->client = client; | ||
| 55 | mutex_unlock(&tegra->subdevs_lock); | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | static int host1x_subdev_unregister(struct tegra_drm *tegra, | ||
| 61 | struct host1x_subdev *subdev) | ||
| 62 | { | ||
| 63 | mutex_lock(&tegra->subdevs_lock); | ||
| 64 | list_del_init(&subdev->list); | ||
| 65 | mutex_unlock(&tegra->subdevs_lock); | ||
| 66 | |||
| 67 | of_node_put(subdev->np); | ||
| 68 | kfree(subdev); | ||
| 69 | |||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int tegra_parse_dt(struct tegra_drm *tegra) | ||
| 74 | { | ||
| 75 | static const char * const compat[] = { | ||
| 76 | "nvidia,tegra20-dc", | ||
| 77 | "nvidia,tegra20-hdmi", | ||
| 78 | "nvidia,tegra20-gr2d", | ||
| 79 | "nvidia,tegra30-dc", | ||
| 80 | "nvidia,tegra30-hdmi", | ||
| 81 | "nvidia,tegra30-gr2d", | ||
| 82 | }; | ||
| 83 | unsigned int i; | ||
| 84 | int err; | ||
| 85 | |||
| 86 | for (i = 0; i < ARRAY_SIZE(compat); i++) { | ||
| 87 | struct device_node *np; | ||
| 88 | |||
| 89 | for_each_child_of_node(tegra->dev->of_node, np) { | ||
| 90 | if (of_device_is_compatible(np, compat[i]) && | ||
| 91 | of_device_is_available(np)) { | ||
| 92 | err = host1x_subdev_add(tegra, np); | ||
| 93 | if (err < 0) | ||
| 94 | return err; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | int tegra_drm_alloc(struct platform_device *pdev) | ||
| 103 | { | 27 | { |
| 28 | struct host1x_device *device = to_host1x_device(drm->dev); | ||
| 104 | struct tegra_drm *tegra; | 29 | struct tegra_drm *tegra; |
| 105 | int err; | 30 | int err; |
| 106 | 31 | ||
| 107 | tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); | 32 | tegra = kzalloc(sizeof(*tegra), GFP_KERNEL); |
| 108 | if (!tegra) | 33 | if (!tegra) |
| 109 | return -ENOMEM; | 34 | return -ENOMEM; |
| 110 | 35 | ||
| 111 | mutex_init(&tegra->subdevs_lock); | 36 | dev_set_drvdata(drm->dev, tegra); |
| 112 | INIT_LIST_HEAD(&tegra->subdevs); | ||
| 113 | INIT_LIST_HEAD(&tegra->active); | ||
| 114 | mutex_init(&tegra->clients_lock); | 37 | mutex_init(&tegra->clients_lock); |
| 115 | INIT_LIST_HEAD(&tegra->clients); | 38 | INIT_LIST_HEAD(&tegra->clients); |
| 116 | tegra->dev = &pdev->dev; | ||
| 117 | |||
| 118 | err = tegra_parse_dt(tegra); | ||
| 119 | if (err < 0) { | ||
| 120 | dev_err(&pdev->dev, "failed to parse DT: %d\n", err); | ||
| 121 | return err; | ||
| 122 | } | ||
| 123 | |||
| 124 | host1x_set_drm_data(&pdev->dev, tegra); | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm) | ||
| 130 | { | ||
| 131 | struct host1x_client *client; | ||
| 132 | int err; | ||
| 133 | |||
| 134 | mutex_lock(&tegra->clients_lock); | ||
| 135 | |||
| 136 | list_for_each_entry(client, &tegra->clients, list) { | ||
| 137 | struct tegra_drm_client *tdc = to_tegra_drm_client(client); | ||
| 138 | |||
| 139 | /* associate client with DRM device */ | ||
| 140 | tdc->drm = drm; | ||
| 141 | |||
| 142 | if (client->ops && client->ops->init) { | ||
| 143 | err = client->ops->init(client); | ||
| 144 | if (err < 0) { | ||
| 145 | dev_err(tegra->dev, | ||
| 146 | "DRM setup failed for %s: %d\n", | ||
| 147 | dev_name(client->dev), err); | ||
| 148 | mutex_unlock(&tegra->clients_lock); | ||
| 149 | return err; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | mutex_unlock(&tegra->clients_lock); | ||
| 155 | |||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | int tegra_drm_exit(struct tegra_drm *tegra) | ||
| 160 | { | ||
| 161 | struct host1x_client *client; | ||
| 162 | struct platform_device *pdev; | ||
| 163 | int err; | ||
| 164 | |||
| 165 | if (!tegra->drm) | ||
| 166 | return 0; | ||
| 167 | |||
| 168 | mutex_lock(&tegra->clients_lock); | ||
| 169 | |||
| 170 | list_for_each_entry_reverse(client, &tegra->clients, list) { | ||
| 171 | if (client->ops && client->ops->exit) { | ||
| 172 | err = client->ops->exit(client); | ||
| 173 | if (err < 0) { | ||
| 174 | dev_err(tegra->dev, | ||
| 175 | "DRM cleanup failed for %s: %d\n", | ||
| 176 | dev_name(client->dev), err); | ||
| 177 | mutex_unlock(&tegra->clients_lock); | ||
| 178 | return err; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | mutex_unlock(&tegra->clients_lock); | ||
| 184 | |||
| 185 | pdev = to_platform_device(tegra->dev); | ||
| 186 | drm_platform_exit(&tegra_drm_driver, pdev); | ||
| 187 | tegra->drm = NULL; | ||
| 188 | |||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | int host1x_register_client(struct tegra_drm *tegra, | ||
| 193 | struct host1x_client *client) | ||
| 194 | { | ||
| 195 | struct host1x_subdev *subdev, *tmp; | ||
| 196 | int err; | ||
| 197 | |||
| 198 | mutex_lock(&tegra->clients_lock); | ||
| 199 | list_add_tail(&client->list, &tegra->clients); | ||
| 200 | mutex_unlock(&tegra->clients_lock); | ||
| 201 | |||
| 202 | list_for_each_entry_safe(subdev, tmp, &tegra->subdevs, list) | ||
| 203 | if (subdev->np == client->dev->of_node) | ||
| 204 | host1x_subdev_register(tegra, subdev, client); | ||
| 205 | |||
| 206 | if (list_empty(&tegra->subdevs)) { | ||
| 207 | struct platform_device *pdev = to_platform_device(tegra->dev); | ||
| 208 | |||
| 209 | err = drm_platform_init(&tegra_drm_driver, pdev); | ||
| 210 | if (err < 0) { | ||
| 211 | dev_err(tegra->dev, "drm_platform_init(): %d\n", err); | ||
| 212 | return err; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | int host1x_unregister_client(struct tegra_drm *tegra, | ||
| 220 | struct host1x_client *client) | ||
| 221 | { | ||
| 222 | struct host1x_subdev *subdev, *tmp; | ||
| 223 | int err; | ||
| 224 | |||
| 225 | list_for_each_entry_safe(subdev, tmp, &tegra->active, list) { | ||
| 226 | if (subdev->client == client) { | ||
| 227 | err = tegra_drm_exit(tegra); | ||
| 228 | if (err < 0) { | ||
| 229 | dev_err(tegra->dev, "tegra_drm_exit(): %d\n", | ||
| 230 | err); | ||
| 231 | return err; | ||
| 232 | } | ||
| 233 | |||
| 234 | host1x_subdev_unregister(tegra, subdev); | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | mutex_lock(&tegra->clients_lock); | ||
| 240 | list_del_init(&client->list); | ||
| 241 | mutex_unlock(&tegra->clients_lock); | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | ||
| 247 | { | ||
| 248 | struct tegra_drm *tegra; | ||
| 249 | int err; | ||
| 250 | |||
| 251 | tegra = host1x_get_drm_data(drm->dev); | ||
| 252 | drm->dev_private = tegra; | 39 | drm->dev_private = tegra; |
| 253 | tegra->drm = drm; | 40 | tegra->drm = drm; |
| 254 | 41 | ||
| 255 | drm_mode_config_init(drm); | 42 | drm_mode_config_init(drm); |
| 256 | 43 | ||
| 257 | err = tegra_drm_init(tegra, drm); | 44 | err = host1x_device_init(device); |
| 258 | if (err < 0) | 45 | if (err < 0) |
| 259 | return err; | 46 | return err; |
| 260 | 47 | ||
| @@ -280,9 +67,16 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
| 280 | 67 | ||
| 281 | static int tegra_drm_unload(struct drm_device *drm) | 68 | static int tegra_drm_unload(struct drm_device *drm) |
| 282 | { | 69 | { |
| 70 | struct host1x_device *device = to_host1x_device(drm->dev); | ||
| 71 | int err; | ||
| 72 | |||
| 283 | drm_kms_helper_poll_fini(drm); | 73 | drm_kms_helper_poll_fini(drm); |
| 284 | tegra_drm_fb_exit(drm); | 74 | tegra_drm_fb_exit(drm); |
| 285 | 75 | ||
| 76 | err = host1x_device_exit(device); | ||
| 77 | if (err < 0) | ||
| 78 | return err; | ||
| 79 | |||
| 286 | drm_mode_config_cleanup(drm); | 80 | drm_mode_config_cleanup(drm); |
| 287 | 81 | ||
| 288 | return 0; | 82 | return 0; |
| @@ -370,10 +164,11 @@ static int tegra_gem_mmap(struct drm_device *drm, void *data, | |||
| 370 | static int tegra_syncpt_read(struct drm_device *drm, void *data, | 164 | static int tegra_syncpt_read(struct drm_device *drm, void *data, |
| 371 | struct drm_file *file) | 165 | struct drm_file *file) |
| 372 | { | 166 | { |
| 167 | struct host1x *host = dev_get_drvdata(drm->dev->parent); | ||
| 373 | struct drm_tegra_syncpt_read *args = data; | 168 | struct drm_tegra_syncpt_read *args = data; |
| 374 | struct host1x *host = dev_get_drvdata(drm->dev); | 169 | struct host1x_syncpt *sp; |
| 375 | struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); | ||
| 376 | 170 | ||
| 171 | sp = host1x_syncpt_get(host, args->id); | ||
| 377 | if (!sp) | 172 | if (!sp) |
| 378 | return -EINVAL; | 173 | return -EINVAL; |
| 379 | 174 | ||
| @@ -384,10 +179,11 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data, | |||
| 384 | static int tegra_syncpt_incr(struct drm_device *drm, void *data, | 179 | static int tegra_syncpt_incr(struct drm_device *drm, void *data, |
| 385 | struct drm_file *file) | 180 | struct drm_file *file) |
| 386 | { | 181 | { |
| 182 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); | ||
| 387 | struct drm_tegra_syncpt_incr *args = data; | 183 | struct drm_tegra_syncpt_incr *args = data; |
| 388 | struct host1x *host = dev_get_drvdata(drm->dev); | 184 | struct host1x_syncpt *sp; |
| 389 | struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); | ||
| 390 | 185 | ||
| 186 | sp = host1x_syncpt_get(host1x, args->id); | ||
| 391 | if (!sp) | 187 | if (!sp) |
| 392 | return -EINVAL; | 188 | return -EINVAL; |
| 393 | 189 | ||
| @@ -397,10 +193,11 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data, | |||
| 397 | static int tegra_syncpt_wait(struct drm_device *drm, void *data, | 193 | static int tegra_syncpt_wait(struct drm_device *drm, void *data, |
| 398 | struct drm_file *file) | 194 | struct drm_file *file) |
| 399 | { | 195 | { |
| 196 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); | ||
| 400 | struct drm_tegra_syncpt_wait *args = data; | 197 | struct drm_tegra_syncpt_wait *args = data; |
| 401 | struct host1x *host = dev_get_drvdata(drm->dev); | 198 | struct host1x_syncpt *sp; |
| 402 | struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); | ||
| 403 | 199 | ||
| 200 | sp = host1x_syncpt_get(host1x, args->id); | ||
| 404 | if (!sp) | 201 | if (!sp) |
| 405 | return -EINVAL; | 202 | return -EINVAL; |
| 406 | 203 | ||
| @@ -422,7 +219,7 @@ static int tegra_open_channel(struct drm_device *drm, void *data, | |||
| 422 | if (!context) | 219 | if (!context) |
| 423 | return -ENOMEM; | 220 | return -ENOMEM; |
| 424 | 221 | ||
| 425 | list_for_each_entry(client, &tegra->clients, base.list) | 222 | list_for_each_entry(client, &tegra->clients, list) |
| 426 | if (client->base.class == args->client) { | 223 | if (client->base.class == args->client) { |
| 427 | err = client->ops->open_channel(client, context); | 224 | err = client->ops->open_channel(client, context); |
| 428 | if (err) | 225 | if (err) |
| @@ -441,8 +238,8 @@ static int tegra_open_channel(struct drm_device *drm, void *data, | |||
| 441 | static int tegra_close_channel(struct drm_device *drm, void *data, | 238 | static int tegra_close_channel(struct drm_device *drm, void *data, |
| 442 | struct drm_file *file) | 239 | struct drm_file *file) |
| 443 | { | 240 | { |
| 444 | struct drm_tegra_close_channel *args = data; | ||
| 445 | struct tegra_drm_file *fpriv = file->driver_priv; | 241 | struct tegra_drm_file *fpriv = file->driver_priv; |
| 242 | struct drm_tegra_close_channel *args = data; | ||
| 446 | struct tegra_drm_context *context; | 243 | struct tegra_drm_context *context; |
| 447 | 244 | ||
| 448 | context = tegra_drm_get_context(args->context); | 245 | context = tegra_drm_get_context(args->context); |
| @@ -652,3 +449,97 @@ struct drm_driver tegra_drm_driver = { | |||
| 652 | .minor = DRIVER_MINOR, | 449 | .minor = DRIVER_MINOR, |
| 653 | .patchlevel = DRIVER_PATCHLEVEL, | 450 | .patchlevel = DRIVER_PATCHLEVEL, |
| 654 | }; | 451 | }; |
| 452 | |||
| 453 | int tegra_drm_register_client(struct tegra_drm *tegra, | ||
| 454 | struct tegra_drm_client *client) | ||
| 455 | { | ||
| 456 | mutex_lock(&tegra->clients_lock); | ||
| 457 | list_add_tail(&client->list, &tegra->clients); | ||
| 458 | mutex_unlock(&tegra->clients_lock); | ||
| 459 | |||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | int tegra_drm_unregister_client(struct tegra_drm *tegra, | ||
| 464 | struct tegra_drm_client *client) | ||
| 465 | { | ||
| 466 | mutex_lock(&tegra->clients_lock); | ||
| 467 | list_del_init(&client->list); | ||
| 468 | mutex_unlock(&tegra->clients_lock); | ||
| 469 | |||
| 470 | return 0; | ||
| 471 | } | ||
| 472 | |||
| 473 | static int host1x_drm_probe(struct host1x_device *device) | ||
| 474 | { | ||
| 475 | return drm_host1x_init(&tegra_drm_driver, device); | ||
| 476 | } | ||
| 477 | |||
| 478 | static int host1x_drm_remove(struct host1x_device *device) | ||
| 479 | { | ||
| 480 | drm_host1x_exit(&tegra_drm_driver, device); | ||
| 481 | |||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | static const struct of_device_id host1x_drm_subdevs[] = { | ||
| 486 | { .compatible = "nvidia,tegra20-dc", }, | ||
| 487 | { .compatible = "nvidia,tegra20-hdmi", }, | ||
| 488 | { .compatible = "nvidia,tegra20-gr2d", }, | ||
| 489 | { .compatible = "nvidia,tegra30-dc", }, | ||
| 490 | { .compatible = "nvidia,tegra30-hdmi", }, | ||
| 491 | { .compatible = "nvidia,tegra30-gr2d", }, | ||
| 492 | { /* sentinel */ } | ||
| 493 | }; | ||
| 494 | |||
| 495 | static struct host1x_driver host1x_drm_driver = { | ||
| 496 | .name = "drm", | ||
| 497 | .probe = host1x_drm_probe, | ||
| 498 | .remove = host1x_drm_remove, | ||
| 499 | .subdevs = host1x_drm_subdevs, | ||
| 500 | }; | ||
| 501 | |||
| 502 | static int __init host1x_drm_init(void) | ||
| 503 | { | ||
| 504 | int err; | ||
| 505 | |||
| 506 | err = host1x_driver_register(&host1x_drm_driver); | ||
| 507 | if (err < 0) | ||
| 508 | return err; | ||
| 509 | |||
| 510 | err = platform_driver_register(&tegra_dc_driver); | ||
| 511 | if (err < 0) | ||
| 512 | goto unregister_host1x; | ||
| 513 | |||
| 514 | err = platform_driver_register(&tegra_hdmi_driver); | ||
| 515 | if (err < 0) | ||
| 516 | goto unregister_dc; | ||
| 517 | |||
| 518 | err = platform_driver_register(&tegra_gr2d_driver); | ||
| 519 | if (err < 0) | ||
| 520 | goto unregister_hdmi; | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | |||
| 524 | unregister_hdmi: | ||
| 525 | platform_driver_unregister(&tegra_hdmi_driver); | ||
| 526 | unregister_dc: | ||
| 527 | platform_driver_unregister(&tegra_dc_driver); | ||
| 528 | unregister_host1x: | ||
| 529 | host1x_driver_unregister(&host1x_drm_driver); | ||
| 530 | return err; | ||
| 531 | } | ||
| 532 | module_init(host1x_drm_init); | ||
| 533 | |||
| 534 | static void __exit host1x_drm_exit(void) | ||
| 535 | { | ||
| 536 | platform_driver_unregister(&tegra_gr2d_driver); | ||
| 537 | platform_driver_unregister(&tegra_hdmi_driver); | ||
| 538 | platform_driver_unregister(&tegra_dc_driver); | ||
| 539 | host1x_driver_unregister(&host1x_drm_driver); | ||
| 540 | } | ||
| 541 | module_exit(host1x_drm_exit); | ||
| 542 | |||
| 543 | MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); | ||
| 544 | MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); | ||
| 545 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 8c26c6b1f5e1..25522e23c7b8 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h | |||
| @@ -32,11 +32,6 @@ struct tegra_fbdev { | |||
| 32 | 32 | ||
| 33 | struct tegra_drm { | 33 | struct tegra_drm { |
| 34 | struct drm_device *drm; | 34 | struct drm_device *drm; |
| 35 | struct device *dev; | ||
| 36 | |||
| 37 | struct mutex subdevs_lock; | ||
| 38 | struct list_head subdevs; | ||
| 39 | struct list_head active; | ||
| 40 | 35 | ||
| 41 | struct mutex clients_lock; | 36 | struct mutex clients_lock; |
| 42 | struct list_head clients; | 37 | struct list_head clients; |
| @@ -63,29 +58,29 @@ struct tegra_drm_client_ops { | |||
| 63 | 58 | ||
| 64 | struct tegra_drm_client { | 59 | struct tegra_drm_client { |
| 65 | struct host1x_client base; | 60 | struct host1x_client base; |
| 66 | struct drm_device *drm; | 61 | struct list_head list; |
| 67 | 62 | ||
| 68 | const struct tegra_drm_client_ops *ops; | 63 | const struct tegra_drm_client_ops *ops; |
| 69 | }; | 64 | }; |
| 70 | 65 | ||
| 71 | static inline struct tegra_drm_client * | 66 | static inline struct tegra_drm_client * |
| 72 | to_tegra_drm_client(struct host1x_client *client) | 67 | host1x_to_drm_client(struct host1x_client *client) |
| 73 | { | 68 | { |
| 74 | return container_of(client, struct tegra_drm_client, base); | 69 | return container_of(client, struct tegra_drm_client, base); |
| 75 | } | 70 | } |
| 76 | 71 | ||
| 72 | extern int tegra_drm_register_client(struct tegra_drm *tegra, | ||
| 73 | struct tegra_drm_client *client); | ||
| 74 | extern int tegra_drm_unregister_client(struct tegra_drm *tegra, | ||
| 75 | struct tegra_drm_client *client); | ||
| 76 | |||
| 77 | extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); | 77 | extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); |
| 78 | extern int tegra_drm_exit(struct tegra_drm *tegra); | 78 | extern int tegra_drm_exit(struct tegra_drm *tegra); |
| 79 | 79 | ||
| 80 | extern int host1x_register_client(struct tegra_drm *tegra, | ||
| 81 | struct host1x_client *client); | ||
| 82 | extern int host1x_unregister_client(struct tegra_drm *tegra, | ||
| 83 | struct host1x_client *client); | ||
| 84 | |||
| 85 | struct tegra_output; | 80 | struct tegra_output; |
| 86 | 81 | ||
| 87 | struct tegra_dc { | 82 | struct tegra_dc { |
| 88 | struct tegra_drm_client client; | 83 | struct host1x_client client; |
| 89 | struct device *dev; | 84 | struct device *dev; |
| 90 | spinlock_t lock; | 85 | spinlock_t lock; |
| 91 | 86 | ||
| @@ -109,7 +104,7 @@ struct tegra_dc { | |||
| 109 | }; | 104 | }; |
| 110 | 105 | ||
| 111 | static inline struct tegra_dc * | 106 | static inline struct tegra_dc * |
| 112 | tegra_drm_client_to_dc(struct tegra_drm_client *client) | 107 | host1x_client_to_dc(struct host1x_client *client) |
| 113 | { | 108 | { |
| 114 | return container_of(client, struct tegra_dc, client); | 109 | return container_of(client, struct tegra_dc, client); |
| 115 | } | 110 | } |
| @@ -235,6 +230,10 @@ static inline int tegra_output_check_mode(struct tegra_output *output, | |||
| 235 | return output ? -ENOSYS : -EINVAL; | 230 | return output ? -ENOSYS : -EINVAL; |
| 236 | } | 231 | } |
| 237 | 232 | ||
| 233 | /* from bus.c */ | ||
| 234 | int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device); | ||
| 235 | void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device); | ||
| 236 | |||
| 238 | /* from rgb.c */ | 237 | /* from rgb.c */ |
| 239 | extern int tegra_dc_rgb_probe(struct tegra_dc *dc); | 238 | extern int tegra_dc_rgb_probe(struct tegra_dc *dc); |
| 240 | extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); | 239 | extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); |
| @@ -252,6 +251,8 @@ extern int tegra_drm_fb_init(struct drm_device *drm); | |||
| 252 | extern void tegra_drm_fb_exit(struct drm_device *drm); | 251 | extern void tegra_drm_fb_exit(struct drm_device *drm); |
| 253 | extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); | 252 | extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); |
| 254 | 253 | ||
| 255 | extern struct drm_driver tegra_drm_driver; | 254 | extern struct platform_driver tegra_dc_driver; |
| 255 | extern struct platform_driver tegra_hdmi_driver; | ||
| 256 | extern struct platform_driver tegra_gr2d_driver; | ||
| 256 | 257 | ||
| 257 | #endif /* HOST1X_DRM_H */ | 258 | #endif /* HOST1X_DRM_H */ |
diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c index dfb822428ca0..4e407e30da1c 100644 --- a/drivers/gpu/host1x/drm/gr2d.c +++ b/drivers/gpu/host1x/drm/gr2d.c | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
| 18 | 18 | ||
| 19 | #include "host1x_client.h" | ||
| 20 | #include "drm.h" | 19 | #include "drm.h" |
| 21 | #include "gem.h" | 20 | #include "gem.h" |
| 22 | 21 | ||
| @@ -35,19 +34,45 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client) | |||
| 35 | return container_of(client, struct gr2d, client); | 34 | return container_of(client, struct gr2d, client); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | static int gr2d_client_init(struct host1x_client *client) | 37 | static int gr2d_init(struct host1x_client *client) |
| 39 | { | 38 | { |
| 40 | return 0; | 39 | struct tegra_drm_client *drm = host1x_to_drm_client(client); |
| 40 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); | ||
| 41 | struct gr2d *gr2d = to_gr2d(drm); | ||
| 42 | |||
| 43 | gr2d->channel = host1x_channel_request(client->dev); | ||
| 44 | if (!gr2d->channel) | ||
| 45 | return -ENOMEM; | ||
| 46 | |||
| 47 | client->syncpts[0] = host1x_syncpt_request(client->dev, false); | ||
| 48 | if (!client->syncpts[0]) { | ||
| 49 | host1x_channel_free(gr2d->channel); | ||
| 50 | return -ENOMEM; | ||
| 51 | } | ||
| 52 | |||
| 53 | return tegra_drm_register_client(tegra, drm); | ||
| 41 | } | 54 | } |
| 42 | 55 | ||
| 43 | static int gr2d_client_exit(struct host1x_client *client) | 56 | static int gr2d_exit(struct host1x_client *client) |
| 44 | { | 57 | { |
| 58 | struct tegra_drm_client *drm = host1x_to_drm_client(client); | ||
| 59 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); | ||
| 60 | struct gr2d *gr2d = to_gr2d(drm); | ||
| 61 | int err; | ||
| 62 | |||
| 63 | err = tegra_drm_unregister_client(tegra, drm); | ||
| 64 | if (err < 0) | ||
| 65 | return err; | ||
| 66 | |||
| 67 | host1x_syncpt_free(client->syncpts[0]); | ||
| 68 | host1x_channel_free(gr2d->channel); | ||
| 69 | |||
| 45 | return 0; | 70 | return 0; |
| 46 | } | 71 | } |
| 47 | 72 | ||
| 48 | static const struct host1x_client_ops gr2d_client_ops = { | 73 | static const struct host1x_client_ops gr2d_client_ops = { |
| 49 | .init = gr2d_client_init, | 74 | .init = gr2d_init, |
| 50 | .exit = gr2d_client_exit, | 75 | .exit = gr2d_exit, |
| 51 | }; | 76 | }; |
| 52 | 77 | ||
| 53 | static int gr2d_open_channel(struct tegra_drm_client *client, | 78 | static int gr2d_open_channel(struct tegra_drm_client *client, |
| @@ -240,7 +265,6 @@ static const u32 gr2d_addr_regs[] = { | |||
| 240 | 265 | ||
| 241 | static int gr2d_probe(struct platform_device *pdev) | 266 | static int gr2d_probe(struct platform_device *pdev) |
| 242 | { | 267 | { |
| 243 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 244 | struct device *dev = &pdev->dev; | 268 | struct device *dev = &pdev->dev; |
| 245 | struct host1x_syncpt **syncpts; | 269 | struct host1x_syncpt **syncpts; |
| 246 | struct gr2d *gr2d; | 270 | struct gr2d *gr2d; |
| @@ -267,25 +291,17 @@ static int gr2d_probe(struct platform_device *pdev) | |||
| 267 | return err; | 291 | return err; |
| 268 | } | 292 | } |
| 269 | 293 | ||
| 270 | gr2d->channel = host1x_channel_request(dev); | ||
| 271 | if (!gr2d->channel) | ||
| 272 | return -ENOMEM; | ||
| 273 | |||
| 274 | *syncpts = host1x_syncpt_request(dev, false); | ||
| 275 | if (!(*syncpts)) { | ||
| 276 | host1x_channel_free(gr2d->channel); | ||
| 277 | return -ENOMEM; | ||
| 278 | } | ||
| 279 | |||
| 280 | INIT_LIST_HEAD(&gr2d->client.base.list); | 294 | INIT_LIST_HEAD(&gr2d->client.base.list); |
| 281 | gr2d->client.base.ops = &gr2d_client_ops; | 295 | gr2d->client.base.ops = &gr2d_client_ops; |
| 282 | gr2d->client.base.dev = dev; | 296 | gr2d->client.base.dev = dev; |
| 283 | gr2d->client.base.class = HOST1X_CLASS_GR2D; | 297 | gr2d->client.base.class = HOST1X_CLASS_GR2D; |
| 284 | gr2d->client.base.syncpts = syncpts; | 298 | gr2d->client.base.syncpts = syncpts; |
| 285 | gr2d->client.base.num_syncpts = 1; | 299 | gr2d->client.base.num_syncpts = 1; |
| 300 | |||
| 301 | INIT_LIST_HEAD(&gr2d->client.list); | ||
| 286 | gr2d->client.ops = &gr2d_ops; | 302 | gr2d->client.ops = &gr2d_ops; |
| 287 | 303 | ||
| 288 | err = host1x_register_client(tegra, &gr2d->client.base); | 304 | err = host1x_client_register(&gr2d->client.base); |
| 289 | if (err < 0) { | 305 | if (err < 0) { |
| 290 | dev_err(dev, "failed to register host1x client: %d\n", err); | 306 | dev_err(dev, "failed to register host1x client: %d\n", err); |
| 291 | return err; | 307 | return err; |
| @@ -302,22 +318,16 @@ static int gr2d_probe(struct platform_device *pdev) | |||
| 302 | 318 | ||
| 303 | static int gr2d_remove(struct platform_device *pdev) | 319 | static int gr2d_remove(struct platform_device *pdev) |
| 304 | { | 320 | { |
| 305 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 306 | struct gr2d *gr2d = platform_get_drvdata(pdev); | 321 | struct gr2d *gr2d = platform_get_drvdata(pdev); |
| 307 | unsigned int i; | ||
| 308 | int err; | 322 | int err; |
| 309 | 323 | ||
| 310 | err = host1x_unregister_client(tegra, &gr2d->client.base); | 324 | err = host1x_client_unregister(&gr2d->client.base); |
| 311 | if (err < 0) { | 325 | if (err < 0) { |
| 312 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | 326 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", |
| 313 | err); | 327 | err); |
| 314 | return err; | 328 | return err; |
| 315 | } | 329 | } |
| 316 | 330 | ||
| 317 | for (i = 0; i < gr2d->client.base.num_syncpts; i++) | ||
| 318 | host1x_syncpt_free(gr2d->client.base.syncpts[i]); | ||
| 319 | |||
| 320 | host1x_channel_free(gr2d->channel); | ||
| 321 | clk_disable_unprepare(gr2d->clk); | 331 | clk_disable_unprepare(gr2d->clk); |
| 322 | 332 | ||
| 323 | return 0; | 333 | return 0; |
diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index a2370045993c..f5663d15670b 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c | |||
| @@ -13,13 +13,12 @@ | |||
| 13 | #include <linux/hdmi.h> | 13 | #include <linux/hdmi.h> |
| 14 | #include <linux/regulator/consumer.h> | 14 | #include <linux/regulator/consumer.h> |
| 15 | 15 | ||
| 16 | #include "host1x_client.h" | ||
| 17 | #include "hdmi.h" | 16 | #include "hdmi.h" |
| 18 | #include "drm.h" | 17 | #include "drm.h" |
| 19 | #include "dc.h" | 18 | #include "dc.h" |
| 20 | 19 | ||
| 21 | struct tegra_hdmi { | 20 | struct tegra_hdmi { |
| 22 | struct tegra_drm_client client; | 21 | struct host1x_client client; |
| 23 | struct tegra_output output; | 22 | struct tegra_output output; |
| 24 | struct device *dev; | 23 | struct device *dev; |
| 25 | 24 | ||
| @@ -43,7 +42,7 @@ struct tegra_hdmi { | |||
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| 45 | static inline struct tegra_hdmi * | 44 | static inline struct tegra_hdmi * |
| 46 | tegra_drm_client_to_hdmi(struct tegra_drm_client *client) | 45 | host1x_client_to_hdmi(struct host1x_client *client) |
| 47 | { | 46 | { |
| 48 | return container_of(client, struct tegra_hdmi, client); | 47 | return container_of(client, struct tegra_hdmi, client); |
| 49 | } | 48 | } |
| @@ -1118,22 +1117,22 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) | |||
| 1118 | 1117 | ||
| 1119 | static int tegra_hdmi_init(struct host1x_client *client) | 1118 | static int tegra_hdmi_init(struct host1x_client *client) |
| 1120 | { | 1119 | { |
| 1121 | struct tegra_drm_client *drm = to_tegra_drm_client(client); | 1120 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); |
| 1122 | struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); | 1121 | struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
| 1123 | int err; | 1122 | int err; |
| 1124 | 1123 | ||
| 1125 | hdmi->output.type = TEGRA_OUTPUT_HDMI; | 1124 | hdmi->output.type = TEGRA_OUTPUT_HDMI; |
| 1126 | hdmi->output.dev = client->dev; | 1125 | hdmi->output.dev = client->dev; |
| 1127 | hdmi->output.ops = &hdmi_ops; | 1126 | hdmi->output.ops = &hdmi_ops; |
| 1128 | 1127 | ||
| 1129 | err = tegra_output_init(drm->drm, &hdmi->output); | 1128 | err = tegra_output_init(tegra->drm, &hdmi->output); |
| 1130 | if (err < 0) { | 1129 | if (err < 0) { |
| 1131 | dev_err(client->dev, "output setup failed: %d\n", err); | 1130 | dev_err(client->dev, "output setup failed: %d\n", err); |
| 1132 | return err; | 1131 | return err; |
| 1133 | } | 1132 | } |
| 1134 | 1133 | ||
| 1135 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | 1134 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
| 1136 | err = tegra_hdmi_debugfs_init(hdmi, drm->drm->primary); | 1135 | err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary); |
| 1137 | if (err < 0) | 1136 | if (err < 0) |
| 1138 | dev_err(client->dev, "debugfs setup failed: %d\n", err); | 1137 | dev_err(client->dev, "debugfs setup failed: %d\n", err); |
| 1139 | } | 1138 | } |
| @@ -1143,8 +1142,7 @@ static int tegra_hdmi_init(struct host1x_client *client) | |||
| 1143 | 1142 | ||
| 1144 | static int tegra_hdmi_exit(struct host1x_client *client) | 1143 | static int tegra_hdmi_exit(struct host1x_client *client) |
| 1145 | { | 1144 | { |
| 1146 | struct tegra_drm_client *drm = to_tegra_drm_client(client); | 1145 | struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
| 1147 | struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); | ||
| 1148 | int err; | 1146 | int err; |
| 1149 | 1147 | ||
| 1150 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | 1148 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
| @@ -1176,7 +1174,6 @@ static const struct host1x_client_ops hdmi_client_ops = { | |||
| 1176 | 1174 | ||
| 1177 | static int tegra_hdmi_probe(struct platform_device *pdev) | 1175 | static int tegra_hdmi_probe(struct platform_device *pdev) |
| 1178 | { | 1176 | { |
| 1179 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 1180 | struct tegra_hdmi *hdmi; | 1177 | struct tegra_hdmi *hdmi; |
| 1181 | struct resource *regs; | 1178 | struct resource *regs; |
| 1182 | int err; | 1179 | int err; |
| @@ -1247,11 +1244,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
| 1247 | 1244 | ||
| 1248 | hdmi->irq = err; | 1245 | hdmi->irq = err; |
| 1249 | 1246 | ||
| 1250 | INIT_LIST_HEAD(&hdmi->client.base.list); | 1247 | INIT_LIST_HEAD(&hdmi->client.list); |
| 1251 | hdmi->client.base.ops = &hdmi_client_ops; | 1248 | hdmi->client.ops = &hdmi_client_ops; |
| 1252 | hdmi->client.base.dev = &pdev->dev; | 1249 | hdmi->client.dev = &pdev->dev; |
| 1253 | 1250 | ||
| 1254 | err = host1x_register_client(tegra, &hdmi->client.base); | 1251 | err = host1x_client_register(&hdmi->client); |
| 1255 | if (err < 0) { | 1252 | if (err < 0) { |
| 1256 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | 1253 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", |
| 1257 | err); | 1254 | err); |
| @@ -1265,11 +1262,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
| 1265 | 1262 | ||
| 1266 | static int tegra_hdmi_remove(struct platform_device *pdev) | 1263 | static int tegra_hdmi_remove(struct platform_device *pdev) |
| 1267 | { | 1264 | { |
| 1268 | struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); | ||
| 1269 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); | 1265 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); |
| 1270 | int err; | 1266 | int err; |
| 1271 | 1267 | ||
| 1272 | err = host1x_unregister_client(tegra, &hdmi->client.base); | 1268 | err = host1x_client_unregister(&hdmi->client); |
| 1273 | if (err < 0) { | 1269 | if (err < 0) { |
| 1274 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | 1270 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", |
| 1275 | err); | 1271 | err); |
