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

Commit 72702a80 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: update the fence support in sde" into msm-4.9

parents 519dacfd 39323d48
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm -Idrivers/gpu/drm/msm/dsi-staging
ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
ccflags-$(CONFIG_SYNC) += -Idrivers/staging/android
ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi
ccflags-y += -Idrivers/gpu/drm/msm/sde

@@ -71,7 +70,7 @@ msm_drm-$(CONFIG_DRM_MSM_MDP4) += mdp/mdp4/mdp4_crtc.o \
	mdp/mdp4/mdp4_plane.o

msm_drm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
msm_drm-$(CONFIG_SYNC) += sde/sde_fence.o
msm_drm-$(CONFIG_SYNC_FILE) += sde/sde_fence.o
msm_drm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
msm_drm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
msm_drm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_phy_8996.o
+2 −2
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ struct sde_connector_ops {
 * @mmu_secure: MMU id for secure buffers
 * @mmu_unsecure: MMU id for unsecure buffers
 * @name: ASCII name of connector
 * @retire_fence: Retire fence reference
 * @retire_fence: Retire fence context reference
 * @ops: Local callback function pointer table
 * @property_info: Private structure for generic property handling
 * @property_data: Array of private data for generic property handling
@@ -152,7 +152,7 @@ struct sde_connector {

	char name[SDE_CONNECTOR_NAME_SIZE];

	struct sde_fence retire_fence;
	struct sde_fence_context retire_fence;
	struct sde_connector_ops ops;

	struct msm_property_info property_info;
+8 −6
Original line number Diff line number Diff line
@@ -693,6 +693,7 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
	struct drm_plane *plane = NULL;
	uint32_t wait_ms = 1;
	ktime_t kt_end, kt_wait;
	int rc = 0;

	SDE_DEBUG("\n");

@@ -712,18 +713,19 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
	 * Limit total wait time to INPUT_FENCE_TIMEOUT, but still call
	 * sde_plane_wait_input_fence with wait_ms == 0 after the timeout so
	 * that each plane can check its fence status and react appropriately
	 * if its fence has timed out.
	 * if its fence has timed out. Call input fence wait multiple times if
	 * fence wait is interrupted due to interrupt call.
	 */
	drm_atomic_crtc_for_each_plane(plane, crtc) {
		if (wait_ms) {
			/* determine updated wait time */
		do {
			kt_wait = ktime_sub(kt_end, ktime_get());
			if (ktime_compare(kt_wait, ktime_set(0, 0)) >= 0)
				wait_ms = ktime_to_ms(kt_wait);
			else
				wait_ms = 0;
		}
		sde_plane_wait_input_fence(plane, wait_ms);

			rc = sde_plane_wait_input_fence(plane, wait_ms);
		} while (wait_ms && rc == -ERESTARTSYS);
	}
}

@@ -1692,7 +1694,7 @@ static int sde_crtc_atomic_get_property(struct drm_crtc *crtc,
		cstate = to_sde_crtc_state(state);
		i = msm_property_index(&sde_crtc->property_info, property);
		if (i == CRTC_PROP_OUTPUT_FENCE) {
			int offset = sde_crtc_get_property(cstate,
			uint32_t offset = sde_crtc_get_property(cstate,
					CRTC_PROP_OUTPUT_FENCE_OFFSET);

			ret = sde_fence_create(
+2 −1
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ struct sde_crtc_frame_event {
 * @drm_requested_vblank : Whether vblanks have been enabled in the encoder
 * @property_info : Opaque structure for generic property support
 * @property_defaults : Array of default values for generic property support
 * @output_fence  : output release fence context
 * @stage_cfg     : H/w mixer stage configuration
 * @debugfs_root  : Parent of debugfs node
 * @vblank_cb_count : count of vblank callback since last reset
@@ -122,7 +123,7 @@ struct sde_crtc {
	struct drm_property_blob *blob_info;

	/* output fence support */
	struct sde_fence output_fence;
	struct sde_fence_context output_fence;

	struct sde_hw_stage_cfg stage_cfg;
	struct dentry *debugfs_root;
+224 −121
Original line number Diff line number Diff line
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2017, The Linux Foundation. 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 and
@@ -10,8 +10,8 @@
 * GNU General Public License for more details.
 */

#include <sync.h>
#include <sw_sync.h>
#include <linux/sync_file.h>
#include <linux/fence.h>
#include "msm_drv.h"
#include "sde_kms.h"
#include "sde_fence.h"
@@ -19,31 +19,41 @@
void *sde_sync_get(uint64_t fd)
{
	/* force signed compare, fdget accepts an int argument */
	return (signed int)fd >= 0 ? sync_fence_fdget(fd) : NULL;
	return (signed int)fd >= 0 ? sync_file_get_fence(fd) : NULL;
}

void sde_sync_put(void *fence)
{
	if (fence)
		sync_fence_put(fence);
		fence_put(fence);
}

int sde_sync_wait(void *fence, long timeout_ms)
signed long sde_sync_wait(void *fnc, long timeout_ms)
{
	struct fence *fence = fnc;

	if (!fence)
		return -EINVAL;
	return sync_fence_wait(fence, timeout_ms);
	else if (fence_is_signaled(fence))
		return timeout_ms ? msecs_to_jiffies(timeout_ms) : 1;

	return fence_wait_timeout(fence, true,
				msecs_to_jiffies(timeout_ms));
}

uint32_t sde_sync_get_name_prefix(void *fence)
{
	char *name;
	const char *name;
	uint32_t i, prefix;
	struct fence *f = fence;

	if (!fence)
		return 0x0;
		return 0;

	name = f->ops->get_driver_name(f);
	if (!name)
		return 0;

	name = ((struct sync_fence *)fence)->name;
	prefix = 0x0;
	for (i = 0; i < sizeof(uint32_t) && name[i]; ++i)
		prefix = (prefix << CHAR_BIT) | name[i];
@@ -51,125 +61,208 @@ uint32_t sde_sync_get_name_prefix(void *fence)
	return prefix;
}

#if IS_ENABLED(CONFIG_SW_SYNC)
/**
 * struct sde_fence - release/retire fence structure
 * @fence: base fence structure
 * @name: name of each fence- it is fence timeline + commit_count
 * @fence_list: list to associated this fence on timeline/context
 * @fd: fd attached to this fence - debugging purpose.
 */
struct sde_fence {
	struct fence base;
	struct sde_fence_context *ctx;
	char name[SDE_FENCE_NAME_SIZE];
	struct list_head	fence_list;
	int fd;
};

static inline struct sde_fence *to_sde_fence(struct fence *fence)
{
	return container_of(fence, struct sde_fence, base);
}

static const char *sde_fence_get_driver_name(struct fence *fence)
{
	struct sde_fence *f = to_sde_fence(fence);

	return f->name;
}

static const char *sde_fence_get_timeline_name(struct fence *fence)
{
	struct sde_fence *f = to_sde_fence(fence);

	return f->ctx->name;
}

static bool sde_fence_enable_signaling(struct fence *fence)
{
	return true;
}

static bool sde_fence_signaled(struct fence *fence)
{
	struct sde_fence *f = to_sde_fence(fence);
	bool status;

	status = (int)(fence->seqno - f->ctx->done_count) <= 0 ? true : false;
	SDE_DEBUG("status:%d fence seq:%d and timeline:%d\n",
			status, fence->seqno, f->ctx->done_count);
	return status;
}

static void sde_fence_release(struct fence *fence)
{
	struct sde_fence *f = to_sde_fence(fence);

	kfree_rcu(f, base.rcu);
}

static void sde_fence_value_str(struct fence *fence,
				    char *str, int size)
{
	snprintf(str, size, "%d", fence->seqno);
}

static void sde_fence_timeline_value_str(struct fence *fence,
					     char *str, int size)
{
	struct sde_fence *f = to_sde_fence(fence);

	snprintf(str, size, "%d", f->ctx->done_count);
}

static struct fence_ops sde_fence_ops = {
	.get_driver_name = sde_fence_get_driver_name,
	.get_timeline_name = sde_fence_get_timeline_name,
	.enable_signaling = sde_fence_enable_signaling,
	.signaled = sde_fence_signaled,
	.wait = fence_default_wait,
	.release = sde_fence_release,
	.fence_value_str = sde_fence_value_str,
	.timeline_value_str = sde_fence_timeline_value_str,
};

/**
 * _sde_fence_create_fd - create fence object and return an fd for it
 * This function is NOT thread-safe.
 * @timeline: Timeline to associate with fence
 * @name: Name for fence
 * @val: Timeline value at which to signal the fence
 * Return: File descriptor on success, or error code on error
 */
static int _sde_fence_create_fd(void *timeline, const char *name, uint32_t val)
static int _sde_fence_create_fd(void *fence_ctx, uint32_t val)
{
	struct sync_pt *sync_pt;
	struct sync_fence *fence;
	struct sde_fence *sde_fence;
	struct sync_file *sync_file;
	signed int fd = -EINVAL;
	struct sde_fence_context *ctx = fence_ctx;
	unsigned long flags;

	if (!timeline) {
		SDE_ERROR("invalid timeline\n");
	if (!ctx) {
		SDE_ERROR("invalid context\n");
		goto exit;
	}

	if (!name)
		name = "sde_fence";
	sde_fence = kzalloc(sizeof(*sde_fence), GFP_KERNEL);
	if (!sde_fence)
		return -ENOMEM;

	/* create sync point */
	sync_pt = sw_sync_pt_create(timeline, val);
	if (sync_pt == NULL) {
		SDE_ERROR("failed to create sync point, %s\n", name);
		goto exit;
	}
	snprintf(sde_fence->name, SDE_FENCE_NAME_SIZE, "fence%u", val);

	/* create fence */
	fence = sync_fence_create(name, sync_pt);
	if (fence == NULL) {
		sync_pt_free(sync_pt);
		SDE_ERROR("couldn't create fence, %s\n", name);
		goto exit;
	}
	fence_init(&sde_fence->base, &sde_fence_ops, &ctx->lock,
		ctx->context, val);

	/* create fd */
	fd = get_unused_fd_flags(0);
	if (fd < 0) {
		SDE_ERROR("failed to get_unused_fd_flags(), %s\n", name);
		sync_fence_put(fence);
		fence_put(&sde_fence->base);
		SDE_ERROR("failed to get_unused_fd_flags(), %s\n",
							sde_fence->name);
		goto exit;
	}

	sync_fence_install(fence, fd);
	/* create fence */
	sync_file = sync_file_create(&sde_fence->base);
	if (sync_file == NULL) {
		put_unused_fd(fd);
		fence_put(&sde_fence->base);
		SDE_ERROR("couldn't create fence, %s\n", sde_fence->name);
		goto exit;
	}

	fd_install(fd, sync_file->file);

	spin_lock_irqsave(&ctx->lock, flags);
	sde_fence->ctx = fence_ctx;
	sde_fence->fd = fd;
	list_add_tail(&sde_fence->fence_list, &ctx->fence_list_head);
	kref_get(&ctx->kref);
	spin_unlock_irqrestore(&ctx->lock, flags);
exit:
	return fd;
}

/**
 * SDE_FENCE_TIMELINE_NAME - macro for accessing s/w timeline's name
 * @fence: Pointer to sde fence structure
 * @drm_id: ID number of owning DRM Object
 * Returns: Pointer to timeline name string
 */
#define SDE_FENCE_TIMELINE_NAME(fence) \
	(((struct sw_sync_timeline *)fence->timeline)->obj.name)

int sde_fence_init(struct sde_fence *fence,
int sde_fence_init(struct sde_fence_context *ctx,
		const char *name,
		uint32_t drm_id)
{
	if (!fence) {
	if (!ctx) {
		SDE_ERROR("invalid argument(s)\n");
		return -EINVAL;
	}
	memset(ctx, 0, sizeof(*ctx));

	fence->timeline = sw_sync_timeline_create(name ? name : "sde");
	if (!fence->timeline) {
		SDE_ERROR("failed to create timeline\n");
		return -ENOMEM;
	}
	strlcpy(ctx->name, name, SDE_FENCE_NAME_SIZE);
	ctx->drm_id = drm_id;
	kref_init(&ctx->kref);
	ctx->context = fence_context_alloc(1);

	fence->commit_count = 0;
	fence->done_count = 0;
	fence->drm_id = drm_id;
	spin_lock_init(&ctx->lock);
	INIT_LIST_HEAD(&ctx->fence_list_head);

	mutex_init(&fence->fence_lock);
	return 0;
}

static void sde_fence_destroy(struct kref *kref)
{
}

void sde_fence_deinit(struct sde_fence *fence)
void sde_fence_deinit(struct sde_fence_context *ctx)
{
	if (!fence) {
	if (!ctx) {
		SDE_ERROR("invalid fence\n");
		return;
	}

	mutex_destroy(&fence->fence_lock);
	if (fence->timeline)
		sync_timeline_destroy(fence->timeline);
	kref_put(&ctx->kref, sde_fence_destroy);
}

int sde_fence_prepare(struct sde_fence *fence)
void sde_fence_prepare(struct sde_fence_context *ctx)
{
	if (!fence) {
		SDE_ERROR("invalid fence\n");
		return -EINVAL;
	}
	unsigned long flags;

	mutex_lock(&fence->fence_lock);
	++fence->commit_count;
	SDE_EVT32(fence->drm_id, fence->commit_count, fence->done_count);
	mutex_unlock(&fence->fence_lock);
	return 0;
	if (!ctx) {
		SDE_ERROR("invalid argument(s), fence %pK\n", ctx);
	} else {
		spin_lock_irqsave(&ctx->lock, flags);
		++ctx->commit_count;
		spin_unlock_irqrestore(&ctx->lock, flags);
	}
}

int sde_fence_create(struct sde_fence *fence, uint64_t *val, int offset)
int sde_fence_create(struct sde_fence_context *ctx, uint64_t *val,
							uint32_t offset)
{
	uint32_t trigger_value;
	int fd, rc = -EINVAL;
	unsigned long flags;

	if (!ctx || !val) {
		SDE_ERROR("invalid argument(s), fence %d, pval %d\n",
				ctx != NULL, val != NULL);
		return rc;
	}

	if (!fence || !fence->timeline || !val) {
		SDE_ERROR("invalid argument(s), fence %pK, pval %pK\n",
				fence, val);
	} else  {
	/*
	 * Allow created fences to have a constant offset with respect
	 * to the timeline. This allows us to delay the fence signalling
@@ -178,55 +271,65 @@ int sde_fence_create(struct sde_fence *fence, uint64_t *val, int offset)
	 * after an additional delay of one commit, rather than at the
	 * end of the current one.
	 */
		mutex_lock(&fence->fence_lock);
		trigger_value = fence->commit_count + (int32_t)offset;
		fd = _sde_fence_create_fd(fence->timeline,
				SDE_FENCE_TIMELINE_NAME(fence),
				trigger_value);
	spin_lock_irqsave(&ctx->lock, flags);
	trigger_value = ctx->commit_count + offset;

	spin_unlock_irqrestore(&ctx->lock, flags);

	fd = _sde_fence_create_fd(ctx, trigger_value);
	*val = fd;
	SDE_DEBUG("fence_create::fd:%d trigger:%d commit:%d offset:%d\n",
				fd, trigger_value, ctx->commit_count, offset);

		SDE_EVT32(fence->drm_id, trigger_value, fd);
		mutex_unlock(&fence->fence_lock);
	SDE_EVT32(ctx->drm_id, trigger_value, fd);

	if (fd >= 0)
		rc = 0;
	}
	else
		rc = fd;

	return rc;
}

void sde_fence_signal(struct sde_fence *fence, bool is_error)
void sde_fence_signal(struct sde_fence_context *ctx, bool is_error)
{
	if (!fence || !fence->timeline) {
		SDE_ERROR("invalid fence, %pK\n", fence);
	unsigned long flags;
	struct sde_fence *fc, *next;

	if (!ctx) {
		SDE_ERROR("invalid ctx, %pK\n", ctx);
		return;
	} else if (is_error) {
		return;
	}

	mutex_lock(&fence->fence_lock);
	if ((fence->done_count - fence->commit_count) < 0)
		++fence->done_count;
	else
		SDE_ERROR("detected extra signal attempt!\n");
	spin_lock_irqsave(&ctx->lock, flags);
	if ((int)(ctx->done_count - ctx->commit_count) < 0) {
		++ctx->done_count;
	} else {
		SDE_ERROR("extra signal attempt! done count:%d commit:%d\n",
					ctx->done_count, ctx->commit_count);
		goto end;
	}

	/*
	 * Always advance 'done' counter,
	 * but only advance timeline if !error
	 */
	if (!is_error) {
		int32_t val;

		val = fence->done_count;
		val -= ((struct sw_sync_timeline *)
				fence->timeline)->value;
		if (val < 0)
			SDE_ERROR("invalid value\n");
		else
			sw_sync_timeline_inc(fence->timeline, (int)val);
	if (list_empty(&ctx->fence_list_head)) {
		SDE_DEBUG("nothing to trigger!-no get_prop call\n");
		goto end;
	}

	SDE_DEBUG("fence_signal:done count:%d commit count:%d\n",
					ctx->commit_count, ctx->done_count);

	list_for_each_entry_safe(fc, next, &ctx->fence_list_head,
				 fence_list) {
		if (fence_is_signaled_locked(&fc->base)) {
			list_del_init(&fc->fence_list);
			kref_put(&ctx->kref, sde_fence_destroy);
		}
	}

	SDE_EVT32(fence->drm_id, fence->done_count,
			((struct sw_sync_timeline *) fence->timeline)->value);
	SDE_EVT32(ctx->drm_id, ctx->done_count);

	mutex_unlock(&fence->fence_lock);
end:
	spin_unlock_irqrestore(&ctx->lock, flags);
}
#endif
Loading