From 4231c6b01af9f0f3eeca4b8d0d87125d78233b41 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Fri, 22 Mar 2013 16:34:05 +0200 Subject: drm/tegra: Move drm to live under host1x Make drm part of host1x driver. Signed-off-by: Arto Merilainen Signed-off-by: Terje Bergstrom Reviewed-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Erik Faye-Lund Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/drm.c | 217 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 drivers/gpu/host1x/drm/drm.c (limited to 'drivers/gpu/host1x/drm/drm.c') diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c new file mode 100644 index 00000000000..9d452df5bca --- /dev/null +++ b/drivers/gpu/host1x/drm/drm.c @@ -0,0 +1,217 @@ +/* + * 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 +#include +#include + +#include +#include + +#include "drm.h" + +#define DRIVER_NAME "tegra" +#define DRIVER_DESC "NVIDIA Tegra graphics" +#define DRIVER_DATE "20120330" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +static int tegra_drm_load(struct drm_device *drm, unsigned long flags) +{ + struct device *dev = drm->dev; + struct host1x *host1x; + int err; + + host1x = dev_get_drvdata(dev); + drm->dev_private = host1x; + host1x->drm = drm; + + drm_mode_config_init(drm); + + err = host1x_drm_init(host1x, drm); + if (err < 0) + return err; + + err = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (err < 0) + return err; + + err = tegra_drm_fb_init(drm); + if (err < 0) + return err; + + drm_kms_helper_poll_init(drm); + + return 0; +} + +static int tegra_drm_unload(struct drm_device *drm) +{ + drm_kms_helper_poll_fini(drm); + tegra_drm_fb_exit(drm); + + drm_mode_config_cleanup(drm); + + return 0; +} + +static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) +{ + return 0; +} + +static void tegra_drm_lastclose(struct drm_device *drm) +{ + struct host1x *host1x = drm->dev_private; + + drm_fbdev_cma_restore_mode(host1x->fbdev); +} + +static struct drm_ioctl_desc tegra_drm_ioctls[] = { +}; + +static const struct file_operations tegra_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (dc->pipe == pipe) + return crtc; + } + + return NULL; +} + +static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc) +{ + /* TODO: implement real hardware counter using syncpoints */ + return drm_vblank_count(dev, crtc); +} + +static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) +{ + struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (!crtc) + return -ENODEV; + + tegra_dc_enable_vblank(dc); + + return 0; +} + +static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe) +{ + struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (crtc) + tegra_dc_disable_vblank(dc); +} + +static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) + tegra_dc_cancel_page_flip(crtc, file); +} + +#ifdef CONFIG_DEBUG_FS +static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)s->private; + struct drm_device *drm = node->minor->dev; + struct drm_framebuffer *fb; + + mutex_lock(&drm->mode_config.fb_lock); + + list_for_each_entry(fb, &drm->mode_config.fb_list, head) { + seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", + fb->base.id, fb->width, fb->height, fb->depth, + fb->bits_per_pixel, + atomic_read(&fb->refcount.refcount)); + } + + mutex_unlock(&drm->mode_config.fb_lock); + + return 0; +} + +static struct drm_info_list tegra_debugfs_list[] = { + { "framebuffers", tegra_debugfs_framebuffers, 0 }, +}; + +static int tegra_debugfs_init(struct drm_minor *minor) +{ + return drm_debugfs_create_files(tegra_debugfs_list, + ARRAY_SIZE(tegra_debugfs_list), + minor->debugfs_root, minor); +} + +static void tegra_debugfs_cleanup(struct drm_minor *minor) +{ + drm_debugfs_remove_files(tegra_debugfs_list, + ARRAY_SIZE(tegra_debugfs_list), minor); +} +#endif + +struct drm_driver tegra_drm_driver = { + .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, + .load = tegra_drm_load, + .unload = tegra_drm_unload, + .open = tegra_drm_open, + .preclose = tegra_drm_preclose, + .lastclose = tegra_drm_lastclose, + + .get_vblank_counter = tegra_drm_get_vblank_counter, + .enable_vblank = tegra_drm_enable_vblank, + .disable_vblank = tegra_drm_disable_vblank, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = tegra_debugfs_init, + .debugfs_cleanup = tegra_debugfs_cleanup, +#endif + + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_cma_dumb_destroy, + + .ioctls = tegra_drm_ioctls, + .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), + .fops = &tegra_drm_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; -- cgit v1.2.3-70-g09d2 From c89c0ea63fcd045bdc17076fd078676e1da0c41a Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Fri, 22 Mar 2013 16:34:06 +0200 Subject: gpu: host1x: drm: Rename host1x to host1x_drm Both host1x and drm drivers have host1x structures. This patch renames the host1x structure under drm to follow name host1x_drm. Signed-off-by: Arto Merilainen Signed-off-by: Terje Bergstrom Reviewed-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Erik Faye-Lund Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/dc.c | 4 ++-- drivers/gpu/host1x/drm/drm.c | 4 ++-- drivers/gpu/host1x/drm/drm.h | 14 +++++++------- drivers/gpu/host1x/drm/fb.c | 6 +++--- drivers/gpu/host1x/drm/hdmi.c | 4 ++-- drivers/gpu/host1x/drm/host1x.c | 22 ++++++++++++---------- 6 files changed, 28 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/host1x/drm/drm.c') diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index de94707b9db..d1f66099826 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -1097,7 +1097,7 @@ static const struct host1x_client_ops dc_client_ops = { static int tegra_dc_probe(struct platform_device *pdev) { - struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent); struct resource *regs; struct tegra_dc *dc; int err; @@ -1160,7 +1160,7 @@ static int tegra_dc_probe(struct platform_device *pdev) static int tegra_dc_remove(struct platform_device *pdev) { - struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent); struct tegra_dc *dc = platform_get_drvdata(pdev); int err; diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 9d452df5bca..6c59bcdb65e 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -26,7 +26,7 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) { struct device *dev = drm->dev; - struct host1x *host1x; + struct host1x_drm *host1x; int err; host1x = dev_get_drvdata(dev); @@ -69,7 +69,7 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) static void tegra_drm_lastclose(struct drm_device *drm) { - struct host1x *host1x = drm->dev_private; + struct host1x_drm *host1x = drm->dev_private; drm_fbdev_cma_restore_mode(host1x->fbdev); } diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index a6c011da2ce..7fedb6c6319 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -18,7 +18,7 @@ #include #include -struct host1x { +struct host1x_drm { struct drm_device *drm; struct device *dev; void __iomem *regs; @@ -44,7 +44,7 @@ struct host1x_client_ops { }; struct host1x_client { - struct host1x *host1x; + struct host1x_drm *host1x; struct device *dev; const struct host1x_client_ops *ops; @@ -52,12 +52,12 @@ struct host1x_client { struct list_head list; }; -extern int host1x_drm_init(struct host1x *host1x, struct drm_device *drm); -extern int host1x_drm_exit(struct host1x *host1x); +extern int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm); +extern int host1x_drm_exit(struct host1x_drm *host1x); -extern int host1x_register_client(struct host1x *host1x, +extern int host1x_register_client(struct host1x_drm *host1x, struct host1x_client *client); -extern int host1x_unregister_client(struct host1x *host1x, +extern int host1x_unregister_client(struct host1x_drm *host1x, struct host1x_client *client); struct tegra_output; @@ -66,7 +66,7 @@ struct tegra_dc { struct host1x_client client; spinlock_t lock; - struct host1x *host1x; + struct host1x_drm *host1x; struct device *dev; struct drm_crtc base; diff --git a/drivers/gpu/host1x/drm/fb.c b/drivers/gpu/host1x/drm/fb.c index 03914953cb1..6ed885aead4 100644 --- a/drivers/gpu/host1x/drm/fb.c +++ b/drivers/gpu/host1x/drm/fb.c @@ -11,7 +11,7 @@ static void tegra_drm_fb_output_poll_changed(struct drm_device *drm) { - struct host1x *host1x = drm->dev_private; + struct host1x_drm *host1x = drm->dev_private; drm_fbdev_cma_hotplug_event(host1x->fbdev); } @@ -23,7 +23,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { int tegra_drm_fb_init(struct drm_device *drm) { - struct host1x *host1x = drm->dev_private; + struct host1x_drm *host1x = drm->dev_private; struct drm_fbdev_cma *fbdev; drm->mode_config.min_width = 0; @@ -46,7 +46,7 @@ int tegra_drm_fb_init(struct drm_device *drm) void tegra_drm_fb_exit(struct drm_device *drm) { - struct host1x *host1x = drm->dev_private; + struct host1x_drm *host1x = drm->dev_private; drm_fbdev_cma_fini(host1x->fbdev); } diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index bb747f6cd1a..f438f805b76 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -1189,7 +1189,7 @@ static const struct host1x_client_ops hdmi_client_ops = { static int tegra_hdmi_probe(struct platform_device *pdev) { - struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent); struct tegra_hdmi *hdmi; struct resource *regs; int err; @@ -1278,7 +1278,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev) static int tegra_hdmi_remove(struct platform_device *pdev) { - struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct host1x_drm *host1x = dev_get_drvdata(pdev->dev.parent); struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); int err; diff --git a/drivers/gpu/host1x/drm/host1x.c b/drivers/gpu/host1x/drm/host1x.c index 92e25a7e00e..c3d8ee54d2f 100644 --- a/drivers/gpu/host1x/drm/host1x.c +++ b/drivers/gpu/host1x/drm/host1x.c @@ -21,7 +21,8 @@ struct host1x_drm_client { struct list_head list; }; -static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np) +static int host1x_add_drm_client(struct host1x_drm *host1x, + struct device_node *np) { struct host1x_drm_client *client; @@ -37,7 +38,7 @@ static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np) return 0; } -static int host1x_activate_drm_client(struct host1x *host1x, +static int host1x_activate_drm_client(struct host1x_drm *host1x, struct host1x_drm_client *drm, struct host1x_client *client) { @@ -50,7 +51,7 @@ static int host1x_activate_drm_client(struct host1x *host1x, return 0; } -static int host1x_remove_drm_client(struct host1x *host1x, +static int host1x_remove_drm_client(struct host1x_drm *host1x, struct host1x_drm_client *client) { mutex_lock(&host1x->drm_clients_lock); @@ -63,7 +64,7 @@ static int host1x_remove_drm_client(struct host1x *host1x, return 0; } -static int host1x_parse_dt(struct host1x *host1x) +static int host1x_parse_dt(struct host1x_drm *host1x) { static const char * const compat[] = { "nvidia,tegra20-dc", @@ -92,7 +93,7 @@ static int host1x_parse_dt(struct host1x *host1x) static int tegra_host1x_probe(struct platform_device *pdev) { - struct host1x *host1x; + struct host1x_drm *host1x; struct resource *regs; int err; @@ -156,14 +157,14 @@ err: static int tegra_host1x_remove(struct platform_device *pdev) { - struct host1x *host1x = platform_get_drvdata(pdev); + struct host1x_drm *host1x = platform_get_drvdata(pdev); clk_disable_unprepare(host1x->clk); return 0; } -int host1x_drm_init(struct host1x *host1x, struct drm_device *drm) +int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm) { struct host1x_client *client; @@ -186,7 +187,7 @@ int host1x_drm_init(struct host1x *host1x, struct drm_device *drm) return 0; } -int host1x_drm_exit(struct host1x *host1x) +int host1x_drm_exit(struct host1x_drm *host1x) { struct platform_device *pdev = to_platform_device(host1x->dev); struct host1x_client *client; @@ -216,7 +217,8 @@ int host1x_drm_exit(struct host1x *host1x) return 0; } -int host1x_register_client(struct host1x *host1x, struct host1x_client *client) +int host1x_register_client(struct host1x_drm *host1x, + struct host1x_client *client) { struct host1x_drm_client *drm, *tmp; int err; @@ -244,7 +246,7 @@ int host1x_register_client(struct host1x *host1x, struct host1x_client *client) return 0; } -int host1x_unregister_client(struct host1x *host1x, +int host1x_unregister_client(struct host1x_drm *host1x, struct host1x_client *client) { struct host1x_drm_client *drm, *tmp; -- cgit v1.2.3-70-g09d2 From 692e6d7be8099225f04b2d97299bc03479a5fcdb Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Fri, 22 Mar 2013 16:34:07 +0200 Subject: 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 Signed-off-by: Terje Bergstrom Reviewed-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Erik Faye-Lund Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 2 +- drivers/gpu/host1x/dev.c | 58 ++++++- drivers/gpu/host1x/dev.h | 6 + drivers/gpu/host1x/drm/Kconfig | 2 +- drivers/gpu/host1x/drm/dc.c | 5 +- drivers/gpu/host1x/drm/drm.c | 214 +++++++++++++++++++++++- drivers/gpu/host1x/drm/drm.h | 3 - drivers/gpu/host1x/drm/hdmi.c | 5 +- drivers/gpu/host1x/drm/host1x.c | 329 ------------------------------------- drivers/gpu/host1x/host1x_client.h | 35 ++++ 10 files changed, 317 insertions(+), 342 deletions(-) delete mode 100644 drivers/gpu/host1x/drm/host1x.c create mode 100644 drivers/gpu/host1x/host1x_client.h (limited to 'drivers/gpu/host1x/drm/drm.c') diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 4761e8af7bd..9a6fc767f6b 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -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 diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 96897242fcc..8ce9889cefd 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -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 "); MODULE_AUTHOR("Terje Bergstrom "); MODULE_DESCRIPTION("Host1x driver for Tegra products"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 4d16fe92400..a1607d6e135 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -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 diff --git a/drivers/gpu/host1x/drm/Kconfig b/drivers/gpu/host1x/drm/Kconfig index 8267691b7aa..33f8f7a3919 100644 --- a/drivers/gpu/host1x/drm/Kconfig +++ b/drivers/gpu/host1x/drm/Kconfig @@ -1,5 +1,5 @@ config DRM_TEGRA - tristate "NVIDIA Tegra DRM" + bool "NVIDIA Tegra DRM" depends on DRM && OF select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index d1f66099826..29a79b66f36 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -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; diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 6c59bcdb65e..901f0b47815 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -14,6 +14,7 @@ #include #include +#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; diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 7fedb6c6319..0b8738fc444 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -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 */ diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index f438f805b76..01097da09f7 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -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; diff --git a/drivers/gpu/host1x/drm/host1x.c b/drivers/gpu/host1x/drm/host1x.c deleted file mode 100644 index c3d8ee54d2f..00000000000 --- a/drivers/gpu/host1x/drm/host1x.c +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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 "); -MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/host1x/host1x_client.h b/drivers/gpu/host1x/host1x_client.h new file mode 100644 index 00000000000..9b85f10f4a4 --- /dev/null +++ b/drivers/gpu/host1x/host1x_client.h @@ -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 . + */ + +#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 -- cgit v1.2.3-70-g09d2 From de2ba664c30fcdb0f678ab6cbb57e01a0b206085 Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Fri, 22 Mar 2013 16:34:08 +0200 Subject: gpu: host1x: drm: Add memory manager and fb This patch introduces a memory manager for tegra drm and moves existing parts to use it. As cma framebuffer helpers can no more be used, this patch adds also a separate framebuffer driver for tegra. Signed-off-by: Arto Merilainen Signed-off-by: Terje Bergstrom Reviewed-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Erik Faye-Lund Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 1 + drivers/gpu/host1x/drm/Kconfig | 8 +- drivers/gpu/host1x/drm/dc.c | 23 +-- drivers/gpu/host1x/drm/drm.c | 17 ++- drivers/gpu/host1x/drm/drm.h | 18 ++- drivers/gpu/host1x/drm/fb.c | 338 ++++++++++++++++++++++++++++++++++++++++- drivers/gpu/host1x/drm/gem.c | 270 ++++++++++++++++++++++++++++++++ drivers/gpu/host1x/drm/gem.h | 59 +++++++ 8 files changed, 700 insertions(+), 34 deletions(-) create mode 100644 drivers/gpu/host1x/drm/gem.c create mode 100644 drivers/gpu/host1x/drm/gem.h (limited to 'drivers/gpu/host1x/drm/drm.c') diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 9a6fc767f6b..3768dbc2718 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -15,4 +15,5 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG 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 obj-$(CONFIG_TEGRA_HOST1X) += host1x.o diff --git a/drivers/gpu/host1x/drm/Kconfig b/drivers/gpu/host1x/drm/Kconfig index 33f8f7a3919..9a28901d07c 100644 --- a/drivers/gpu/host1x/drm/Kconfig +++ b/drivers/gpu/host1x/drm/Kconfig @@ -2,11 +2,9 @@ config DRM_TEGRA bool "NVIDIA Tegra DRM" depends on DRM && OF select DRM_KMS_HELPER - select DRM_GEM_CMA_HELPER - select DRM_KMS_CMA_HELPER - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT help Choose this option if you have an NVIDIA Tegra SoC. diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index 29a79b66f36..85ea6163a7b 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -14,9 +14,10 @@ #include #include -#include "drm.h" -#include "dc.h" #include "host1x_client.h" +#include "dc.h" +#include "drm.h" +#include "gem.h" struct tegra_plane { struct drm_plane base; @@ -52,9 +53,9 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, window.bits_per_pixel = fb->bits_per_pixel; for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); + struct tegra_bo *bo = tegra_fb_get_plane(fb, i); - window.base[i] = gem->paddr + fb->offsets[i]; + window.base[i] = bo->paddr + fb->offsets[i]; /* * Tegra doesn't support different strides for U and V planes @@ -137,7 +138,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, struct drm_framebuffer *fb) { - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, 0); + struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); unsigned long value; tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -145,7 +146,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, value = fb->offsets[0] + y * fb->pitches[0] + x * fb->bits_per_pixel / 8; - tegra_dc_writel(dc, gem->paddr + value, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); value = GENERAL_UPDATE | WIN_A_UPDATE; @@ -187,20 +188,20 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) { struct drm_device *drm = dc->base.dev; struct drm_crtc *crtc = &dc->base; - struct drm_gem_cma_object *gem; unsigned long flags, base; + struct tegra_bo *bo; if (!dc->event) return; - gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); + bo = tegra_fb_get_plane(crtc->fb, 0); /* check if new start address has been latched */ tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - if (base == gem->paddr + crtc->fb->offsets[0]) { + if (base == bo->paddr + crtc->fb->offsets[0]) { spin_lock_irqsave(&drm->event_lock, flags); drm_send_vblank_event(drm, dc->pipe, dc->event); drm_vblank_put(drm, dc->pipe); @@ -570,7 +571,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *adjusted, int x, int y, struct drm_framebuffer *old_fb) { - struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(crtc->fb, 0); + struct tegra_bo *bo = tegra_fb_get_plane(crtc->fb, 0); struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc_window window; unsigned long div, value; @@ -617,7 +618,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, window.format = tegra_dc_format(crtc->fb->pixel_format); window.bits_per_pixel = crtc->fb->bits_per_pixel; window.stride[0] = crtc->fb->pitches[0]; - window.base[0] = gem->paddr; + window.base[0] = bo->paddr; err = tegra_dc_setup_window(dc, 0, &window); if (err < 0) diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 901f0b47815..c4e45c16f99 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -15,7 +15,10 @@ #include #include "host1x_client.h" +#include "dev.h" #include "drm.h" +#include "gem.h" +#include "syncpt.h" #define DRIVER_NAME "tegra" #define DRIVER_DESC "NVIDIA Tegra graphics" @@ -281,7 +284,7 @@ static void tegra_drm_lastclose(struct drm_device *drm) { struct host1x_drm *host1x = drm->dev_private; - drm_fbdev_cma_restore_mode(host1x->fbdev); + tegra_fbdev_restore_mode(host1x->fbdev); } static struct drm_ioctl_desc tegra_drm_ioctls[] = { @@ -292,7 +295,7 @@ static const struct file_operations tegra_drm_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_cma_mmap, + .mmap = tegra_drm_mmap, .poll = drm_poll, .fasync = drm_fasync, .read = drm_read, @@ -408,11 +411,11 @@ struct drm_driver tegra_drm_driver = { .debugfs_cleanup = tegra_debugfs_cleanup, #endif - .gem_free_object = drm_gem_cma_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, - .dumb_create = drm_gem_cma_dumb_create, - .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_cma_dumb_destroy, + .gem_free_object = tegra_bo_free_object, + .gem_vm_ops = &tegra_bo_vm_ops, + .dumb_create = tegra_bo_dumb_create, + .dumb_map_offset = tegra_bo_dumb_map_offset, + .dumb_destroy = tegra_bo_dumb_destroy, .ioctls = tegra_drm_ioctls, .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 0b8738fc444..3864a39f8ad 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -14,10 +14,19 @@ #include #include #include -#include -#include #include +struct tegra_fb { + struct drm_framebuffer base; + struct tegra_bo **planes; + unsigned int num_planes; +}; + +struct tegra_fbdev { + struct drm_fb_helper base; + struct tegra_fb *fb; +}; + struct host1x_drm { struct drm_device *drm; struct device *dev; @@ -33,7 +42,7 @@ struct host1x_drm { struct mutex clients_lock; struct list_head clients; - struct drm_fbdev_cma *fbdev; + struct tegra_fbdev *fbdev; }; struct host1x_client; @@ -226,8 +235,11 @@ extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output extern int tegra_output_exit(struct tegra_output *output); /* from fb.c */ +struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, + unsigned int index); 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; diff --git a/drivers/gpu/host1x/drm/fb.c b/drivers/gpu/host1x/drm/fb.c index 6ed885aead4..979a3e32b78 100644 --- a/drivers/gpu/host1x/drm/fb.c +++ b/drivers/gpu/host1x/drm/fb.c @@ -1,30 +1,343 @@ /* - * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012-2013 Avionic Design GmbH * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. * + * Based on the KMS/FB CMA helpers + * Copyright (C) 2012 Analog Device Inc. + * * 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 + #include "drm.h" +#include "gem.h" + +static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb) +{ + return container_of(fb, struct tegra_fb, base); +} + +static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) +{ + return container_of(helper, struct tegra_fbdev, base); +} + +struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, + unsigned int index) +{ + struct tegra_fb *fb = to_tegra_fb(framebuffer); + + if (index >= drm_format_num_planes(framebuffer->pixel_format)) + return NULL; + + return fb->planes[index]; +} + +static void tegra_fb_destroy(struct drm_framebuffer *framebuffer) +{ + struct tegra_fb *fb = to_tegra_fb(framebuffer); + unsigned int i; + + for (i = 0; i < fb->num_planes; i++) { + struct tegra_bo *bo = fb->planes[i]; + + if (bo) + drm_gem_object_unreference_unlocked(&bo->gem); + } + + drm_framebuffer_cleanup(framebuffer); + kfree(fb->planes); + kfree(fb); +} + +static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer, + struct drm_file *file, unsigned int *handle) +{ + struct tegra_fb *fb = to_tegra_fb(framebuffer); + + return drm_gem_handle_create(file, &fb->planes[0]->gem, handle); +} + +static struct drm_framebuffer_funcs tegra_fb_funcs = { + .destroy = tegra_fb_destroy, + .create_handle = tegra_fb_create_handle, +}; + +static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, + struct drm_mode_fb_cmd2 *mode_cmd, + struct tegra_bo **planes, + unsigned int num_planes) +{ + struct tegra_fb *fb; + unsigned int i; + int err; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) + return ERR_PTR(-ENOMEM); + + fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL); + if (!fb->planes) + return ERR_PTR(-ENOMEM); + + fb->num_planes = num_planes; + + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + + for (i = 0; i < fb->num_planes; i++) + fb->planes[i] = planes[i]; + + err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs); + if (err < 0) { + dev_err(drm->dev, "failed to initialize framebuffer: %d\n", + err); + kfree(fb->planes); + kfree(fb); + return ERR_PTR(err); + } + + return fb; +} + +static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, + struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd) +{ + unsigned int hsub, vsub, i; + struct tegra_bo *planes[4]; + struct drm_gem_object *gem; + struct tegra_fb *fb; + int err; + + hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); + vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); + + for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) { + unsigned int width = cmd->width / (i ? hsub : 1); + unsigned int height = cmd->height / (i ? vsub : 1); + unsigned int size, bpp; + + gem = drm_gem_object_lookup(drm, file, cmd->handles[i]); + if (!gem) { + err = -ENXIO; + goto unreference; + } + + bpp = drm_format_plane_cpp(cmd->pixel_format, i); + + size = (height - 1) * cmd->pitches[i] + + width * bpp + cmd->offsets[i]; + + if (gem->size < size) { + err = -EINVAL; + goto unreference; + } + + planes[i] = to_tegra_bo(gem); + } + + fb = tegra_fb_alloc(drm, cmd, planes, i); + if (IS_ERR(fb)) { + err = PTR_ERR(fb); + goto unreference; + } + + return &fb->base; + +unreference: + while (i--) + drm_gem_object_unreference_unlocked(&planes[i]->gem); -static void tegra_drm_fb_output_poll_changed(struct drm_device *drm) + return ERR_PTR(err); +} + +static struct fb_ops tegra_fb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int tegra_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct tegra_fbdev *fbdev = to_tegra_fbdev(helper); + struct drm_device *drm = helper->dev; + struct drm_mode_fb_cmd2 cmd = { 0 }; + unsigned int bytes_per_pixel; + struct drm_framebuffer *fb; + unsigned long offset; + struct fb_info *info; + struct tegra_bo *bo; + size_t size; + int err; + + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); + + cmd.width = sizes->surface_width; + cmd.height = sizes->surface_height; + cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; + cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = cmd.pitches[0] * cmd.height; + + bo = tegra_bo_create(drm, size); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + info = framebuffer_alloc(0, drm->dev); + if (!info) { + dev_err(drm->dev, "failed to allocate framebuffer info\n"); + tegra_bo_free_object(&bo->gem); + return -ENOMEM; + } + + fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1); + if (IS_ERR(fbdev->fb)) { + dev_err(drm->dev, "failed to allocate DRM framebuffer\n"); + err = PTR_ERR(fbdev->fb); + goto release; + } + + fb = &fbdev->fb->base; + helper->fb = fb; + helper->fbdev = info; + + info->par = helper; + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &tegra_fb_ops; + + err = fb_alloc_cmap(&info->cmap, 256, 0); + if (err < 0) { + dev_err(drm->dev, "failed to allocate color map: %d\n", err); + goto destroy; + } + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(info, helper, fb->width, fb->height); + + offset = info->var.xoffset * bytes_per_pixel + + info->var.yoffset * fb->pitches[0]; + + drm->mode_config.fb_base = (resource_size_t)bo->paddr; + info->screen_base = bo->vaddr + offset; + info->screen_size = size; + info->fix.smem_start = (unsigned long)(bo->paddr + offset); + info->fix.smem_len = size; + + return 0; + +destroy: + drm_framebuffer_unregister_private(fb); + tegra_fb_destroy(fb); +release: + framebuffer_release(info); + return err; +} + +static struct drm_fb_helper_funcs tegra_fb_helper_funcs = { + .fb_probe = tegra_fbdev_probe, +}; + +static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, + unsigned int preferred_bpp, + unsigned int num_crtc, + unsigned int max_connectors) +{ + struct drm_fb_helper *helper; + struct tegra_fbdev *fbdev; + int err; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(drm->dev, "failed to allocate DRM fbdev\n"); + return ERR_PTR(-ENOMEM); + } + + fbdev->base.funcs = &tegra_fb_helper_funcs; + helper = &fbdev->base; + + err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); + if (err < 0) { + dev_err(drm->dev, "failed to initialize DRM FB helper\n"); + goto free; + } + + err = drm_fb_helper_single_add_all_connectors(&fbdev->base); + if (err < 0) { + dev_err(drm->dev, "failed to add connectors\n"); + goto fini; + } + + drm_helper_disable_unused_functions(drm); + + err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); + if (err < 0) { + dev_err(drm->dev, "failed to set initial configuration\n"); + goto fini; + } + + return fbdev; + +fini: + drm_fb_helper_fini(&fbdev->base); +free: + kfree(fbdev); + return ERR_PTR(err); +} + +static void tegra_fbdev_free(struct tegra_fbdev *fbdev) +{ + struct fb_info *info = fbdev->base.fbdev; + + if (info) { + int err; + + err = unregister_framebuffer(info); + if (err < 0) + DRM_DEBUG_KMS("failed to unregister framebuffer\n"); + + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + + framebuffer_release(info); + } + + if (fbdev->fb) { + drm_framebuffer_unregister_private(&fbdev->fb->base); + tegra_fb_destroy(&fbdev->fb->base); + } + + drm_fb_helper_fini(&fbdev->base); + kfree(fbdev); +} + +static void tegra_fb_output_poll_changed(struct drm_device *drm) { struct host1x_drm *host1x = drm->dev_private; - drm_fbdev_cma_hotplug_event(host1x->fbdev); + if (host1x->fbdev) + drm_fb_helper_hotplug_event(&host1x->fbdev->base); } static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { - .fb_create = drm_fb_cma_create, - .output_poll_changed = tegra_drm_fb_output_poll_changed, + .fb_create = tegra_fb_create, + .output_poll_changed = tegra_fb_output_poll_changed, }; int tegra_drm_fb_init(struct drm_device *drm) { struct host1x_drm *host1x = drm->dev_private; - struct drm_fbdev_cma *fbdev; + struct tegra_fbdev *fbdev; drm->mode_config.min_width = 0; drm->mode_config.min_height = 0; @@ -34,7 +347,7 @@ int tegra_drm_fb_init(struct drm_device *drm) drm->mode_config.funcs = &tegra_drm_mode_funcs; - fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc, drm->mode_config.num_connector); if (IS_ERR(fbdev)) return PTR_ERR(fbdev); @@ -48,5 +361,14 @@ void tegra_drm_fb_exit(struct drm_device *drm) { struct host1x_drm *host1x = drm->dev_private; - drm_fbdev_cma_fini(host1x->fbdev); + tegra_fbdev_free(host1x->fbdev); +} + +void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) +{ + if (fbdev) { + drm_modeset_lock_all(fbdev->base.dev); + drm_fb_helper_restore_fbdev_mode(&fbdev->base); + drm_modeset_unlock_all(fbdev->base.dev); + } } diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c new file mode 100644 index 00000000000..c5e9a9b494c --- /dev/null +++ b/drivers/gpu/host1x/drm/gem.c @@ -0,0 +1,270 @@ +/* + * NVIDIA Tegra DRM GEM helper functions + * + * Copyright (C) 2012 Sascha Hauer, Pengutronix + * Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved. + * + * Based on the GEM/CMA helpers + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "gem.h" + +static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo) +{ + return container_of(bo, struct tegra_bo, base); +} + +static void tegra_bo_put(struct host1x_bo *bo) +{ + struct tegra_bo *obj = host1x_to_drm_bo(bo); + struct drm_device *drm = obj->gem.dev; + + mutex_lock(&drm->struct_mutex); + drm_gem_object_unreference(&obj->gem); + mutex_unlock(&drm->struct_mutex); +} + +static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) +{ + struct tegra_bo *obj = host1x_to_drm_bo(bo); + + return obj->paddr; +} + +static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) +{ +} + +static void *tegra_bo_mmap(struct host1x_bo *bo) +{ + struct tegra_bo *obj = host1x_to_drm_bo(bo); + + return obj->vaddr; +} + +static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) +{ +} + +static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) +{ + struct tegra_bo *obj = host1x_to_drm_bo(bo); + + return obj->vaddr + page * PAGE_SIZE; +} + +static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, + void *addr) +{ +} + +static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) +{ + struct tegra_bo *obj = host1x_to_drm_bo(bo); + struct drm_device *drm = obj->gem.dev; + + mutex_lock(&drm->struct_mutex); + drm_gem_object_reference(&obj->gem); + mutex_unlock(&drm->struct_mutex); + + return bo; +} + +const struct host1x_bo_ops tegra_bo_ops = { + .get = tegra_bo_get, + .put = tegra_bo_put, + .pin = tegra_bo_pin, + .unpin = tegra_bo_unpin, + .mmap = tegra_bo_mmap, + .munmap = tegra_bo_munmap, + .kmap = tegra_bo_kmap, + .kunmap = tegra_bo_kunmap, +}; + +static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) +{ + dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); +} + +unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo) +{ + return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT; +} + +struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) +{ + struct tegra_bo *bo; + int err; + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return ERR_PTR(-ENOMEM); + + host1x_bo_init(&bo->base, &tegra_bo_ops); + size = round_up(size, PAGE_SIZE); + + bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, + GFP_KERNEL | __GFP_NOWARN); + if (!bo->vaddr) { + dev_err(drm->dev, "failed to allocate buffer with size %u\n", + size); + err = -ENOMEM; + goto err_dma; + } + + err = drm_gem_object_init(drm, &bo->gem, size); + if (err) + goto err_init; + + err = drm_gem_create_mmap_offset(&bo->gem); + if (err) + goto err_mmap; + + return bo; + +err_mmap: + drm_gem_object_release(&bo->gem); +err_init: + tegra_bo_destroy(drm, bo); +err_dma: + kfree(bo); + + return ERR_PTR(err); + +} + +struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, + struct drm_device *drm, + unsigned int size, + unsigned int *handle) +{ + struct tegra_bo *bo; + int ret; + + bo = tegra_bo_create(drm, size); + if (IS_ERR(bo)) + return bo; + + ret = drm_gem_handle_create(file, &bo->gem, handle); + if (ret) + goto err; + + drm_gem_object_unreference_unlocked(&bo->gem); + + return bo; + +err: + tegra_bo_free_object(&bo->gem); + return ERR_PTR(ret); +} + +void tegra_bo_free_object(struct drm_gem_object *gem) +{ + struct tegra_bo *bo = to_tegra_bo(gem); + + if (gem->map_list.map) + drm_gem_free_mmap_offset(gem); + + drm_gem_object_release(gem); + tegra_bo_destroy(gem->dev, bo); + + kfree(bo); +} + +int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + struct tegra_bo *bo; + + if (args->pitch < min_pitch) + args->pitch = min_pitch; + + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + + bo = tegra_bo_create_with_handle(file, drm, args->size, + &args->handle); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + return 0; +} + +int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, + uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *gem; + struct tegra_bo *bo; + + mutex_lock(&drm->struct_mutex); + + gem = drm_gem_object_lookup(drm, file, handle); + if (!gem) { + dev_err(drm->dev, "failed to lookup GEM object\n"); + mutex_unlock(&drm->struct_mutex); + return -EINVAL; + } + + bo = to_tegra_bo(gem); + + *offset = tegra_bo_get_mmap_offset(bo); + + drm_gem_object_unreference(gem); + + mutex_unlock(&drm->struct_mutex); + + return 0; +} + +const struct vm_operations_struct tegra_bo_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct drm_gem_object *gem; + struct tegra_bo *bo; + int ret; + + ret = drm_gem_mmap(file, vma); + if (ret) + return ret; + + gem = vma->vm_private_data; + bo = to_tegra_bo(gem); + + ret = remap_pfn_range(vma, vma->vm_start, bo->paddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if (ret) + drm_gem_vm_close(vma); + + return ret; +} + +int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, + unsigned int handle) +{ + return drm_gem_handle_delete(file, handle); +} diff --git a/drivers/gpu/host1x/drm/gem.h b/drivers/gpu/host1x/drm/gem.h new file mode 100644 index 00000000000..34de2b486eb --- /dev/null +++ b/drivers/gpu/host1x/drm/gem.h @@ -0,0 +1,59 @@ +/* + * Tegra host1x GEM implementation + * + * 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, + * 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 . + */ + +#ifndef __HOST1X_GEM_H +#define __HOST1X_GEM_H + +#include +#include + +#include "host1x_bo.h" + +struct tegra_bo { + struct drm_gem_object gem; + struct host1x_bo base; + dma_addr_t paddr; + void *vaddr; +}; + +static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) +{ + return container_of(gem, struct tegra_bo, gem); +} + +extern const struct host1x_bo_ops tegra_bo_ops; + +struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size); +struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, + struct drm_device *drm, + unsigned int size, + unsigned int *handle); +void tegra_bo_free_object(struct drm_gem_object *gem); +unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo); +int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, + struct drm_mode_create_dumb *args); +int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, + uint32_t handle, uint64_t *offset); +int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, + unsigned int handle); + +int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); + +extern const struct vm_operations_struct tegra_bo_vm_ops; + +#endif -- cgit v1.2.3-70-g09d2 From d43f81cbaf43531a977e8b4c4427f19acf8a5061 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Fri, 22 Mar 2013 16:34:09 +0200 Subject: drm/tegra: Add gr2d device Add client driver for 2D device, and IOCTLs to pass work to host1x channel for 2D. Also adds functions that can be called to access sync points from DRM. Signed-off-by: Arto Merilainen Signed-off-by: Terje Bergstrom Reviewed-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Erik Faye-Lund Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 1 + drivers/gpu/host1x/dev.c | 7 + drivers/gpu/host1x/drm/Kconfig | 8 + drivers/gpu/host1x/drm/drm.c | 212 +++++++++++++++++++++++++- drivers/gpu/host1x/drm/drm.h | 27 +++- drivers/gpu/host1x/drm/gr2d.c | 339 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/host1x/host1x.h | 4 +- include/uapi/drm/Kbuild | 1 + include/uapi/drm/tegra_drm.h | 136 +++++++++++++++++ 9 files changed, 732 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/host1x/drm/gr2d.c create mode 100644 include/uapi/drm/tegra_drm.h (limited to 'drivers/gpu/host1x/drm/drm.c') diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 3768dbc2718..3b037b6e029 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -16,4 +16,5 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG 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 obj-$(CONFIG_TEGRA_HOST1X) += host1x.o diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 8ce9889cefd..28e28a23d44 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -209,11 +209,17 @@ static int __init tegra_host1x_init(void) 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 return 0; #ifdef CONFIG_DRM_TEGRA +unregister_hdmi: + platform_driver_unregister(&tegra_hdmi_driver); unregister_dc: platform_driver_unregister(&tegra_dc_driver); unregister_host1x: @@ -226,6 +232,7 @@ 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 diff --git a/drivers/gpu/host1x/drm/Kconfig b/drivers/gpu/host1x/drm/Kconfig index 9a28901d07c..aa9d0768d6e 100644 --- a/drivers/gpu/host1x/drm/Kconfig +++ b/drivers/gpu/host1x/drm/Kconfig @@ -13,6 +13,14 @@ config DRM_TEGRA if DRM_TEGRA +config DRM_TEGRA_STAGING + bool "Enable HOST1X interface" + depends on STAGING + help + Say yes if HOST1X should be available for userspace DRM users. + + If unsure, choose N. + config DRM_TEGRA_DEBUG bool "NVIDIA Tegra DRM debug support" help diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index c4e45c16f99..2b561c9118c 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Avionic Design GmbH - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012-2013 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 @@ -14,6 +14,9 @@ #include #include +#include +#include + #include "host1x_client.h" #include "dev.h" #include "drm.h" @@ -81,8 +84,10 @@ static int host1x_parse_dt(struct host1x_drm *host1x) static const char * const compat[] = { "nvidia,tegra20-dc", "nvidia,tegra20-hdmi", + "nvidia,tegra20-gr2d", "nvidia,tegra30-dc", "nvidia,tegra30-hdmi", + "nvidia,tegra30-gr2d", }; unsigned int i; int err; @@ -277,9 +282,24 @@ static int tegra_drm_unload(struct drm_device *drm) static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) { + struct host1x_drm_file *fpriv; + + fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); + if (!fpriv) + return -ENOMEM; + + INIT_LIST_HEAD(&fpriv->contexts); + filp->driver_priv = fpriv; + return 0; } +static void host1x_drm_context_free(struct host1x_drm_context *context) +{ + context->client->ops->close_channel(context); + kfree(context); +} + static void tegra_drm_lastclose(struct drm_device *drm) { struct host1x_drm *host1x = drm->dev_private; @@ -287,7 +307,190 @@ static void tegra_drm_lastclose(struct drm_device *drm) tegra_fbdev_restore_mode(host1x->fbdev); } +#ifdef CONFIG_DRM_TEGRA_STAGING +static bool host1x_drm_file_owns_context(struct host1x_drm_file *file, + struct host1x_drm_context *context) +{ + struct host1x_drm_context *ctx; + + list_for_each_entry(ctx, &file->contexts, list) + if (ctx == context) + return true; + + return false; +} + +static int tegra_gem_create(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_gem_create *args = data; + struct tegra_bo *bo; + + bo = tegra_bo_create_with_handle(file, drm, args->size, + &args->handle); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + return 0; +} + +static int tegra_gem_mmap(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_gem_mmap *args = data; + struct drm_gem_object *gem; + struct tegra_bo *bo; + + gem = drm_gem_object_lookup(drm, file, args->handle); + if (!gem) + return -EINVAL; + + bo = to_tegra_bo(gem); + + args->offset = tegra_bo_get_mmap_offset(bo); + + drm_gem_object_unreference(gem); + + return 0; +} + +static int tegra_syncpt_read(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_syncpt_read *args = data; + struct host1x *host = dev_get_drvdata(drm->dev); + struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + + if (!sp) + return -EINVAL; + + args->value = host1x_syncpt_read_min(sp); + return 0; +} + +static int tegra_syncpt_incr(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_syncpt_incr *args = data; + struct host1x *host = dev_get_drvdata(drm->dev); + struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + + if (!sp) + return -EINVAL; + + host1x_syncpt_incr(sp); + return 0; +} + +static int tegra_syncpt_wait(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_syncpt_wait *args = data; + struct host1x *host = dev_get_drvdata(drm->dev); + struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + + if (!sp) + return -EINVAL; + + return host1x_syncpt_wait(sp, args->thresh, args->timeout, + &args->value); +} + +static int tegra_open_channel(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_open_channel *args = data; + struct host1x_client *client; + struct host1x_drm_context *context; + struct host1x_drm_file *fpriv = file->driver_priv; + struct host1x_drm *host1x = drm->dev_private; + int err = -ENODEV; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return -ENOMEM; + + list_for_each_entry(client, &host1x->clients, list) + if (client->class == args->client) { + err = client->ops->open_channel(client, context); + if (err) + break; + + context->client = client; + list_add(&context->list, &fpriv->contexts); + args->context = (uintptr_t)context; + return 0; + } + + kfree(context); + return err; +} + +static int tegra_close_channel(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_close_channel *args = data; + struct host1x_drm_file *fpriv = file->driver_priv; + struct host1x_drm_context *context = + (struct host1x_drm_context *)(uintptr_t)args->context; + + if (!host1x_drm_file_owns_context(fpriv, context)) + return -EINVAL; + + list_del(&context->list); + host1x_drm_context_free(context); + + return 0; +} + +static int tegra_get_syncpt(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_get_syncpt *args = data; + struct host1x_drm_file *fpriv = file->driver_priv; + struct host1x_drm_context *context = + (struct host1x_drm_context *)(uintptr_t)args->context; + struct host1x_syncpt *syncpt; + + if (!host1x_drm_file_owns_context(fpriv, context)) + return -ENODEV; + + if (args->index >= context->client->num_syncpts) + return -EINVAL; + + syncpt = context->client->syncpts[args->index]; + args->id = host1x_syncpt_id(syncpt); + + return 0; +} + +static int tegra_submit(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct drm_tegra_submit *args = data; + struct host1x_drm_file *fpriv = file->driver_priv; + struct host1x_drm_context *context = + (struct host1x_drm_context *)(uintptr_t)args->context; + + if (!host1x_drm_file_owns_context(fpriv, context)) + return -ENODEV; + + return context->client->ops->submit(context, args, drm, file); +} +#endif + static struct drm_ioctl_desc tegra_drm_ioctls[] = { +#ifdef CONFIG_DRM_TEGRA_STAGING + DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED), +#endif }; static const struct file_operations tegra_drm_fops = { @@ -349,10 +552,17 @@ static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe) static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) { + struct host1x_drm_file *fpriv = file->driver_priv; + struct host1x_drm_context *context, *tmp; struct drm_crtc *crtc; list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) tegra_dc_cancel_page_flip(crtc, file); + + list_for_each_entry_safe(context, tmp, &fpriv->contexts, list) + host1x_drm_context_free(context); + + kfree(fpriv); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 3864a39f8ad..02ce020f257 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Avionic Design GmbH - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012-2013 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 @@ -15,6 +15,9 @@ #include #include #include +#include + +#include "host1x.h" struct tegra_fb { struct drm_framebuffer base; @@ -47,9 +50,25 @@ struct host1x_drm { struct host1x_client; +struct host1x_drm_context { + struct host1x_client *client; + struct host1x_channel *channel; + struct list_head list; +}; + struct host1x_client_ops { int (*drm_init)(struct host1x_client *client, struct drm_device *drm); int (*drm_exit)(struct host1x_client *client); + int (*open_channel)(struct host1x_client *client, + struct host1x_drm_context *context); + void (*close_channel)(struct host1x_drm_context *context); + int (*submit)(struct host1x_drm_context *context, + struct drm_tegra_submit *args, struct drm_device *drm, + struct drm_file *file); +}; + +struct host1x_drm_file { + struct list_head contexts; }; struct host1x_client { @@ -58,6 +77,12 @@ struct host1x_client { const struct host1x_client_ops *ops; + enum host1x_class class; + struct host1x_channel *channel; + + struct host1x_syncpt **syncpts; + unsigned int num_syncpts; + struct list_head list; }; diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c new file mode 100644 index 00000000000..6a45ae090ee --- /dev/null +++ b/drivers/gpu/host1x/drm/gr2d.c @@ -0,0 +1,339 @@ +/* + * drivers/video/tegra/host/gr2d/gr2d.c + * + * Tegra Graphics 2D + * + * 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, + * 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 . + */ + +#include +#include +#include +#include + +#include "channel.h" +#include "drm.h" +#include "gem.h" +#include "job.h" +#include "host1x.h" +#include "host1x_bo.h" +#include "host1x_client.h" +#include "syncpt.h" + +struct gr2d { + struct host1x_client client; + struct clk *clk; + struct host1x_channel *channel; + unsigned long *addr_regs; +}; + +static inline struct gr2d *to_gr2d(struct host1x_client *client) +{ + return container_of(client, struct gr2d, client); +} + +static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg); + +static int gr2d_client_init(struct host1x_client *client, + struct drm_device *drm) +{ + return 0; +} + +static int gr2d_client_exit(struct host1x_client *client) +{ + return 0; +} + +static int gr2d_open_channel(struct host1x_client *client, + struct host1x_drm_context *context) +{ + struct gr2d *gr2d = to_gr2d(client); + + context->channel = host1x_channel_get(gr2d->channel); + + if (!context->channel) + return -ENOMEM; + + return 0; +} + +static void gr2d_close_channel(struct host1x_drm_context *context) +{ + host1x_channel_put(context->channel); +} + +static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm, + struct drm_file *file, + u32 handle) +{ + struct drm_gem_object *gem; + struct tegra_bo *bo; + + gem = drm_gem_object_lookup(drm, file, handle); + if (!gem) + return 0; + + mutex_lock(&drm->struct_mutex); + drm_gem_object_unreference(gem); + mutex_unlock(&drm->struct_mutex); + + bo = to_tegra_bo(gem); + return &bo->base; +} + +static int gr2d_submit(struct host1x_drm_context *context, + struct drm_tegra_submit *args, struct drm_device *drm, + struct drm_file *file) +{ + struct host1x_job *job; + unsigned int num_cmdbufs = args->num_cmdbufs; + unsigned int num_relocs = args->num_relocs; + unsigned int num_waitchks = args->num_waitchks; + struct drm_tegra_cmdbuf __user *cmdbufs = + (void * __user)(uintptr_t)args->cmdbufs; + struct drm_tegra_reloc __user *relocs = + (void * __user)(uintptr_t)args->relocs; + struct drm_tegra_waitchk __user *waitchks = + (void * __user)(uintptr_t)args->waitchks; + struct drm_tegra_syncpt syncpt; + int err; + + /* We don't yet support other than one syncpt_incr struct per submit */ + if (args->num_syncpts != 1) + return -EINVAL; + + job = host1x_job_alloc(context->channel, args->num_cmdbufs, + args->num_relocs, args->num_waitchks); + if (!job) + return -ENOMEM; + + job->num_relocs = args->num_relocs; + job->num_waitchk = args->num_waitchks; + job->client = (u32)args->context; + job->class = context->client->class; + job->serialize = true; + + while (num_cmdbufs) { + struct drm_tegra_cmdbuf cmdbuf; + struct host1x_bo *bo; + + err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf)); + if (err) + goto fail; + + bo = host1x_bo_lookup(drm, file, cmdbuf.handle); + if (!bo) + goto fail; + + host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); + num_cmdbufs--; + cmdbufs++; + } + + err = copy_from_user(job->relocarray, relocs, + sizeof(*relocs) * num_relocs); + if (err) + goto fail; + + while (num_relocs--) { + struct host1x_reloc *reloc = &job->relocarray[num_relocs]; + struct host1x_bo *cmdbuf, *target; + + cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf); + target = host1x_bo_lookup(drm, file, (u32)reloc->target); + + reloc->cmdbuf = cmdbuf; + reloc->target = target; + + if (!reloc->target || !reloc->cmdbuf) + goto fail; + } + + err = copy_from_user(job->waitchk, waitchks, + sizeof(*waitchks) * num_waitchks); + if (err) + goto fail; + + err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts, + sizeof(syncpt)); + if (err) + goto fail; + + job->syncpt_id = syncpt.id; + job->syncpt_incrs = syncpt.incrs; + job->timeout = 10000; + job->is_addr_reg = gr2d_is_addr_reg; + + if (args->timeout && args->timeout < 10000) + job->timeout = args->timeout; + + err = host1x_job_pin(job, context->client->dev); + if (err) + goto fail; + + err = host1x_job_submit(job); + if (err) + goto fail_submit; + + args->fence = job->syncpt_end; + + host1x_job_put(job); + return 0; + +fail_submit: + host1x_job_unpin(job); +fail: + host1x_job_put(job); + return err; +} + +static struct host1x_client_ops gr2d_client_ops = { + .drm_init = gr2d_client_init, + .drm_exit = gr2d_client_exit, + .open_channel = gr2d_open_channel, + .close_channel = gr2d_close_channel, + .submit = gr2d_submit, +}; + +static void gr2d_init_addr_reg_map(struct device *dev, struct gr2d *gr2d) +{ + const u32 gr2d_addr_regs[] = {0x1a, 0x1b, 0x26, 0x2b, 0x2c, 0x2d, 0x31, + 0x32, 0x48, 0x49, 0x4a, 0x4b, 0x4c}; + unsigned long *bitmap; + int i; + + bitmap = devm_kzalloc(dev, DIV_ROUND_UP(256, BITS_PER_BYTE), + GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); ++i) { + u32 reg = gr2d_addr_regs[i]; + bitmap[BIT_WORD(reg)] |= BIT_MASK(reg); + } + + gr2d->addr_regs = bitmap; +} + +static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg) +{ + struct gr2d *gr2d = dev_get_drvdata(dev); + + switch (class) { + case HOST1X_CLASS_HOST1X: + return reg == 0x2b; + case HOST1X_CLASS_GR2D: + case HOST1X_CLASS_GR2D_SB: + reg &= 0xff; + if (gr2d->addr_regs[BIT_WORD(reg)] & BIT_MASK(reg)) + return 1; + default: + return 0; + } +} + +static const struct of_device_id gr2d_match[] = { + { .compatible = "nvidia,tegra30-gr2d" }, + { .compatible = "nvidia,tegra20-gr2d" }, + { }, +}; + +static int gr2d_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct host1x_drm *host1x = host1x_get_drm_data(dev->parent); + int err; + struct gr2d *gr2d = NULL; + struct host1x_syncpt **syncpts; + + gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL); + if (!gr2d) + return -ENOMEM; + + syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL); + if (!syncpts) + return -ENOMEM; + + gr2d->clk = devm_clk_get(dev, NULL); + if (IS_ERR(gr2d->clk)) { + dev_err(dev, "cannot get clock\n"); + return PTR_ERR(gr2d->clk); + } + + err = clk_prepare_enable(gr2d->clk); + if (err) { + dev_err(dev, "cannot turn on clock\n"); + return err; + } + + gr2d->channel = host1x_channel_request(dev); + if (!gr2d->channel) + return -ENOMEM; + + *syncpts = host1x_syncpt_request(dev, 0); + if (!(*syncpts)) { + host1x_channel_free(gr2d->channel); + return -ENOMEM; + } + + gr2d->client.ops = &gr2d_client_ops; + gr2d->client.dev = dev; + gr2d->client.class = HOST1X_CLASS_GR2D; + gr2d->client.syncpts = syncpts; + gr2d->client.num_syncpts = 1; + + err = host1x_register_client(host1x, &gr2d->client); + if (err < 0) { + dev_err(dev, "failed to register host1x client: %d\n", err); + return err; + } + + gr2d_init_addr_reg_map(dev, gr2d); + + platform_set_drvdata(pdev, gr2d); + + return 0; +} + +static int __exit gr2d_remove(struct platform_device *pdev) +{ + struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent); + struct gr2d *gr2d = platform_get_drvdata(pdev); + unsigned int i; + int err; + + err = host1x_unregister_client(host1x, &gr2d->client); + if (err < 0) { + dev_err(&pdev->dev, "failed to unregister client: %d\n", err); + return err; + } + + for (i = 0; i < gr2d->client.num_syncpts; i++) + host1x_syncpt_free(gr2d->client.syncpts[i]); + + host1x_channel_free(gr2d->channel); + clk_disable_unprepare(gr2d->clk); + + return 0; +} + +struct platform_driver tegra_gr2d_driver = { + .probe = gr2d_probe, + .remove = __exit_p(gr2d_remove), + .driver = { + .owner = THIS_MODULE, + .name = "gr2d", + .of_match_table = gr2d_match, + } +}; diff --git a/drivers/gpu/host1x/host1x.h b/drivers/gpu/host1x/host1x.h index bca6563f676..a2bc1e65e97 100644 --- a/drivers/gpu/host1x/host1x.h +++ b/drivers/gpu/host1x/host1x.h @@ -22,7 +22,9 @@ #define __LINUX_HOST1X_H enum host1x_class { - HOST1X_CLASS_HOST1X = 0x1 + HOST1X_CLASS_HOST1X = 0x1, + HOST1X_CLASS_GR2D = 0x51, + HOST1X_CLASS_GR2D_SB = 0x52 }; #endif diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild index a042a957296..119487e05e6 100644 --- a/include/uapi/drm/Kbuild +++ b/include/uapi/drm/Kbuild @@ -13,5 +13,6 @@ header-y += r128_drm.h header-y += radeon_drm.h header-y += savage_drm.h header-y += sis_drm.h +header-y += tegra_drm.h header-y += via_drm.h header-y += vmwgfx_drm.h diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h new file mode 100644 index 00000000000..6e132a2f742 --- /dev/null +++ b/include/uapi/drm/tegra_drm.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * 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 . + */ + +#ifndef _UAPI_TEGRA_DRM_H_ +#define _UAPI_TEGRA_DRM_H_ + +struct drm_tegra_gem_create { + __u64 size; + __u32 flags; + __u32 handle; +}; + +struct drm_tegra_gem_mmap { + __u32 handle; + __u32 offset; +}; + +struct drm_tegra_syncpt_read { + __u32 id; + __u32 value; +}; + +struct drm_tegra_syncpt_incr { + __u32 id; + __u32 pad; +}; + +struct drm_tegra_syncpt_wait { + __u32 id; + __u32 thresh; + __u32 timeout; + __u32 value; +}; + +#define DRM_TEGRA_NO_TIMEOUT (0xffffffff) + +struct drm_tegra_open_channel { + __u32 client; + __u32 pad; + __u64 context; +}; + +struct drm_tegra_close_channel { + __u64 context; +}; + +struct drm_tegra_get_syncpt { + __u64 context; + __u32 index; + __u32 id; +}; + +struct drm_tegra_syncpt { + __u32 id; + __u32 incrs; +}; + +struct drm_tegra_cmdbuf { + __u32 handle; + __u32 offset; + __u32 words; + __u32 pad; +}; + +struct drm_tegra_reloc { + struct { + __u32 handle; + __u32 offset; + } cmdbuf; + struct { + __u32 handle; + __u32 offset; + } target; + __u32 shift; + __u32 pad; +}; + +struct drm_tegra_waitchk { + __u32 handle; + __u32 offset; + __u32 syncpt; + __u32 thresh; +}; + +struct drm_tegra_submit { + __u64 context; + __u32 num_syncpts; + __u32 num_cmdbufs; + __u32 num_relocs; + __u32 num_waitchks; + __u32 waitchk_mask; + __u32 timeout; + __u32 pad; + __u64 syncpts; + __u64 cmdbufs; + __u64 relocs; + __u64 waitchks; + __u32 fence; /* Return value */ + + __u32 reserved[5]; /* future expansion */ +}; + +#define DRM_TEGRA_GEM_CREATE 0x00 +#define DRM_TEGRA_GEM_MMAP 0x01 +#define DRM_TEGRA_SYNCPT_READ 0x02 +#define DRM_TEGRA_SYNCPT_INCR 0x03 +#define DRM_TEGRA_SYNCPT_WAIT 0x04 +#define DRM_TEGRA_OPEN_CHANNEL 0x05 +#define DRM_TEGRA_CLOSE_CHANNEL 0x06 +#define DRM_TEGRA_GET_SYNCPT 0x07 +#define DRM_TEGRA_SUBMIT 0x08 + +#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) +#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap) +#define DRM_IOCTL_TEGRA_SYNCPT_READ DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_READ, struct drm_tegra_syncpt_read) +#define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr) +#define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait) +#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel) +#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel) +#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt) +#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit) + +#endif -- cgit v1.2.3-70-g09d2