| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2017 Samsung Electronics Co.Ltd |
|---|
| 3 | 4 | * Author: |
|---|
| 4 | | - * Andrzej Pietrasiewicz <andrzej.p@samsung.com> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 8 | | - * published by the Free Software Foundationr |
|---|
| 5 | + * Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> |
|---|
| 9 | 6 | */ |
|---|
| 10 | 7 | |
|---|
| 11 | | -#include <linux/kernel.h> |
|---|
| 8 | +#include <linux/clk.h> |
|---|
| 12 | 9 | #include <linux/component.h> |
|---|
| 13 | 10 | #include <linux/err.h> |
|---|
| 14 | 11 | #include <linux/interrupt.h> |
|---|
| 15 | 12 | #include <linux/io.h> |
|---|
| 16 | | -#include <linux/platform_device.h> |
|---|
| 17 | | -#include <linux/clk.h> |
|---|
| 13 | +#include <linux/kernel.h> |
|---|
| 18 | 14 | #include <linux/of_device.h> |
|---|
| 15 | +#include <linux/platform_device.h> |
|---|
| 19 | 16 | #include <linux/pm_runtime.h> |
|---|
| 20 | 17 | |
|---|
| 21 | | -#include <drm/drmP.h> |
|---|
| 18 | +#include <drm/drm_fourcc.h> |
|---|
| 22 | 19 | #include <drm/exynos_drm.h> |
|---|
| 23 | | -#include "regs-scaler.h" |
|---|
| 24 | | -#include "exynos_drm_fb.h" |
|---|
| 20 | + |
|---|
| 25 | 21 | #include "exynos_drm_drv.h" |
|---|
| 26 | | -#include "exynos_drm_iommu.h" |
|---|
| 22 | +#include "exynos_drm_fb.h" |
|---|
| 27 | 23 | #include "exynos_drm_ipp.h" |
|---|
| 24 | +#include "regs-scaler.h" |
|---|
| 28 | 25 | |
|---|
| 29 | 26 | #define scaler_read(offset) readl(scaler->regs + (offset)) |
|---|
| 30 | 27 | #define scaler_write(cfg, offset) writel(cfg, scaler->regs + (offset)) |
|---|
| .. | .. |
|---|
| 42 | 39 | struct scaler_context { |
|---|
| 43 | 40 | struct exynos_drm_ipp ipp; |
|---|
| 44 | 41 | struct drm_device *drm_dev; |
|---|
| 42 | + void *dma_priv; |
|---|
| 45 | 43 | struct device *dev; |
|---|
| 46 | 44 | void __iomem *regs; |
|---|
| 47 | 45 | struct clk *clock[SCALER_MAX_CLK]; |
|---|
| .. | .. |
|---|
| 49 | 47 | const struct scaler_data *scaler_data; |
|---|
| 50 | 48 | }; |
|---|
| 51 | 49 | |
|---|
| 52 | | -static u32 scaler_get_format(u32 drm_fmt) |
|---|
| 53 | | -{ |
|---|
| 54 | | - switch (drm_fmt) { |
|---|
| 55 | | - case DRM_FORMAT_NV12: |
|---|
| 56 | | - return SCALER_YUV420_2P_UV; |
|---|
| 57 | | - case DRM_FORMAT_NV21: |
|---|
| 58 | | - return SCALER_YUV420_2P_VU; |
|---|
| 59 | | - case DRM_FORMAT_YUV420: |
|---|
| 60 | | - return SCALER_YUV420_3P; |
|---|
| 61 | | - case DRM_FORMAT_YUYV: |
|---|
| 62 | | - return SCALER_YUV422_1P_YUYV; |
|---|
| 63 | | - case DRM_FORMAT_UYVY: |
|---|
| 64 | | - return SCALER_YUV422_1P_UYVY; |
|---|
| 65 | | - case DRM_FORMAT_YVYU: |
|---|
| 66 | | - return SCALER_YUV422_1P_YVYU; |
|---|
| 67 | | - case DRM_FORMAT_NV16: |
|---|
| 68 | | - return SCALER_YUV422_2P_UV; |
|---|
| 69 | | - case DRM_FORMAT_NV61: |
|---|
| 70 | | - return SCALER_YUV422_2P_VU; |
|---|
| 71 | | - case DRM_FORMAT_YUV422: |
|---|
| 72 | | - return SCALER_YUV422_3P; |
|---|
| 73 | | - case DRM_FORMAT_NV24: |
|---|
| 74 | | - return SCALER_YUV444_2P_UV; |
|---|
| 75 | | - case DRM_FORMAT_NV42: |
|---|
| 76 | | - return SCALER_YUV444_2P_VU; |
|---|
| 77 | | - case DRM_FORMAT_YUV444: |
|---|
| 78 | | - return SCALER_YUV444_3P; |
|---|
| 79 | | - case DRM_FORMAT_RGB565: |
|---|
| 80 | | - return SCALER_RGB_565; |
|---|
| 81 | | - case DRM_FORMAT_XRGB1555: |
|---|
| 82 | | - return SCALER_ARGB1555; |
|---|
| 83 | | - case DRM_FORMAT_ARGB1555: |
|---|
| 84 | | - return SCALER_ARGB1555; |
|---|
| 85 | | - case DRM_FORMAT_XRGB4444: |
|---|
| 86 | | - return SCALER_ARGB4444; |
|---|
| 87 | | - case DRM_FORMAT_ARGB4444: |
|---|
| 88 | | - return SCALER_ARGB4444; |
|---|
| 89 | | - case DRM_FORMAT_XRGB8888: |
|---|
| 90 | | - return SCALER_ARGB8888; |
|---|
| 91 | | - case DRM_FORMAT_ARGB8888: |
|---|
| 92 | | - return SCALER_ARGB8888; |
|---|
| 93 | | - case DRM_FORMAT_RGBX8888: |
|---|
| 94 | | - return SCALER_RGBA8888; |
|---|
| 95 | | - case DRM_FORMAT_RGBA8888: |
|---|
| 96 | | - return SCALER_RGBA8888; |
|---|
| 97 | | - default: |
|---|
| 98 | | - break; |
|---|
| 99 | | - } |
|---|
| 50 | +struct scaler_format { |
|---|
| 51 | + u32 drm_fmt; |
|---|
| 52 | + u32 internal_fmt; |
|---|
| 53 | + u32 chroma_tile_w; |
|---|
| 54 | + u32 chroma_tile_h; |
|---|
| 55 | +}; |
|---|
| 100 | 56 | |
|---|
| 101 | | - return 0; |
|---|
| 57 | +static const struct scaler_format scaler_formats[] = { |
|---|
| 58 | + { DRM_FORMAT_NV12, SCALER_YUV420_2P_UV, 8, 8 }, |
|---|
| 59 | + { DRM_FORMAT_NV21, SCALER_YUV420_2P_VU, 8, 8 }, |
|---|
| 60 | + { DRM_FORMAT_YUV420, SCALER_YUV420_3P, 8, 8 }, |
|---|
| 61 | + { DRM_FORMAT_YUYV, SCALER_YUV422_1P_YUYV, 16, 16 }, |
|---|
| 62 | + { DRM_FORMAT_UYVY, SCALER_YUV422_1P_UYVY, 16, 16 }, |
|---|
| 63 | + { DRM_FORMAT_YVYU, SCALER_YUV422_1P_YVYU, 16, 16 }, |
|---|
| 64 | + { DRM_FORMAT_NV16, SCALER_YUV422_2P_UV, 8, 16 }, |
|---|
| 65 | + { DRM_FORMAT_NV61, SCALER_YUV422_2P_VU, 8, 16 }, |
|---|
| 66 | + { DRM_FORMAT_YUV422, SCALER_YUV422_3P, 8, 16 }, |
|---|
| 67 | + { DRM_FORMAT_NV24, SCALER_YUV444_2P_UV, 16, 16 }, |
|---|
| 68 | + { DRM_FORMAT_NV42, SCALER_YUV444_2P_VU, 16, 16 }, |
|---|
| 69 | + { DRM_FORMAT_YUV444, SCALER_YUV444_3P, 16, 16 }, |
|---|
| 70 | + { DRM_FORMAT_RGB565, SCALER_RGB_565, 0, 0 }, |
|---|
| 71 | + { DRM_FORMAT_XRGB1555, SCALER_ARGB1555, 0, 0 }, |
|---|
| 72 | + { DRM_FORMAT_ARGB1555, SCALER_ARGB1555, 0, 0 }, |
|---|
| 73 | + { DRM_FORMAT_XRGB4444, SCALER_ARGB4444, 0, 0 }, |
|---|
| 74 | + { DRM_FORMAT_ARGB4444, SCALER_ARGB4444, 0, 0 }, |
|---|
| 75 | + { DRM_FORMAT_XRGB8888, SCALER_ARGB8888, 0, 0 }, |
|---|
| 76 | + { DRM_FORMAT_ARGB8888, SCALER_ARGB8888, 0, 0 }, |
|---|
| 77 | + { DRM_FORMAT_RGBX8888, SCALER_RGBA8888, 0, 0 }, |
|---|
| 78 | + { DRM_FORMAT_RGBA8888, SCALER_RGBA8888, 0, 0 }, |
|---|
| 79 | +}; |
|---|
| 80 | + |
|---|
| 81 | +static const struct scaler_format *scaler_get_format(u32 drm_fmt) |
|---|
| 82 | +{ |
|---|
| 83 | + int i; |
|---|
| 84 | + |
|---|
| 85 | + for (i = 0; i < ARRAY_SIZE(scaler_formats); i++) |
|---|
| 86 | + if (scaler_formats[i].drm_fmt == drm_fmt) |
|---|
| 87 | + return &scaler_formats[i]; |
|---|
| 88 | + |
|---|
| 89 | + return NULL; |
|---|
| 102 | 90 | } |
|---|
| 103 | 91 | |
|---|
| 104 | 92 | static inline int scaler_reset(struct scaler_context *scaler) |
|---|
| .. | .. |
|---|
| 152 | 140 | } |
|---|
| 153 | 141 | |
|---|
| 154 | 142 | static inline void scaler_set_src_fmt(struct scaler_context *scaler, |
|---|
| 155 | | - u32 src_fmt) |
|---|
| 143 | + u32 src_fmt, u32 tile) |
|---|
| 156 | 144 | { |
|---|
| 157 | 145 | u32 val; |
|---|
| 158 | 146 | |
|---|
| 159 | | - val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt); |
|---|
| 147 | + val = SCALER_SRC_CFG_SET_COLOR_FORMAT(src_fmt) | (tile << 10); |
|---|
| 160 | 148 | scaler_write(val, SCALER_SRC_CFG); |
|---|
| 161 | 149 | } |
|---|
| 162 | 150 | |
|---|
| .. | .. |
|---|
| 188 | 176 | scaler_write(val, SCALER_SRC_SPAN); |
|---|
| 189 | 177 | } |
|---|
| 190 | 178 | |
|---|
| 191 | | -static inline void scaler_set_src_luma_pos(struct scaler_context *scaler, |
|---|
| 192 | | - struct drm_exynos_ipp_task_rect *src_pos) |
|---|
| 179 | +static inline void scaler_set_src_luma_chroma_pos(struct scaler_context *scaler, |
|---|
| 180 | + struct drm_exynos_ipp_task_rect *src_pos, |
|---|
| 181 | + const struct scaler_format *fmt) |
|---|
| 193 | 182 | { |
|---|
| 194 | 183 | u32 val; |
|---|
| 195 | 184 | |
|---|
| 196 | 185 | val = SCALER_SRC_Y_POS_SET_YH_POS(src_pos->x << 2); |
|---|
| 197 | 186 | val |= SCALER_SRC_Y_POS_SET_YV_POS(src_pos->y << 2); |
|---|
| 198 | 187 | scaler_write(val, SCALER_SRC_Y_POS); |
|---|
| 199 | | - scaler_write(val, SCALER_SRC_C_POS); /* ATTENTION! */ |
|---|
| 188 | + val = SCALER_SRC_C_POS_SET_CH_POS( |
|---|
| 189 | + (src_pos->x * fmt->chroma_tile_w / 16) << 2); |
|---|
| 190 | + val |= SCALER_SRC_C_POS_SET_CV_POS( |
|---|
| 191 | + (src_pos->y * fmt->chroma_tile_h / 16) << 2); |
|---|
| 192 | + scaler_write(val, SCALER_SRC_C_POS); |
|---|
| 200 | 193 | } |
|---|
| 201 | 194 | |
|---|
| 202 | 195 | static inline void scaler_set_src_wh(struct scaler_context *scaler, |
|---|
| .. | .. |
|---|
| 366 | 359 | struct scaler_context *scaler = |
|---|
| 367 | 360 | container_of(ipp, struct scaler_context, ipp); |
|---|
| 368 | 361 | |
|---|
| 369 | | - u32 src_fmt = scaler_get_format(task->src.buf.fourcc); |
|---|
| 370 | 362 | struct drm_exynos_ipp_task_rect *src_pos = &task->src.rect; |
|---|
| 371 | | - |
|---|
| 372 | | - u32 dst_fmt = scaler_get_format(task->dst.buf.fourcc); |
|---|
| 373 | 363 | struct drm_exynos_ipp_task_rect *dst_pos = &task->dst.rect; |
|---|
| 364 | + const struct scaler_format *src_fmt, *dst_fmt; |
|---|
| 365 | + |
|---|
| 366 | + src_fmt = scaler_get_format(task->src.buf.fourcc); |
|---|
| 367 | + dst_fmt = scaler_get_format(task->dst.buf.fourcc); |
|---|
| 374 | 368 | |
|---|
| 375 | 369 | pm_runtime_get_sync(scaler->dev); |
|---|
| 376 | 370 | if (scaler_reset(scaler)) { |
|---|
| .. | .. |
|---|
| 380 | 374 | |
|---|
| 381 | 375 | scaler->task = task; |
|---|
| 382 | 376 | |
|---|
| 383 | | - scaler_set_src_fmt(scaler, src_fmt); |
|---|
| 377 | + scaler_set_src_fmt( |
|---|
| 378 | + scaler, src_fmt->internal_fmt, task->src.buf.modifier != 0); |
|---|
| 384 | 379 | scaler_set_src_base(scaler, &task->src); |
|---|
| 385 | 380 | scaler_set_src_span(scaler, &task->src); |
|---|
| 386 | | - scaler_set_src_luma_pos(scaler, src_pos); |
|---|
| 381 | + scaler_set_src_luma_chroma_pos(scaler, src_pos, src_fmt); |
|---|
| 387 | 382 | scaler_set_src_wh(scaler, src_pos); |
|---|
| 388 | 383 | |
|---|
| 389 | | - scaler_set_dst_fmt(scaler, dst_fmt); |
|---|
| 384 | + scaler_set_dst_fmt(scaler, dst_fmt->internal_fmt); |
|---|
| 390 | 385 | scaler_set_dst_base(scaler, &task->dst); |
|---|
| 391 | 386 | scaler_set_dst_span(scaler, &task->dst); |
|---|
| 392 | 387 | scaler_set_dst_luma_pos(scaler, dst_pos); |
|---|
| .. | .. |
|---|
| 455 | 450 | struct exynos_drm_ipp *ipp = &scaler->ipp; |
|---|
| 456 | 451 | |
|---|
| 457 | 452 | scaler->drm_dev = drm_dev; |
|---|
| 458 | | - drm_iommu_attach_device(drm_dev, dev); |
|---|
| 453 | + ipp->drm_dev = drm_dev; |
|---|
| 454 | + exynos_drm_register_dma(drm_dev, dev, &scaler->dma_priv); |
|---|
| 459 | 455 | |
|---|
| 460 | | - exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, |
|---|
| 456 | + exynos_drm_ipp_register(dev, ipp, &ipp_funcs, |
|---|
| 461 | 457 | DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE | |
|---|
| 462 | 458 | DRM_EXYNOS_IPP_CAP_SCALE | DRM_EXYNOS_IPP_CAP_CONVERT, |
|---|
| 463 | 459 | scaler->scaler_data->formats, |
|---|
| .. | .. |
|---|
| 472 | 468 | void *data) |
|---|
| 473 | 469 | { |
|---|
| 474 | 470 | struct scaler_context *scaler = dev_get_drvdata(dev); |
|---|
| 475 | | - struct drm_device *drm_dev = data; |
|---|
| 476 | 471 | struct exynos_drm_ipp *ipp = &scaler->ipp; |
|---|
| 477 | 472 | |
|---|
| 478 | | - exynos_drm_ipp_unregister(drm_dev, ipp); |
|---|
| 479 | | - drm_iommu_detach_device(scaler->drm_dev, scaler->dev); |
|---|
| 473 | + exynos_drm_ipp_unregister(dev, ipp); |
|---|
| 474 | + exynos_drm_unregister_dma(scaler->drm_dev, scaler->dev, |
|---|
| 475 | + &scaler->dma_priv); |
|---|
| 480 | 476 | } |
|---|
| 481 | 477 | |
|---|
| 482 | 478 | static const struct component_ops scaler_component_ops = { |
|---|
| .. | .. |
|---|
| 506 | 502 | return PTR_ERR(scaler->regs); |
|---|
| 507 | 503 | |
|---|
| 508 | 504 | irq = platform_get_irq(pdev, 0); |
|---|
| 509 | | - if (irq < 0) { |
|---|
| 510 | | - dev_err(dev, "failed to get irq\n"); |
|---|
| 505 | + if (irq < 0) |
|---|
| 511 | 506 | return irq; |
|---|
| 512 | | - } |
|---|
| 513 | 507 | |
|---|
| 514 | 508 | ret = devm_request_threaded_irq(dev, irq, NULL, scaler_irq_handler, |
|---|
| 515 | 509 | IRQF_ONESHOT, "drm_scaler", scaler); |
|---|
| .. | .. |
|---|
| 617 | 611 | .v = { 65536 * 1 / 4, 65536 * 16 }) }, |
|---|
| 618 | 612 | }; |
|---|
| 619 | 613 | |
|---|
| 614 | +static const struct drm_exynos_ipp_limit scaler_5420_tile_limits[] = { |
|---|
| 615 | + { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K })}, |
|---|
| 616 | + { IPP_SIZE_LIMIT(AREA, .h.align = 16, .v.align = 16) }, |
|---|
| 617 | + { IPP_SCALE_LIMIT(.h = {1, 1}, .v = {1, 1})}, |
|---|
| 618 | + { } |
|---|
| 619 | +}; |
|---|
| 620 | + |
|---|
| 621 | +#define IPP_SRCDST_TILE_FORMAT(f, l) \ |
|---|
| 622 | + IPP_SRCDST_MFORMAT(f, DRM_FORMAT_MOD_SAMSUNG_16_16_TILE, (l)) |
|---|
| 623 | + |
|---|
| 620 | 624 | static const struct exynos_drm_ipp_formats exynos5420_formats[] = { |
|---|
| 621 | 625 | /* SCALER_YUV420_2P_UV */ |
|---|
| 622 | 626 | { IPP_SRCDST_FORMAT(NV21, scaler_5420_two_pixel_hv_limits) }, |
|---|
| .. | .. |
|---|
| 680 | 684 | |
|---|
| 681 | 685 | /* SCALER_RGBA8888 */ |
|---|
| 682 | 686 | { IPP_SRCDST_FORMAT(RGBA8888, scaler_5420_one_pixel_limits) }, |
|---|
| 687 | + |
|---|
| 688 | + /* SCALER_YUV420_2P_UV TILE */ |
|---|
| 689 | + { IPP_SRCDST_TILE_FORMAT(NV21, scaler_5420_tile_limits) }, |
|---|
| 690 | + |
|---|
| 691 | + /* SCALER_YUV420_2P_VU TILE */ |
|---|
| 692 | + { IPP_SRCDST_TILE_FORMAT(NV12, scaler_5420_tile_limits) }, |
|---|
| 693 | + |
|---|
| 694 | + /* SCALER_YUV420_3P TILE */ |
|---|
| 695 | + { IPP_SRCDST_TILE_FORMAT(YUV420, scaler_5420_tile_limits) }, |
|---|
| 696 | + |
|---|
| 697 | + /* SCALER_YUV422_1P_YUYV TILE */ |
|---|
| 698 | + { IPP_SRCDST_TILE_FORMAT(YUYV, scaler_5420_tile_limits) }, |
|---|
| 683 | 699 | }; |
|---|
| 684 | 700 | |
|---|
| 685 | 701 | static const struct scaler_data exynos5420_data = { |
|---|