18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Samsung Electronics Co.Ltd
38c2ecf20Sopenharmony_ci * Authors:
48c2ecf20Sopenharmony_ci *	Marek Szyprowski <m.szyprowski@samsung.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Exynos DRM Image Post Processing (IPP) related functions
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
98c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
108c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
118c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
128c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
138c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
168c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_mode.h>
248c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h"
278c2ecf20Sopenharmony_ci#include "exynos_drm_gem.h"
288c2ecf20Sopenharmony_ci#include "exynos_drm_ipp.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int num_ipp;
318c2ecf20Sopenharmony_cistatic LIST_HEAD(ipp_list);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/**
348c2ecf20Sopenharmony_ci * exynos_drm_ipp_register - Register a new picture processor hardware module
358c2ecf20Sopenharmony_ci * @dev: DRM device
368c2ecf20Sopenharmony_ci * @ipp: ipp module to init
378c2ecf20Sopenharmony_ci * @funcs: callbacks for the new ipp object
388c2ecf20Sopenharmony_ci * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*)
398c2ecf20Sopenharmony_ci * @formats: array of supported formats
408c2ecf20Sopenharmony_ci * @num_formats: size of the supported formats array
418c2ecf20Sopenharmony_ci * @name: name (for debugging purposes)
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Initializes a ipp module.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * Returns:
468c2ecf20Sopenharmony_ci * Zero on success, error code on failure.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ciint exynos_drm_ipp_register(struct device *dev, struct exynos_drm_ipp *ipp,
498c2ecf20Sopenharmony_ci		const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
508c2ecf20Sopenharmony_ci		const struct exynos_drm_ipp_formats *formats,
518c2ecf20Sopenharmony_ci		unsigned int num_formats, const char *name)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	WARN_ON(!ipp);
548c2ecf20Sopenharmony_ci	WARN_ON(!funcs);
558c2ecf20Sopenharmony_ci	WARN_ON(!formats);
568c2ecf20Sopenharmony_ci	WARN_ON(!num_formats);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	spin_lock_init(&ipp->lock);
598c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ipp->todo_list);
608c2ecf20Sopenharmony_ci	init_waitqueue_head(&ipp->done_wq);
618c2ecf20Sopenharmony_ci	ipp->dev = dev;
628c2ecf20Sopenharmony_ci	ipp->funcs = funcs;
638c2ecf20Sopenharmony_ci	ipp->capabilities = caps;
648c2ecf20Sopenharmony_ci	ipp->name = name;
658c2ecf20Sopenharmony_ci	ipp->formats = formats;
668c2ecf20Sopenharmony_ci	ipp->num_formats = num_formats;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* ipp_list modification is serialized by component framework */
698c2ecf20Sopenharmony_ci	list_add_tail(&ipp->head, &ipp_list);
708c2ecf20Sopenharmony_ci	ipp->id = num_ipp++;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(dev, "Registered ipp %d\n", ipp->id);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/**
788c2ecf20Sopenharmony_ci * exynos_drm_ipp_unregister - Unregister the picture processor module
798c2ecf20Sopenharmony_ci * @dev: DRM device
808c2ecf20Sopenharmony_ci * @ipp: ipp module
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_civoid exynos_drm_ipp_unregister(struct device *dev,
838c2ecf20Sopenharmony_ci			       struct exynos_drm_ipp *ipp)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	WARN_ON(ipp->task);
868c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&ipp->todo_list));
878c2ecf20Sopenharmony_ci	list_del(&ipp->head);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/**
918c2ecf20Sopenharmony_ci * exynos_drm_ipp_ioctl_get_res_ioctl - enumerate all ipp modules
928c2ecf20Sopenharmony_ci * @dev: DRM device
938c2ecf20Sopenharmony_ci * @data: ioctl data
948c2ecf20Sopenharmony_ci * @file_priv: DRM file info
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * Construct a list of ipp ids.
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * Called by the user via ioctl.
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * Returns:
1018c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ciint exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
1048c2ecf20Sopenharmony_ci				 struct drm_file *file_priv)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct drm_exynos_ioctl_ipp_get_res *resp = data;
1078c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp;
1088c2ecf20Sopenharmony_ci	uint32_t __user *ipp_ptr = (uint32_t __user *)
1098c2ecf20Sopenharmony_ci						(unsigned long)resp->ipp_id_ptr;
1108c2ecf20Sopenharmony_ci	unsigned int count = num_ipp, copied = 0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * This ioctl is called twice, once to determine how much space is
1148c2ecf20Sopenharmony_ci	 * needed, and the 2nd time to fill it.
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	if (count && resp->count_ipps >= count) {
1178c2ecf20Sopenharmony_ci		list_for_each_entry(ipp, &ipp_list, head) {
1188c2ecf20Sopenharmony_ci			if (put_user(ipp->id, ipp_ptr + copied))
1198c2ecf20Sopenharmony_ci				return -EFAULT;
1208c2ecf20Sopenharmony_ci			copied++;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	resp->count_ipps = count;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic inline struct exynos_drm_ipp *__ipp_get(uint32_t id)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	list_for_each_entry(ipp, &ipp_list, head)
1338c2ecf20Sopenharmony_ci		if (ipp->id == id)
1348c2ecf20Sopenharmony_ci			return ipp;
1358c2ecf20Sopenharmony_ci	return NULL;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * exynos_drm_ipp_ioctl_get_caps - get ipp module capabilities and formats
1408c2ecf20Sopenharmony_ci * @dev: DRM device
1418c2ecf20Sopenharmony_ci * @data: ioctl data
1428c2ecf20Sopenharmony_ci * @file_priv: DRM file info
1438c2ecf20Sopenharmony_ci *
1448c2ecf20Sopenharmony_ci * Construct a structure describing ipp module capabilities.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Called by the user via ioctl.
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci * Returns:
1498c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure.
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_ciint exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
1528c2ecf20Sopenharmony_ci				  struct drm_file *file_priv)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct drm_exynos_ioctl_ipp_get_caps *resp = data;
1558c2ecf20Sopenharmony_ci	void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr;
1568c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp;
1578c2ecf20Sopenharmony_ci	int i;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	ipp = __ipp_get(resp->ipp_id);
1608c2ecf20Sopenharmony_ci	if (!ipp)
1618c2ecf20Sopenharmony_ci		return -ENOENT;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	resp->ipp_id = ipp->id;
1648c2ecf20Sopenharmony_ci	resp->capabilities = ipp->capabilities;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/*
1678c2ecf20Sopenharmony_ci	 * This ioctl is called twice, once to determine how much space is
1688c2ecf20Sopenharmony_ci	 * needed, and the 2nd time to fill it.
1698c2ecf20Sopenharmony_ci	 */
1708c2ecf20Sopenharmony_ci	if (resp->formats_count >= ipp->num_formats) {
1718c2ecf20Sopenharmony_ci		for (i = 0; i < ipp->num_formats; i++) {
1728c2ecf20Sopenharmony_ci			struct drm_exynos_ipp_format tmp = {
1738c2ecf20Sopenharmony_ci				.fourcc = ipp->formats[i].fourcc,
1748c2ecf20Sopenharmony_ci				.type = ipp->formats[i].type,
1758c2ecf20Sopenharmony_ci				.modifier = ipp->formats[i].modifier,
1768c2ecf20Sopenharmony_ci			};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			if (copy_to_user(ptr, &tmp, sizeof(tmp)))
1798c2ecf20Sopenharmony_ci				return -EFAULT;
1808c2ecf20Sopenharmony_ci			ptr += sizeof(tmp);
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	resp->formats_count = ipp->num_formats;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic inline const struct exynos_drm_ipp_formats *__ipp_format_get(
1898c2ecf20Sopenharmony_ci				struct exynos_drm_ipp *ipp, uint32_t fourcc,
1908c2ecf20Sopenharmony_ci				uint64_t mod, unsigned int type)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	int i;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	for (i = 0; i < ipp->num_formats; i++) {
1958c2ecf20Sopenharmony_ci		if ((ipp->formats[i].type & type) &&
1968c2ecf20Sopenharmony_ci		    ipp->formats[i].fourcc == fourcc &&
1978c2ecf20Sopenharmony_ci		    ipp->formats[i].modifier == mod)
1988c2ecf20Sopenharmony_ci			return &ipp->formats[i];
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci	return NULL;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/**
2048c2ecf20Sopenharmony_ci * exynos_drm_ipp_get_limits_ioctl - get ipp module limits
2058c2ecf20Sopenharmony_ci * @dev: DRM device
2068c2ecf20Sopenharmony_ci * @data: ioctl data
2078c2ecf20Sopenharmony_ci * @file_priv: DRM file info
2088c2ecf20Sopenharmony_ci *
2098c2ecf20Sopenharmony_ci * Construct a structure describing ipp module limitations for provided
2108c2ecf20Sopenharmony_ci * picture format.
2118c2ecf20Sopenharmony_ci *
2128c2ecf20Sopenharmony_ci * Called by the user via ioctl.
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * Returns:
2158c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure.
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_ciint exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
2188c2ecf20Sopenharmony_ci				    struct drm_file *file_priv)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct drm_exynos_ioctl_ipp_get_limits *resp = data;
2218c2ecf20Sopenharmony_ci	void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr;
2228c2ecf20Sopenharmony_ci	const struct exynos_drm_ipp_formats *format;
2238c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE &&
2268c2ecf20Sopenharmony_ci	    resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION)
2278c2ecf20Sopenharmony_ci		return -EINVAL;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	ipp = __ipp_get(resp->ipp_id);
2308c2ecf20Sopenharmony_ci	if (!ipp)
2318c2ecf20Sopenharmony_ci		return -ENOENT;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	format = __ipp_format_get(ipp, resp->fourcc, resp->modifier,
2348c2ecf20Sopenharmony_ci				  resp->type);
2358c2ecf20Sopenharmony_ci	if (!format)
2368c2ecf20Sopenharmony_ci		return -EINVAL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/*
2398c2ecf20Sopenharmony_ci	 * This ioctl is called twice, once to determine how much space is
2408c2ecf20Sopenharmony_ci	 * needed, and the 2nd time to fill it.
2418c2ecf20Sopenharmony_ci	 */
2428c2ecf20Sopenharmony_ci	if (format->num_limits && resp->limits_count >= format->num_limits)
2438c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)ptr, format->limits,
2448c2ecf20Sopenharmony_ci				 sizeof(*format->limits) * format->num_limits))
2458c2ecf20Sopenharmony_ci			return -EFAULT;
2468c2ecf20Sopenharmony_ci	resp->limits_count = format->num_limits;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistruct drm_pending_exynos_ipp_event {
2528c2ecf20Sopenharmony_ci	struct drm_pending_event base;
2538c2ecf20Sopenharmony_ci	struct drm_exynos_ipp_event event;
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic inline struct exynos_drm_ipp_task *
2578c2ecf20Sopenharmony_ci			exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_task *task;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	task = kzalloc(sizeof(*task), GFP_KERNEL);
2628c2ecf20Sopenharmony_ci	if (!task)
2638c2ecf20Sopenharmony_ci		return NULL;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	task->dev = ipp->dev;
2668c2ecf20Sopenharmony_ci	task->ipp = ipp;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* some defaults */
2698c2ecf20Sopenharmony_ci	task->src.rect.w = task->dst.rect.w = UINT_MAX;
2708c2ecf20Sopenharmony_ci	task->src.rect.h = task->dst.rect.h = UINT_MAX;
2718c2ecf20Sopenharmony_ci	task->transform.rotation = DRM_MODE_ROTATE_0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "Allocated task %pK\n", task);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return task;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic const struct exynos_drm_param_map {
2798c2ecf20Sopenharmony_ci	unsigned int id;
2808c2ecf20Sopenharmony_ci	unsigned int size;
2818c2ecf20Sopenharmony_ci	unsigned int offset;
2828c2ecf20Sopenharmony_ci} exynos_drm_ipp_params_maps[] = {
2838c2ecf20Sopenharmony_ci	{
2848c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
2858c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_buffer),
2868c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, src.buf),
2878c2ecf20Sopenharmony_ci	}, {
2888c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_BUFFER |
2898c2ecf20Sopenharmony_ci			DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
2908c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_buffer),
2918c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, dst.buf),
2928c2ecf20Sopenharmony_ci	}, {
2938c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
2948c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_rect),
2958c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, src.rect),
2968c2ecf20Sopenharmony_ci	}, {
2978c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_RECTANGLE |
2988c2ecf20Sopenharmony_ci			DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
2998c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_rect),
3008c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, dst.rect),
3018c2ecf20Sopenharmony_ci	}, {
3028c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_TRANSFORM,
3038c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_transform),
3048c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, transform),
3058c2ecf20Sopenharmony_ci	}, {
3068c2ecf20Sopenharmony_ci		DRM_EXYNOS_IPP_TASK_ALPHA,
3078c2ecf20Sopenharmony_ci		sizeof(struct drm_exynos_ipp_task_alpha),
3088c2ecf20Sopenharmony_ci		offsetof(struct exynos_drm_ipp_task, alpha),
3098c2ecf20Sopenharmony_ci	},
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task,
3138c2ecf20Sopenharmony_ci				   struct drm_exynos_ioctl_ipp_commit *arg)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps;
3168c2ecf20Sopenharmony_ci	void __user *params = (void __user *)(unsigned long)arg->params_ptr;
3178c2ecf20Sopenharmony_ci	unsigned int size = arg->params_size;
3188c2ecf20Sopenharmony_ci	uint32_t id;
3198c2ecf20Sopenharmony_ci	int i;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	while (size) {
3228c2ecf20Sopenharmony_ci		if (get_user(id, (uint32_t __user *)params))
3238c2ecf20Sopenharmony_ci			return -EFAULT;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++)
3268c2ecf20Sopenharmony_ci			if (map[i].id == id)
3278c2ecf20Sopenharmony_ci				break;
3288c2ecf20Sopenharmony_ci		if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) ||
3298c2ecf20Sopenharmony_ci		    map[i].size > size)
3308c2ecf20Sopenharmony_ci			return -EINVAL;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci		if (copy_from_user((void *)task + map[i].offset, params,
3338c2ecf20Sopenharmony_ci				   map[i].size))
3348c2ecf20Sopenharmony_ci			return -EFAULT;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		params += map[i].size;
3378c2ecf20Sopenharmony_ci		size -= map[i].size;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev,
3418c2ecf20Sopenharmony_ci			     "Got task %pK configuration from userspace\n",
3428c2ecf20Sopenharmony_ci			     task);
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf,
3478c2ecf20Sopenharmony_ci					    struct drm_file *filp)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	int ret = 0;
3508c2ecf20Sopenharmony_ci	int i;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* get GEM buffers and check their size */
3538c2ecf20Sopenharmony_ci	for (i = 0; i < buf->format->num_planes; i++) {
3548c2ecf20Sopenharmony_ci		unsigned int height = (i == 0) ? buf->buf.height :
3558c2ecf20Sopenharmony_ci			     DIV_ROUND_UP(buf->buf.height, buf->format->vsub);
3568c2ecf20Sopenharmony_ci		unsigned long size = height * buf->buf.pitch[i];
3578c2ecf20Sopenharmony_ci		struct exynos_drm_gem *gem = exynos_drm_gem_get(filp,
3588c2ecf20Sopenharmony_ci							    buf->buf.gem_id[i]);
3598c2ecf20Sopenharmony_ci		if (!gem) {
3608c2ecf20Sopenharmony_ci			ret = -ENOENT;
3618c2ecf20Sopenharmony_ci			goto gem_free;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci		buf->exynos_gem[i] = gem;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		if (size + buf->buf.offset[i] > buf->exynos_gem[i]->size) {
3668c2ecf20Sopenharmony_ci			i++;
3678c2ecf20Sopenharmony_ci			ret = -EINVAL;
3688c2ecf20Sopenharmony_ci			goto gem_free;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr +
3718c2ecf20Sopenharmony_ci				   buf->buf.offset[i];
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_cigem_free:
3768c2ecf20Sopenharmony_ci	while (i--) {
3778c2ecf20Sopenharmony_ci		exynos_drm_gem_put(buf->exynos_gem[i]);
3788c2ecf20Sopenharmony_ci		buf->exynos_gem[i] = NULL;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	return ret;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	int i;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (!buf->exynos_gem[0])
3888c2ecf20Sopenharmony_ci		return;
3898c2ecf20Sopenharmony_ci	for (i = 0; i < buf->format->num_planes; i++)
3908c2ecf20Sopenharmony_ci		exynos_drm_gem_put(buf->exynos_gem[i]);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp,
3948c2ecf20Sopenharmony_ci				 struct exynos_drm_ipp_task *task)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "Freeing task %pK\n", task);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	exynos_drm_ipp_task_release_buf(&task->src);
3998c2ecf20Sopenharmony_ci	exynos_drm_ipp_task_release_buf(&task->dst);
4008c2ecf20Sopenharmony_ci	if (task->event)
4018c2ecf20Sopenharmony_ci		drm_event_cancel_free(ipp->drm_dev, &task->event->base);
4028c2ecf20Sopenharmony_ci	kfree(task);
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistruct drm_ipp_limit {
4068c2ecf20Sopenharmony_ci	struct drm_exynos_ipp_limit_val h;
4078c2ecf20Sopenharmony_ci	struct drm_exynos_ipp_limit_val v;
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cienum drm_ipp_size_id {
4118c2ecf20Sopenharmony_ci	IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic const enum drm_exynos_ipp_limit_type limit_id_fallback[IPP_LIMIT_MAX][4] = {
4158c2ecf20Sopenharmony_ci	[IPP_LIMIT_BUFFER]  = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4168c2ecf20Sopenharmony_ci	[IPP_LIMIT_AREA]    = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
4178c2ecf20Sopenharmony_ci				DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4188c2ecf20Sopenharmony_ci	[IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED,
4198c2ecf20Sopenharmony_ci				DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
4208c2ecf20Sopenharmony_ci				DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4218c2ecf20Sopenharmony_ci};
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic inline void __limit_set_val(unsigned int *ptr, unsigned int val)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	if (!*ptr)
4268c2ecf20Sopenharmony_ci		*ptr = val;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void __get_size_limit(const struct drm_exynos_ipp_limit *limits,
4308c2ecf20Sopenharmony_ci			     unsigned int num_limits, enum drm_ipp_size_id id,
4318c2ecf20Sopenharmony_ci			     struct drm_ipp_limit *res)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	const struct drm_exynos_ipp_limit *l = limits;
4348c2ecf20Sopenharmony_ci	int i = 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	memset(res, 0, sizeof(*res));
4378c2ecf20Sopenharmony_ci	for (i = 0; limit_id_fallback[id][i]; i++)
4388c2ecf20Sopenharmony_ci		for (l = limits; l - limits < num_limits; l++) {
4398c2ecf20Sopenharmony_ci			if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) !=
4408c2ecf20Sopenharmony_ci			      DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) ||
4418c2ecf20Sopenharmony_ci			    ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) !=
4428c2ecf20Sopenharmony_ci						     limit_id_fallback[id][i]))
4438c2ecf20Sopenharmony_ci				continue;
4448c2ecf20Sopenharmony_ci			__limit_set_val(&res->h.min, l->h.min);
4458c2ecf20Sopenharmony_ci			__limit_set_val(&res->h.max, l->h.max);
4468c2ecf20Sopenharmony_ci			__limit_set_val(&res->h.align, l->h.align);
4478c2ecf20Sopenharmony_ci			__limit_set_val(&res->v.min, l->v.min);
4488c2ecf20Sopenharmony_ci			__limit_set_val(&res->v.max, l->v.max);
4498c2ecf20Sopenharmony_ci			__limit_set_val(&res->v.align, l->v.align);
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic inline bool __align_check(unsigned int val, unsigned int align)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	if (align && (val & (align - 1))) {
4568c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n",
4578c2ecf20Sopenharmony_ci				 val, align);
4588c2ecf20Sopenharmony_ci		return false;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	return true;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic inline bool __size_limit_check(unsigned int val,
4648c2ecf20Sopenharmony_ci				 struct drm_exynos_ipp_limit_val *l)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	if ((l->min && val < l->min) || (l->max && val > l->max)) {
4678c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n",
4688c2ecf20Sopenharmony_ci				 val, l->min, l->max);
4698c2ecf20Sopenharmony_ci		return false;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	return __align_check(val, l->align);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
4758c2ecf20Sopenharmony_ci	const struct drm_exynos_ipp_limit *limits, unsigned int num_limits,
4768c2ecf20Sopenharmony_ci	bool rotate, bool swap)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA;
4798c2ecf20Sopenharmony_ci	struct drm_ipp_limit l;
4808c2ecf20Sopenharmony_ci	struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v;
4818c2ecf20Sopenharmony_ci	int real_width = buf->buf.pitch[0] / buf->format->cpp[0];
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (!limits)
4848c2ecf20Sopenharmony_ci		return 0;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	__get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l);
4878c2ecf20Sopenharmony_ci	if (!__size_limit_check(real_width, &l.h) ||
4888c2ecf20Sopenharmony_ci	    !__size_limit_check(buf->buf.height, &l.v))
4898c2ecf20Sopenharmony_ci		return -EINVAL;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (swap) {
4928c2ecf20Sopenharmony_ci		lv = &l.h;
4938c2ecf20Sopenharmony_ci		lh = &l.v;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	__get_size_limit(limits, num_limits, id, &l);
4968c2ecf20Sopenharmony_ci	if (!__size_limit_check(buf->rect.w, lh) ||
4978c2ecf20Sopenharmony_ci	    !__align_check(buf->rect.x, lh->align) ||
4988c2ecf20Sopenharmony_ci	    !__size_limit_check(buf->rect.h, lv) ||
4998c2ecf20Sopenharmony_ci	    !__align_check(buf->rect.y, lv->align))
5008c2ecf20Sopenharmony_ci		return -EINVAL;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic inline bool __scale_limit_check(unsigned int src, unsigned int dst,
5068c2ecf20Sopenharmony_ci				       unsigned int min, unsigned int max)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	if ((max && (dst << 16) > src * max) ||
5098c2ecf20Sopenharmony_ci	    (min && (dst << 16) < src * min)) {
5108c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n",
5118c2ecf20Sopenharmony_ci			 src, dst,
5128c2ecf20Sopenharmony_ci			 min >> 16, 100000 * (min & 0xffff) / (1 << 16),
5138c2ecf20Sopenharmony_ci			 max >> 16, 100000 * (max & 0xffff) / (1 << 16));
5148c2ecf20Sopenharmony_ci		return false;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	return true;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_check_scale_limits(
5208c2ecf20Sopenharmony_ci				struct drm_exynos_ipp_task_rect *src,
5218c2ecf20Sopenharmony_ci				struct drm_exynos_ipp_task_rect *dst,
5228c2ecf20Sopenharmony_ci				const struct drm_exynos_ipp_limit *limits,
5238c2ecf20Sopenharmony_ci				unsigned int num_limits, bool swap)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	const struct drm_exynos_ipp_limit_val *lh, *lv;
5268c2ecf20Sopenharmony_ci	int dw, dh;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	for (; num_limits; limits++, num_limits--)
5298c2ecf20Sopenharmony_ci		if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) ==
5308c2ecf20Sopenharmony_ci		    DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE)
5318c2ecf20Sopenharmony_ci			break;
5328c2ecf20Sopenharmony_ci	if (!num_limits)
5338c2ecf20Sopenharmony_ci		return 0;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	lh = (!swap) ? &limits->h : &limits->v;
5368c2ecf20Sopenharmony_ci	lv = (!swap) ? &limits->v : &limits->h;
5378c2ecf20Sopenharmony_ci	dw = (!swap) ? dst->w : dst->h;
5388c2ecf20Sopenharmony_ci	dh = (!swap) ? dst->h : dst->w;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (!__scale_limit_check(src->w, dw, lh->min, lh->max) ||
5418c2ecf20Sopenharmony_ci	    !__scale_limit_check(src->h, dh, lv->min, lv->max))
5428c2ecf20Sopenharmony_ci		return -EINVAL;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return 0;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_check_format(struct exynos_drm_ipp_task *task,
5488c2ecf20Sopenharmony_ci				       struct exynos_drm_ipp_buffer *buf,
5498c2ecf20Sopenharmony_ci				       struct exynos_drm_ipp_buffer *src,
5508c2ecf20Sopenharmony_ci				       struct exynos_drm_ipp_buffer *dst,
5518c2ecf20Sopenharmony_ci				       bool rotate, bool swap)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	const struct exynos_drm_ipp_formats *fmt;
5548c2ecf20Sopenharmony_ci	int ret, i;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	fmt = __ipp_format_get(task->ipp, buf->buf.fourcc, buf->buf.modifier,
5578c2ecf20Sopenharmony_ci			       buf == src ? DRM_EXYNOS_IPP_FORMAT_SOURCE :
5588c2ecf20Sopenharmony_ci					    DRM_EXYNOS_IPP_FORMAT_DESTINATION);
5598c2ecf20Sopenharmony_ci	if (!fmt) {
5608c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(task->dev,
5618c2ecf20Sopenharmony_ci				     "Task %pK: %s format not supported\n",
5628c2ecf20Sopenharmony_ci				     task, buf == src ? "src" : "dst");
5638c2ecf20Sopenharmony_ci		return -EINVAL;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* basic checks */
5678c2ecf20Sopenharmony_ci	if (buf->buf.width == 0 || buf->buf.height == 0)
5688c2ecf20Sopenharmony_ci		return -EINVAL;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	buf->format = drm_format_info(buf->buf.fourcc);
5718c2ecf20Sopenharmony_ci	for (i = 0; i < buf->format->num_planes; i++) {
5728c2ecf20Sopenharmony_ci		unsigned int width = (i == 0) ? buf->buf.width :
5738c2ecf20Sopenharmony_ci			     DIV_ROUND_UP(buf->buf.width, buf->format->hsub);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		if (buf->buf.pitch[i] == 0)
5768c2ecf20Sopenharmony_ci			buf->buf.pitch[i] = width * buf->format->cpp[i];
5778c2ecf20Sopenharmony_ci		if (buf->buf.pitch[i] < width * buf->format->cpp[i])
5788c2ecf20Sopenharmony_ci			return -EINVAL;
5798c2ecf20Sopenharmony_ci		if (!buf->buf.gem_id[i])
5808c2ecf20Sopenharmony_ci			return -ENOENT;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* pitch for additional planes must match */
5848c2ecf20Sopenharmony_ci	if (buf->format->num_planes > 2 &&
5858c2ecf20Sopenharmony_ci	    buf->buf.pitch[1] != buf->buf.pitch[2])
5868c2ecf20Sopenharmony_ci		return -EINVAL;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/* check driver limits */
5898c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_check_size_limits(buf, fmt->limits,
5908c2ecf20Sopenharmony_ci					       fmt->num_limits,
5918c2ecf20Sopenharmony_ci					       rotate,
5928c2ecf20Sopenharmony_ci					       buf == dst ? swap : false);
5938c2ecf20Sopenharmony_ci	if (ret)
5948c2ecf20Sopenharmony_ci		return ret;
5958c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
5968c2ecf20Sopenharmony_ci						fmt->limits,
5978c2ecf20Sopenharmony_ci						fmt->num_limits, swap);
5988c2ecf20Sopenharmony_ci	return ret;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp = task->ipp;
6048c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
6058c2ecf20Sopenharmony_ci	unsigned int rotation = task->transform.rotation;
6068c2ecf20Sopenharmony_ci	int ret = 0;
6078c2ecf20Sopenharmony_ci	bool swap = drm_rotation_90_or_270(rotation);
6088c2ecf20Sopenharmony_ci	bool rotate = (rotation != DRM_MODE_ROTATE_0);
6098c2ecf20Sopenharmony_ci	bool scale = false;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "Checking task %pK\n", task);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (src->rect.w == UINT_MAX)
6148c2ecf20Sopenharmony_ci		src->rect.w = src->buf.width;
6158c2ecf20Sopenharmony_ci	if (src->rect.h == UINT_MAX)
6168c2ecf20Sopenharmony_ci		src->rect.h = src->buf.height;
6178c2ecf20Sopenharmony_ci	if (dst->rect.w == UINT_MAX)
6188c2ecf20Sopenharmony_ci		dst->rect.w = dst->buf.width;
6198c2ecf20Sopenharmony_ci	if (dst->rect.h == UINT_MAX)
6208c2ecf20Sopenharmony_ci		dst->rect.h = dst->buf.height;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (src->rect.x + src->rect.w > (src->buf.width) ||
6238c2ecf20Sopenharmony_ci	    src->rect.y + src->rect.h > (src->buf.height) ||
6248c2ecf20Sopenharmony_ci	    dst->rect.x + dst->rect.w > (dst->buf.width) ||
6258c2ecf20Sopenharmony_ci	    dst->rect.y + dst->rect.h > (dst->buf.height)) {
6268c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(task->dev,
6278c2ecf20Sopenharmony_ci				     "Task %pK: defined area is outside provided buffers\n",
6288c2ecf20Sopenharmony_ci				     task);
6298c2ecf20Sopenharmony_ci		return -EINVAL;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if ((!swap && (src->rect.w != dst->rect.w ||
6338c2ecf20Sopenharmony_ci		       src->rect.h != dst->rect.h)) ||
6348c2ecf20Sopenharmony_ci	    (swap && (src->rect.w != dst->rect.h ||
6358c2ecf20Sopenharmony_ci		      src->rect.h != dst->rect.w)))
6368c2ecf20Sopenharmony_ci		scale = true;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) &&
6398c2ecf20Sopenharmony_ci	     (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) ||
6408c2ecf20Sopenharmony_ci	    (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) ||
6418c2ecf20Sopenharmony_ci	    (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) ||
6428c2ecf20Sopenharmony_ci	    (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) &&
6438c2ecf20Sopenharmony_ci	     src->buf.fourcc != dst->buf.fourcc)) {
6448c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: hw capabilities exceeded\n",
6458c2ecf20Sopenharmony_ci				     task);
6468c2ecf20Sopenharmony_ci		return -EINVAL;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_check_format(task, src, src, dst, rotate, swap);
6508c2ecf20Sopenharmony_ci	if (ret)
6518c2ecf20Sopenharmony_ci		return ret;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_check_format(task, dst, src, dst, false, swap);
6548c2ecf20Sopenharmony_ci	if (ret)
6558c2ecf20Sopenharmony_ci		return ret;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(ipp->dev, "Task %pK: all checks done.\n",
6588c2ecf20Sopenharmony_ci			     task);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return ret;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task,
6648c2ecf20Sopenharmony_ci				     struct drm_file *filp)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
6678c2ecf20Sopenharmony_ci	int ret = 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "Setting buffer for task %pK\n",
6708c2ecf20Sopenharmony_ci			     task);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_task_setup_buffer(src, filp);
6738c2ecf20Sopenharmony_ci	if (ret) {
6748c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(task->dev,
6758c2ecf20Sopenharmony_ci				     "Task %pK: src buffer setup failed\n",
6768c2ecf20Sopenharmony_ci				     task);
6778c2ecf20Sopenharmony_ci		return ret;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_task_setup_buffer(dst, filp);
6808c2ecf20Sopenharmony_ci	if (ret) {
6818c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(task->dev,
6828c2ecf20Sopenharmony_ci				     "Task %pK: dst buffer setup failed\n",
6838c2ecf20Sopenharmony_ci				     task);
6848c2ecf20Sopenharmony_ci		return ret;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: buffers prepared.\n",
6888c2ecf20Sopenharmony_ci			     task);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return ret;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task,
6958c2ecf20Sopenharmony_ci				 struct drm_file *file_priv, uint64_t user_data)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct drm_pending_exynos_ipp_event *e = NULL;
6988c2ecf20Sopenharmony_ci	int ret;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
7018c2ecf20Sopenharmony_ci	if (!e)
7028c2ecf20Sopenharmony_ci		return -ENOMEM;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	e->event.base.type = DRM_EXYNOS_IPP_EVENT;
7058c2ecf20Sopenharmony_ci	e->event.base.length = sizeof(e->event);
7068c2ecf20Sopenharmony_ci	e->event.user_data = user_data;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ret = drm_event_reserve_init(task->ipp->drm_dev, file_priv, &e->base,
7098c2ecf20Sopenharmony_ci				     &e->event.base);
7108c2ecf20Sopenharmony_ci	if (ret)
7118c2ecf20Sopenharmony_ci		goto free;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	task->event = e;
7148c2ecf20Sopenharmony_ci	return 0;
7158c2ecf20Sopenharmony_cifree:
7168c2ecf20Sopenharmony_ci	kfree(e);
7178c2ecf20Sopenharmony_ci	return ret;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct timespec64 now;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	ktime_get_ts64(&now);
7258c2ecf20Sopenharmony_ci	task->event->event.tv_sec = now.tv_sec;
7268c2ecf20Sopenharmony_ci	task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC;
7278c2ecf20Sopenharmony_ci	task->event->event.sequence = atomic_inc_return(&task->ipp->sequence);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	drm_send_event(task->ipp->drm_dev, &task->event->base);
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	int ret = task->ret;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (ret == 0 && task->event) {
7378c2ecf20Sopenharmony_ci		exynos_drm_ipp_event_send(task);
7388c2ecf20Sopenharmony_ci		/* ensure event won't be canceled on task free */
7398c2ecf20Sopenharmony_ci		task->event = NULL;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	exynos_drm_ipp_task_free(task->ipp, task);
7438c2ecf20Sopenharmony_ci	return ret;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_cleanup_work(struct work_struct *work)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_task *task = container_of(work,
7498c2ecf20Sopenharmony_ci				      struct exynos_drm_ipp_task, cleanup_work);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	exynos_drm_ipp_task_cleanup(task);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci/**
7578c2ecf20Sopenharmony_ci * exynos_drm_ipp_task_done - finish given task and set return code
7588c2ecf20Sopenharmony_ci * @task: ipp task to finish
7598c2ecf20Sopenharmony_ci * @ret: error code or 0 if operation has been performed successfully
7608c2ecf20Sopenharmony_ci */
7618c2ecf20Sopenharmony_civoid exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp = task->ipp;
7648c2ecf20Sopenharmony_ci	unsigned long flags;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(task->dev, "ipp: %d, task %pK done: %d\n",
7678c2ecf20Sopenharmony_ci			     ipp->id, task, ret);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipp->lock, flags);
7708c2ecf20Sopenharmony_ci	if (ipp->task == task)
7718c2ecf20Sopenharmony_ci		ipp->task = NULL;
7728c2ecf20Sopenharmony_ci	task->flags |= DRM_EXYNOS_IPP_TASK_DONE;
7738c2ecf20Sopenharmony_ci	task->ret = ret;
7748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipp->lock, flags);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	exynos_drm_ipp_next_task(ipp);
7778c2ecf20Sopenharmony_ci	wake_up(&ipp->done_wq);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) {
7808c2ecf20Sopenharmony_ci		INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work);
7818c2ecf20Sopenharmony_ci		schedule_work(&task->cleanup_work);
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_task *task;
7888c2ecf20Sopenharmony_ci	unsigned long flags;
7898c2ecf20Sopenharmony_ci	int ret;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, try to run new task\n",
7928c2ecf20Sopenharmony_ci			     ipp->id);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipp->lock, flags);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (ipp->task || list_empty(&ipp->todo_list)) {
7978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ipp->lock, flags);
7988c2ecf20Sopenharmony_ci		return;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task,
8028c2ecf20Sopenharmony_ci				head);
8038c2ecf20Sopenharmony_ci	list_del_init(&task->head);
8048c2ecf20Sopenharmony_ci	ipp->task = task;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipp->lock, flags);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(ipp->dev,
8098c2ecf20Sopenharmony_ci			     "ipp: %d, selected task %pK to run\n", ipp->id,
8108c2ecf20Sopenharmony_ci			     task);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	ret = ipp->funcs->commit(ipp, task);
8138c2ecf20Sopenharmony_ci	if (ret)
8148c2ecf20Sopenharmony_ci		exynos_drm_ipp_task_done(task, ret);
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp,
8188c2ecf20Sopenharmony_ci					 struct exynos_drm_ipp_task *task)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	unsigned long flags;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipp->lock, flags);
8238c2ecf20Sopenharmony_ci	list_add(&task->head, &ipp->todo_list);
8248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipp->lock, flags);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	exynos_drm_ipp_next_task(ipp);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp,
8308c2ecf20Sopenharmony_ci				      struct exynos_drm_ipp_task *task)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	unsigned long flags;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipp->lock, flags);
8358c2ecf20Sopenharmony_ci	if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) {
8368c2ecf20Sopenharmony_ci		/* already completed task */
8378c2ecf20Sopenharmony_ci		exynos_drm_ipp_task_cleanup(task);
8388c2ecf20Sopenharmony_ci	} else if (ipp->task != task) {
8398c2ecf20Sopenharmony_ci		/* task has not been scheduled for execution yet */
8408c2ecf20Sopenharmony_ci		list_del_init(&task->head);
8418c2ecf20Sopenharmony_ci		exynos_drm_ipp_task_cleanup(task);
8428c2ecf20Sopenharmony_ci	} else {
8438c2ecf20Sopenharmony_ci		/*
8448c2ecf20Sopenharmony_ci		 * currently processed task, call abort() and perform
8458c2ecf20Sopenharmony_ci		 * cleanup with async worker
8468c2ecf20Sopenharmony_ci		 */
8478c2ecf20Sopenharmony_ci		task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
8488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ipp->lock, flags);
8498c2ecf20Sopenharmony_ci		if (ipp->funcs->abort)
8508c2ecf20Sopenharmony_ci			ipp->funcs->abort(ipp, task);
8518c2ecf20Sopenharmony_ci		return;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipp->lock, flags);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/**
8578c2ecf20Sopenharmony_ci * exynos_drm_ipp_commit_ioctl - perform image processing operation
8588c2ecf20Sopenharmony_ci * @dev: DRM device
8598c2ecf20Sopenharmony_ci * @data: ioctl data
8608c2ecf20Sopenharmony_ci * @file_priv: DRM file info
8618c2ecf20Sopenharmony_ci *
8628c2ecf20Sopenharmony_ci * Construct a ipp task from the set of properties provided from the user
8638c2ecf20Sopenharmony_ci * and try to schedule it to framebuffer processor hardware.
8648c2ecf20Sopenharmony_ci *
8658c2ecf20Sopenharmony_ci * Called by the user via ioctl.
8668c2ecf20Sopenharmony_ci *
8678c2ecf20Sopenharmony_ci * Returns:
8688c2ecf20Sopenharmony_ci * Zero on success, negative errno on failure.
8698c2ecf20Sopenharmony_ci */
8708c2ecf20Sopenharmony_ciint exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data,
8718c2ecf20Sopenharmony_ci				struct drm_file *file_priv)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct drm_exynos_ioctl_ipp_commit *arg = data;
8748c2ecf20Sopenharmony_ci	struct exynos_drm_ipp *ipp;
8758c2ecf20Sopenharmony_ci	struct exynos_drm_ipp_task *task;
8768c2ecf20Sopenharmony_ci	int ret = 0;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved)
8798c2ecf20Sopenharmony_ci		return -EINVAL;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* can't test and expect an event at the same time */
8828c2ecf20Sopenharmony_ci	if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) &&
8838c2ecf20Sopenharmony_ci			(arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT))
8848c2ecf20Sopenharmony_ci		return -EINVAL;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	ipp = __ipp_get(arg->ipp_id);
8878c2ecf20Sopenharmony_ci	if (!ipp)
8888c2ecf20Sopenharmony_ci		return -ENOENT;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	task = exynos_drm_ipp_task_alloc(ipp);
8918c2ecf20Sopenharmony_ci	if (!task)
8928c2ecf20Sopenharmony_ci		return -ENOMEM;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_task_set(task, arg);
8958c2ecf20Sopenharmony_ci	if (ret)
8968c2ecf20Sopenharmony_ci		goto free;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_task_check(task);
8998c2ecf20Sopenharmony_ci	if (ret)
9008c2ecf20Sopenharmony_ci		goto free;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	ret = exynos_drm_ipp_task_setup_buffers(task, file_priv);
9038c2ecf20Sopenharmony_ci	if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY)
9048c2ecf20Sopenharmony_ci		goto free;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) {
9078c2ecf20Sopenharmony_ci		ret = exynos_drm_ipp_event_create(task, file_priv,
9088c2ecf20Sopenharmony_ci						 arg->user_data);
9098c2ecf20Sopenharmony_ci		if (ret)
9108c2ecf20Sopenharmony_ci			goto free;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/*
9148c2ecf20Sopenharmony_ci	 * Queue task for processing on the hardware. task object will be
9158c2ecf20Sopenharmony_ci	 * then freed after exynos_drm_ipp_task_done()
9168c2ecf20Sopenharmony_ci	 */
9178c2ecf20Sopenharmony_ci	if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) {
9188c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(ipp->dev,
9198c2ecf20Sopenharmony_ci				     "ipp: %d, nonblocking processing task %pK\n",
9208c2ecf20Sopenharmony_ci				     ipp->id, task);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
9238c2ecf20Sopenharmony_ci		exynos_drm_ipp_schedule_task(task->ipp, task);
9248c2ecf20Sopenharmony_ci		ret = 0;
9258c2ecf20Sopenharmony_ci	} else {
9268c2ecf20Sopenharmony_ci		DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, processing task %pK\n",
9278c2ecf20Sopenharmony_ci				     ipp->id, task);
9288c2ecf20Sopenharmony_ci		exynos_drm_ipp_schedule_task(ipp, task);
9298c2ecf20Sopenharmony_ci		ret = wait_event_interruptible(ipp->done_wq,
9308c2ecf20Sopenharmony_ci					task->flags & DRM_EXYNOS_IPP_TASK_DONE);
9318c2ecf20Sopenharmony_ci		if (ret)
9328c2ecf20Sopenharmony_ci			exynos_drm_ipp_task_abort(ipp, task);
9338c2ecf20Sopenharmony_ci		else
9348c2ecf20Sopenharmony_ci			ret = exynos_drm_ipp_task_cleanup(task);
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	return ret;
9378c2ecf20Sopenharmony_cifree:
9388c2ecf20Sopenharmony_ci	exynos_drm_ipp_task_free(ipp, task);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	return ret;
9418c2ecf20Sopenharmony_ci}
942