Commit 692e6d7b authored by Terje Bergstrom's avatar Terje Bergstrom Committed by Thierry Reding

gpu: host1x: Remove second host1x driver

Remove second host1x driver, and bind tegra-drm to the new host1x
driver. The logic to parse device tree and track clients is moved
to drm.c.
Signed-off-by: default avatarArto Merilainen <amerilainen@nvidia.com>
Signed-off-by: default avatarTerje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: Thierry Reding's avatarThierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding's avatarThierry Reding <thierry.reding@avionic-design.de>
Tested-by: Erik Faye-Lund 's avatarErik Faye-Lund <kusmabite@gmail.com>
Signed-off-by: Thierry Reding's avatarThierry Reding <thierry.reding@avionic-design.de>
parent c89c0ea6
......@@ -13,6 +13,6 @@ host1x-y = \
ccflags-y += -Iinclude/drm
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o drm/host1x.o
host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o
host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
......@@ -32,6 +32,19 @@
#include "channel.h"
#include "debug.h"
#include "hw/host1x01.h"
#include "host1x_client.h"
void host1x_set_drm_data(struct device *dev, void *data)
{
struct host1x *host1x = dev_get_drvdata(dev);
host1x->drm_data = data;
}
void *host1x_get_drm_data(struct device *dev)
{
struct host1x *host1x = dev_get_drvdata(dev);
return host1x->drm_data;
}
void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
{
......@@ -150,6 +163,8 @@ static int host1x_probe(struct platform_device *pdev)
host1x_debug_init(host);
host1x_drm_alloc(pdev);
return 0;
fail_deinit_syncpt:
......@@ -168,7 +183,7 @@ static int __exit host1x_remove(struct platform_device *pdev)
return 0;
}
static struct platform_driver platform_driver = {
static struct platform_driver tegra_host1x_driver = {
.probe = host1x_probe,
.remove = __exit_p(host1x_remove),
.driver = {
......@@ -178,8 +193,47 @@ static struct platform_driver platform_driver = {
},
};
module_platform_driver(platform_driver);
static int __init tegra_host1x_init(void)
{
int err;
err = platform_driver_register(&tegra_host1x_driver);
if (err < 0)
return err;
#ifdef CONFIG_DRM_TEGRA
err = platform_driver_register(&tegra_dc_driver);
if (err < 0)
goto unregister_host1x;
err = platform_driver_register(&tegra_hdmi_driver);
if (err < 0)
goto unregister_dc;
#endif
return 0;
#ifdef CONFIG_DRM_TEGRA
unregister_dc:
platform_driver_unregister(&tegra_dc_driver);
unregister_host1x:
platform_driver_unregister(&tegra_host1x_driver);
return err;
#endif
}
module_init(tegra_host1x_init);
static void __exit tegra_host1x_exit(void)
{
#ifdef CONFIG_DRM_TEGRA
platform_driver_unregister(&tegra_hdmi_driver);
platform_driver_unregister(&tegra_dc_driver);
#endif
platform_driver_unregister(&tegra_host1x_driver);
}
module_exit(tegra_host1x_exit);
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
MODULE_DESCRIPTION("Host1x driver for Tegra products");
MODULE_LICENSE("GPL");
......@@ -124,6 +124,8 @@ struct host1x {
unsigned int num_allocated_channels;
struct dentry *debugfs;
void *drm_data;
};
void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
......@@ -299,4 +301,8 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
host->debug_op->show_mlocks(host, o);
}
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_gr2d_driver;
#endif
config DRM_TEGRA
tristate "NVIDIA Tegra DRM"
bool "NVIDIA Tegra DRM"
depends on DRM && OF
select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER
......
......@@ -16,6 +16,7 @@
#include "drm.h"
#include "dc.h"
#include "host1x_client.h"
struct tegra_plane {
struct drm_plane base;
......@@ -1097,7 +1098,7 @@ static const struct host1x_client_ops dc_client_ops = {
static int tegra_dc_probe(struct platform_device *pdev)
{
struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent);
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct resource *regs;
struct tegra_dc *dc;
int err;
......@@ -1160,7 +1161,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
static int tegra_dc_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent);
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct tegra_dc *dc = platform_get_drvdata(pdev);
int err;
......
......@@ -14,6 +14,7 @@
#include <linux/dma-mapping.h>
#include <asm/dma-iommu.h>
#include "host1x_client.h"
#include "drm.h"
#define DRIVER_NAME "tegra"
......@@ -23,13 +24,222 @@
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
struct host1x_drm_client {
struct host1x_client *client;
struct device_node *np;
struct list_head list;
};
static int host1x_add_drm_client(struct host1x_drm *host1x,
struct device_node *np)
{
struct host1x_drm_client *client;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return -ENOMEM;
INIT_LIST_HEAD(&client->list);
client->np = of_node_get(np);
list_add_tail(&client->list, &host1x->drm_clients);
return 0;
}
static int host1x_activate_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *drm,
struct host1x_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&drm->list);
list_add_tail(&drm->list, &host1x->drm_active);
drm->client = client;
mutex_unlock(&host1x->drm_clients_lock);
return 0;
}
static int host1x_remove_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->drm_clients_lock);
of_node_put(client->np);
kfree(client);
return 0;
}
static int host1x_parse_dt(struct host1x_drm *host1x)
{
static const char * const compat[] = {
"nvidia,tegra20-dc",
"nvidia,tegra20-hdmi",
"nvidia,tegra30-dc",
"nvidia,tegra30-hdmi",
};
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(compat); i++) {
struct device_node *np;
for_each_child_of_node(host1x->dev->of_node, np) {
if (of_device_is_compatible(np, compat[i]) &&
of_device_is_available(np)) {
err = host1x_add_drm_client(host1x, np);
if (err < 0)
return err;
}
}
}
return 0;
}
int host1x_drm_alloc(struct platform_device *pdev)
{
struct host1x_drm *host1x;
int err;
host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
if (!host1x)
return -ENOMEM;
mutex_init(&host1x->drm_clients_lock);
INIT_LIST_HEAD(&host1x->drm_clients);
INIT_LIST_HEAD(&host1x->drm_active);
mutex_init(&host1x->clients_lock);
INIT_LIST_HEAD(&host1x->clients);
host1x->dev = &pdev->dev;
err = host1x_parse_dt(host1x);
if (err < 0) {
dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
return err;
}
host1x_set_drm_data(&pdev->dev, host1x);
return 0;
}
int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
{
struct host1x_client *client;
mutex_lock(&host1x->clients_lock);
list_for_each_entry(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_init) {
int err = client->ops->drm_init(client, drm);
if (err < 0) {
dev_err(host1x->dev,
"DRM setup failed for %s: %d\n",
dev_name(client->dev), err);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
return 0;
}
int host1x_drm_exit(struct host1x_drm *host1x)
{
struct platform_device *pdev = to_platform_device(host1x->dev);
struct host1x_client *client;
if (!host1x->drm)
return 0;
mutex_lock(&host1x->clients_lock);
list_for_each_entry_reverse(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_exit) {
int err = client->ops->drm_exit(client);
if (err < 0) {
dev_err(host1x->dev,
"DRM cleanup failed for %s: %d\n",
dev_name(client->dev), err);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
drm_platform_exit(&tegra_drm_driver, pdev);
host1x->drm = NULL;
return 0;
}
int host1x_register_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
mutex_lock(&host1x->clients_lock);
list_add_tail(&client->list, &host1x->clients);
mutex_unlock(&host1x->clients_lock);
list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
if (drm->np == client->dev->of_node)
host1x_activate_drm_client(host1x, drm, client);
if (list_empty(&host1x->drm_clients)) {
struct platform_device *pdev = to_platform_device(host1x->dev);
err = drm_platform_init(&tegra_drm_driver, pdev);
if (err < 0) {
dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
return err;
}
}
return 0;
}
int host1x_unregister_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
if (drm->client == client) {
err = host1x_drm_exit(host1x);
if (err < 0) {
dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
err);
return err;
}
host1x_remove_drm_client(host1x, drm);
break;
}
}
mutex_lock(&host1x->clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->clients_lock);
return 0;
}
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
{
struct device *dev = drm->dev;
struct host1x_drm *host1x;
int err;
host1x = dev_get_drvdata(dev);
host1x = host1x_get_drm_data(drm->dev);
drm->dev_private = host1x;
host1x->drm = drm;
......
......@@ -229,9 +229,6 @@ extern int tegra_output_exit(struct tegra_output *output);
extern int tegra_drm_fb_init(struct drm_device *drm);
extern void tegra_drm_fb_exit(struct drm_device *drm);
extern struct platform_driver tegra_host1x_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_dc_driver;
extern struct drm_driver tegra_drm_driver;
#endif /* HOST1X_DRM_H */
......@@ -22,6 +22,7 @@
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
#include "host1x_client.h"
struct tegra_hdmi {
struct host1x_client client;
......@@ -1189,7 +1190,7 @@ static const struct host1x_client_ops hdmi_client_ops = {
static int tegra_hdmi_probe(struct platform_device *pdev)
{
struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent);
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct tegra_hdmi *hdmi;
struct resource *regs;
int err;
......@@ -1278,7 +1279,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
static int tegra_hdmi_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent);
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
int err;
......
/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include "drm.h"
struct host1x_drm_client {
struct host1x_client *client;
struct device_node *np;
struct list_head list;
};
static int host1x_add_drm_client(struct host1x_drm *host1x,
struct device_node *np)
{
struct host1x_drm_client *client;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return -ENOMEM;
INIT_LIST_HEAD(&client->list);
client->np = of_node_get(np);
list_add_tail(&client->list, &host1x->drm_clients);
return 0;
}
static int host1x_activate_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *drm,
struct host1x_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&drm->list);
list_add_tail(&drm->list, &host1x->drm_active);
drm->client = client;
mutex_unlock(&host1x->drm_clients_lock);
return 0;
}
static int host1x_remove_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->drm_clients_lock);
of_node_put(client->np);
kfree(client);
return 0;
}
static int host1x_parse_dt(struct host1x_drm *host1x)
{
static const char * const compat[] = {
"nvidia,tegra20-dc",
"nvidia,tegra20-hdmi",
"nvidia,tegra30-dc",
"nvidia,tegra30-hdmi",
};
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(compat); i++) {
struct device_node *np;
for_each_child_of_node(host1x->dev->of_node, np) {
if (of_device_is_compatible(np, compat[i]) &&
of_device_is_available(np)) {
err = host1x_add_drm_client(host1x, np);
if (err < 0)
return err;
}
}
}
return 0;
}
static int tegra_host1x_probe(struct platform_device *pdev)
{
struct host1x_drm *host1x;
struct resource *regs;
int err;
host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
if (!host1x)
return -ENOMEM;
mutex_init(&host1x->drm_clients_lock);
INIT_LIST_HEAD(&host1x->drm_clients);
INIT_LIST_HEAD(&host1x->drm_active);
mutex_init(&host1x->clients_lock);
INIT_LIST_HEAD(&host1x->clients);
host1x->dev = &pdev->dev;
err = host1x_parse_dt(host1x);
if (err < 0) {
dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
return err;
}
host1x->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host1x->clk))
return PTR_ERR(host1x->clk);
err = clk_prepare_enable(host1x->clk);
if (err < 0)
return err;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
err = -ENXIO;
goto err;
}
err = platform_get_irq(pdev, 0);
if (err < 0)
goto err;
host1x->syncpt = err;
err = platform_get_irq(pdev, 1);
if (err < 0)
goto err;
host1x->irq = err;
host1x->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host1x->regs)) {
err = PTR_ERR(host1x->regs);
goto err;
}
platform_set_drvdata(pdev, host1x);
return 0;
err:
clk_disable_unprepare(host1x->clk);
return err;
}
static int tegra_host1x_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = platform_get_drvdata(pdev);
clk_disable_unprepare(host1x->clk);
return 0;
}
int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
{
struct host1x_client *client;
mutex_lock(&host1x->clients_lock);
list_for_each_entry(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_init) {
int err = client->ops->drm_init(client, drm);
if (err < 0) {
dev_err(host1x->dev,
"DRM setup failed for %s: %d\n",
dev_name(client->dev), err);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
return 0;
}
int host1x_drm_exit(struct host1x_drm *host1x)
{
struct platform_device *pdev = to_platform_device(host1x->dev);
struct host1x_client *client;
if (!host1x->drm)
return 0;
mutex_lock(&host1x->clients_lock);
list_for_each_entry_reverse(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_exit) {
int err = client->ops->drm_exit(client);
if (err < 0) {
dev_err(host1x->dev,
"DRM cleanup failed for %s: %d\n",
dev_name(client->dev), err);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
drm_platform_exit(&tegra_drm_driver, pdev);
host1x->drm = NULL;
return 0;
}
int host1x_register_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
mutex_lock(&host1x->clients_lock);
list_add_tail(&client->list, &host1x->clients);
mutex_unlock(&host1x->clients_lock);
list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
if (drm->np == client->dev->of_node)
host1x_activate_drm_client(host1x, drm, client);
if (list_empty(&host1x->drm_clients)) {
struct platform_device *pdev = to_platform_device(host1x->dev);
err = drm_platform_init(&tegra_drm_driver, pdev);
if (err < 0) {
dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
return err;
}
}
client->host1x = host1x;
return 0;
}
int host1x_unregister_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
if (drm->client == client) {
err = host1x_drm_exit(host1x);
if (err < 0) {
dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
err);
return err;
}
host1x_remove_drm_client(host1x, drm);
break;
}
}
mutex_lock(&host1x->clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->clients_lock);
return 0;
}
static struct of_device_id tegra_host1x_of_match[] = {
{ .compatible = "nvidia,tegra30-host1x", },
{ .compatible = "nvidia,tegra20-host1x", },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_host1x_of_match);
struct platform_driver tegra_host1x_driver = {
.driver = {
.name = "tegra-host1x",
.owner = THIS_MODULE,
.of_match_table = tegra_host1x_of_match,
},
.probe = tegra_host1x_probe,
.remove = tegra_host1x_remove,
};
static int __init tegra_host1x_init(void)
{
int err;
err = platform_driver_register(&tegra_host1x_driver);
if (err < 0)
return err;
err = platform_driver_register(&tegra_dc_driver);
if (err < 0)
goto unregister_host1x;
err = platform_driver_register(&tegra_hdmi_driver);
if (err < 0)
goto unregister_dc;
return 0;
unregister_dc:
platform_driver_unregister(&tegra_dc_driver);
unregister_host1x:
platform_driver_unregister(&tegra_host1x_driver);
return err;
}
module_init(tegra_host1x_init);
static void __exit tegra_host1x_exit(void)
{