.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Samsung Electronics Co.Ltd |
---|
3 | 4 | * Authors: Joonyoung Shim <jy0922.shim@samsung.com> |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundationr |
---|
8 | 5 | */ |
---|
9 | 6 | |
---|
10 | | -#include <linux/kernel.h> |
---|
11 | 7 | #include <linux/clk.h> |
---|
12 | 8 | #include <linux/component.h> |
---|
| 9 | +#include <linux/delay.h> |
---|
| 10 | +#include <linux/dma-mapping.h> |
---|
13 | 11 | #include <linux/err.h> |
---|
14 | 12 | #include <linux/interrupt.h> |
---|
15 | 13 | #include <linux/io.h> |
---|
| 14 | +#include <linux/kernel.h> |
---|
| 15 | +#include <linux/of.h> |
---|
16 | 16 | #include <linux/platform_device.h> |
---|
17 | 17 | #include <linux/pm_runtime.h> |
---|
18 | 18 | #include <linux/slab.h> |
---|
| 19 | +#include <linux/uaccess.h> |
---|
19 | 20 | #include <linux/workqueue.h> |
---|
20 | | -#include <linux/dma-mapping.h> |
---|
21 | | -#include <linux/of.h> |
---|
22 | 21 | |
---|
23 | | -#include <drm/drmP.h> |
---|
| 22 | +#include <drm/drm_file.h> |
---|
24 | 23 | #include <drm/exynos_drm.h> |
---|
| 24 | + |
---|
25 | 25 | #include "exynos_drm_drv.h" |
---|
26 | 26 | #include "exynos_drm_g2d.h" |
---|
27 | 27 | #include "exynos_drm_gem.h" |
---|
28 | | -#include "exynos_drm_iommu.h" |
---|
29 | 28 | |
---|
30 | 29 | #define G2D_HW_MAJOR_VER 4 |
---|
31 | 30 | #define G2D_HW_MINOR_VER 1 |
---|
.. | .. |
---|
233 | 232 | |
---|
234 | 233 | struct g2d_data { |
---|
235 | 234 | struct device *dev; |
---|
| 235 | + void *dma_priv; |
---|
236 | 236 | struct clk *gate_clk; |
---|
237 | 237 | void __iomem *regs; |
---|
238 | 238 | int irq; |
---|
.. | .. |
---|
268 | 268 | static int g2d_init_cmdlist(struct g2d_data *g2d) |
---|
269 | 269 | { |
---|
270 | 270 | struct device *dev = g2d->dev; |
---|
271 | | - struct g2d_cmdlist_node *node = g2d->cmdlist_node; |
---|
| 271 | + struct g2d_cmdlist_node *node; |
---|
272 | 272 | int nr; |
---|
273 | 273 | int ret; |
---|
274 | 274 | struct g2d_buf_info *buf_info; |
---|
.. | .. |
---|
395 | 395 | return; |
---|
396 | 396 | |
---|
397 | 397 | out: |
---|
398 | | - dma_unmap_sg(to_dma_dev(g2d->drm_dev), g2d_userptr->sgt->sgl, |
---|
399 | | - g2d_userptr->sgt->nents, DMA_BIDIRECTIONAL); |
---|
| 398 | + dma_unmap_sgtable(to_dma_dev(g2d->drm_dev), g2d_userptr->sgt, |
---|
| 399 | + DMA_BIDIRECTIONAL, 0); |
---|
400 | 400 | |
---|
401 | 401 | pages = frame_vector_pages(g2d_userptr->vec); |
---|
402 | 402 | if (!IS_ERR(pages)) { |
---|
.. | .. |
---|
430 | 430 | int ret; |
---|
431 | 431 | |
---|
432 | 432 | if (!size) { |
---|
433 | | - DRM_ERROR("invalid userptr size.\n"); |
---|
| 433 | + DRM_DEV_ERROR(g2d->dev, "invalid userptr size.\n"); |
---|
434 | 434 | return ERR_PTR(-EINVAL); |
---|
435 | 435 | } |
---|
436 | 436 | |
---|
.. | .. |
---|
483 | 483 | ret = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE, |
---|
484 | 484 | g2d_userptr->vec); |
---|
485 | 485 | if (ret != npages) { |
---|
486 | | - DRM_ERROR("failed to get user pages from userptr.\n"); |
---|
| 486 | + DRM_DEV_ERROR(g2d->dev, |
---|
| 487 | + "failed to get user pages from userptr.\n"); |
---|
487 | 488 | if (ret < 0) |
---|
488 | 489 | goto err_destroy_framevec; |
---|
489 | 490 | ret = -EFAULT; |
---|
.. | .. |
---|
504 | 505 | frame_vector_pages(g2d_userptr->vec), |
---|
505 | 506 | npages, offset, size, GFP_KERNEL); |
---|
506 | 507 | if (ret < 0) { |
---|
507 | | - DRM_ERROR("failed to get sgt from pages.\n"); |
---|
| 508 | + DRM_DEV_ERROR(g2d->dev, "failed to get sgt from pages.\n"); |
---|
508 | 509 | goto err_free_sgt; |
---|
509 | 510 | } |
---|
510 | 511 | |
---|
511 | 512 | g2d_userptr->sgt = sgt; |
---|
512 | 513 | |
---|
513 | | - if (!dma_map_sg(to_dma_dev(g2d->drm_dev), sgt->sgl, sgt->nents, |
---|
514 | | - DMA_BIDIRECTIONAL)) { |
---|
515 | | - DRM_ERROR("failed to map sgt with dma region.\n"); |
---|
516 | | - ret = -ENOMEM; |
---|
| 514 | + ret = dma_map_sgtable(to_dma_dev(g2d->drm_dev), sgt, |
---|
| 515 | + DMA_BIDIRECTIONAL, 0); |
---|
| 516 | + if (ret) { |
---|
| 517 | + DRM_DEV_ERROR(g2d->dev, "failed to map sgt with dma region.\n"); |
---|
517 | 518 | goto err_sg_free_table; |
---|
518 | 519 | } |
---|
519 | 520 | |
---|
.. | .. |
---|
561 | 562 | g2d->current_pool = 0; |
---|
562 | 563 | } |
---|
563 | 564 | |
---|
564 | | -static enum g2d_reg_type g2d_get_reg_type(int reg_offset) |
---|
| 565 | +static enum g2d_reg_type g2d_get_reg_type(struct g2d_data *g2d, int reg_offset) |
---|
565 | 566 | { |
---|
566 | 567 | enum g2d_reg_type reg_type; |
---|
567 | 568 | |
---|
.. | .. |
---|
594 | 595 | break; |
---|
595 | 596 | default: |
---|
596 | 597 | reg_type = REG_TYPE_NONE; |
---|
597 | | - DRM_ERROR("Unknown register offset![%d]\n", reg_offset); |
---|
| 598 | + DRM_DEV_ERROR(g2d->dev, "Unknown register offset![%d]\n", |
---|
| 599 | + reg_offset); |
---|
598 | 600 | break; |
---|
599 | 601 | } |
---|
600 | 602 | |
---|
.. | .. |
---|
628 | 630 | return bpp; |
---|
629 | 631 | } |
---|
630 | 632 | |
---|
631 | | -static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, |
---|
632 | | - enum g2d_reg_type reg_type, |
---|
633 | | - unsigned long size) |
---|
| 633 | +static bool g2d_check_buf_desc_is_valid(struct g2d_data *g2d, |
---|
| 634 | + struct g2d_buf_desc *buf_desc, |
---|
| 635 | + enum g2d_reg_type reg_type, |
---|
| 636 | + unsigned long size) |
---|
634 | 637 | { |
---|
635 | 638 | int width, height; |
---|
636 | 639 | unsigned long bpp, last_pos; |
---|
.. | .. |
---|
645 | 648 | /* This check also makes sure that right_x > left_x. */ |
---|
646 | 649 | width = (int)buf_desc->right_x - (int)buf_desc->left_x; |
---|
647 | 650 | if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { |
---|
648 | | - DRM_ERROR("width[%d] is out of range!\n", width); |
---|
| 651 | + DRM_DEV_ERROR(g2d->dev, "width[%d] is out of range!\n", width); |
---|
649 | 652 | return false; |
---|
650 | 653 | } |
---|
651 | 654 | |
---|
652 | 655 | /* This check also makes sure that bottom_y > top_y. */ |
---|
653 | 656 | height = (int)buf_desc->bottom_y - (int)buf_desc->top_y; |
---|
654 | 657 | if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { |
---|
655 | | - DRM_ERROR("height[%d] is out of range!\n", height); |
---|
| 658 | + DRM_DEV_ERROR(g2d->dev, |
---|
| 659 | + "height[%d] is out of range!\n", height); |
---|
656 | 660 | return false; |
---|
657 | 661 | } |
---|
658 | 662 | |
---|
.. | .. |
---|
671 | 675 | */ |
---|
672 | 676 | |
---|
673 | 677 | if (last_pos >= size) { |
---|
674 | | - DRM_ERROR("last engine access position [%lu] " |
---|
675 | | - "is out of range [%lu]!\n", last_pos, size); |
---|
| 678 | + DRM_DEV_ERROR(g2d->dev, "last engine access position [%lu] " |
---|
| 679 | + "is out of range [%lu]!\n", last_pos, size); |
---|
676 | 680 | return false; |
---|
677 | 681 | } |
---|
678 | 682 | |
---|
.. | .. |
---|
702 | 706 | offset = cmdlist->data[reg_pos]; |
---|
703 | 707 | handle = cmdlist->data[reg_pos + 1]; |
---|
704 | 708 | |
---|
705 | | - reg_type = g2d_get_reg_type(offset); |
---|
| 709 | + reg_type = g2d_get_reg_type(g2d, offset); |
---|
706 | 710 | if (reg_type == REG_TYPE_NONE) { |
---|
707 | 711 | ret = -EFAULT; |
---|
708 | 712 | goto err; |
---|
.. | .. |
---|
719 | 723 | goto err; |
---|
720 | 724 | } |
---|
721 | 725 | |
---|
722 | | - if (!g2d_check_buf_desc_is_valid(buf_desc, |
---|
| 726 | + if (!g2d_check_buf_desc_is_valid(g2d, buf_desc, |
---|
723 | 727 | reg_type, exynos_gem->size)) { |
---|
724 | 728 | exynos_drm_gem_put(exynos_gem); |
---|
725 | 729 | ret = -EFAULT; |
---|
.. | .. |
---|
737 | 741 | goto err; |
---|
738 | 742 | } |
---|
739 | 743 | |
---|
740 | | - if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, |
---|
741 | | - g2d_userptr.size)) { |
---|
| 744 | + if (!g2d_check_buf_desc_is_valid(g2d, buf_desc, |
---|
| 745 | + reg_type, |
---|
| 746 | + g2d_userptr.size)) { |
---|
742 | 747 | ret = -EFAULT; |
---|
743 | 748 | goto err; |
---|
744 | 749 | } |
---|
.. | .. |
---|
846 | 851 | * |
---|
847 | 852 | * Has to be called under runqueue lock. |
---|
848 | 853 | */ |
---|
849 | | -static void g2d_remove_runqueue_nodes(struct g2d_data *g2d, struct drm_file* file) |
---|
| 854 | +static void g2d_remove_runqueue_nodes(struct g2d_data *g2d, struct drm_file *file) |
---|
850 | 855 | { |
---|
851 | 856 | struct g2d_runqueue_node *node, *n; |
---|
852 | 857 | |
---|
.. | .. |
---|
1045 | 1050 | if (!for_addr) |
---|
1046 | 1051 | goto err; |
---|
1047 | 1052 | |
---|
1048 | | - reg_type = g2d_get_reg_type(reg_offset); |
---|
| 1053 | + reg_type = g2d_get_reg_type(g2d, reg_offset); |
---|
1049 | 1054 | |
---|
1050 | 1055 | /* check userptr buffer type. */ |
---|
1051 | 1056 | if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { |
---|
.. | .. |
---|
1059 | 1064 | if (for_addr) |
---|
1060 | 1065 | goto err; |
---|
1061 | 1066 | |
---|
1062 | | - reg_type = g2d_get_reg_type(reg_offset); |
---|
| 1067 | + reg_type = g2d_get_reg_type(g2d, reg_offset); |
---|
1063 | 1068 | |
---|
1064 | 1069 | buf_desc = &buf_info->descs[reg_type]; |
---|
1065 | 1070 | buf_desc->stride = cmdlist->data[index + 1]; |
---|
.. | .. |
---|
1069 | 1074 | if (for_addr) |
---|
1070 | 1075 | goto err; |
---|
1071 | 1076 | |
---|
1072 | | - reg_type = g2d_get_reg_type(reg_offset); |
---|
| 1077 | + reg_type = g2d_get_reg_type(g2d, reg_offset); |
---|
1073 | 1078 | |
---|
1074 | 1079 | buf_desc = &buf_info->descs[reg_type]; |
---|
1075 | 1080 | value = cmdlist->data[index + 1]; |
---|
.. | .. |
---|
1081 | 1086 | if (for_addr) |
---|
1082 | 1087 | goto err; |
---|
1083 | 1088 | |
---|
1084 | | - reg_type = g2d_get_reg_type(reg_offset); |
---|
| 1089 | + reg_type = g2d_get_reg_type(g2d, reg_offset); |
---|
1085 | 1090 | |
---|
1086 | 1091 | buf_desc = &buf_info->descs[reg_type]; |
---|
1087 | 1092 | value = cmdlist->data[index + 1]; |
---|
.. | .. |
---|
1094 | 1099 | if (for_addr) |
---|
1095 | 1100 | goto err; |
---|
1096 | 1101 | |
---|
1097 | | - reg_type = g2d_get_reg_type(reg_offset); |
---|
| 1102 | + reg_type = g2d_get_reg_type(g2d, reg_offset); |
---|
1098 | 1103 | |
---|
1099 | 1104 | buf_desc = &buf_info->descs[reg_type]; |
---|
1100 | 1105 | value = cmdlist->data[index + 1]; |
---|
.. | .. |
---|
1405 | 1410 | return ret; |
---|
1406 | 1411 | } |
---|
1407 | 1412 | |
---|
1408 | | - ret = drm_iommu_attach_device(drm_dev, dev); |
---|
| 1413 | + ret = exynos_drm_register_dma(drm_dev, dev, &g2d->dma_priv); |
---|
1409 | 1414 | if (ret < 0) { |
---|
1410 | 1415 | dev_err(dev, "failed to enable iommu.\n"); |
---|
1411 | 1416 | g2d_fini_cmdlist(g2d); |
---|
.. | .. |
---|
1430 | 1435 | priv->g2d_dev = NULL; |
---|
1431 | 1436 | |
---|
1432 | 1437 | cancel_work_sync(&g2d->runqueue_work); |
---|
1433 | | - drm_iommu_detach_device(g2d->drm_dev, dev); |
---|
| 1438 | + exynos_drm_unregister_dma(g2d->drm_dev, dev, &g2d->dma_priv); |
---|
1434 | 1439 | } |
---|
1435 | 1440 | |
---|
1436 | 1441 | static const struct component_ops g2d_component_ops = { |
---|
.. | .. |
---|
1493 | 1498 | |
---|
1494 | 1499 | g2d->irq = platform_get_irq(pdev, 0); |
---|
1495 | 1500 | if (g2d->irq < 0) { |
---|
1496 | | - dev_err(dev, "failed to get irq\n"); |
---|
1497 | 1501 | ret = g2d->irq; |
---|
1498 | 1502 | goto err_put_clk; |
---|
1499 | 1503 | } |
---|