Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d43f81cb authored by Terje Bergstrom's avatar Terje Bergstrom Committed by Thierry Reding
Browse files

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: default avatarArto Merilainen <amerilainen@nvidia.com>
Signed-off-by: default avatarTerje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: default avatarThierry Reding <thierry.reding@avionic-design.de>
Tested-by: default avatarThierry Reding <thierry.reding@avionic-design.de>
Tested-by: default avatarErik Faye-Lund <kusmabite@gmail.com>
Signed-off-by: default avatarThierry Reding <thierry.reding@avionic-design.de>
parent de2ba664
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+7 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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
+211 −1
Original line number Diff line number Diff line
/*
 * 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 <linux/dma-mapping.h>
#include <asm/dma-iommu.h>

#include <drm/drm.h>
#include <drm/drmP.h>

#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
+26 −1
Original line number Diff line number Diff line
/*
 * 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 <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fixed.h>
#include <uapi/drm/tegra_drm.h>

#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;
};

Loading