2 * Copyright (C) 2012 Avionic Design GmbH
3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
10 #include <linux/clk.h>
11 #include <linux/err.h>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
18 struct host1x_drm_client {
19 struct host1x_client *client;
20 struct device_node *np;
21 struct list_head list;
24 static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np)
26 struct host1x_drm_client *client;
28 client = kzalloc(sizeof(*client), GFP_KERNEL);
32 INIT_LIST_HEAD(&client->list);
33 client->np = of_node_get(np);
35 list_add_tail(&client->list, &host1x->drm_clients);
40 static int host1x_activate_drm_client(struct host1x *host1x,
41 struct host1x_drm_client *drm,
42 struct host1x_client *client)
44 mutex_lock(&host1x->drm_clients_lock);
45 list_del_init(&drm->list);
46 list_add_tail(&drm->list, &host1x->drm_active);
48 mutex_unlock(&host1x->drm_clients_lock);
53 static int host1x_remove_drm_client(struct host1x *host1x,
54 struct host1x_drm_client *client)
56 mutex_lock(&host1x->drm_clients_lock);
57 list_del_init(&client->list);
58 mutex_unlock(&host1x->drm_clients_lock);
60 of_node_put(client->np);
66 static int host1x_parse_dt(struct host1x *host1x)
68 static const char * const compat[] = {
70 "nvidia,tegra20-hdmi",
72 "nvidia,tegra30-hdmi",
77 for (i = 0; i < ARRAY_SIZE(compat); i++) {
78 struct device_node *np;
80 for_each_child_of_node(host1x->dev->of_node, np) {
81 if (of_device_is_compatible(np, compat[i]) &&
82 of_device_is_available(np)) {
83 err = host1x_add_drm_client(host1x, np);
93 static int tegra_host1x_probe(struct platform_device *pdev)
95 struct host1x *host1x;
96 struct resource *regs;
99 host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
103 mutex_init(&host1x->drm_clients_lock);
104 INIT_LIST_HEAD(&host1x->drm_clients);
105 INIT_LIST_HEAD(&host1x->drm_active);
106 mutex_init(&host1x->clients_lock);
107 INIT_LIST_HEAD(&host1x->clients);
108 host1x->dev = &pdev->dev;
110 err = host1x_parse_dt(host1x);
112 dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
116 host1x->clk = devm_clk_get(&pdev->dev, NULL);
117 if (IS_ERR(host1x->clk))
118 return PTR_ERR(host1x->clk);
120 err = clk_prepare_enable(host1x->clk);
124 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130 err = platform_get_irq(pdev, 0);
134 host1x->syncpt = err;
136 err = platform_get_irq(pdev, 1);
142 host1x->regs = devm_ioremap_resource(&pdev->dev, regs);
143 if (IS_ERR(host1x->regs)) {
144 err = PTR_ERR(host1x->regs);
148 platform_set_drvdata(pdev, host1x);
153 clk_disable_unprepare(host1x->clk);
157 static int tegra_host1x_remove(struct platform_device *pdev)
159 struct host1x *host1x = platform_get_drvdata(pdev);
161 clk_disable_unprepare(host1x->clk);
166 int host1x_drm_init(struct host1x *host1x, struct drm_device *drm)
168 struct host1x_client *client;
170 mutex_lock(&host1x->clients_lock);
172 list_for_each_entry(client, &host1x->clients, list) {
173 if (client->ops && client->ops->drm_init) {
174 int err = client->ops->drm_init(client, drm);
177 "DRM setup failed for %s: %d\n",
178 dev_name(client->dev), err);
184 mutex_unlock(&host1x->clients_lock);
189 int host1x_drm_exit(struct host1x *host1x)
191 struct platform_device *pdev = to_platform_device(host1x->dev);
192 struct host1x_client *client;
197 mutex_lock(&host1x->clients_lock);
199 list_for_each_entry_reverse(client, &host1x->clients, list) {
200 if (client->ops && client->ops->drm_exit) {
201 int err = client->ops->drm_exit(client);
204 "DRM cleanup failed for %s: %d\n",
205 dev_name(client->dev), err);
211 mutex_unlock(&host1x->clients_lock);
213 drm_platform_exit(&tegra_drm_driver, pdev);
219 int host1x_register_client(struct host1x *host1x, struct host1x_client *client)
221 struct host1x_drm_client *drm, *tmp;
224 mutex_lock(&host1x->clients_lock);
225 list_add_tail(&client->list, &host1x->clients);
226 mutex_unlock(&host1x->clients_lock);
228 list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
229 if (drm->np == client->dev->of_node)
230 host1x_activate_drm_client(host1x, drm, client);
232 if (list_empty(&host1x->drm_clients)) {
233 struct platform_device *pdev = to_platform_device(host1x->dev);
235 err = drm_platform_init(&tegra_drm_driver, pdev);
237 dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
242 client->host1x = host1x;
247 int host1x_unregister_client(struct host1x *host1x,
248 struct host1x_client *client)
250 struct host1x_drm_client *drm, *tmp;
253 list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
254 if (drm->client == client) {
255 err = host1x_drm_exit(host1x);
257 dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
262 host1x_remove_drm_client(host1x, drm);
267 mutex_lock(&host1x->clients_lock);
268 list_del_init(&client->list);
269 mutex_unlock(&host1x->clients_lock);
274 static struct of_device_id tegra_host1x_of_match[] = {
275 { .compatible = "nvidia,tegra30-host1x", },
276 { .compatible = "nvidia,tegra20-host1x", },
279 MODULE_DEVICE_TABLE(of, tegra_host1x_of_match);
281 struct platform_driver tegra_host1x_driver = {
283 .name = "tegra-host1x",
284 .owner = THIS_MODULE,
285 .of_match_table = tegra_host1x_of_match,
287 .probe = tegra_host1x_probe,
288 .remove = tegra_host1x_remove,
291 static int __init tegra_host1x_init(void)
295 err = platform_driver_register(&tegra_host1x_driver);
299 err = platform_driver_register(&tegra_dc_driver);
301 goto unregister_host1x;
303 err = platform_driver_register(&tegra_hdmi_driver);
310 platform_driver_unregister(&tegra_dc_driver);
312 platform_driver_unregister(&tegra_host1x_driver);
315 module_init(tegra_host1x_init);
317 static void __exit tegra_host1x_exit(void)
319 platform_driver_unregister(&tegra_hdmi_driver);
320 platform_driver_unregister(&tegra_dc_driver);
321 platform_driver_unregister(&tegra_host1x_driver);
323 module_exit(tegra_host1x_exit);
326 MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
327 MODULE_LICENSE("GPL");