Commit 776dc384 authored by Thierry Reding's avatar Thierry Reding

drm/tegra: Move subdevice infrastructure to host1x

The Tegra DRM driver currently uses some infrastructure to defer the DRM
core initialization until all required devices have registered. The same
infrastructure can potentially be used by any other driver that requires
more than a single sub-device of the host1x module.

Make the infrastructure more generic and keep only the DRM specific code
in the DRM part of the driver. Eventually this will make it easy to move
the DRM driver part back to the DRM subsystem.
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 35d747a8
ccflags-y = -Idrivers/gpu/host1x
host1x-y = \
bus.o \
syncpt.o \
dev.o \
intr.o \
......@@ -17,4 +18,5 @@ 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/gem.o
host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o
host1x-$(CONFIG_DRM_TEGRA) += drm/bus.o
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
This diff is collapsed.
/*
* Copyright (c) 2013, NVIDIA Corporation.
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012-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,
......@@ -14,22 +15,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HOST1X_CLIENT_H
#define HOST1X_CLIENT_H
#ifndef HOST1X_BUS_H
#define HOST1X_BUS_H
struct device;
struct platform_device;
struct host1x;
#ifdef CONFIG_DRM_TEGRA
int tegra_drm_alloc(struct platform_device *pdev);
#else
static inline int tegra_drm_alloc(struct platform_device *pdev)
{
return 0;
}
#endif
int host1x_bus_init(void);
void host1x_bus_exit(void);
void host1x_set_drm_data(struct device *dev, void *data);
void *host1x_get_drm_data(struct device *dev);
int host1x_register(struct host1x *host1x);
int host1x_unregister(struct host1x *host1x);
#endif
......@@ -27,24 +27,12 @@
#define CREATE_TRACE_POINTS
#include <trace/events/host1x.h>
#include "bus.h"
#include "dev.h"
#include "intr.h"
#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 ? host1x->drm_data : NULL;
}
void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
{
......@@ -114,6 +102,9 @@ static int host1x_probe(struct platform_device *pdev)
if (!host)
return -ENOMEM;
mutex_init(&host->devices_lock);
INIT_LIST_HEAD(&host->devices);
INIT_LIST_HEAD(&host->list);
host->dev = &pdev->dev;
host->info = id->data;
......@@ -163,10 +154,14 @@ static int host1x_probe(struct platform_device *pdev)
host1x_debug_init(host);
tegra_drm_alloc(pdev);
err = host1x_register(host);
if (err < 0)
goto fail_deinit_intr;
return 0;
fail_deinit_intr:
host1x_intr_deinit(host);
fail_deinit_syncpt:
host1x_syncpt_deinit(host);
return err;
......@@ -176,6 +171,7 @@ static int host1x_remove(struct platform_device *pdev)
{
struct host1x *host = platform_get_drvdata(pdev);
host1x_unregister(host);
host1x_intr_deinit(host);
host1x_syncpt_deinit(host);
clk_disable_unprepare(host->clk);
......@@ -196,46 +192,24 @@ static int __init tegra_host1x_init(void)
{
int err;
err = platform_driver_register(&tegra_host1x_driver);
err = host1x_bus_init();
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;
err = platform_driver_register(&tegra_gr2d_driver);
if (err < 0)
goto unregister_hdmi;
#endif
err = platform_driver_register(&tegra_host1x_driver);
if (err < 0) {
host1x_bus_exit();
return err;
}
return 0;
#ifdef CONFIG_DRM_TEGRA
unregister_hdmi:
platform_driver_unregister(&tegra_hdmi_driver);
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_gr2d_driver);
platform_driver_unregister(&tegra_hdmi_driver);
platform_driver_unregister(&tegra_dc_driver);
#endif
platform_driver_unregister(&tegra_host1x_driver);
host1x_bus_exit();
}
module_exit(tegra_host1x_exit);
......
......@@ -125,7 +125,10 @@ struct host1x {
struct dentry *debugfs;
void *drm_data;
struct mutex devices_lock;
struct list_head devices;
struct list_head list;
};
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)
host->debug_op->show_mlocks(host, o);
}
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_gr2d_driver;
#endif
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* 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 "drm.h"
static int drm_host1x_set_busid(struct drm_device *dev,
struct drm_master *master)
{
const char *device = dev_name(dev->dev);
const char *driver = dev->driver->name;
const char *bus = dev->dev->bus->name;
int length;
master->unique_len = strlen(bus) + 1 + strlen(device);
master->unique_size = master->unique_len;
master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
if (!master->unique)
return -ENOMEM;
snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device);
length = strlen(driver) + 1 + master->unique_len;
dev->devname = kmalloc(length + 1, GFP_KERNEL);
if (!dev->devname)
return -ENOMEM;
snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique);
return 0;
}
static struct drm_bus drm_host1x_bus = {
.bus_type = DRIVER_BUS_HOST1X,
.set_busid = drm_host1x_set_busid,
};
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
{
struct drm_device *drm;
int ret;
INIT_LIST_HEAD(&driver->device_list);
driver->bus = &drm_host1x_bus;
drm = drm_dev_alloc(driver, &device->dev);
if (!drm)
return -ENOMEM;
ret = drm_dev_register(drm, 0);
if (ret)
goto err_free;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
driver->major, driver->minor, driver->patchlevel,
driver->date, drm->primary->index);
return 0;
err_free:
drm_dev_free(drm);
return ret;
}
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
{
struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
drm_put_dev(tegra->drm);
}
......@@ -11,7 +11,6 @@
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
#include "host1x_client.h"
#include "dc.h"
#include "drm.h"
#include "gem.h"
......@@ -1040,28 +1039,28 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
static int tegra_dc_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = to_tegra_drm_client(client);
struct tegra_dc *dc = tegra_drm_client_to_dc(drm);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_dc *dc = host1x_client_to_dc(client);
int err;
dc->pipe = drm->drm->mode_config.num_crtc;
dc->pipe = tegra->drm->mode_config.num_crtc;
drm_crtc_init(drm->drm, &dc->base, &tegra_crtc_funcs);
drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
drm_mode_crtc_set_gamma_size(&dc->base, 256);
drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
err = tegra_dc_rgb_init(drm->drm, dc);
err = tegra_dc_rgb_init(tegra->drm, dc);
if (err < 0 && err != -ENODEV) {
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
return err;
}
err = tegra_dc_add_planes(drm->drm, dc);
err = tegra_dc_add_planes(tegra->drm, dc);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_dc_debugfs_init(dc, drm->drm->primary);
err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
if (err < 0)
dev_err(dc->dev, "debugfs setup failed: %d\n", err);
}
......@@ -1079,8 +1078,7 @@ static int tegra_dc_init(struct host1x_client *client)
static int tegra_dc_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = to_tegra_drm_client(client);
struct tegra_dc *dc = tegra_drm_client_to_dc(drm);
struct tegra_dc *dc = host1x_client_to_dc(client);
int err;
devm_free_irq(dc->dev, dc->irq, dc);
......@@ -1107,7 +1105,6 @@ static const struct host1x_client_ops dc_client_ops = {
static int tegra_dc_probe(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct resource *regs;
struct tegra_dc *dc;
int err;
......@@ -1141,9 +1138,9 @@ static int tegra_dc_probe(struct platform_device *pdev)
return -ENXIO;
}
INIT_LIST_HEAD(&dc->client.base.list);
dc->client.base.ops = &dc_client_ops;
dc->client.base.dev = &pdev->dev;
INIT_LIST_HEAD(&dc->client.list);
dc->client.ops = &dc_client_ops;
dc->client.dev = &pdev->dev;
err = tegra_dc_rgb_probe(dc);
if (err < 0 && err != -ENODEV) {
......@@ -1151,7 +1148,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
return err;
}
err = host1x_register_client(tegra, &dc->client.base);
err = host1x_client_register(&dc->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
......@@ -1165,11 +1162,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
static int tegra_dc_remove(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct tegra_dc *dc = platform_get_drvdata(pdev);
int err;
err = host1x_unregister_client(tegra, &dc->client.base);
err = host1x_client_unregister(&dc->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
......
This diff is collapsed.
......@@ -32,11 +32,6 @@ struct tegra_fbdev {
struct tegra_drm {
struct drm_device *drm;
struct device *dev;
struct mutex subdevs_lock;
struct list_head subdevs;
struct list_head active;
struct mutex clients_lock;
struct list_head clients;
......@@ -63,29 +58,29 @@ struct tegra_drm_client_ops {
struct tegra_drm_client {
struct host1x_client base;
struct drm_device *drm;
struct list_head list;
const struct tegra_drm_client_ops *ops;
};
static inline struct tegra_drm_client *
to_tegra_drm_client(struct host1x_client *client)
host1x_to_drm_client(struct host1x_client *client)
{
return container_of(client, struct tegra_drm_client, base);
}
extern int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
extern int tegra_drm_exit(struct tegra_drm *tegra);
extern int host1x_register_client(struct tegra_drm *tegra,
struct host1x_client *client);
extern int host1x_unregister_client(struct tegra_drm *tegra,
struct host1x_client *client);
struct tegra_output;
struct tegra_dc {
struct tegra_drm_client client;
struct host1x_client client;
struct device *dev;
spinlock_t lock;
......@@ -109,7 +104,7 @@ struct tegra_dc {
};
static inline struct tegra_dc *
tegra_drm_client_to_dc(struct tegra_drm_client *client)
host1x_client_to_dc(struct host1x_client *client)
{
return container_of(client, struct tegra_dc, client);
}
......@@ -235,6 +230,10 @@ static inline int tegra_output_check_mode(struct tegra_output *output,
return output ? -ENOSYS : -EINVAL;
}
/* from bus.c */
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
/* from rgb.c */
extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
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);
extern void tegra_drm_fb_exit(struct drm_device *drm);
extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
extern struct drm_driver tegra_drm_driver;
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_gr2d_driver;
#endif /* HOST1X_DRM_H */
......@@ -16,7 +16,6 @@
#include <linux/clk.h>
#include "host1x_client.h"
#include "drm.h"
#include "gem.h"
......@@ -35,19 +34,45 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
return container_of(client, struct gr2d, client);
}
static int gr2d_client_init(struct host1x_client *client)
static int gr2d_init(struct host1x_client *client)
{
return 0;
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct gr2d *gr2d = to_gr2d(drm);
gr2d->channel = host1x_channel_request(client->dev);
if (!gr2d->channel)
return -ENOMEM;
client->syncpts[0] = host1x_syncpt_request(client->dev, false);
if (!client->syncpts[0]) {
host1x_channel_free(gr2d->channel);
return -ENOMEM;
}
return tegra_drm_register_client(tegra, drm);
}
static int gr2d_client_exit(struct host1x_client *client)
static int gr2d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct gr2d *gr2d = to_gr2d(drm);
int err;
err = tegra_drm_unregister_client(tegra, drm);
if (err < 0)
return err;
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_free(gr2d->channel);
return 0;
}
static const struct host1x_client_ops gr2d_client_ops = {
.init = gr2d_client_init,
.exit = gr2d_client_exit,
.init = gr2d_init,
.exit = gr2d_exit,
};
static int gr2d_open_channel(struct tegra_drm_client *client,
......@@ -240,7 +265,6 @@ static const u32 gr2d_addr_regs[] = {
static int gr2d_probe(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct host1x_syncpt **syncpts;
struct gr2d *gr2d;
......@@ -267,25 +291,17 @@ static int gr2d_probe(struct platform_device *pdev)
return err;
}
gr2d->channel = host1x_channel_request(dev);
if (!gr2d->channel)
return -ENOMEM;
*syncpts = host1x_syncpt_request(dev, false);
if (!(*syncpts)) {
host1x_channel_free(gr2d->channel);
return -ENOMEM;
}
INIT_LIST_HEAD(&gr2d->client.base.list);
gr2d->client.base.ops = &gr2d_client_ops;
gr2d->client.base.dev = dev;
gr2d->client.base.class = HOST1X_CLASS_GR2D;
gr2d->client.base.syncpts = syncpts;
gr2d->client.base.num_syncpts = 1;
INIT_LIST_HEAD(&gr2d->client.list);
gr2d->client.ops = &gr2d_ops;
err = host1x_register_client(tegra, &gr2d->client.base);
err = host1x_client_register(&gr2d->client.base);
if (err < 0) {
dev_err(dev, "failed to register host1x client: %d\n", err);
return err;
......@@ -302,22 +318,16 @@ static int gr2d_probe(struct platform_device *pdev)
static int gr2d_remove(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct gr2d *gr2d = platform_get_drvdata(pdev);
unsigned int i;
int err;
err = host1x_unregister_client(tegra, &gr2d->client.base);
err = host1x_client_unregister(&gr2d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
return err;
}
for (i = 0; i < gr2d->client.base.num_syncpts; i++)
host1x_syncpt_free(gr2d->client.base.syncpts[i]);
host1x_channel_free(gr2d->channel);
clk_disable_unprepare(gr2d->clk);
return 0;
......
......@@ -13,13 +13,12 @@
#include <linux/hdmi.h>
#include <linux/regulator/consumer.h>
#include "host1x_client.h"
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
struct tegra_hdmi {
struct tegra_drm_client client;
struct host1x_client client;
struct tegra_output output;
struct device *dev;
......@@ -43,7 +42,7 @@ struct tegra_hdmi {
};
static inline struct tegra_hdmi *
tegra_drm_client_to_hdmi(struct tegra_drm_client *client)
host1x_client_to_hdmi(struct host1x_client *client)
{
return container_of(client, struct tegra_hdmi, client);
}
......@@ -1118,22 +1117,22 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
static int tegra_hdmi_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = to_tegra_drm_client(client);
struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
hdmi->output.type = TEGRA_OUTPUT_HDMI;
hdmi->output.dev = client->dev;
hdmi->output.ops = &hdmi_ops;
err = tegra_output_init(drm->drm, &hdmi->output);
err = tegra_output_init(tegra->drm, &hdmi->output);
if (err < 0) {
dev_err(client->dev, "output setup failed: %d\n", err);
return err;
}
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_hdmi_debugfs_init(hdmi, drm->drm->primary);
err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary);
if (err < 0)
dev_err(client->dev, "debugfs setup failed: %d\n", err);
}
......@@ -1143,8 +1142,7 @@ static int tegra_hdmi_init(struct host1x_client *client)
static int tegra_hdmi_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = to_tegra_drm_client(client);
struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm);
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
......@@ -1176,7 +1174,6 @@ static const struct host1x_client_ops hdmi_client_ops = {
static int tegra_hdmi_probe(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct tegra_hdmi *hdmi;
struct resource *regs;
int err;
......@@ -1247,11 +1244,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->irq = err;
INIT_LIST_HEAD(&hdmi->client.base.list);
hdmi->client.base.ops = &hdmi_client_ops;
hdmi->client.base.dev = &pdev->dev;
INIT_LIST_HEAD(&hdmi->client.list);
hdmi->client.ops = &hdmi_client_ops;
hdmi->client.dev = &pdev->dev;
err = host1x_register_client(tegra, &hdmi->client.base);
err = host1x_client_register(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
......@@ -1265,11 +1262,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
static int tegra_hdmi_remove(struct platform_device *pdev)
{
struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent);
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
int err;
err = host1x_unregister_client(tegra, &hdmi->client.base);
err = host1x_client_unregister(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
......
......@@ -150,6 +150,7 @@ int drm_err(const char *func, const char *format, ...);
#define DRIVER_BUS_PCI 0x1
#define DRIVER_BUS_PLATFORM 0x2
#define DRIVER_BUS_USB 0x3
#define DRIVER_BUS_HOST1X 0x4
/***********************************************************************/
/** \name Begin the DRM... */
......
......@@ -19,7 +19,7 @@
#ifndef __LINUX_HOST1X_H
#define __LINUX_HOST1X_H
#include <linux/kref.h>
#include <linux/device.h>
#include <linux/types.h>
enum host1x_class {
......@@ -37,6 +37,7 @@ struct host1x_client_ops {
struct host1x_client {
struct list_head list;
struct device *parent;
struct device *dev;
const struct host1x_client_ops *ops;
......@@ -230,4 +231,46 @@ void host1x_job_put(struct host1x_job *job);
int host1x_job_pin(struct host1x_job *job, struct device *dev);
void host1x_job_unpin(struct host1x_job *job);
/*
* subdevice probe infrastructure
*/
struct host1x_device;
struct host1x_driver {
const struct of_device_id *subdevs;
struct list_head list;
const char *name;
int (*probe)(struct host1x_device *device);
int (*remove)(struct host1x_device *device);
};
int host1x_driver_register(struct host1x_driver *driver);
void host1x_driver_unregister(struct host1x_driver *driver);
struct host1x_device {
struct host1x_driver *driver;
struct list_head list;
struct device dev;
struct mutex subdevs_lock;
struct list_head subdevs;
struct list_head active;
struct mutex clients_lock;
struct list_head clients;
};
static inline struct host1x_device *to_host1x_device(struct device *dev)
{
return container_of(dev, struct host1x_device, dev);
}
int host1x_device_init(struct host1x_device *device);
int host1x_device_exit(struct host1x_device *device);
int host1x_client_register(struct host1x_client *client);
int host1x_client_unregister(struct host1x_client *client);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment