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: Arto Merilainen <amerilainen@nvidia.com> Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Erik Faye-Lund <kusmabite@gmail.com> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
This commit is contained in:
parent
c89c0ea63f
commit
692e6d7be8
10 changed files with 318 additions and 343 deletions
|
@ -13,6 +13,6 @@ host1x-y = \
|
||||||
ccflags-y += -Iinclude/drm
|
ccflags-y += -Iinclude/drm
|
||||||
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
|
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
|
host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o
|
||||||
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
|
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
|
||||||
|
|
|
@ -32,6 +32,19 @@
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "hw/host1x01.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)
|
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_debug_init(host);
|
||||||
|
|
||||||
|
host1x_drm_alloc(pdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_deinit_syncpt:
|
fail_deinit_syncpt:
|
||||||
|
@ -168,7 +183,7 @@ static int __exit host1x_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver platform_driver = {
|
static struct platform_driver tegra_host1x_driver = {
|
||||||
.probe = host1x_probe,
|
.probe = host1x_probe,
|
||||||
.remove = __exit_p(host1x_remove),
|
.remove = __exit_p(host1x_remove),
|
||||||
.driver = {
|
.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_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
|
||||||
MODULE_DESCRIPTION("Host1x driver for Tegra products");
|
MODULE_DESCRIPTION("Host1x driver for Tegra products");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -124,6 +124,8 @@ struct host1x {
|
||||||
unsigned int num_allocated_channels;
|
unsigned int num_allocated_channels;
|
||||||
|
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
|
|
||||||
|
void *drm_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
|
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);
|
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
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
config DRM_TEGRA
|
config DRM_TEGRA
|
||||||
tristate "NVIDIA Tegra DRM"
|
bool "NVIDIA Tegra DRM"
|
||||||
depends on DRM && OF
|
depends on DRM && OF
|
||||||
select DRM_KMS_HELPER
|
select DRM_KMS_HELPER
|
||||||
select DRM_GEM_CMA_HELPER
|
select DRM_GEM_CMA_HELPER
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "drm.h"
|
#include "drm.h"
|
||||||
#include "dc.h"
|
#include "dc.h"
|
||||||
|
#include "host1x_client.h"
|
||||||
|
|
||||||
struct tegra_plane {
|
struct tegra_plane {
|
||||||
struct drm_plane base;
|
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)
|
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 resource *regs;
|
||||||
struct tegra_dc *dc;
|
struct tegra_dc *dc;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1160,7 +1161,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
static int tegra_dc_remove(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);
|
struct tegra_dc *dc = platform_get_drvdata(pdev);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <asm/dma-iommu.h>
|
#include <asm/dma-iommu.h>
|
||||||
|
|
||||||
|
#include "host1x_client.h"
|
||||||
#include "drm.h"
|
#include "drm.h"
|
||||||
|
|
||||||
#define DRIVER_NAME "tegra"
|
#define DRIVER_NAME "tegra"
|
||||||
|
@ -23,13 +24,222 @@
|
||||||
#define DRIVER_MINOR 0
|
#define DRIVER_MINOR 0
|
||||||
#define DRIVER_PATCHLEVEL 0
|
#define DRIVER_PATCHLEVEL 0
|
||||||
|
|
||||||
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
|
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 device *dev = drm->dev;
|
|
||||||
struct host1x_drm *host1x;
|
struct host1x_drm *host1x;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
host1x = dev_get_drvdata(dev);
|
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 host1x_drm *host1x;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
host1x = host1x_get_drm_data(drm->dev);
|
||||||
drm->dev_private = host1x;
|
drm->dev_private = host1x;
|
||||||
host1x->drm = drm;
|
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 int tegra_drm_fb_init(struct drm_device *drm);
|
||||||
extern void tegra_drm_fb_exit(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;
|
extern struct drm_driver tegra_drm_driver;
|
||||||
|
|
||||||
#endif /* HOST1X_DRM_H */
|
#endif /* HOST1X_DRM_H */
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "hdmi.h"
|
#include "hdmi.h"
|
||||||
#include "drm.h"
|
#include "drm.h"
|
||||||
#include "dc.h"
|
#include "dc.h"
|
||||||
|
#include "host1x_client.h"
|
||||||
|
|
||||||
struct tegra_hdmi {
|
struct tegra_hdmi {
|
||||||
struct host1x_client client;
|
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)
|
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 tegra_hdmi *hdmi;
|
||||||
struct resource *regs;
|
struct resource *regs;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1278,7 +1279,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
static int tegra_hdmi_remove(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);
|
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
|
@ -1,329 +0,0 @@
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&tegra_hdmi_driver);
|
|
||||||
platform_driver_unregister(&tegra_dc_driver);
|
|
||||||
platform_driver_unregister(&tegra_host1x_driver);
|
|
||||||
}
|
|
||||||
module_exit(tegra_host1x_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
|
||||||
MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
35
drivers/gpu/host1x/host1x_client.h
Normal file
35
drivers/gpu/host1x/host1x_client.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013, NVIDIA Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HOST1X_CLIENT_H
|
||||||
|
#define HOST1X_CLIENT_H
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct platform_device;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRM_TEGRA
|
||||||
|
int host1x_drm_alloc(struct platform_device *pdev);
|
||||||
|
#else
|
||||||
|
static inline int host1x_drm_alloc(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void host1x_set_drm_data(struct device *dev, void *data);
|
||||||
|
void *host1x_get_drm_data(struct device *dev);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue