root/drivers/gpu/drm/exynos/exynos_drm_ipp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exynos_drm_ipp_register
  2. exynos_drm_ipp_unregister
  3. exynos_drm_ipp_get_res_ioctl
  4. __ipp_get
  5. exynos_drm_ipp_get_caps_ioctl
  6. __ipp_format_get
  7. exynos_drm_ipp_get_limits_ioctl
  8. exynos_drm_ipp_task_alloc
  9. exynos_drm_ipp_task_set
  10. exynos_drm_ipp_task_setup_buffer
  11. exynos_drm_ipp_task_release_buf
  12. exynos_drm_ipp_task_free
  13. __limit_set_val
  14. __get_size_limit
  15. __align_check
  16. __size_limit_check
  17. exynos_drm_ipp_check_size_limits
  18. __scale_limit_check
  19. exynos_drm_ipp_check_scale_limits
  20. exynos_drm_ipp_check_format
  21. exynos_drm_ipp_task_check
  22. exynos_drm_ipp_task_setup_buffers
  23. exynos_drm_ipp_event_create
  24. exynos_drm_ipp_event_send
  25. exynos_drm_ipp_task_cleanup
  26. exynos_drm_ipp_cleanup_work
  27. exynos_drm_ipp_task_done
  28. exynos_drm_ipp_next_task
  29. exynos_drm_ipp_schedule_task
  30. exynos_drm_ipp_task_abort
  31. exynos_drm_ipp_commit_ioctl

   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 
  30 static int num_ipp;
  31 static 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  */
  48 int 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  */
  82 void 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_ioctl_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  */
 103 int 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 
 128 static 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_ioctl_get_caps - 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  */
 151 int 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 
 188 static 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  */
 217 int 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 
 251 struct drm_pending_exynos_ipp_event {
 252         struct drm_pending_event base;
 253         struct drm_exynos_ipp_event event;
 254 };
 255 
 256 static 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 
 278 static 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 
 312 static 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 
 346 static 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;
 375 gem_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 
 383 static 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 
 393 static 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 
 405 struct drm_ipp_limit {
 406         struct drm_exynos_ipp_limit_val h;
 407         struct drm_exynos_ipp_limit_val v;
 408 };
 409 
 410 enum drm_ipp_size_id {
 411         IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
 412 };
 413 
 414 static 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 
 423 static inline void __limit_set_val(unsigned int *ptr, unsigned int val)
 424 {
 425         if (!*ptr)
 426                 *ptr = val;
 427 }
 428 
 429 static 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 
 453 static 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 
 463 static 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 
 474 static 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 
 505 static 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 
 519 static 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 
 547 static 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 
 601 static 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 
 663 static 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 
 694 static 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;
 715 free:
 716         kfree(e);
 717         return ret;
 718 }
 719 
 720 static 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 
 732 static 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 
 746 static 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 
 754 static 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  */
 761 void 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 
 785 static 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 
 817 static 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 
 829 static 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  */
 870 int 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;
 937 free:
 938         exynos_drm_ipp_task_free(ipp, task);
 939 
 940         return ret;
 941 }

/* [<][>][^][v][top][bottom][index][help] */