linux/drivers/gpu/drm/exynos/exynos_drm_ipp.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Samsung Electronics Co.Ltd
   3 * Authors:
   4 *      Marek Szyprowski <m.szyprowski@samsung.com>
   5 *
   6 * Exynos DRM Image Post Processing (IPP) related functions
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a
   9 * copy of this software and associated documentation files (the "Software"),
  10 * to deal in the Software without restriction, including without limitation
  11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  12 * and/or sell copies of the Software, and to permit persons to whom the
  13 * Software is furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 */
  18
  19#include <linux/uaccess.h>
  20
  21#include <drm/drm_file.h>
  22#include <drm/drm_fourcc.h>
  23#include <drm/drm_mode.h>
  24#include <drm/exynos_drm.h>
  25
  26#include "exynos_drm_drv.h"
  27#include "exynos_drm_gem.h"
  28#include "exynos_drm_ipp.h"
  29
  30static int num_ipp;
  31static LIST_HEAD(ipp_list);
  32
  33/**
  34 * exynos_drm_ipp_register - Register a new picture processor hardware module
  35 * @dev: DRM device
  36 * @ipp: ipp module to init
  37 * @funcs: callbacks for the new ipp object
  38 * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*)
  39 * @formats: array of supported formats
  40 * @num_formats: size of the supported formats array
  41 * @name: name (for debugging purposes)
  42 *
  43 * Initializes a ipp module.
  44 *
  45 * Returns:
  46 * Zero on success, error code on failure.
  47 */
  48int exynos_drm_ipp_register(struct device *dev, struct exynos_drm_ipp *ipp,
  49                const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
  50                const struct exynos_drm_ipp_formats *formats,
  51                unsigned int num_formats, const char *name)
  52{
  53        WARN_ON(!ipp);
  54        WARN_ON(!funcs);
  55        WARN_ON(!formats);
  56        WARN_ON(!num_formats);
  57
  58        spin_lock_init(&ipp->lock);
  59        INIT_LIST_HEAD(&ipp->todo_list);
  60        init_waitqueue_head(&ipp->done_wq);
  61        ipp->dev = dev;
  62        ipp->funcs = funcs;
  63        ipp->capabilities = caps;
  64        ipp->name = name;
  65        ipp->formats = formats;
  66        ipp->num_formats = num_formats;
  67
  68        /* ipp_list modification is serialized by component framework */
  69        list_add_tail(&ipp->head, &ipp_list);
  70        ipp->id = num_ipp++;
  71
  72        DRM_DEV_DEBUG_DRIVER(dev, "Registered ipp %d\n", ipp->id);
  73
  74        return 0;
  75}
  76
  77/**
  78 * exynos_drm_ipp_unregister - Unregister the picture processor module
  79 * @dev: DRM device
  80 * @ipp: ipp module
  81 */
  82void exynos_drm_ipp_unregister(struct device *dev,
  83                               struct exynos_drm_ipp *ipp)
  84{
  85        WARN_ON(ipp->task);
  86        WARN_ON(!list_empty(&ipp->todo_list));
  87        list_del(&ipp->head);
  88}
  89
  90/**
  91 * exynos_drm_ipp_get_res_ioctl - enumerate all ipp modules
  92 * @dev: DRM device
  93 * @data: ioctl data
  94 * @file_priv: DRM file info
  95 *
  96 * Construct a list of ipp ids.
  97 *
  98 * Called by the user via ioctl.
  99 *
 100 * Returns:
 101 * Zero on success, negative errno on failure.
 102 */
 103int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
 104                                 struct drm_file *file_priv)
 105{
 106        struct drm_exynos_ioctl_ipp_get_res *resp = data;
 107        struct exynos_drm_ipp *ipp;
 108        uint32_t __user *ipp_ptr = (uint32_t __user *)
 109                                                (unsigned long)resp->ipp_id_ptr;
 110        unsigned int count = num_ipp, copied = 0;
 111
 112        /*
 113         * This ioctl is called twice, once to determine how much space is
 114         * needed, and the 2nd time to fill it.
 115         */
 116        if (count && resp->count_ipps >= count) {
 117                list_for_each_entry(ipp, &ipp_list, head) {
 118                        if (put_user(ipp->id, ipp_ptr + copied))
 119                                return -EFAULT;
 120                        copied++;
 121                }
 122        }
 123        resp->count_ipps = count;
 124
 125        return 0;
 126}
 127
 128static inline struct exynos_drm_ipp *__ipp_get(uint32_t id)
 129{
 130        struct exynos_drm_ipp *ipp;
 131
 132        list_for_each_entry(ipp, &ipp_list, head)
 133                if (ipp->id == id)
 134                        return ipp;
 135        return NULL;
 136}
 137
 138/**
 139 * exynos_drm_ipp_get_caps_ioctl - get ipp module capabilities and formats
 140 * @dev: DRM device
 141 * @data: ioctl data
 142 * @file_priv: DRM file info
 143 *
 144 * Construct a structure describing ipp module capabilities.
 145 *
 146 * Called by the user via ioctl.
 147 *
 148 * Returns:
 149 * Zero on success, negative errno on failure.
 150 */
 151int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
 152                                  struct drm_file *file_priv)
 153{
 154        struct drm_exynos_ioctl_ipp_get_caps *resp = data;
 155        void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr;
 156        struct exynos_drm_ipp *ipp;
 157        int i;
 158
 159        ipp = __ipp_get(resp->ipp_id);
 160        if (!ipp)
 161                return -ENOENT;
 162
 163        resp->ipp_id = ipp->id;
 164        resp->capabilities = ipp->capabilities;
 165
 166        /*
 167         * This ioctl is called twice, once to determine how much space is
 168         * needed, and the 2nd time to fill it.
 169         */
 170        if (resp->formats_count >= ipp->num_formats) {
 171                for (i = 0; i < ipp->num_formats; i++) {
 172                        struct drm_exynos_ipp_format tmp = {
 173                                .fourcc = ipp->formats[i].fourcc,
 174                                .type = ipp->formats[i].type,
 175                                .modifier = ipp->formats[i].modifier,
 176                        };
 177
 178                        if (copy_to_user(ptr, &tmp, sizeof(tmp)))
 179                                return -EFAULT;
 180                        ptr += sizeof(tmp);
 181                }
 182        }
 183        resp->formats_count = ipp->num_formats;
 184
 185        return 0;
 186}
 187
 188static inline const struct exynos_drm_ipp_formats *__ipp_format_get(
 189                                struct exynos_drm_ipp *ipp, uint32_t fourcc,
 190                                uint64_t mod, unsigned int type)
 191{
 192        int i;
 193
 194        for (i = 0; i < ipp->num_formats; i++) {
 195                if ((ipp->formats[i].type & type) &&
 196                    ipp->formats[i].fourcc == fourcc &&
 197                    ipp->formats[i].modifier == mod)
 198                        return &ipp->formats[i];
 199        }
 200        return NULL;
 201}
 202
 203/**
 204 * exynos_drm_ipp_get_limits_ioctl - get ipp module limits
 205 * @dev: DRM device
 206 * @data: ioctl data
 207 * @file_priv: DRM file info
 208 *
 209 * Construct a structure describing ipp module limitations for provided
 210 * picture format.
 211 *
 212 * Called by the user via ioctl.
 213 *
 214 * Returns:
 215 * Zero on success, negative errno on failure.
 216 */
 217int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
 218                                    struct drm_file *file_priv)
 219{
 220        struct drm_exynos_ioctl_ipp_get_limits *resp = data;
 221        void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr;
 222        const struct exynos_drm_ipp_formats *format;
 223        struct exynos_drm_ipp *ipp;
 224
 225        if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE &&
 226            resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION)
 227                return -EINVAL;
 228
 229        ipp = __ipp_get(resp->ipp_id);
 230        if (!ipp)
 231                return -ENOENT;
 232
 233        format = __ipp_format_get(ipp, resp->fourcc, resp->modifier,
 234                                  resp->type);
 235        if (!format)
 236                return -EINVAL;
 237
 238        /*
 239         * This ioctl is called twice, once to determine how much space is
 240         * needed, and the 2nd time to fill it.
 241         */
 242        if (format->num_limits && resp->limits_count >= format->num_limits)
 243                if (copy_to_user((void __user *)ptr, format->limits,
 244                                 sizeof(*format->limits) * format->num_limits))
 245                        return -EFAULT;
 246        resp->limits_count = format->num_limits;
 247
 248        return 0;
 249}
 250
 251struct drm_pending_exynos_ipp_event {
 252        struct drm_pending_event base;
 253        struct drm_exynos_ipp_event event;
 254};
 255
 256static inline struct exynos_drm_ipp_task *
 257                        exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp)
 258{
 259        struct exynos_drm_ipp_task *task;
 260
 261        task = kzalloc(sizeof(*task), GFP_KERNEL);
 262        if (!task)
 263                return NULL;
 264
 265        task->dev = ipp->dev;
 266        task->ipp = ipp;
 267
 268        /* some defaults */
 269        task->src.rect.w = task->dst.rect.w = UINT_MAX;
 270        task->src.rect.h = task->dst.rect.h = UINT_MAX;
 271        task->transform.rotation = DRM_MODE_ROTATE_0;
 272
 273        DRM_DEV_DEBUG_DRIVER(task->dev, "Allocated task %pK\n", task);
 274
 275        return task;
 276}
 277
 278static const struct exynos_drm_param_map {
 279        unsigned int id;
 280        unsigned int size;
 281        unsigned int offset;
 282} exynos_drm_ipp_params_maps[] = {
 283        {
 284                DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
 285                sizeof(struct drm_exynos_ipp_task_buffer),
 286                offsetof(struct exynos_drm_ipp_task, src.buf),
 287        }, {
 288                DRM_EXYNOS_IPP_TASK_BUFFER |
 289                        DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
 290                sizeof(struct drm_exynos_ipp_task_buffer),
 291                offsetof(struct exynos_drm_ipp_task, dst.buf),
 292        }, {
 293                DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
 294                sizeof(struct drm_exynos_ipp_task_rect),
 295                offsetof(struct exynos_drm_ipp_task, src.rect),
 296        }, {
 297                DRM_EXYNOS_IPP_TASK_RECTANGLE |
 298                        DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
 299                sizeof(struct drm_exynos_ipp_task_rect),
 300                offsetof(struct exynos_drm_ipp_task, dst.rect),
 301        }, {
 302                DRM_EXYNOS_IPP_TASK_TRANSFORM,
 303                sizeof(struct drm_exynos_ipp_task_transform),
 304                offsetof(struct exynos_drm_ipp_task, transform),
 305        }, {
 306                DRM_EXYNOS_IPP_TASK_ALPHA,
 307                sizeof(struct drm_exynos_ipp_task_alpha),
 308                offsetof(struct exynos_drm_ipp_task, alpha),
 309        },
 310};
 311
 312static int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task,
 313                                   struct drm_exynos_ioctl_ipp_commit *arg)
 314{
 315        const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps;
 316        void __user *params = (void __user *)(unsigned long)arg->params_ptr;
 317        unsigned int size = arg->params_size;
 318        uint32_t id;
 319        int i;
 320
 321        while (size) {
 322                if (get_user(id, (uint32_t __user *)params))
 323                        return -EFAULT;
 324
 325                for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++)
 326                        if (map[i].id == id)
 327                                break;
 328                if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) ||
 329                    map[i].size > size)
 330                        return -EINVAL;
 331
 332                if (copy_from_user((void *)task + map[i].offset, params,
 333                                   map[i].size))
 334                        return -EFAULT;
 335
 336                params += map[i].size;
 337                size -= map[i].size;
 338        }
 339
 340        DRM_DEV_DEBUG_DRIVER(task->dev,
 341                             "Got task %pK configuration from userspace\n",
 342                             task);
 343        return 0;
 344}
 345
 346static int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf,
 347                                            struct drm_file *filp)
 348{
 349        int ret = 0;
 350        int i;
 351
 352        /* get GEM buffers and check their size */
 353        for (i = 0; i < buf->format->num_planes; i++) {
 354                unsigned int height = (i == 0) ? buf->buf.height :
 355                             DIV_ROUND_UP(buf->buf.height, buf->format->vsub);
 356                unsigned long size = height * buf->buf.pitch[i];
 357                struct exynos_drm_gem *gem = exynos_drm_gem_get(filp,
 358                                                            buf->buf.gem_id[i]);
 359                if (!gem) {
 360                        ret = -ENOENT;
 361                        goto gem_free;
 362                }
 363                buf->exynos_gem[i] = gem;
 364
 365                if (size + buf->buf.offset[i] > buf->exynos_gem[i]->size) {
 366                        i++;
 367                        ret = -EINVAL;
 368                        goto gem_free;
 369                }
 370                buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr +
 371                                   buf->buf.offset[i];
 372        }
 373
 374        return 0;
 375gem_free:
 376        while (i--) {
 377                exynos_drm_gem_put(buf->exynos_gem[i]);
 378                buf->exynos_gem[i] = NULL;
 379        }
 380        return ret;
 381}
 382
 383static void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf)
 384{
 385        int i;
 386
 387        if (!buf->exynos_gem[0])
 388                return;
 389        for (i = 0; i < buf->format->num_planes; i++)
 390                exynos_drm_gem_put(buf->exynos_gem[i]);
 391}
 392
 393static void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp,
 394                                 struct exynos_drm_ipp_task *task)
 395{
 396        DRM_DEV_DEBUG_DRIVER(task->dev, "Freeing task %pK\n", task);
 397
 398        exynos_drm_ipp_task_release_buf(&task->src);
 399        exynos_drm_ipp_task_release_buf(&task->dst);
 400        if (task->event)
 401                drm_event_cancel_free(ipp->drm_dev, &task->event->base);
 402        kfree(task);
 403}
 404
 405struct drm_ipp_limit {
 406        struct drm_exynos_ipp_limit_val h;
 407        struct drm_exynos_ipp_limit_val v;
 408};
 409
 410enum drm_ipp_size_id {
 411        IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
 412};
 413
 414static const enum drm_exynos_ipp_limit_type limit_id_fallback[IPP_LIMIT_MAX][4] = {
 415        [IPP_LIMIT_BUFFER]  = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
 416        [IPP_LIMIT_AREA]    = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
 417                                DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
 418        [IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED,
 419                                DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
 420                                DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
 421};
 422
 423static inline void __limit_set_val(unsigned int *ptr, unsigned int val)
 424{
 425        if (!*ptr)
 426                *ptr = val;
 427}
 428
 429static void __get_size_limit(const struct drm_exynos_ipp_limit *limits,
 430                             unsigned int num_limits, enum drm_ipp_size_id id,
 431                             struct drm_ipp_limit *res)
 432{
 433        const struct drm_exynos_ipp_limit *l = limits;
 434        int i = 0;
 435
 436        memset(res, 0, sizeof(*res));
 437        for (i = 0; limit_id_fallback[id][i]; i++)
 438                for (l = limits; l - limits < num_limits; l++) {
 439                        if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) !=
 440                              DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) ||
 441                            ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) !=
 442                                                     limit_id_fallback[id][i]))
 443                                continue;
 444                        __limit_set_val(&res->h.min, l->h.min);
 445                        __limit_set_val(&res->h.max, l->h.max);
 446                        __limit_set_val(&res->h.align, l->h.align);
 447                        __limit_set_val(&res->v.min, l->v.min);
 448                        __limit_set_val(&res->v.max, l->v.max);
 449                        __limit_set_val(&res->v.align, l->v.align);
 450                }
 451}
 452
 453static inline bool __align_check(unsigned int val, unsigned int align)
 454{
 455        if (align && (val & (align - 1))) {
 456                DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n",
 457                                 val, align);
 458                return false;
 459        }
 460        return true;
 461}
 462
 463static inline bool __size_limit_check(unsigned int val,
 464                                 struct drm_exynos_ipp_limit_val *l)
 465{
 466        if ((l->min && val < l->min) || (l->max && val > l->max)) {
 467                DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n",
 468                                 val, l->min, l->max);
 469                return false;
 470        }
 471        return __align_check(val, l->align);
 472}
 473
 474static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
 475        const struct drm_exynos_ipp_limit *limits, unsigned int num_limits,
 476        bool rotate, bool swap)
 477{
 478        enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA;
 479        struct drm_ipp_limit l;
 480        struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v;
 481        int real_width = buf->buf.pitch[0] / buf->format->cpp[0];
 482
 483        if (!limits)
 484                return 0;
 485
 486        __get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l);
 487        if (!__size_limit_check(real_width, &l.h) ||
 488            !__size_limit_check(buf->buf.height, &l.v))
 489                return -EINVAL;
 490
 491        if (swap) {
 492                lv = &l.h;
 493                lh = &l.v;
 494        }
 495        __get_size_limit(limits, num_limits, id, &l);
 496        if (!__size_limit_check(buf->rect.w, lh) ||
 497            !__align_check(buf->rect.x, lh->align) ||
 498            !__size_limit_check(buf->rect.h, lv) ||
 499            !__align_check(buf->rect.y, lv->align))
 500                return -EINVAL;
 501
 502        return 0;
 503}
 504
 505static inline bool __scale_limit_check(unsigned int src, unsigned int dst,
 506                                       unsigned int min, unsigned int max)
 507{
 508        if ((max && (dst << 16) > src * max) ||
 509            (min && (dst << 16) < src * min)) {
 510                DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n",
 511                         src, dst,
 512                         min >> 16, 100000 * (min & 0xffff) / (1 << 16),
 513                         max >> 16, 100000 * (max & 0xffff) / (1 << 16));
 514                return false;
 515        }
 516        return true;
 517}
 518
 519static int exynos_drm_ipp_check_scale_limits(
 520                                struct drm_exynos_ipp_task_rect *src,
 521                                struct drm_exynos_ipp_task_rect *dst,
 522                                const struct drm_exynos_ipp_limit *limits,
 523                                unsigned int num_limits, bool swap)
 524{
 525        const struct drm_exynos_ipp_limit_val *lh, *lv;
 526        int dw, dh;
 527
 528        for (; num_limits; limits++, num_limits--)
 529                if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) ==
 530                    DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE)
 531                        break;
 532        if (!num_limits)
 533                return 0;
 534
 535        lh = (!swap) ? &limits->h : &limits->v;
 536        lv = (!swap) ? &limits->v : &limits->h;
 537        dw = (!swap) ? dst->w : dst->h;
 538        dh = (!swap) ? dst->h : dst->w;
 539
 540        if (!__scale_limit_check(src->w, dw, lh->min, lh->max) ||
 541            !__scale_limit_check(src->h, dh, lv->min, lv->max))
 542                return -EINVAL;
 543
 544        return 0;
 545}
 546
 547static int exynos_drm_ipp_check_format(struct exynos_drm_ipp_task *task,
 548                                       struct exynos_drm_ipp_buffer *buf,
 549                                       struct exynos_drm_ipp_buffer *src,
 550                                       struct exynos_drm_ipp_buffer *dst,
 551                                       bool rotate, bool swap)
 552{
 553        const struct exynos_drm_ipp_formats *fmt;
 554        int ret, i;
 555
 556        fmt = __ipp_format_get(task->ipp, buf->buf.fourcc, buf->buf.modifier,
 557                               buf == src ? DRM_EXYNOS_IPP_FORMAT_SOURCE :
 558                                            DRM_EXYNOS_IPP_FORMAT_DESTINATION);
 559        if (!fmt) {
 560                DRM_DEV_DEBUG_DRIVER(task->dev,
 561                                     "Task %pK: %s format not supported\n",
 562                                     task, buf == src ? "src" : "dst");
 563                return -EINVAL;
 564        }
 565
 566        /* basic checks */
 567        if (buf->buf.width == 0 || buf->buf.height == 0)
 568                return -EINVAL;
 569
 570        buf->format = drm_format_info(buf->buf.fourcc);
 571        for (i = 0; i < buf->format->num_planes; i++) {
 572                unsigned int width = (i == 0) ? buf->buf.width :
 573                             DIV_ROUND_UP(buf->buf.width, buf->format->hsub);
 574
 575                if (buf->buf.pitch[i] == 0)
 576                        buf->buf.pitch[i] = width * buf->format->cpp[i];
 577                if (buf->buf.pitch[i] < width * buf->format->cpp[i])
 578                        return -EINVAL;
 579                if (!buf->buf.gem_id[i])
 580                        return -ENOENT;
 581        }
 582
 583        /* pitch for additional planes must match */
 584        if (buf->format->num_planes > 2 &&
 585            buf->buf.pitch[1] != buf->buf.pitch[2])
 586                return -EINVAL;
 587
 588        /* check driver limits */
 589        ret = exynos_drm_ipp_check_size_limits(buf, fmt->limits,
 590                                               fmt->num_limits,
 591                                               rotate,
 592                                               buf == dst ? swap : false);
 593        if (ret)
 594                return ret;
 595        ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
 596                                                fmt->limits,
 597                                                fmt->num_limits, swap);
 598        return ret;
 599}
 600
 601static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
 602{
 603        struct exynos_drm_ipp *ipp = task->ipp;
 604        struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
 605        unsigned int rotation = task->transform.rotation;
 606        int ret = 0;
 607        bool swap = drm_rotation_90_or_270(rotation);
 608        bool rotate = (rotation != DRM_MODE_ROTATE_0);
 609        bool scale = false;
 610
 611        DRM_DEV_DEBUG_DRIVER(task->dev, "Checking task %pK\n", task);
 612
 613        if (src->rect.w == UINT_MAX)
 614                src->rect.w = src->buf.width;
 615        if (src->rect.h == UINT_MAX)
 616                src->rect.h = src->buf.height;
 617        if (dst->rect.w == UINT_MAX)
 618                dst->rect.w = dst->buf.width;
 619        if (dst->rect.h == UINT_MAX)
 620                dst->rect.h = dst->buf.height;
 621
 622        if (src->rect.x + src->rect.w > (src->buf.width) ||
 623            src->rect.y + src->rect.h > (src->buf.height) ||
 624            dst->rect.x + dst->rect.w > (dst->buf.width) ||
 625            dst->rect.y + dst->rect.h > (dst->buf.height)) {
 626                DRM_DEV_DEBUG_DRIVER(task->dev,
 627                                     "Task %pK: defined area is outside provided buffers\n",
 628                                     task);
 629                return -EINVAL;
 630        }
 631
 632        if ((!swap && (src->rect.w != dst->rect.w ||
 633                       src->rect.h != dst->rect.h)) ||
 634            (swap && (src->rect.w != dst->rect.h ||
 635                      src->rect.h != dst->rect.w)))
 636                scale = true;
 637
 638        if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) &&
 639             (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) ||
 640            (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) ||
 641            (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) ||
 642            (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) &&
 643             src->buf.fourcc != dst->buf.fourcc)) {
 644                DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: hw capabilities exceeded\n",
 645                                     task);
 646                return -EINVAL;
 647        }
 648
 649        ret = exynos_drm_ipp_check_format(task, src, src, dst, rotate, swap);
 650        if (ret)
 651                return ret;
 652
 653        ret = exynos_drm_ipp_check_format(task, dst, src, dst, false, swap);
 654        if (ret)
 655                return ret;
 656
 657        DRM_DEV_DEBUG_DRIVER(ipp->dev, "Task %pK: all checks done.\n",
 658                             task);
 659
 660        return ret;
 661}
 662
 663static int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task,
 664                                     struct drm_file *filp)
 665{
 666        struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
 667        int ret = 0;
 668
 669        DRM_DEV_DEBUG_DRIVER(task->dev, "Setting buffer for task %pK\n",
 670                             task);
 671
 672        ret = exynos_drm_ipp_task_setup_buffer(src, filp);
 673        if (ret) {
 674                DRM_DEV_DEBUG_DRIVER(task->dev,
 675                                     "Task %pK: src buffer setup failed\n",
 676                                     task);
 677                return ret;
 678        }
 679        ret = exynos_drm_ipp_task_setup_buffer(dst, filp);
 680        if (ret) {
 681                DRM_DEV_DEBUG_DRIVER(task->dev,
 682                                     "Task %pK: dst buffer setup failed\n",
 683                                     task);
 684                return ret;
 685        }
 686
 687        DRM_DEV_DEBUG_DRIVER(task->dev, "Task %pK: buffers prepared.\n",
 688                             task);
 689
 690        return ret;
 691}
 692
 693
 694static int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task,
 695                                 struct drm_file *file_priv, uint64_t user_data)
 696{
 697        struct drm_pending_exynos_ipp_event *e = NULL;
 698        int ret;
 699
 700        e = kzalloc(sizeof(*e), GFP_KERNEL);
 701        if (!e)
 702                return -ENOMEM;
 703
 704        e->event.base.type = DRM_EXYNOS_IPP_EVENT;
 705        e->event.base.length = sizeof(e->event);
 706        e->event.user_data = user_data;
 707
 708        ret = drm_event_reserve_init(task->ipp->drm_dev, file_priv, &e->base,
 709                                     &e->event.base);
 710        if (ret)
 711                goto free;
 712
 713        task->event = e;
 714        return 0;
 715free:
 716        kfree(e);
 717        return ret;
 718}
 719
 720static void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task)
 721{
 722        struct timespec64 now;
 723
 724        ktime_get_ts64(&now);
 725        task->event->event.tv_sec = now.tv_sec;
 726        task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC;
 727        task->event->event.sequence = atomic_inc_return(&task->ipp->sequence);
 728
 729        drm_send_event(task->ipp->drm_dev, &task->event->base);
 730}
 731
 732static int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task)
 733{
 734        int ret = task->ret;
 735
 736        if (ret == 0 && task->event) {
 737                exynos_drm_ipp_event_send(task);
 738                /* ensure event won't be canceled on task free */
 739                task->event = NULL;
 740        }
 741
 742        exynos_drm_ipp_task_free(task->ipp, task);
 743        return ret;
 744}
 745
 746static void exynos_drm_ipp_cleanup_work(struct work_struct *work)
 747{
 748        struct exynos_drm_ipp_task *task = container_of(work,
 749                                      struct exynos_drm_ipp_task, cleanup_work);
 750
 751        exynos_drm_ipp_task_cleanup(task);
 752}
 753
 754static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp);
 755
 756/**
 757 * exynos_drm_ipp_task_done - finish given task and set return code
 758 * @task: ipp task to finish
 759 * @ret: error code or 0 if operation has been performed successfully
 760 */
 761void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret)
 762{
 763        struct exynos_drm_ipp *ipp = task->ipp;
 764        unsigned long flags;
 765
 766        DRM_DEV_DEBUG_DRIVER(task->dev, "ipp: %d, task %pK done: %d\n",
 767                             ipp->id, task, ret);
 768
 769        spin_lock_irqsave(&ipp->lock, flags);
 770        if (ipp->task == task)
 771                ipp->task = NULL;
 772        task->flags |= DRM_EXYNOS_IPP_TASK_DONE;
 773        task->ret = ret;
 774        spin_unlock_irqrestore(&ipp->lock, flags);
 775
 776        exynos_drm_ipp_next_task(ipp);
 777        wake_up(&ipp->done_wq);
 778
 779        if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) {
 780                INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work);
 781                schedule_work(&task->cleanup_work);
 782        }
 783}
 784
 785static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp)
 786{
 787        struct exynos_drm_ipp_task *task;
 788        unsigned long flags;
 789        int ret;
 790
 791        DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, try to run new task\n",
 792                             ipp->id);
 793
 794        spin_lock_irqsave(&ipp->lock, flags);
 795
 796        if (ipp->task || list_empty(&ipp->todo_list)) {
 797                spin_unlock_irqrestore(&ipp->lock, flags);
 798                return;
 799        }
 800
 801        task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task,
 802                                head);
 803        list_del_init(&task->head);
 804        ipp->task = task;
 805
 806        spin_unlock_irqrestore(&ipp->lock, flags);
 807
 808        DRM_DEV_DEBUG_DRIVER(ipp->dev,
 809                             "ipp: %d, selected task %pK to run\n", ipp->id,
 810                             task);
 811
 812        ret = ipp->funcs->commit(ipp, task);
 813        if (ret)
 814                exynos_drm_ipp_task_done(task, ret);
 815}
 816
 817static void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp,
 818                                         struct exynos_drm_ipp_task *task)
 819{
 820        unsigned long flags;
 821
 822        spin_lock_irqsave(&ipp->lock, flags);
 823        list_add(&task->head, &ipp->todo_list);
 824        spin_unlock_irqrestore(&ipp->lock, flags);
 825
 826        exynos_drm_ipp_next_task(ipp);
 827}
 828
 829static void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp,
 830                                      struct exynos_drm_ipp_task *task)
 831{
 832        unsigned long flags;
 833
 834        spin_lock_irqsave(&ipp->lock, flags);
 835        if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) {
 836                /* already completed task */
 837                exynos_drm_ipp_task_cleanup(task);
 838        } else if (ipp->task != task) {
 839                /* task has not been scheduled for execution yet */
 840                list_del_init(&task->head);
 841                exynos_drm_ipp_task_cleanup(task);
 842        } else {
 843                /*
 844                 * currently processed task, call abort() and perform
 845                 * cleanup with async worker
 846                 */
 847                task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
 848                spin_unlock_irqrestore(&ipp->lock, flags);
 849                if (ipp->funcs->abort)
 850                        ipp->funcs->abort(ipp, task);
 851                return;
 852        }
 853        spin_unlock_irqrestore(&ipp->lock, flags);
 854}
 855
 856/**
 857 * exynos_drm_ipp_commit_ioctl - perform image processing operation
 858 * @dev: DRM device
 859 * @data: ioctl data
 860 * @file_priv: DRM file info
 861 *
 862 * Construct a ipp task from the set of properties provided from the user
 863 * and try to schedule it to framebuffer processor hardware.
 864 *
 865 * Called by the user via ioctl.
 866 *
 867 * Returns:
 868 * Zero on success, negative errno on failure.
 869 */
 870int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data,
 871                                struct drm_file *file_priv)
 872{
 873        struct drm_exynos_ioctl_ipp_commit *arg = data;
 874        struct exynos_drm_ipp *ipp;
 875        struct exynos_drm_ipp_task *task;
 876        int ret = 0;
 877
 878        if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved)
 879                return -EINVAL;
 880
 881        /* can't test and expect an event at the same time */
 882        if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) &&
 883                        (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT))
 884                return -EINVAL;
 885
 886        ipp = __ipp_get(arg->ipp_id);
 887        if (!ipp)
 888                return -ENOENT;
 889
 890        task = exynos_drm_ipp_task_alloc(ipp);
 891        if (!task)
 892                return -ENOMEM;
 893
 894        ret = exynos_drm_ipp_task_set(task, arg);
 895        if (ret)
 896                goto free;
 897
 898        ret = exynos_drm_ipp_task_check(task);
 899        if (ret)
 900                goto free;
 901
 902        ret = exynos_drm_ipp_task_setup_buffers(task, file_priv);
 903        if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY)
 904                goto free;
 905
 906        if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) {
 907                ret = exynos_drm_ipp_event_create(task, file_priv,
 908                                                 arg->user_data);
 909                if (ret)
 910                        goto free;
 911        }
 912
 913        /*
 914         * Queue task for processing on the hardware. task object will be
 915         * then freed after exynos_drm_ipp_task_done()
 916         */
 917        if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) {
 918                DRM_DEV_DEBUG_DRIVER(ipp->dev,
 919                                     "ipp: %d, nonblocking processing task %pK\n",
 920                                     ipp->id, task);
 921
 922                task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
 923                exynos_drm_ipp_schedule_task(task->ipp, task);
 924                ret = 0;
 925        } else {
 926                DRM_DEV_DEBUG_DRIVER(ipp->dev, "ipp: %d, processing task %pK\n",
 927                                     ipp->id, task);
 928                exynos_drm_ipp_schedule_task(ipp, task);
 929                ret = wait_event_interruptible(ipp->done_wq,
 930                                        task->flags & DRM_EXYNOS_IPP_TASK_DONE);
 931                if (ret)
 932                        exynos_drm_ipp_task_abort(ipp, task);
 933                else
 934                        ret = exynos_drm_ipp_task_cleanup(task);
 935        }
 936        return ret;
 937free:
 938        exynos_drm_ipp_task_free(ipp, task);
 939
 940        return ret;
 941}
 942