19913f74fSMarek Szyprowski /*
29913f74fSMarek Szyprowski * Copyright (C) 2017 Samsung Electronics Co.Ltd
39913f74fSMarek Szyprowski * Authors:
49913f74fSMarek Szyprowski * Marek Szyprowski <m.szyprowski@samsung.com>
59913f74fSMarek Szyprowski *
69913f74fSMarek Szyprowski * Exynos DRM Image Post Processing (IPP) related functions
79913f74fSMarek Szyprowski *
89913f74fSMarek Szyprowski * Permission is hereby granted, free of charge, to any person obtaining a
99913f74fSMarek Szyprowski * copy of this software and associated documentation files (the "Software"),
109913f74fSMarek Szyprowski * to deal in the Software without restriction, including without limitation
119913f74fSMarek Szyprowski * the rights to use, copy, modify, merge, publish, distribute, sublicense,
129913f74fSMarek Szyprowski * and/or sell copies of the Software, and to permit persons to whom the
139913f74fSMarek Szyprowski * Software is furnished to do so, subject to the following conditions:
149913f74fSMarek Szyprowski *
159913f74fSMarek Szyprowski * The above copyright notice and this permission notice shall be included in
169913f74fSMarek Szyprowski * all copies or substantial portions of the Software.
179913f74fSMarek Szyprowski */
189913f74fSMarek Szyprowski
19226024b1SSam Ravnborg #include <linux/uaccess.h>
20226024b1SSam Ravnborg
21*90bb087fSVille Syrjälä #include <drm/drm_blend.h>
22226024b1SSam Ravnborg #include <drm/drm_file.h>
23226024b1SSam Ravnborg #include <drm/drm_fourcc.h>
249913f74fSMarek Szyprowski #include <drm/drm_mode.h>
252bda34d7SSam Ravnborg #include <drm/exynos_drm.h>
269913f74fSMarek Szyprowski
279913f74fSMarek Szyprowski #include "exynos_drm_drv.h"
289913f74fSMarek Szyprowski #include "exynos_drm_gem.h"
299913f74fSMarek Szyprowski #include "exynos_drm_ipp.h"
309913f74fSMarek Szyprowski
319913f74fSMarek Szyprowski static int num_ipp;
329913f74fSMarek Szyprowski static LIST_HEAD(ipp_list);
339913f74fSMarek Szyprowski
349913f74fSMarek Szyprowski /**
359913f74fSMarek Szyprowski * exynos_drm_ipp_register - Register a new picture processor hardware module
369913f74fSMarek Szyprowski * @dev: DRM device
379913f74fSMarek Szyprowski * @ipp: ipp module to init
389913f74fSMarek Szyprowski * @funcs: callbacks for the new ipp object
399913f74fSMarek Szyprowski * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*)
409913f74fSMarek Szyprowski * @formats: array of supported formats
419913f74fSMarek Szyprowski * @num_formats: size of the supported formats array
429913f74fSMarek Szyprowski * @name: name (for debugging purposes)
439913f74fSMarek Szyprowski *
449913f74fSMarek Szyprowski * Initializes a ipp module.
459913f74fSMarek Szyprowski *
469913f74fSMarek Szyprowski * Returns:
479913f74fSMarek Szyprowski * Zero on success, error code on failure.
489913f74fSMarek Szyprowski */
exynos_drm_ipp_register(struct device * dev,struct exynos_drm_ipp * ipp,const struct exynos_drm_ipp_funcs * funcs,unsigned int caps,const struct exynos_drm_ipp_formats * formats,unsigned int num_formats,const char * name)498b955034SInki Dae int exynos_drm_ipp_register(struct device *dev, struct exynos_drm_ipp *ipp,
509913f74fSMarek Szyprowski const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
519913f74fSMarek Szyprowski const struct exynos_drm_ipp_formats *formats,
529913f74fSMarek Szyprowski unsigned int num_formats, const char *name)
539913f74fSMarek Szyprowski {
549913f74fSMarek Szyprowski WARN_ON(!ipp);
559913f74fSMarek Szyprowski WARN_ON(!funcs);
569913f74fSMarek Szyprowski WARN_ON(!formats);
579913f74fSMarek Szyprowski WARN_ON(!num_formats);
589913f74fSMarek Szyprowski
599913f74fSMarek Szyprowski spin_lock_init(&ipp->lock);
609913f74fSMarek Szyprowski INIT_LIST_HEAD(&ipp->todo_list);
619913f74fSMarek Szyprowski init_waitqueue_head(&ipp->done_wq);
629913f74fSMarek Szyprowski ipp->dev = dev;
639913f74fSMarek Szyprowski ipp->funcs = funcs;
649913f74fSMarek Szyprowski ipp->capabilities = caps;
659913f74fSMarek Szyprowski ipp->name = name;
669913f74fSMarek Szyprowski ipp->formats = formats;
679913f74fSMarek Szyprowski ipp->num_formats = num_formats;
689913f74fSMarek Szyprowski
699913f74fSMarek Szyprowski /* ipp_list modification is serialized by component framework */
709913f74fSMarek Szyprowski list_add_tail(&ipp->head, &ipp_list);
719913f74fSMarek Szyprowski ipp->id = num_ipp++;
729913f74fSMarek Szyprowski
738b955034SInki Dae DRM_DEV_DEBUG_DRIVER(dev, "Registered ipp %d\n", ipp->id);
749913f74fSMarek Szyprowski
759913f74fSMarek Szyprowski return 0;
769913f74fSMarek Szyprowski }
779913f74fSMarek Szyprowski
789913f74fSMarek Szyprowski /**
799913f74fSMarek Szyprowski * exynos_drm_ipp_unregister - Unregister the picture processor module
809913f74fSMarek Szyprowski * @dev: DRM device
819913f74fSMarek Szyprowski * @ipp: ipp module
829913f74fSMarek Szyprowski */
exynos_drm_ipp_unregister(struct device * dev,struct exynos_drm_ipp * ipp)838b955034SInki Dae void exynos_drm_ipp_unregister(struct device *dev,
849913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp)
859913f74fSMarek Szyprowski {
869913f74fSMarek Szyprowski WARN_ON(ipp->task);
879913f74fSMarek Szyprowski WARN_ON(!list_empty(&ipp->todo_list));
889913f74fSMarek Szyprowski list_del(&ipp->head);
899913f74fSMarek Szyprowski }
909913f74fSMarek Szyprowski
919913f74fSMarek Szyprowski /**
926668da9fSLee Jones * exynos_drm_ipp_get_res_ioctl - enumerate all ipp modules
939913f74fSMarek Szyprowski * @dev: DRM device
949913f74fSMarek Szyprowski * @data: ioctl data
959913f74fSMarek Szyprowski * @file_priv: DRM file info
969913f74fSMarek Szyprowski *
979913f74fSMarek Szyprowski * Construct a list of ipp ids.
989913f74fSMarek Szyprowski *
999913f74fSMarek Szyprowski * Called by the user via ioctl.
1009913f74fSMarek Szyprowski *
1019913f74fSMarek Szyprowski * Returns:
1029913f74fSMarek Szyprowski * Zero on success, negative errno on failure.
1039913f74fSMarek Szyprowski */
exynos_drm_ipp_get_res_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1049913f74fSMarek Szyprowski int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
1059913f74fSMarek Szyprowski struct drm_file *file_priv)
1069913f74fSMarek Szyprowski {
1079913f74fSMarek Szyprowski struct drm_exynos_ioctl_ipp_get_res *resp = data;
1089913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp;
1099913f74fSMarek Szyprowski uint32_t __user *ipp_ptr = (uint32_t __user *)
1109913f74fSMarek Szyprowski (unsigned long)resp->ipp_id_ptr;
1119913f74fSMarek Szyprowski unsigned int count = num_ipp, copied = 0;
1129913f74fSMarek Szyprowski
1139913f74fSMarek Szyprowski /*
1149913f74fSMarek Szyprowski * This ioctl is called twice, once to determine how much space is
1159913f74fSMarek Szyprowski * needed, and the 2nd time to fill it.
1169913f74fSMarek Szyprowski */
1179913f74fSMarek Szyprowski if (count && resp->count_ipps >= count) {
1189913f74fSMarek Szyprowski list_for_each_entry(ipp, &ipp_list, head) {
1199913f74fSMarek Szyprowski if (put_user(ipp->id, ipp_ptr + copied))
1209913f74fSMarek Szyprowski return -EFAULT;
1219913f74fSMarek Szyprowski copied++;
1229913f74fSMarek Szyprowski }
1239913f74fSMarek Szyprowski }
1249913f74fSMarek Szyprowski resp->count_ipps = count;
1259913f74fSMarek Szyprowski
1269913f74fSMarek Szyprowski return 0;
1279913f74fSMarek Szyprowski }
1289913f74fSMarek Szyprowski
__ipp_get(uint32_t id)1299913f74fSMarek Szyprowski static inline struct exynos_drm_ipp *__ipp_get(uint32_t id)
1309913f74fSMarek Szyprowski {
1319913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp;
1329913f74fSMarek Szyprowski
1339913f74fSMarek Szyprowski list_for_each_entry(ipp, &ipp_list, head)
1349913f74fSMarek Szyprowski if (ipp->id == id)
1359913f74fSMarek Szyprowski return ipp;
1369913f74fSMarek Szyprowski return NULL;
1379913f74fSMarek Szyprowski }
1389913f74fSMarek Szyprowski
1399913f74fSMarek Szyprowski /**
1406668da9fSLee Jones * exynos_drm_ipp_get_caps_ioctl - get ipp module capabilities and formats
1419913f74fSMarek Szyprowski * @dev: DRM device
1429913f74fSMarek Szyprowski * @data: ioctl data
1439913f74fSMarek Szyprowski * @file_priv: DRM file info
1449913f74fSMarek Szyprowski *
1459913f74fSMarek Szyprowski * Construct a structure describing ipp module capabilities.
1469913f74fSMarek Szyprowski *
1479913f74fSMarek Szyprowski * Called by the user via ioctl.
1489913f74fSMarek Szyprowski *
1499913f74fSMarek Szyprowski * Returns:
1509913f74fSMarek Szyprowski * Zero on success, negative errno on failure.
1519913f74fSMarek Szyprowski */
exynos_drm_ipp_get_caps_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)1529913f74fSMarek Szyprowski int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
1539913f74fSMarek Szyprowski struct drm_file *file_priv)
1549913f74fSMarek Szyprowski {
1559913f74fSMarek Szyprowski struct drm_exynos_ioctl_ipp_get_caps *resp = data;
1569913f74fSMarek Szyprowski void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr;
1579913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp;
1589913f74fSMarek Szyprowski int i;
1599913f74fSMarek Szyprowski
1609913f74fSMarek Szyprowski ipp = __ipp_get(resp->ipp_id);
1619913f74fSMarek Szyprowski if (!ipp)
1629913f74fSMarek Szyprowski return -ENOENT;
1639913f74fSMarek Szyprowski
1649913f74fSMarek Szyprowski resp->ipp_id = ipp->id;
1659913f74fSMarek Szyprowski resp->capabilities = ipp->capabilities;
1669913f74fSMarek Szyprowski
1679913f74fSMarek Szyprowski /*
1689913f74fSMarek Szyprowski * This ioctl is called twice, once to determine how much space is
1699913f74fSMarek Szyprowski * needed, and the 2nd time to fill it.
1709913f74fSMarek Szyprowski */
1719913f74fSMarek Szyprowski if (resp->formats_count >= ipp->num_formats) {
1729913f74fSMarek Szyprowski for (i = 0; i < ipp->num_formats; i++) {
1739913f74fSMarek Szyprowski struct drm_exynos_ipp_format tmp = {
1749913f74fSMarek Szyprowski .fourcc = ipp->formats[i].fourcc,
1759913f74fSMarek Szyprowski .type = ipp->formats[i].type,
1769913f74fSMarek Szyprowski .modifier = ipp->formats[i].modifier,
1779913f74fSMarek Szyprowski };
1789913f74fSMarek Szyprowski
1799913f74fSMarek Szyprowski if (copy_to_user(ptr, &tmp, sizeof(tmp)))
1809913f74fSMarek Szyprowski return -EFAULT;
1819913f74fSMarek Szyprowski ptr += sizeof(tmp);
1829913f74fSMarek Szyprowski }
1839913f74fSMarek Szyprowski }
1849913f74fSMarek Szyprowski resp->formats_count = ipp->num_formats;
1859913f74fSMarek Szyprowski
1869913f74fSMarek Szyprowski return 0;
1879913f74fSMarek Szyprowski }
1889913f74fSMarek Szyprowski
__ipp_format_get(struct exynos_drm_ipp * ipp,uint32_t fourcc,uint64_t mod,unsigned int type)1899913f74fSMarek Szyprowski static inline const struct exynos_drm_ipp_formats *__ipp_format_get(
1909913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp, uint32_t fourcc,
1919913f74fSMarek Szyprowski uint64_t mod, unsigned int type)
1929913f74fSMarek Szyprowski {
1939913f74fSMarek Szyprowski int i;
1949913f74fSMarek Szyprowski
1959913f74fSMarek Szyprowski for (i = 0; i < ipp->num_formats; i++) {
1969913f74fSMarek Szyprowski if ((ipp->formats[i].type & type) &&
1979913f74fSMarek Szyprowski ipp->formats[i].fourcc == fourcc &&
1989913f74fSMarek Szyprowski ipp->formats[i].modifier == mod)
1999913f74fSMarek Szyprowski return &ipp->formats[i];
2009913f74fSMarek Szyprowski }
2019913f74fSMarek Szyprowski return NULL;
2029913f74fSMarek Szyprowski }
2039913f74fSMarek Szyprowski
2049913f74fSMarek Szyprowski /**
2059913f74fSMarek Szyprowski * exynos_drm_ipp_get_limits_ioctl - get ipp module limits
2069913f74fSMarek Szyprowski * @dev: DRM device
2079913f74fSMarek Szyprowski * @data: ioctl data
2089913f74fSMarek Szyprowski * @file_priv: DRM file info
2099913f74fSMarek Szyprowski *
2109913f74fSMarek Szyprowski * Construct a structure describing ipp module limitations for provided
2119913f74fSMarek Szyprowski * picture format.
2129913f74fSMarek Szyprowski *
2139913f74fSMarek Szyprowski * Called by the user via ioctl.
2149913f74fSMarek Szyprowski *
2159913f74fSMarek Szyprowski * Returns:
2169913f74fSMarek Szyprowski * Zero on success, negative errno on failure.
2179913f74fSMarek Szyprowski */
exynos_drm_ipp_get_limits_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)2189913f74fSMarek Szyprowski int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
2199913f74fSMarek Szyprowski struct drm_file *file_priv)
2209913f74fSMarek Szyprowski {
2219913f74fSMarek Szyprowski struct drm_exynos_ioctl_ipp_get_limits *resp = data;
2229913f74fSMarek Szyprowski void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr;
2239913f74fSMarek Szyprowski const struct exynos_drm_ipp_formats *format;
2249913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp;
2259913f74fSMarek Szyprowski
2269913f74fSMarek Szyprowski if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE &&
2279913f74fSMarek Szyprowski resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION)
2289913f74fSMarek Szyprowski return -EINVAL;
2299913f74fSMarek Szyprowski
2309913f74fSMarek Szyprowski ipp = __ipp_get(resp->ipp_id);
2319913f74fSMarek Szyprowski if (!ipp)
2329913f74fSMarek Szyprowski return -ENOENT;
2339913f74fSMarek Szyprowski
2349913f74fSMarek Szyprowski format = __ipp_format_get(ipp, resp->fourcc, resp->modifier,
2359913f74fSMarek Szyprowski resp->type);
2369913f74fSMarek Szyprowski if (!format)
2379913f74fSMarek Szyprowski return -EINVAL;
2389913f74fSMarek Szyprowski
2399913f74fSMarek Szyprowski /*
2409913f74fSMarek Szyprowski * This ioctl is called twice, once to determine how much space is
2419913f74fSMarek Szyprowski * needed, and the 2nd time to fill it.
2429913f74fSMarek Szyprowski */
2439913f74fSMarek Szyprowski if (format->num_limits && resp->limits_count >= format->num_limits)
2449913f74fSMarek Szyprowski if (copy_to_user((void __user *)ptr, format->limits,
2459913f74fSMarek Szyprowski sizeof(*format->limits) * format->num_limits))
2469913f74fSMarek Szyprowski return -EFAULT;
2479913f74fSMarek Szyprowski resp->limits_count = format->num_limits;
2489913f74fSMarek Szyprowski
2499913f74fSMarek Szyprowski return 0;
2509913f74fSMarek Szyprowski }
2519913f74fSMarek Szyprowski
2529913f74fSMarek Szyprowski struct drm_pending_exynos_ipp_event {
2539913f74fSMarek Szyprowski struct drm_pending_event base;
2549913f74fSMarek Szyprowski struct drm_exynos_ipp_event event;
2559913f74fSMarek Szyprowski };
2569913f74fSMarek Szyprowski
2579913f74fSMarek Szyprowski static inline struct exynos_drm_ipp_task *
exynos_drm_ipp_task_alloc(struct exynos_drm_ipp * ipp)2589913f74fSMarek Szyprowski exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp)
2599913f74fSMarek Szyprowski {
2609913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task;
2619913f74fSMarek Szyprowski
2629913f74fSMarek Szyprowski task = kzalloc(sizeof(*task), GFP_KERNEL);
2639913f74fSMarek Szyprowski if (!task)
2649913f74fSMarek Szyprowski return NULL;
2659913f74fSMarek Szyprowski
2669913f74fSMarek Szyprowski task->dev = ipp->dev;
2679913f74fSMarek Szyprowski task->ipp = ipp;
2689913f74fSMarek Szyprowski
2699913f74fSMarek Szyprowski /* some defaults */
2709913f74fSMarek Szyprowski task->src.rect.w = task->dst.rect.w = UINT_MAX;
2719913f74fSMarek Szyprowski task->src.rect.h = task->dst.rect.h = UINT_MAX;
2729913f74fSMarek Szyprowski task->transform.rotation = DRM_MODE_ROTATE_0;
2739913f74fSMarek Szyprowski
2748b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Allocated task %pK\n", task);
2759913f74fSMarek Szyprowski
2769913f74fSMarek Szyprowski return task;
2779913f74fSMarek Szyprowski }
2789913f74fSMarek Szyprowski
2799913f74fSMarek Szyprowski static const struct exynos_drm_param_map {
2809913f74fSMarek Szyprowski unsigned int id;
2819913f74fSMarek Szyprowski unsigned int size;
2829913f74fSMarek Szyprowski unsigned int offset;
2839913f74fSMarek Szyprowski } exynos_drm_ipp_params_maps[] = {
2849913f74fSMarek Szyprowski {
2859913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
2869913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_buffer),
2879913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, src.buf),
2889913f74fSMarek Szyprowski }, {
2899913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_BUFFER |
2909913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
2919913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_buffer),
2929913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, dst.buf),
2939913f74fSMarek Szyprowski }, {
2949913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
2959913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_rect),
2969913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, src.rect),
2979913f74fSMarek Szyprowski }, {
2989913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_RECTANGLE |
2999913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
3009913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_rect),
3019913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, dst.rect),
3029913f74fSMarek Szyprowski }, {
3039913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_TRANSFORM,
3049913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_transform),
3059913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, transform),
3069913f74fSMarek Szyprowski }, {
3079913f74fSMarek Szyprowski DRM_EXYNOS_IPP_TASK_ALPHA,
3089913f74fSMarek Szyprowski sizeof(struct drm_exynos_ipp_task_alpha),
3099913f74fSMarek Szyprowski offsetof(struct exynos_drm_ipp_task, alpha),
3109913f74fSMarek Szyprowski },
3119913f74fSMarek Szyprowski };
3129913f74fSMarek Szyprowski
exynos_drm_ipp_task_set(struct exynos_drm_ipp_task * task,struct drm_exynos_ioctl_ipp_commit * arg)3139913f74fSMarek Szyprowski static int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task,
3149913f74fSMarek Szyprowski struct drm_exynos_ioctl_ipp_commit *arg)
3159913f74fSMarek Szyprowski {
3169913f74fSMarek Szyprowski const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps;
3179913f74fSMarek Szyprowski void __user *params = (void __user *)(unsigned long)arg->params_ptr;
3189913f74fSMarek Szyprowski unsigned int size = arg->params_size;
3199913f74fSMarek Szyprowski uint32_t id;
3209913f74fSMarek Szyprowski int i;
3219913f74fSMarek Szyprowski
3229913f74fSMarek Szyprowski while (size) {
3239913f74fSMarek Szyprowski if (get_user(id, (uint32_t __user *)params))
3249913f74fSMarek Szyprowski return -EFAULT;
3259913f74fSMarek Szyprowski
3269913f74fSMarek Szyprowski for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++)
3279913f74fSMarek Szyprowski if (map[i].id == id)
3289913f74fSMarek Szyprowski break;
3299913f74fSMarek Szyprowski if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) ||
3309913f74fSMarek Szyprowski map[i].size > size)
3319913f74fSMarek Szyprowski return -EINVAL;
3329913f74fSMarek Szyprowski
3339913f74fSMarek Szyprowski if (copy_from_user((void *)task + map[i].offset, params,
3349913f74fSMarek Szyprowski map[i].size))
3359913f74fSMarek Szyprowski return -EFAULT;
3369913f74fSMarek Szyprowski
3379913f74fSMarek Szyprowski params += map[i].size;
3389913f74fSMarek Szyprowski size -= map[i].size;
3399913f74fSMarek Szyprowski }
3409913f74fSMarek Szyprowski
3418b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev,
3426be90056SInki Dae "Got task %pK configuration from userspace\n",
3436be90056SInki Dae task);
3449913f74fSMarek Szyprowski return 0;
3459913f74fSMarek Szyprowski }
3469913f74fSMarek Szyprowski
exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer * buf,struct drm_file * filp)3479913f74fSMarek Szyprowski static int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf,
3489913f74fSMarek Szyprowski struct drm_file *filp)
3499913f74fSMarek Szyprowski {
3509913f74fSMarek Szyprowski int ret = 0;
3519913f74fSMarek Szyprowski int i;
3529913f74fSMarek Szyprowski
3539913f74fSMarek Szyprowski /* get GEM buffers and check their size */
3549913f74fSMarek Szyprowski for (i = 0; i < buf->format->num_planes; i++) {
3559913f74fSMarek Szyprowski unsigned int height = (i == 0) ? buf->buf.height :
3569913f74fSMarek Szyprowski DIV_ROUND_UP(buf->buf.height, buf->format->vsub);
3579913f74fSMarek Szyprowski unsigned long size = height * buf->buf.pitch[i];
358e978de54SMarek Szyprowski struct exynos_drm_gem *gem = exynos_drm_gem_get(filp,
3599913f74fSMarek Szyprowski buf->buf.gem_id[i]);
360e978de54SMarek Szyprowski if (!gem) {
3619913f74fSMarek Szyprowski ret = -ENOENT;
3629913f74fSMarek Szyprowski goto gem_free;
3639913f74fSMarek Szyprowski }
364e978de54SMarek Szyprowski buf->exynos_gem[i] = gem;
3659913f74fSMarek Szyprowski
3669913f74fSMarek Szyprowski if (size + buf->buf.offset[i] > buf->exynos_gem[i]->size) {
3679913f74fSMarek Szyprowski i++;
3689913f74fSMarek Szyprowski ret = -EINVAL;
3699913f74fSMarek Szyprowski goto gem_free;
3709913f74fSMarek Szyprowski }
3719913f74fSMarek Szyprowski buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr +
3729913f74fSMarek Szyprowski buf->buf.offset[i];
3739913f74fSMarek Szyprowski }
3749913f74fSMarek Szyprowski
3759913f74fSMarek Szyprowski return 0;
3769913f74fSMarek Szyprowski gem_free:
3779913f74fSMarek Szyprowski while (i--) {
378e978de54SMarek Szyprowski exynos_drm_gem_put(buf->exynos_gem[i]);
3799913f74fSMarek Szyprowski buf->exynos_gem[i] = NULL;
3809913f74fSMarek Szyprowski }
3819913f74fSMarek Szyprowski return ret;
3829913f74fSMarek Szyprowski }
3839913f74fSMarek Szyprowski
exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer * buf)3849913f74fSMarek Szyprowski static void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf)
3859913f74fSMarek Szyprowski {
3869913f74fSMarek Szyprowski int i;
3879913f74fSMarek Szyprowski
3889913f74fSMarek Szyprowski if (!buf->exynos_gem[0])
3899913f74fSMarek Szyprowski return;
3909913f74fSMarek Szyprowski for (i = 0; i < buf->format->num_planes; i++)
391e978de54SMarek Szyprowski exynos_drm_gem_put(buf->exynos_gem[i]);
3929913f74fSMarek Szyprowski }
3939913f74fSMarek Szyprowski
exynos_drm_ipp_task_free(struct exynos_drm_ipp * ipp,struct exynos_drm_ipp_task * task)3949913f74fSMarek Szyprowski static void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp,
3959913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task)
3969913f74fSMarek Szyprowski {
3978b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Freeing task %pK\n", task);
3989913f74fSMarek Szyprowski
3999913f74fSMarek Szyprowski exynos_drm_ipp_task_release_buf(&task->src);
4009913f74fSMarek Szyprowski exynos_drm_ipp_task_release_buf(&task->dst);
4019913f74fSMarek Szyprowski if (task->event)
4028b955034SInki Dae drm_event_cancel_free(ipp->drm_dev, &task->event->base);
4039913f74fSMarek Szyprowski kfree(task);
4049913f74fSMarek Szyprowski }
4059913f74fSMarek Szyprowski
4069913f74fSMarek Szyprowski struct drm_ipp_limit {
4079913f74fSMarek Szyprowski struct drm_exynos_ipp_limit_val h;
4089913f74fSMarek Szyprowski struct drm_exynos_ipp_limit_val v;
4099913f74fSMarek Szyprowski };
4109913f74fSMarek Szyprowski
4119913f74fSMarek Szyprowski enum drm_ipp_size_id {
4129913f74fSMarek Szyprowski IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
4139913f74fSMarek Szyprowski };
4149913f74fSMarek Szyprowski
415e94595b0SStefan Agner static const enum drm_exynos_ipp_limit_type limit_id_fallback[IPP_LIMIT_MAX][4] = {
4169913f74fSMarek Szyprowski [IPP_LIMIT_BUFFER] = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4179913f74fSMarek Szyprowski [IPP_LIMIT_AREA] = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
4189913f74fSMarek Szyprowski DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4199913f74fSMarek Szyprowski [IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED,
4209913f74fSMarek Szyprowski DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
4219913f74fSMarek Szyprowski DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
4229913f74fSMarek Szyprowski };
4239913f74fSMarek Szyprowski
__limit_set_val(unsigned int * ptr,unsigned int val)4249913f74fSMarek Szyprowski static inline void __limit_set_val(unsigned int *ptr, unsigned int val)
4259913f74fSMarek Szyprowski {
4269913f74fSMarek Szyprowski if (!*ptr)
4279913f74fSMarek Szyprowski *ptr = val;
4289913f74fSMarek Szyprowski }
4299913f74fSMarek Szyprowski
__get_size_limit(const struct drm_exynos_ipp_limit * limits,unsigned int num_limits,enum drm_ipp_size_id id,struct drm_ipp_limit * res)4309913f74fSMarek Szyprowski static void __get_size_limit(const struct drm_exynos_ipp_limit *limits,
4319913f74fSMarek Szyprowski unsigned int num_limits, enum drm_ipp_size_id id,
4329913f74fSMarek Szyprowski struct drm_ipp_limit *res)
4339913f74fSMarek Szyprowski {
4349913f74fSMarek Szyprowski const struct drm_exynos_ipp_limit *l = limits;
4359913f74fSMarek Szyprowski int i = 0;
4369913f74fSMarek Szyprowski
4379913f74fSMarek Szyprowski memset(res, 0, sizeof(*res));
4389913f74fSMarek Szyprowski for (i = 0; limit_id_fallback[id][i]; i++)
4399913f74fSMarek Szyprowski for (l = limits; l - limits < num_limits; l++) {
4409913f74fSMarek Szyprowski if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) !=
4419913f74fSMarek Szyprowski DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) ||
4429913f74fSMarek Szyprowski ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) !=
4439913f74fSMarek Szyprowski limit_id_fallback[id][i]))
4449913f74fSMarek Szyprowski continue;
4459913f74fSMarek Szyprowski __limit_set_val(&res->h.min, l->h.min);
4469913f74fSMarek Szyprowski __limit_set_val(&res->h.max, l->h.max);
4479913f74fSMarek Szyprowski __limit_set_val(&res->h.align, l->h.align);
4489913f74fSMarek Szyprowski __limit_set_val(&res->v.min, l->v.min);
4499913f74fSMarek Szyprowski __limit_set_val(&res->v.max, l->v.max);
4509913f74fSMarek Szyprowski __limit_set_val(&res->v.align, l->v.align);
4519913f74fSMarek Szyprowski }
4529913f74fSMarek Szyprowski }
4539913f74fSMarek Szyprowski
__align_check(unsigned int val,unsigned int align)4549913f74fSMarek Szyprowski static inline bool __align_check(unsigned int val, unsigned int align)
4559913f74fSMarek Szyprowski {
4569913f74fSMarek Szyprowski if (align && (val & (align - 1))) {
4579913f74fSMarek Szyprowski DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n",
4589913f74fSMarek Szyprowski val, align);
4599913f74fSMarek Szyprowski return false;
4609913f74fSMarek Szyprowski }
4619913f74fSMarek Szyprowski return true;
4629913f74fSMarek Szyprowski }
4639913f74fSMarek Szyprowski
__size_limit_check(unsigned int val,struct drm_exynos_ipp_limit_val * l)4649913f74fSMarek Szyprowski static inline bool __size_limit_check(unsigned int val,
4659913f74fSMarek Szyprowski struct drm_exynos_ipp_limit_val *l)
4669913f74fSMarek Szyprowski {
4679913f74fSMarek Szyprowski if ((l->min && val < l->min) || (l->max && val > l->max)) {
4689913f74fSMarek Szyprowski DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n",
4699913f74fSMarek Szyprowski val, l->min, l->max);
4709913f74fSMarek Szyprowski return false;
4719913f74fSMarek Szyprowski }
4729913f74fSMarek Szyprowski return __align_check(val, l->align);
4739913f74fSMarek Szyprowski }
4749913f74fSMarek Szyprowski
exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer * buf,const struct drm_exynos_ipp_limit * limits,unsigned int num_limits,bool rotate,bool swap)4759913f74fSMarek Szyprowski static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
4769913f74fSMarek Szyprowski const struct drm_exynos_ipp_limit *limits, unsigned int num_limits,
4779913f74fSMarek Szyprowski bool rotate, bool swap)
4789913f74fSMarek Szyprowski {
4799913f74fSMarek Szyprowski enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA;
4809913f74fSMarek Szyprowski struct drm_ipp_limit l;
4819913f74fSMarek Szyprowski struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v;
48269705ad2SMarek Szyprowski int real_width = buf->buf.pitch[0] / buf->format->cpp[0];
4839913f74fSMarek Szyprowski
4849913f74fSMarek Szyprowski if (!limits)
4859913f74fSMarek Szyprowski return 0;
4869913f74fSMarek Szyprowski
4879913f74fSMarek Szyprowski __get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l);
48869705ad2SMarek Szyprowski if (!__size_limit_check(real_width, &l.h) ||
4899913f74fSMarek Szyprowski !__size_limit_check(buf->buf.height, &l.v))
4909913f74fSMarek Szyprowski return -EINVAL;
4919913f74fSMarek Szyprowski
4929913f74fSMarek Szyprowski if (swap) {
4939913f74fSMarek Szyprowski lv = &l.h;
4949913f74fSMarek Szyprowski lh = &l.v;
4959913f74fSMarek Szyprowski }
4969913f74fSMarek Szyprowski __get_size_limit(limits, num_limits, id, &l);
4979913f74fSMarek Szyprowski if (!__size_limit_check(buf->rect.w, lh) ||
4989913f74fSMarek Szyprowski !__align_check(buf->rect.x, lh->align) ||
4999913f74fSMarek Szyprowski !__size_limit_check(buf->rect.h, lv) ||
5009913f74fSMarek Szyprowski !__align_check(buf->rect.y, lv->align))
5019913f74fSMarek Szyprowski return -EINVAL;
5029913f74fSMarek Szyprowski
5039913f74fSMarek Szyprowski return 0;
5049913f74fSMarek Szyprowski }
5059913f74fSMarek Szyprowski
__scale_limit_check(unsigned int src,unsigned int dst,unsigned int min,unsigned int max)5069913f74fSMarek Szyprowski static inline bool __scale_limit_check(unsigned int src, unsigned int dst,
5079913f74fSMarek Szyprowski unsigned int min, unsigned int max)
5089913f74fSMarek Szyprowski {
5099913f74fSMarek Szyprowski if ((max && (dst << 16) > src * max) ||
5109913f74fSMarek Szyprowski (min && (dst << 16) < src * min)) {
5119913f74fSMarek Szyprowski DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n",
5129913f74fSMarek Szyprowski src, dst,
5139913f74fSMarek Szyprowski min >> 16, 100000 * (min & 0xffff) / (1 << 16),
5149913f74fSMarek Szyprowski max >> 16, 100000 * (max & 0xffff) / (1 << 16));
5159913f74fSMarek Szyprowski return false;
5169913f74fSMarek Szyprowski }
5179913f74fSMarek Szyprowski return true;
5189913f74fSMarek Szyprowski }
5199913f74fSMarek Szyprowski
exynos_drm_ipp_check_scale_limits(struct drm_exynos_ipp_task_rect * src,struct drm_exynos_ipp_task_rect * dst,const struct drm_exynos_ipp_limit * limits,unsigned int num_limits,bool swap)5209913f74fSMarek Szyprowski static int exynos_drm_ipp_check_scale_limits(
5219913f74fSMarek Szyprowski struct drm_exynos_ipp_task_rect *src,
5229913f74fSMarek Szyprowski struct drm_exynos_ipp_task_rect *dst,
5239913f74fSMarek Szyprowski const struct drm_exynos_ipp_limit *limits,
5249913f74fSMarek Szyprowski unsigned int num_limits, bool swap)
5259913f74fSMarek Szyprowski {
5269913f74fSMarek Szyprowski const struct drm_exynos_ipp_limit_val *lh, *lv;
5279913f74fSMarek Szyprowski int dw, dh;
5289913f74fSMarek Szyprowski
5299913f74fSMarek Szyprowski for (; num_limits; limits++, num_limits--)
5309913f74fSMarek Szyprowski if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) ==
5319913f74fSMarek Szyprowski DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE)
5329913f74fSMarek Szyprowski break;
5339913f74fSMarek Szyprowski if (!num_limits)
5349913f74fSMarek Szyprowski return 0;
5359913f74fSMarek Szyprowski
5369913f74fSMarek Szyprowski lh = (!swap) ? &limits->h : &limits->v;
5379913f74fSMarek Szyprowski lv = (!swap) ? &limits->v : &limits->h;
5389913f74fSMarek Szyprowski dw = (!swap) ? dst->w : dst->h;
5399913f74fSMarek Szyprowski dh = (!swap) ? dst->h : dst->w;
5409913f74fSMarek Szyprowski
5419913f74fSMarek Szyprowski if (!__scale_limit_check(src->w, dw, lh->min, lh->max) ||
5429913f74fSMarek Szyprowski !__scale_limit_check(src->h, dh, lv->min, lv->max))
5439913f74fSMarek Szyprowski return -EINVAL;
5449913f74fSMarek Szyprowski
5459913f74fSMarek Szyprowski return 0;
5469913f74fSMarek Szyprowski }
5479913f74fSMarek Szyprowski
exynos_drm_ipp_check_format(struct exynos_drm_ipp_task * task,struct exynos_drm_ipp_buffer * buf,struct exynos_drm_ipp_buffer * src,struct exynos_drm_ipp_buffer * dst,bool rotate,bool swap)54869705ad2SMarek Szyprowski static int exynos_drm_ipp_check_format(struct exynos_drm_ipp_task *task,
54969705ad2SMarek Szyprowski struct exynos_drm_ipp_buffer *buf,
55069705ad2SMarek Szyprowski struct exynos_drm_ipp_buffer *src,
55169705ad2SMarek Szyprowski struct exynos_drm_ipp_buffer *dst,
55269705ad2SMarek Szyprowski bool rotate, bool swap)
55369705ad2SMarek Szyprowski {
55469705ad2SMarek Szyprowski const struct exynos_drm_ipp_formats *fmt;
55569705ad2SMarek Szyprowski int ret, i;
55669705ad2SMarek Szyprowski
55769705ad2SMarek Szyprowski fmt = __ipp_format_get(task->ipp, buf->buf.fourcc, buf->buf.modifier,
55869705ad2SMarek Szyprowski buf == src ? DRM_EXYNOS_IPP_FORMAT_SOURCE :
55969705ad2SMarek Szyprowski DRM_EXYNOS_IPP_FORMAT_DESTINATION);
56069705ad2SMarek Szyprowski if (!fmt) {
5618b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev,
5626be90056SInki Dae "Task %pK: %s format not supported\n",
5636be90056SInki Dae task, buf == src ? "src" : "dst");
56469705ad2SMarek Szyprowski return -EINVAL;
56569705ad2SMarek Szyprowski }
56669705ad2SMarek Szyprowski
56769705ad2SMarek Szyprowski /* basic checks */
56869705ad2SMarek Szyprowski if (buf->buf.width == 0 || buf->buf.height == 0)
56969705ad2SMarek Szyprowski return -EINVAL;
57069705ad2SMarek Szyprowski
57169705ad2SMarek Szyprowski buf->format = drm_format_info(buf->buf.fourcc);
57269705ad2SMarek Szyprowski for (i = 0; i < buf->format->num_planes; i++) {
57369705ad2SMarek Szyprowski unsigned int width = (i == 0) ? buf->buf.width :
57469705ad2SMarek Szyprowski DIV_ROUND_UP(buf->buf.width, buf->format->hsub);
57569705ad2SMarek Szyprowski
57669705ad2SMarek Szyprowski if (buf->buf.pitch[i] == 0)
57769705ad2SMarek Szyprowski buf->buf.pitch[i] = width * buf->format->cpp[i];
57869705ad2SMarek Szyprowski if (buf->buf.pitch[i] < width * buf->format->cpp[i])
57969705ad2SMarek Szyprowski return -EINVAL;
58069705ad2SMarek Szyprowski if (!buf->buf.gem_id[i])
58169705ad2SMarek Szyprowski return -ENOENT;
58269705ad2SMarek Szyprowski }
58369705ad2SMarek Szyprowski
58469705ad2SMarek Szyprowski /* pitch for additional planes must match */
58569705ad2SMarek Szyprowski if (buf->format->num_planes > 2 &&
58669705ad2SMarek Szyprowski buf->buf.pitch[1] != buf->buf.pitch[2])
58769705ad2SMarek Szyprowski return -EINVAL;
58869705ad2SMarek Szyprowski
58969705ad2SMarek Szyprowski /* check driver limits */
59069705ad2SMarek Szyprowski ret = exynos_drm_ipp_check_size_limits(buf, fmt->limits,
59169705ad2SMarek Szyprowski fmt->num_limits,
59269705ad2SMarek Szyprowski rotate,
59369705ad2SMarek Szyprowski buf == dst ? swap : false);
59469705ad2SMarek Szyprowski if (ret)
59569705ad2SMarek Szyprowski return ret;
59669705ad2SMarek Szyprowski ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
59769705ad2SMarek Szyprowski fmt->limits,
59869705ad2SMarek Szyprowski fmt->num_limits, swap);
59969705ad2SMarek Szyprowski return ret;
60069705ad2SMarek Szyprowski }
60169705ad2SMarek Szyprowski
exynos_drm_ipp_task_check(struct exynos_drm_ipp_task * task)6029913f74fSMarek Szyprowski static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
6039913f74fSMarek Szyprowski {
6049913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp = task->ipp;
6059913f74fSMarek Szyprowski struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
6069913f74fSMarek Szyprowski unsigned int rotation = task->transform.rotation;
6079913f74fSMarek Szyprowski int ret = 0;
6089913f74fSMarek Szyprowski bool swap = drm_rotation_90_or_270(rotation);
6099913f74fSMarek Szyprowski bool rotate = (rotation != DRM_MODE_ROTATE_0);
6109913f74fSMarek Szyprowski bool scale = false;
6119913f74fSMarek Szyprowski
6128b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Checking task %pK\n", task);
6139913f74fSMarek Szyprowski
6149913f74fSMarek Szyprowski if (src->rect.w == UINT_MAX)
6159913f74fSMarek Szyprowski src->rect.w = src->buf.width;
6169913f74fSMarek Szyprowski if (src->rect.h == UINT_MAX)
6179913f74fSMarek Szyprowski src->rect.h = src->buf.height;
6189913f74fSMarek Szyprowski if (dst->rect.w == UINT_MAX)
6199913f74fSMarek Szyprowski dst->rect.w = dst->buf.width;
6209913f74fSMarek Szyprowski if (dst->rect.h == UINT_MAX)
6219913f74fSMarek Szyprowski dst->rect.h = dst->buf.height;
6229913f74fSMarek Szyprowski
6239913f74fSMarek Szyprowski if (src->rect.x + src->rect.w > (src->buf.width) ||
6249913f74fSMarek Szyprowski src->rect.y + src->rect.h > (src->buf.height) ||
6259913f74fSMarek Szyprowski dst->rect.x + dst->rect.w > (dst->buf.width) ||
6269913f74fSMarek Szyprowski dst->rect.y + dst->rect.h > (dst->buf.height)) {
6278b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev,
6286be90056SInki Dae "Task %pK: defined area is outside provided buffers\n",
6299913f74fSMarek Szyprowski task);
6309913f74fSMarek Szyprowski return -EINVAL;
6319913f74fSMarek Szyprowski }
6329913f74fSMarek Szyprowski
6339913f74fSMarek Szyprowski if ((!swap && (src->rect.w != dst->rect.w ||
6349913f74fSMarek Szyprowski src->rect.h != dst->rect.h)) ||
6359913f74fSMarek Szyprowski (swap && (src->rect.w != dst->rect.h ||
6369913f74fSMarek Szyprowski src->rect.h != dst->rect.w)))
6379913f74fSMarek Szyprowski scale = true;
6389913f74fSMarek Szyprowski
6399913f74fSMarek Szyprowski if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) &&
6409913f74fSMarek Szyprowski (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) ||
6419913f74fSMarek Szyprowski (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) ||
6429913f74fSMarek Szyprowski (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) ||
6439913f74fSMarek Szyprowski (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) &&
6449913f74fSMarek Szyprowski src->buf.fourcc != dst->buf.fourcc)) {
6458b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: hw capabilities exceeded\n",
6466be90056SInki Dae task);
6479913f74fSMarek Szyprowski return -EINVAL;
6489913f74fSMarek Szyprowski }
6499913f74fSMarek Szyprowski
65069705ad2SMarek Szyprowski ret = exynos_drm_ipp_check_format(task, src, src, dst, rotate, swap);
6519913f74fSMarek Szyprowski if (ret)
6529913f74fSMarek Szyprowski return ret;
6539913f74fSMarek Szyprowski
65469705ad2SMarek Szyprowski ret = exynos_drm_ipp_check_format(task, dst, src, dst, false, swap);
6559913f74fSMarek Szyprowski if (ret)
6569913f74fSMarek Szyprowski return ret;
6579913f74fSMarek Szyprowski
6588b955034SInki Dae DRM_DEV_DEBUG_DRIVER(ipp->dev, "Task %pK: all checks done.\n",
6596be90056SInki Dae task);
6609913f74fSMarek Szyprowski
6619913f74fSMarek Szyprowski return ret;
6629913f74fSMarek Szyprowski }
6639913f74fSMarek Szyprowski
exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task * task,struct drm_file * filp)6649913f74fSMarek Szyprowski static int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task,
6659913f74fSMarek Szyprowski struct drm_file *filp)
6669913f74fSMarek Szyprowski {
6679913f74fSMarek Szyprowski struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
6689913f74fSMarek Szyprowski int ret = 0;
6699913f74fSMarek Szyprowski
6708b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Setting buffer for task %pK\n",
6716be90056SInki Dae task);
6729913f74fSMarek Szyprowski
6739913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_setup_buffer(src, filp);
6749913f74fSMarek Szyprowski if (ret) {
6758b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev,
6766be90056SInki Dae "Task %pK: src buffer setup failed\n",
6776be90056SInki Dae task);
6789913f74fSMarek Szyprowski return ret;
6799913f74fSMarek Szyprowski }
6809913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_setup_buffer(dst, filp);
6819913f74fSMarek Szyprowski if (ret) {
6828b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev,
6836be90056SInki Dae "Task %pK: dst buffer setup failed\n",
6846be90056SInki Dae task);
6859913f74fSMarek Szyprowski return ret;
6869913f74fSMarek Szyprowski }
6879913f74fSMarek Szyprowski
6888b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: buffers prepared.\n",
6896be90056SInki Dae task);
6909913f74fSMarek Szyprowski
6919913f74fSMarek Szyprowski return ret;
6929913f74fSMarek Szyprowski }
6939913f74fSMarek Szyprowski
6949913f74fSMarek Szyprowski
exynos_drm_ipp_event_create(struct exynos_drm_ipp_task * task,struct drm_file * file_priv,uint64_t user_data)6959913f74fSMarek Szyprowski static int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task,
6969913f74fSMarek Szyprowski struct drm_file *file_priv, uint64_t user_data)
6979913f74fSMarek Szyprowski {
6989913f74fSMarek Szyprowski struct drm_pending_exynos_ipp_event *e = NULL;
6999913f74fSMarek Szyprowski int ret;
7009913f74fSMarek Szyprowski
7019913f74fSMarek Szyprowski e = kzalloc(sizeof(*e), GFP_KERNEL);
7029913f74fSMarek Szyprowski if (!e)
7039913f74fSMarek Szyprowski return -ENOMEM;
7049913f74fSMarek Szyprowski
7059913f74fSMarek Szyprowski e->event.base.type = DRM_EXYNOS_IPP_EVENT;
7069913f74fSMarek Szyprowski e->event.base.length = sizeof(e->event);
7079913f74fSMarek Szyprowski e->event.user_data = user_data;
7089913f74fSMarek Szyprowski
7098b955034SInki Dae ret = drm_event_reserve_init(task->ipp->drm_dev, file_priv, &e->base,
7109913f74fSMarek Szyprowski &e->event.base);
7119913f74fSMarek Szyprowski if (ret)
7129913f74fSMarek Szyprowski goto free;
7139913f74fSMarek Szyprowski
7149913f74fSMarek Szyprowski task->event = e;
7159913f74fSMarek Szyprowski return 0;
7169913f74fSMarek Szyprowski free:
7179913f74fSMarek Szyprowski kfree(e);
7189913f74fSMarek Szyprowski return ret;
7199913f74fSMarek Szyprowski }
7209913f74fSMarek Szyprowski
exynos_drm_ipp_event_send(struct exynos_drm_ipp_task * task)7219913f74fSMarek Szyprowski static void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task)
7229913f74fSMarek Szyprowski {
7239913f74fSMarek Szyprowski struct timespec64 now;
7249913f74fSMarek Szyprowski
7259913f74fSMarek Szyprowski ktime_get_ts64(&now);
7269913f74fSMarek Szyprowski task->event->event.tv_sec = now.tv_sec;
7279913f74fSMarek Szyprowski task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC;
7289913f74fSMarek Szyprowski task->event->event.sequence = atomic_inc_return(&task->ipp->sequence);
7299913f74fSMarek Szyprowski
7308b955034SInki Dae drm_send_event(task->ipp->drm_dev, &task->event->base);
7319913f74fSMarek Szyprowski }
7329913f74fSMarek Szyprowski
exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task * task)7339913f74fSMarek Szyprowski static int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task)
7349913f74fSMarek Szyprowski {
7359913f74fSMarek Szyprowski int ret = task->ret;
7369913f74fSMarek Szyprowski
7379913f74fSMarek Szyprowski if (ret == 0 && task->event) {
7389913f74fSMarek Szyprowski exynos_drm_ipp_event_send(task);
7399913f74fSMarek Szyprowski /* ensure event won't be canceled on task free */
7409913f74fSMarek Szyprowski task->event = NULL;
7419913f74fSMarek Szyprowski }
7429913f74fSMarek Szyprowski
7439913f74fSMarek Szyprowski exynos_drm_ipp_task_free(task->ipp, task);
7449913f74fSMarek Szyprowski return ret;
7459913f74fSMarek Szyprowski }
7469913f74fSMarek Szyprowski
exynos_drm_ipp_cleanup_work(struct work_struct * work)7479913f74fSMarek Szyprowski static void exynos_drm_ipp_cleanup_work(struct work_struct *work)
7489913f74fSMarek Szyprowski {
7499913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task = container_of(work,
7509913f74fSMarek Szyprowski struct exynos_drm_ipp_task, cleanup_work);
7519913f74fSMarek Szyprowski
7529913f74fSMarek Szyprowski exynos_drm_ipp_task_cleanup(task);
7539913f74fSMarek Szyprowski }
7549913f74fSMarek Szyprowski
7559913f74fSMarek Szyprowski static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp);
7569913f74fSMarek Szyprowski
7579913f74fSMarek Szyprowski /**
7589913f74fSMarek Szyprowski * exynos_drm_ipp_task_done - finish given task and set return code
7599913f74fSMarek Szyprowski * @task: ipp task to finish
7609913f74fSMarek Szyprowski * @ret: error code or 0 if operation has been performed successfully
7619913f74fSMarek Szyprowski */
exynos_drm_ipp_task_done(struct exynos_drm_ipp_task * task,int ret)7629913f74fSMarek Szyprowski void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret)
7639913f74fSMarek Szyprowski {
7649913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp = task->ipp;
7659913f74fSMarek Szyprowski unsigned long flags;
7669913f74fSMarek Szyprowski
7678b955034SInki Dae DRM_DEV_DEBUG_DRIVER(task->dev, "ipp: %d, task %pK done: %d\n",
7686be90056SInki Dae ipp->id, task, ret);
7699913f74fSMarek Szyprowski
7709913f74fSMarek Szyprowski spin_lock_irqsave(&ipp->lock, flags);
7719913f74fSMarek Szyprowski if (ipp->task == task)
7729913f74fSMarek Szyprowski ipp->task = NULL;
7739913f74fSMarek Szyprowski task->flags |= DRM_EXYNOS_IPP_TASK_DONE;
7749913f74fSMarek Szyprowski task->ret = ret;
7759913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
7769913f74fSMarek Szyprowski
7779913f74fSMarek Szyprowski exynos_drm_ipp_next_task(ipp);
7789913f74fSMarek Szyprowski wake_up(&ipp->done_wq);
7799913f74fSMarek Szyprowski
7809913f74fSMarek Szyprowski if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) {
7819913f74fSMarek Szyprowski INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work);
7829913f74fSMarek Szyprowski schedule_work(&task->cleanup_work);
7839913f74fSMarek Szyprowski }
7849913f74fSMarek Szyprowski }
7859913f74fSMarek Szyprowski
exynos_drm_ipp_next_task(struct exynos_drm_ipp * ipp)7869913f74fSMarek Szyprowski static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp)
7879913f74fSMarek Szyprowski {
7889913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task;
7899913f74fSMarek Szyprowski unsigned long flags;
7909913f74fSMarek Szyprowski int ret;
7919913f74fSMarek Szyprowski
7928b955034SInki Dae DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, try to run new task\n",
7936be90056SInki Dae ipp->id);
7949913f74fSMarek Szyprowski
7959913f74fSMarek Szyprowski spin_lock_irqsave(&ipp->lock, flags);
7969913f74fSMarek Szyprowski
7979913f74fSMarek Szyprowski if (ipp->task || list_empty(&ipp->todo_list)) {
7989913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
7999913f74fSMarek Szyprowski return;
8009913f74fSMarek Szyprowski }
8019913f74fSMarek Szyprowski
8029913f74fSMarek Szyprowski task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task,
8039913f74fSMarek Szyprowski head);
8049913f74fSMarek Szyprowski list_del_init(&task->head);
8059913f74fSMarek Szyprowski ipp->task = task;
8069913f74fSMarek Szyprowski
8079913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
8089913f74fSMarek Szyprowski
8098b955034SInki Dae DRM_DEV_DEBUG_DRIVER(ipp->dev,
8106be90056SInki Dae "ipp: %d, selected task %pK to run\n", ipp->id,
8116be90056SInki Dae task);
8129913f74fSMarek Szyprowski
8139913f74fSMarek Szyprowski ret = ipp->funcs->commit(ipp, task);
8149913f74fSMarek Szyprowski if (ret)
8159913f74fSMarek Szyprowski exynos_drm_ipp_task_done(task, ret);
8169913f74fSMarek Szyprowski }
8179913f74fSMarek Szyprowski
exynos_drm_ipp_schedule_task(struct exynos_drm_ipp * ipp,struct exynos_drm_ipp_task * task)8189913f74fSMarek Szyprowski static void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp,
8199913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task)
8209913f74fSMarek Szyprowski {
8219913f74fSMarek Szyprowski unsigned long flags;
8229913f74fSMarek Szyprowski
8239913f74fSMarek Szyprowski spin_lock_irqsave(&ipp->lock, flags);
8249913f74fSMarek Szyprowski list_add(&task->head, &ipp->todo_list);
8259913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
8269913f74fSMarek Szyprowski
8279913f74fSMarek Szyprowski exynos_drm_ipp_next_task(ipp);
8289913f74fSMarek Szyprowski }
8299913f74fSMarek Szyprowski
exynos_drm_ipp_task_abort(struct exynos_drm_ipp * ipp,struct exynos_drm_ipp_task * task)8309913f74fSMarek Szyprowski static void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp,
8319913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task)
8329913f74fSMarek Szyprowski {
8339913f74fSMarek Szyprowski unsigned long flags;
8349913f74fSMarek Szyprowski
8359913f74fSMarek Szyprowski spin_lock_irqsave(&ipp->lock, flags);
8369913f74fSMarek Szyprowski if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) {
8379913f74fSMarek Szyprowski /* already completed task */
8389913f74fSMarek Szyprowski exynos_drm_ipp_task_cleanup(task);
8399913f74fSMarek Szyprowski } else if (ipp->task != task) {
8409913f74fSMarek Szyprowski /* task has not been scheduled for execution yet */
8419913f74fSMarek Szyprowski list_del_init(&task->head);
8429913f74fSMarek Szyprowski exynos_drm_ipp_task_cleanup(task);
8439913f74fSMarek Szyprowski } else {
8449913f74fSMarek Szyprowski /*
8459913f74fSMarek Szyprowski * currently processed task, call abort() and perform
8469913f74fSMarek Szyprowski * cleanup with async worker
8479913f74fSMarek Szyprowski */
8489913f74fSMarek Szyprowski task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
8499913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
8509913f74fSMarek Szyprowski if (ipp->funcs->abort)
8519913f74fSMarek Szyprowski ipp->funcs->abort(ipp, task);
8529913f74fSMarek Szyprowski return;
8539913f74fSMarek Szyprowski }
8549913f74fSMarek Szyprowski spin_unlock_irqrestore(&ipp->lock, flags);
8559913f74fSMarek Szyprowski }
8569913f74fSMarek Szyprowski
8579913f74fSMarek Szyprowski /**
8589913f74fSMarek Szyprowski * exynos_drm_ipp_commit_ioctl - perform image processing operation
8599913f74fSMarek Szyprowski * @dev: DRM device
8609913f74fSMarek Szyprowski * @data: ioctl data
8619913f74fSMarek Szyprowski * @file_priv: DRM file info
8629913f74fSMarek Szyprowski *
8639913f74fSMarek Szyprowski * Construct a ipp task from the set of properties provided from the user
8649913f74fSMarek Szyprowski * and try to schedule it to framebuffer processor hardware.
8659913f74fSMarek Szyprowski *
8669913f74fSMarek Szyprowski * Called by the user via ioctl.
8679913f74fSMarek Szyprowski *
8689913f74fSMarek Szyprowski * Returns:
8699913f74fSMarek Szyprowski * Zero on success, negative errno on failure.
8709913f74fSMarek Szyprowski */
exynos_drm_ipp_commit_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)8719913f74fSMarek Szyprowski int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data,
8729913f74fSMarek Szyprowski struct drm_file *file_priv)
8739913f74fSMarek Szyprowski {
8749913f74fSMarek Szyprowski struct drm_exynos_ioctl_ipp_commit *arg = data;
8759913f74fSMarek Szyprowski struct exynos_drm_ipp *ipp;
8769913f74fSMarek Szyprowski struct exynos_drm_ipp_task *task;
8779913f74fSMarek Szyprowski int ret = 0;
8789913f74fSMarek Szyprowski
8799913f74fSMarek Szyprowski if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved)
8809913f74fSMarek Szyprowski return -EINVAL;
8819913f74fSMarek Szyprowski
8829913f74fSMarek Szyprowski /* can't test and expect an event at the same time */
8839913f74fSMarek Szyprowski if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) &&
8849913f74fSMarek Szyprowski (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT))
8859913f74fSMarek Szyprowski return -EINVAL;
8869913f74fSMarek Szyprowski
8879913f74fSMarek Szyprowski ipp = __ipp_get(arg->ipp_id);
8889913f74fSMarek Szyprowski if (!ipp)
8899913f74fSMarek Szyprowski return -ENOENT;
8909913f74fSMarek Szyprowski
8919913f74fSMarek Szyprowski task = exynos_drm_ipp_task_alloc(ipp);
8929913f74fSMarek Szyprowski if (!task)
8939913f74fSMarek Szyprowski return -ENOMEM;
8949913f74fSMarek Szyprowski
8959913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_set(task, arg);
8969913f74fSMarek Szyprowski if (ret)
8979913f74fSMarek Szyprowski goto free;
8989913f74fSMarek Szyprowski
8999913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_check(task);
9009913f74fSMarek Szyprowski if (ret)
9019913f74fSMarek Szyprowski goto free;
9029913f74fSMarek Szyprowski
9039913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_setup_buffers(task, file_priv);
9049913f74fSMarek Szyprowski if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY)
9059913f74fSMarek Szyprowski goto free;
9069913f74fSMarek Szyprowski
9079913f74fSMarek Szyprowski if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) {
9089913f74fSMarek Szyprowski ret = exynos_drm_ipp_event_create(task, file_priv,
9099913f74fSMarek Szyprowski arg->user_data);
9109913f74fSMarek Szyprowski if (ret)
9119913f74fSMarek Szyprowski goto free;
9129913f74fSMarek Szyprowski }
9139913f74fSMarek Szyprowski
9149913f74fSMarek Szyprowski /*
9159913f74fSMarek Szyprowski * Queue task for processing on the hardware. task object will be
9169913f74fSMarek Szyprowski * then freed after exynos_drm_ipp_task_done()
9179913f74fSMarek Szyprowski */
9189913f74fSMarek Szyprowski if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) {
9198b955034SInki Dae DRM_DEV_DEBUG_DRIVER(ipp->dev,
9206be90056SInki Dae "ipp: %d, nonblocking processing task %pK\n",
9219913f74fSMarek Szyprowski ipp->id, task);
9229913f74fSMarek Szyprowski
9239913f74fSMarek Szyprowski task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
9249913f74fSMarek Szyprowski exynos_drm_ipp_schedule_task(task->ipp, task);
9259913f74fSMarek Szyprowski ret = 0;
9269913f74fSMarek Szyprowski } else {
9278b955034SInki Dae DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, processing task %pK\n",
9286be90056SInki Dae ipp->id, task);
9299913f74fSMarek Szyprowski exynos_drm_ipp_schedule_task(ipp, task);
9309913f74fSMarek Szyprowski ret = wait_event_interruptible(ipp->done_wq,
9319913f74fSMarek Szyprowski task->flags & DRM_EXYNOS_IPP_TASK_DONE);
9329913f74fSMarek Szyprowski if (ret)
9339913f74fSMarek Szyprowski exynos_drm_ipp_task_abort(ipp, task);
9349913f74fSMarek Szyprowski else
9359913f74fSMarek Szyprowski ret = exynos_drm_ipp_task_cleanup(task);
9369913f74fSMarek Szyprowski }
9379913f74fSMarek Szyprowski return ret;
9389913f74fSMarek Szyprowski free:
9399913f74fSMarek Szyprowski exynos_drm_ipp_task_free(ipp, task);
9409913f74fSMarek Szyprowski
9419913f74fSMarek Szyprowski return ret;
9429913f74fSMarek Szyprowski }
943