From bc7747d3c47de160559735a281b66ea2a9a6626e Mon Sep 17 00:00:00 2001 From: "jonggab.park" Date: Fri, 28 Apr 2017 17:13:56 +0900 Subject: [PATCH] drm/tdm: implement msc driver to support PP. MSC driver is included. This patch is from product kernel. Change-Id: Ibe08ff732fb4e5cc9718b59254cd52f8716bad5b Signed-off-by: Jin-young Jeon Signed-off-by: Inki Dae --- drivers/gpu/drm/tgm/Kconfig | 6 + drivers/gpu/drm/tgm/Makefile | 1 + drivers/gpu/drm/tgm/tdm_pp_msc.c | 1757 +++++++++++++++++++++++++ drivers/gpu/drm/tgm/tdm_pp_msc.h | 34 + drivers/gpu/drm/tgm/tdm_pp_msc_regs.h | 162 +++ drivers/gpu/drm/tgm/tgm_drv.c | 12 + drivers/gpu/drm/tgm/tgm_drv.h | 1 + 7 files changed, 1973 insertions(+) create mode 100644 drivers/gpu/drm/tgm/tdm_pp_msc.c create mode 100644 drivers/gpu/drm/tgm/tdm_pp_msc.h create mode 100644 drivers/gpu/drm/tgm/tdm_pp_msc_regs.h diff --git a/drivers/gpu/drm/tgm/Kconfig b/drivers/gpu/drm/tgm/Kconfig index 9f62c1778cc..59ffde5b1bc 100644 --- a/drivers/gpu/drm/tgm/Kconfig +++ b/drivers/gpu/drm/tgm/Kconfig @@ -36,6 +36,12 @@ config DRM_TDM_PP help Supports post-processing feature. +config DRM_TDM_PP_MSC + bool "TDM PP MSC" + depends on DRM_TDM_PP + help + Choose this option if you want to use MSC for PP. + config DRM_TBM bool "TBM allocates graphics buffer" depends on DRM_TGM diff --git a/drivers/gpu/drm/tgm/Makefile b/drivers/gpu/drm/tgm/Makefile index 8cb08a918ce..b0affe11763 100644 --- a/drivers/gpu/drm/tgm/Makefile +++ b/drivers/gpu/drm/tgm/Makefile @@ -9,6 +9,7 @@ tgm-$(CONFIG_DRM_TDM) += tdm.o tgm-$(CONFIG_DRM_TDM_IRQ) += tdm_irq.o tgm-$(CONFIG_DRM_TDM_IRQ_EXYNOS) += tdm_irq_exynos.o tgm-$(CONFIG_DRM_TDM_PP) += tdm_pp.o +tgm-$(CONFIG_DRM_TDM_PP_MSC) += tdm_pp_msc.o tgm-$(CONFIG_DRM_TBM) += tbm.o tgm-$(CONFIG_DRM_TBM_GEM) += tbm_gem.o tgm-$(CONFIG_DRM_TBM_GEM_DMA_MAPPING) += tbm_gem_dma_mapping.o diff --git a/drivers/gpu/drm/tgm/tdm_pp_msc.c b/drivers/gpu/drm/tgm/tdm_pp_msc.c new file mode 100644 index 00000000000..e1ca9994b72 --- /dev/null +++ b/drivers/gpu/drm/tgm/tdm_pp_msc.c @@ -0,0 +1,1757 @@ +/* + * Copyright (C) 2016 Samsung Electronics Co.Ltd + * Authors: + * Jinyoung Jeon + * Jonggab Park + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tdm_pp.h" +#include "tdm_pp_msc.h" +#include "tdm_pp_msc_regs.h" + +/* + * SC stands for SCaler and + * supports image scaler/rotator and input/output DMA operations. + * input DMA reads image data from the memory. + * output DMA writes image data to memory. + * SC supports image rotation and image effect functions. + * + * M2M operation : supports crop/scale/rotation/csc/blending/ + * dithering/color fill so on. + * Memory ----> SC H/W ----> Memory. + */ + +/* + * TODO + * 1. check suspend/resume api if needed. + * 2. need to check use case platform_device_id. + * 3. check src/dst size with, height. + * 4. need to add supported list in prop_list. + * 5. how can we fix different C src set_size. + */ + +#define SC_MAX_DEVS 1 +#define SC_MAX_SRC 8 +#define SC_MAX_DST 32 +#define SC_RESET_TIMEOUT 50 +#define SC_CLK_RATE 166750000 +#define SC_REG_SZ 32 +#define SC_WIDTH_ITU_709 1280 +#define SC_CROP_MAX 16384 +#define SC_CROP_MIN 16 +#define SC_SCALE_MAX 16384 +#define SC_SCALE_MIN 16 +#define SC_COEF_RATIO 7 +#define SC_COEF_PHASE 9 +#define SC_COEF_ATTR 16 +#define SC_COEF_H_8T 8 +#define SC_COEF_V_4T 4 +#define SC_COEF_DEPTH 3 +#define SC_UP_MAX SC_RATIO(1, 16) +#define SC_DOWN_MIN SC_RATIO(4, 1) +#define SC_DOWN_SWMIN SC_RATIO(16, 1) +#define SCALE_RATIO_CONST(x, y) (u32)((1048576ULL * (x)) / (y)) +#define SC_RATIO(x, y) \ +({ \ + u32 ratio; \ + if (__builtin_constant_p(x) && __builtin_constant_p(y)) {\ + ratio = SCALE_RATIO_CONST(x, y); \ + } else if ((x) < 2048) { \ + ratio = (u32)((1048576UL * (x)) / (y)); \ + } else { \ + unsigned long long dividend = 1048576ULL; \ + dividend *= x; \ + do_div(dividend, y); \ + ratio = (u32)dividend; \ + } \ + ratio; \ +}) + +#define get_sc_context(dev) platform_get_drvdata(to_platform_device(dev)) +#define get_ctx_from_ppdrv(ppdrv) \ +container_of(ppdrv, struct sc_context, ppdrv) +#define sc_read(offset) readl(ctx->regs + (offset)) +#define sc_write(cfg, offset) writel(cfg, ctx->regs + (offset)) + +/* definition of csc type */ +enum sc_csc_type { + CSC_TYPE_NO, + CSC_TYPE_Y2R, + CSC_TYPE_R2Y, +}; + +enum sc_version { + SC_VER_3250, + SC_VER_5260, + SC_VER_5410, +}; + +/* + * A structure of scaler. + * + * @range: narrow, wide. + * @hratio: the scaler's horizontal ratio. + * @vratio: the scaler's vertical ratio. + */ +struct sc_scaler { + bool range; + unsigned long hratio; + unsigned long vratio; +}; + +/* + * A structure of sc context. + * + * @ppdrv: prepare initialization using ppdrv. + * @regs_res: register resources. + * @regs: memory mapped io registers. + * @sc_clk: sc a clock. + * @sc_clk: sc p clock. + * @sc: scaler infomations. + * @id: sc id. + * @irq: irq number. + * @cur_buf_id: id of current buffer. + * @nplanar: number of planar. + * @pre_multi: premultiplied format. + * @rotation: supports rotation of src. + */ +struct sc_context { + struct tdm_ppdrv ppdrv; + struct resource *regs_res; + void __iomem *regs; + struct clk *aclk; + struct clk *pclk; + struct clk *clk_chld; + struct clk *clk_parn; + struct sc_scaler sc; + int irq; + int cur_buf_id[TDM_OPS_MAX]; + int nplanar[TDM_OPS_MAX]; + bool pre_multi; + bool rotation; +}; + +/* Scaling coefficient value */ +static const int sc_coef_8t[SC_COEF_RATIO][SC_COEF_ATTR][SC_COEF_H_8T] = { + { + /* 8:8 or zoom-in */ + {0, 0, 0, 0, 128, 0, 0, 0}, + {0, 1, -2, 7, 127, -6, 2, -1}, + {0, 1, -5, 16, 125, -12, 4, -1}, + {0, 2, -8, 25, 120, -15, 5, -1}, + {-1, 3, -10, 35, 114, -18, 6, -1}, + {-1, 4, -13, 46, 107, -20, 6, -1}, + {-1, 5, -16, 57, 99, -21, 7, -2}, + {-1, 5, -18, 68, 89, -20, 6, -1}, + {-1, 6, -20, 79, 79, -20, 6, -1}, + {-1, 6, -20, 89, 68, -18, 5, -1}, + {-2, 7, -21, 99, 57, -16, 5, -1}, + {-1, 6, -20, 107, 46, -13, 4, -1}, + {-1, 6, -18, 114, 35, -10, 3, -1}, + {-1, 5, -15, 120, 25, -8, 2, 0}, + {-1, 4, -12, 125, 16, -5, 1, 0}, + {-1, 2, -6, 127, 7, -2, 1, 0} + }, { + /* 8:7 Zoom-out */ + {0, 3, -8, 13, 111, 14, -8, 3}, + {-1, 3, -10, 21, 112, 7, -6, 2}, + {-1, 4, -12, 28, 110, 1, -4, 2}, + {-1, 4, -13, 36, 106, -3, -2, 1}, + {-1, 4, -15, 44, 103, -7, -1, 1}, + {-1, 4, -16, 53, 97, -11, 1, 1}, + {-1, 4, -16, 61, 91, -13, 2, 0}, + {-1, 4, -17, 69, 85, -15, 3, 0}, + {0, 3, -16, 77, 77, -16, 3, 0}, + {0, 3, -15, 85, 69, -17, 4, -1}, + {0, 2, -13, 91, 61, -16, 4, -1}, + {1, 1, -11, 97, 53, -16, 4, -1}, + {1, -1, -7, 103, 44, -15, 4, -1}, + {1, -2, -3, 106, 36, -13, 4, -1}, + {2, -4, 1, 110, 28, -12, 4, -1}, + {2, -6, 7, 112, 21, -10, 3, -1} + }, { + /* 8:6 Zoom-out */ + {0, 2, -11, 25, 96, 25, -11, 2}, + {0, 2, -12, 31, 96, 19, -10, 2}, + {0, 2, -12, 37, 94, 14, -9, 2}, + {0, 1, -12, 43, 92, 10, -8, 2}, + {0, 1, -12, 49, 90, 5, -7, 2}, + {1, 0, -12, 55, 86, 1, -5, 2}, + {1, -1, -11, 61, 82, -2, -4, 2}, + {1, -1, -9, 67, 77, -5, -3, 1}, + {1, -2, -7, 72, 72, -7, -2, 1}, + {1, -3, -5, 77, 67, -9, -1, 1}, + {2, -4, -2, 82, 61, -11, -1, 1}, + {2, -5, 1, 86, 55, -12, 0, 1}, + {2, -7, 5, 90, 49, -12, 1, 0}, + {2, -8, 10, 92, 43, -12, 1, 0}, + {2, -9, 14, 94, 37, -12, 2, 0}, + {2, -10, 19, 96, 31, -12, 2, 0} + }, { + /* 8:5 Zoom-out */ + {0, -1, -8, 33, 80, 33, -8, -1}, + {1, -2, -7, 37, 80, 28, -8, -1}, + {1, -2, -7, 41, 79, 24, -8, 0}, + {1, -3, -6, 46, 78, 20, -8, 0}, + {1, -3, -4, 50, 76, 16, -8, 0}, + {1, -4, -3, 54, 74, 13, -7, 0}, + {1, -5, -1, 58, 71, 10, -7, 1}, + {1, -5, 1, 62, 68, 6, -6, 1}, + {1, -6, 4, 65, 65, 4, -6, 1}, + {1, -6, 6, 68, 62, 1, -5, 1}, + {1, -7, 10, 71, 58, -1, -5, 1}, + {0, -7, 13, 74, 54, -3, -4, 1}, + {0, -8, 16, 76, 50, -4, -3, 1}, + {0, -8, 20, 78, 46, -6, -3, 1}, + {0, -8, 24, 79, 41, -7, -2, 1}, + {-1, -8, 28, 80, 37, -7, -2, 1} + }, { + /* 8:4 Zoom-out */ + {0, -3, 0, 35, 64, 35, 0, -3}, + {0, -3, 1, 38, 64, 32, -1, -3}, + {0, -3, 2, 41, 63, 29, -2, -2}, + {0, -4, 4, 43, 63, 27, -3, -2}, + {0, -4, 6, 46, 61, 24, -3, -2}, + {0, -4, 7, 49, 60, 21, -3, -2}, + {-1, -4, 9, 51, 59, 19, -4, -1}, + {-1, -4, 12, 53, 57, 16, -4, -1}, + {-1, -4, 14, 55, 55, 14, -4, -1}, + {-1, -4, 16, 57, 53, 12, -4, -1}, + {-1, -4, 19, 59, 51, 9, -4, -1}, + {-2, -3, 21, 60, 49, 7, -4, 0}, + {-2, -3, 24, 61, 46, 6, -4, 0}, + {-2, -3, 27, 63, 43, 4, -4, 0}, + {-2, -2, 29, 63, 41, 2, -3, 0}, + {-3, -1, 32, 64, 38, 1, -3, 0} + }, { + /* 8:3 Zoom-out */ + {0, -1, 8, 33, 48, 33, 8, -1}, + {-1, -1, 9, 35, 49, 31, 7, -1}, + {-1, -1, 10, 36, 49, 30, 6, -1}, + {-1, -1, 12, 38, 48, 28, 5, -1}, + {-1, 0, 13, 39, 48, 26, 4, -1}, + {-1, 0, 15, 41, 47, 24, 3, -1}, + {-1, 0, 16, 42, 47, 23, 2, -1}, + {-1, 1, 18, 43, 45, 21, 2, -1}, + {-1, 1, 19, 45, 45, 19, 1, -1}, + {-1, 2, 21, 45, 43, 18, 1, -1}, + {-1, 2, 23, 47, 42, 16, 0, -1}, + {-1, 3, 24, 47, 41, 15, 0, -1}, + {-1, 4, 26, 48, 39, 13, 0, -1}, + {-1, 5, 28, 48, 38, 12, -1, -1}, + {-1, 6, 30, 49, 36, 10, -1, -1}, + {-1, 7, 31, 49, 35, 9, -1, -1} + }, + + { /* 8:2 Zoom-out */ + {0, 2, 13, 30, 38, 30, 13, 2}, + {0, 3, 14, 30, 38, 29, 12, 2}, + {0, 3, 15, 31, 38, 28, 11, 2}, + {0, 4, 16, 32, 38, 26, 10, 2}, + {0, 4, 17, 33, 37, 26, 10, 1}, + {0, 5, 18, 34, 37, 24, 9, 1}, + {0, 5, 19, 34, 37, 24, 8, 1}, + {1, 6, 20, 35, 36, 22, 7, 1}, + {1, 6, 21, 36, 36, 21, 6, 1}, + {1, 7, 22, 36, 35, 20, 6, 1}, + {1, 8, 24, 37, 34, 19, 5, 0}, + {1, 9, 24, 37, 34, 18, 5, 0}, + {1, 10, 26, 37, 33, 17, 4, 0}, + {2, 10, 26, 38, 32, 16, 4, 0}, + {2, 11, 28, 38, 31, 15, 3, 0}, + {2, 12, 29, 38, 30, 14, 3, 0} + } +}; + +static const int sc_coef_4t[SC_COEF_RATIO][SC_COEF_ATTR][SC_COEF_V_4T] = { + { + /* 8:8 or zoom-in */ + {0, 0, 128, 0}, + {0, 5, 127, -4}, + {-1, 11, 124, -6}, + {-1, 19, 118, -8}, + {-2, 27, 111, -8}, + {-3, 37, 102, -8}, + {-4, 48, 92, -8}, + {-5, 59, 81, -7}, + {-6, 70, 70, -6}, + {-7, 81, 59, -5}, + {-8, 92, 48, -4}, + {-8, 102, 37, -3}, + {-8, 111, 27, -2}, + {-8, 118, 19, -1}, + {-6, 124, 11, -1}, + {-4, 127, 5, 0} + }, { + /* 8:7 Zoom-out */ + {0, 8, 112, 8}, + {-1, 14, 111, 4}, + {-2, 20, 109, 1}, + {-2, 27, 105, -2}, + {-3, 34, 100, -3}, + {-3, 43, 93, -5}, + {-4, 51, 86, -5}, + {-4, 60, 77, -5}, + {-5, 69, 69, -5}, + {-5, 77, 60, -4}, + {-5, 86, 51, -4}, + {-5, 93, 43, -3}, + {-3, 100, 34, -3}, + {-2, 105, 27, -2}, + {1, 109, 20, -2}, + {4, 111, 14, -1} + }, { + /* 8:6 Zoom-out */ + {0, 16, 96, 16}, + {-2, 21, 97, 12}, + {-2, 26, 96, 8}, + {-2, 32, 93, 5}, + {-2, 39, 89, 2}, + {-2, 46, 84, 0}, + {-3, 53, 79, -1}, + {-2, 59, 73, -2}, + {-2, 66, 66, -2}, + {-2, 73, 59, -2}, + {-1, 79, 53, -3}, + {0, 84, 46, -2}, + {2, 89, 39, -2}, + {5, 93, 32, -2}, + {8, 96, 26, -2}, + {12, 97, 21, -2} + }, { + /* 8:5 Zoom-out */ + {0, 22, 84, 22}, + {-1, 26, 85, 18}, + {-1, 31, 84, 14}, + {-1, 36, 82, 11}, + {-1, 42, 79, 8}, + {-1, 47, 76, 6}, + {0, 52, 72, 4}, + {0, 58, 68, 2}, + {1, 63, 63, 1}, + {2, 68, 58, 0}, + {4, 72, 52, 0}, + {6, 76, 47, -1}, + {8, 79, 42, -1}, + {11, 82, 36, -1}, + {14, 84, 31, -1}, + {18, 85, 26, -1} + }, { + /* 8:4 Zoom-out */ + {0, 26, 76, 26}, + {0, 30, 76, 22}, + {0, 34, 75, 19}, + {1, 38, 73, 16}, + {1, 43, 71, 13}, + {2, 47, 69, 10}, + {3, 51, 66, 8}, + {4, 55, 63, 6}, + {5, 59, 59, 5}, + {6, 63, 55, 4}, + {8, 66, 51, 3}, + {10, 69, 47, 2}, + {13, 71, 43, 1}, + {16, 73, 38, 1}, + {19, 75, 34, 0}, + {22, 76, 30, 0} + }, { + /* 8:3 Zoom-out */ + {0, 29, 70, 29}, + {2, 32, 68, 26}, + {2, 36, 67, 23}, + {3, 39, 66, 20}, + {3, 43, 65, 17}, + {4, 46, 63, 15}, + {5, 50, 61, 12}, + {7, 53, 58, 10}, + {8, 56, 56, 8}, + {10, 58, 53, 7}, + {12, 61, 50, 5}, + {15, 63, 46, 4}, + {17, 65, 43, 3}, + {20, 66, 39, 3}, + {23, 67, 36, 2}, + {26, 68, 32, 2} + }, { + /* 8:2 Zoom-out */ + {0, 32, 64, 32}, + {3, 34, 63, 28}, + {4, 37, 62, 25}, + {4, 40, 62, 22}, + {5, 43, 61, 19}, + {6, 46, 59, 17}, + {7, 48, 58, 15}, + {9, 51, 55, 13}, + {11, 53, 53, 11}, + {13, 55, 51, 9}, + {15, 58, 48, 7}, + {17, 59, 46, 6}, + {19, 61, 43, 5}, + {22, 62, 40, 4}, + {25, 62, 37, 4}, + {28, 63, 34, 3} + }, +}; + +/* + * A structure of csc table. + * + * @narrow_601: narrow 601. + * @wide_601: wide 601. + * @narrow_709: narrow 709. + * @wide_709: wide 709. + */ +struct sc_csc_tab { + int narrow_601[9]; + int wide_601[9]; + int narrow_709[9]; + int wide_709[9]; +}; + +/* CSC(Color Space Conversion) coefficient value */ +static struct sc_csc_tab sc_no_csc = { + { 0x200, 0x000, 0x000, 0x000, 0x200, 0x000, 0x000, 0x000, 0x200 }, +}; + +static struct sc_csc_tab sc_y2r = { + /* (0,1) 601 Narrow */ + { 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE60, 0x254, 0x409, 0x000 }, + /* (0,1) 601 Wide */ + { 0x200, 0x000, 0x2BE, 0x200, 0xF54, 0xE9B, 0x200, 0x377, 0x000 }, + /* (0,1) 709 Narrow */ + { 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE60, 0x254, 0x409, 0x000 }, + /* (0,1) 709 Wide */ + { 0x200, 0x000, 0x314, 0x200, 0xFA2, 0xF15, 0x200, 0x3A2, 0x000 }, +}; + +static struct sc_csc_tab sc_r2y = { + /* (1,0) 601 Narrow */ + { 0x084, 0x102, 0x032, 0xFB4, 0xF6B, 0x0E1, 0x0E1, 0xF44, 0xFDC }, + /* (1,0) 601 Wide */ + { 0x099, 0x12D, 0x03A, 0xFA8, 0xF52, 0x106, 0x106, 0xF25, 0xFD6 }, + /* (1,0) 709 Narrow */ + { 0x05E, 0x13A, 0x020, 0xFCC, 0xF53, 0x0E1, 0x0E1, 0xF34, 0xFEC }, + /* (1,0) 709 Wide */ + { 0x06D, 0x16E, 0x025, 0xFC4, 0xF36, 0x106, 0x106, 0xF12, 0xFE8 }, +}; + +static struct sc_csc_tab *sc_csc_list[] = { + [CSC_TYPE_NO] = &sc_no_csc, + [CSC_TYPE_Y2R] = &sc_y2r, + [CSC_TYPE_R2Y] = &sc_r2y, +}; + +#define SC_BLENDING_INV_BIT_OFFSET 0x10 + +/* define of blending compare */ +enum sc_blending_comp { + ONE = 0, + SRC_A, + SRC_C, + DST_A, + INV_SA = 0x11, + INV_SC, + INV_DA, + ZERO = 0xff, +}; + +/* + * A structure of blending value. + * + * @src_color: source color. + * @src_alpha: source alpha. + * @dst_color: destination color. + * @dst_alpha: destination alpha. + */ +struct sc_blending_val { + u32 src_color; + u32 src_alpha; + u32 dst_color; + u32 dst_alpha; +}; + +static int sc_sw_reset(struct sc_context *ctx) +{ + u32 cfg; + + DRM_INFO("%s\n", __func__); + + /* s/w reset */ + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_SOFT_RST; + sc_write(cfg, SCALER_CFG); + + return 0; +} + +static void sc_handle_irq(struct sc_context *ctx, bool enable) +{ + u32 cfg; + + DRM_INFO("%s:enable[%d]\n", __func__, enable); + + cfg = sc_read(SCALER_INT_EN); + + if (enable) + cfg = SCALER_INT_EN_ALL_v3; + else + cfg &= ~SCALER_INT_EN_ALL_v3; + + sc_write(cfg, SCALER_INT_EN); +} + +static bool sc_check_rgb(u32 fmt) +{ + bool is_rgb = false; + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + is_rgb = true; + break; + default: + break; + } + + return is_rgb; +} + +static int sc_src_set_fmt_nplanar(struct sc_context *ctx, u32 fmt) +{ + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + + DRM_INFO("%s:fmt[0x%x]\n", __func__, fmt); + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + ctx->nplanar[TDM_OPS_SRC] = 1; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV12MT: + ctx->nplanar[TDM_OPS_SRC] = 2; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + ctx->nplanar[TDM_OPS_SRC] = 3; + break; + default: + dev_err(ppdrv->dev, "inavlid number of planar 0x%x.\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int sc_src_set_fmt(struct device *dev, u32 fmt) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + u32 cfg; + + DRM_INFO("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = sc_read(SCALER_SRC_CFG); + cfg &= ~(SCALER_CFG_TILE_EN|SCALER_CFG_FMT_MASK); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= SCALER_CFG_FMT_RGB565; + break; + case DRM_FORMAT_ARGB1555: + cfg |= SCALER_CFG_FMT_ARGB1555; + break; + case DRM_FORMAT_ARGB4444: + cfg |= SCALER_CFG_FMT_ARGB4444; + break; + case DRM_FORMAT_XRGB8888: + cfg |= (SCALER_CFG_FMT_ARGB8888 | + SCALER_CFG_FMT_P_ARGB8888); + break; + case DRM_FORMAT_ARGB8888: + cfg |= SCALER_CFG_FMT_ARGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= SCALER_CFG_FMT_YUYV; + break; + case DRM_FORMAT_YVYU: + cfg |= SCALER_CFG_FMT_YVYU; + break; + case DRM_FORMAT_UYVY: + cfg |= SCALER_CFG_FMT_UYVY; + break; + case DRM_FORMAT_NV21: + cfg |= SCALER_CFG_FMT_YCRCB420_2P; + break; + case DRM_FORMAT_NV61: + cfg |= SCALER_CFG_FMT_YCRCB422_2P; + break; + case DRM_FORMAT_NV12: + cfg |= SCALER_CFG_FMT_YCBCR420_2P; + break; + case DRM_FORMAT_NV16: + cfg |= SCALER_CFG_FMT_YCBCR422_2P; + break; + case DRM_FORMAT_NV12MT: + cfg |= (SCALER_CFG_FMT_YCBCR420_2P | + SCALER_CFG_TILE_EN); + break; + case DRM_FORMAT_YUV422: + cfg |= SCALER_CFG_FMT_YCBCR422_3P; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= SCALER_CFG_FMT_YCBCR420_3P; + break; + default: + dev_err(ppdrv->dev, "invalid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + sc_write(cfg, SCALER_SRC_CFG); + + return sc_src_set_fmt_nplanar(ctx, fmt); +} + +static int sc_src_set_transf(struct device *dev, + enum tdm_degree degree, + enum tdm_flip flip, bool *swap) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + u32 cfg; + + DRM_INFO("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = sc_read(SCALER_ROT_CFG); + cfg &= ~(SCALER_FLIP_MASK | SCALER_ROT_MASK); + + switch (degree) { + case TDM_DEGREE_0: + if (flip & TDM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN; + if (flip & TDM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN; + break; + case TDM_DEGREE_90: + if (flip & TDM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN | SCALER_ROT_270; + else if (flip & TDM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN | SCALER_ROT_270; + else + cfg |= SCALER_ROT_270; + break; + case TDM_DEGREE_180: + cfg |= SCALER_ROT_180; + break; + case TDM_DEGREE_270: + cfg |= SCALER_ROT_90; + break; + default: + dev_err(ppdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + sc_write(cfg, SCALER_ROT_CFG); + + ctx->rotation = cfg & + (SCALER_ROT_90 | SCALER_ROT_270) ? 1 : 0; + *swap = ctx->rotation; + + return 0; +} + +static int sc_src_set_size(struct device *dev, int swap, + struct tdm_pos *pos, struct tdm_sz *sz) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_pos img_pos = *pos; + u32 cfg; + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + } + + DRM_INFO("%s:x[%d]y[%d]w[%d]h[%d]\n", + __func__, pos->x, pos->y, pos->w, pos->h); + + /* pixel offset */ + cfg = (SCALER_SRC_YX(pos->x) | + SCALER_SRC_YY(pos->y)); + sc_write(cfg, SCALER_SRC_Y_POS); + + /* cropped size */ + cfg = (SCALER_SRC_W(pos->w) | + SCALER_SRC_H(pos->h)); + sc_write(cfg, SCALER_SRC_WH); + + DRM_INFO("%s:swap[%d]hsize[%d]vsize[%d]\n", + __func__, swap, sz->hsize, sz->vsize); + + /* span size */ + cfg = sc_read(SCALER_SRC_SPAN); + cfg &= ~(SCALER_SRC_CSPAN_MASK | + SCALER_SRC_YSPAN_MASK); + + cfg |= sz->hsize; + + if (ctx->nplanar[TDM_OPS_SRC] == 2) + cfg |= sz->hsize << 16; + + if (ctx->nplanar[TDM_OPS_SRC] == 3) + cfg |= (sz->hsize >> 1) << 16; + + sc_write(cfg, SCALER_SRC_SPAN); + + return 0; +} + +static int sc_src_set_addr(struct device *dev, + struct tdm_pp_buf_info *buf_info, u32 buf_id, + enum tdm_pp_buf_type buf_type) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + struct tdm_pp_cmd_node *c_node = ppdrv->c_node; + struct tdm_pp_property *property; + struct tdm_pp_config *config; + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + if (!property) { + DRM_ERROR("failed to get property.\n"); + return -EINVAL; + } + + DRM_INFO("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + /* Set current buf_id */ + ctx->cur_buf_id[TDM_OPS_SRC] = buf_id; + + if (buf_id > SC_MAX_SRC) { + dev_info(ppdrv->dev, "inavlid buf_id %d.\n", buf_id); + return -ENOMEM; + } + + /* address register set */ + switch (buf_type) { + case PP_BUF_ENQUEUE: + config = &property->config[TDM_OPS_SRC]; + + sc_write(buf_info->base[TDM_PLANAR_Y], + SCALER_SRC_Y_BASE); + sc_write(buf_info->base[TDM_PLANAR_CB], + SCALER_SRC_CB_BASE); + sc_write(buf_info->base[TDM_PLANAR_CR], + SCALER_SRC_CR_BASE); + + break; + case PP_BUF_DEQUEUE: + default: + /* bypass */ + break; + } + + return 0; +} + +static struct tdm_pp_ops sc_src_ops = { + .set_fmt = sc_src_set_fmt, + .set_transf = sc_src_set_transf, + .set_size = sc_src_set_size, + .set_addr = sc_src_set_addr, +}; + +static int sc_dst_set_fmt_nplanar(struct sc_context *ctx, u32 fmt) +{ + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + u32 cfg; + + DRM_INFO("%s:fmt[0x%x]\n", __func__, fmt); + + switch (fmt) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + cfg = sc_read(SCALER_CFG); + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_DST; + sc_write(cfg, SCALER_CFG); + ctx->nplanar[TDM_OPS_DST] = 1; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + ctx->nplanar[TDM_OPS_DST] = 1; + break; + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV12MT: + ctx->nplanar[TDM_OPS_DST] = 2; + break; + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + ctx->nplanar[TDM_OPS_DST] = 3; + break; + default: + dev_err(ppdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int sc_dst_set_fmt(struct device *dev, u32 fmt) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + u32 cfg; + + DRM_INFO("%s:fmt[0x%x]\n", __func__, fmt); + + cfg = sc_read(SCALER_DST_CFG); + cfg &= ~(SCALER_CFG_SWAP_MASK|SCALER_CFG_FMT_MASK); + + switch (fmt) { + case DRM_FORMAT_RGB565: + cfg |= SCALER_CFG_FMT_RGB565; + break; + case DRM_FORMAT_ARGB1555: + cfg |= SCALER_CFG_FMT_ARGB1555; + break; + case DRM_FORMAT_ARGB4444: + cfg |= SCALER_CFG_FMT_ARGB4444; + break; + case DRM_FORMAT_XRGB8888: + cfg |= (SCALER_CFG_FMT_ARGB8888 | + SCALER_CFG_FMT_P_ARGB8888); + ctx->pre_multi = true; + break; + case DRM_FORMAT_ARGB8888: + cfg |= SCALER_CFG_FMT_ARGB8888; + break; + case DRM_FORMAT_YUYV: + cfg |= SCALER_CFG_FMT_YUYV; + break; + case DRM_FORMAT_YVYU: + cfg |= SCALER_CFG_FMT_YVYU; + break; + case DRM_FORMAT_UYVY: + cfg |= SCALER_CFG_FMT_UYVY; + break; + case DRM_FORMAT_NV21: + cfg |= SCALER_CFG_FMT_YCRCB420_2P; + break; + case DRM_FORMAT_NV61: + cfg |= SCALER_CFG_FMT_YCRCB422_2P; + break; + case DRM_FORMAT_NV12: + cfg |= SCALER_CFG_FMT_YCBCR420_2P; + break; + case DRM_FORMAT_NV16: + cfg |= SCALER_CFG_FMT_YCBCR422_2P; + break; + case DRM_FORMAT_NV12MT: + cfg |= (SCALER_CFG_FMT_YCBCR420_2P | + SCALER_CFG_TILE_EN); + break; + case DRM_FORMAT_YUV422: + cfg |= SCALER_CFG_FMT_YCBCR422_3P; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + cfg |= SCALER_CFG_FMT_YCBCR420_3P; + break; + default: + dev_err(ppdrv->dev, "inavlid target yuv order 0x%x.\n", fmt); + return -EINVAL; + } + + sc_write(cfg, SCALER_DST_CFG); + + return sc_dst_set_fmt_nplanar(ctx, fmt); +} + +static int sc_dst_set_transf(struct device *dev, + enum tdm_degree degree, + enum tdm_flip flip, bool *swap) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + u32 cfg; + + DRM_INFO("%s:degree[%d]flip[0x%x]\n", __func__, + degree, flip); + + cfg = sc_read(SCALER_ROT_CFG); + cfg &= ~(SCALER_FLIP_MASK | SCALER_ROT_MASK); + + switch (degree) { + case TDM_DEGREE_0: + if (flip & TDM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN; + if (flip & TDM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN; + break; + case TDM_DEGREE_90: + if (flip & TDM_FLIP_HORIZONTAL) + cfg |= SCALER_FLIP_X_EN | SCALER_ROT_270; + else if (flip & TDM_FLIP_VERTICAL) + cfg |= SCALER_FLIP_Y_EN | SCALER_ROT_270; + else + cfg |= SCALER_ROT_270; + break; + case TDM_DEGREE_180: + cfg |= (SCALER_FLIP_X_EN | SCALER_FLIP_Y_EN); + break; + case TDM_DEGREE_270: + cfg |= SCALER_ROT_90; + break; + default: + dev_err(ppdrv->dev, "inavlid degree value %d.\n", degree); + return -EINVAL; + } + + sc_write(cfg, SCALER_ROT_CFG); + + ctx->rotation = cfg & + (SCALER_ROT_90 | SCALER_ROT_270) ? 1 : 0; + + *swap = ctx->rotation; + + return 0; +} + +static int sc_set_csc_coef(struct sc_context *ctx, struct sc_scaler *sc, + u32 *fmt, int width) +{ + enum sc_csc_type csc_type = CSC_TYPE_NO; + int *csc_eq_val, i, j; + u32 cfg; + bool itu_709; + + DRM_INFO("%s:sfmt[0x%x]dfmt[0x%x]range[%d]width[%d]\n", __func__, + fmt[TDM_OPS_SRC], fmt[TDM_OPS_DST], + sc->range, width); + + if (fmt[TDM_OPS_SRC] == fmt[TDM_OPS_DST]) { + csc_eq_val = sc_csc_list[csc_type]->narrow_601; + } else { + if (width >= SC_WIDTH_ITU_709) + itu_709 = true; + else + itu_709 = false; + + cfg = sc_read(SCALER_CFG); + if (sc_check_rgb(fmt[TDM_OPS_DST])) { + csc_type = CSC_TYPE_Y2R; + if (sc->range) + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_SRC; + else + cfg |= SCALER_CFG_CSC_Y_OFFSET_SRC; + } else { + csc_type = CSC_TYPE_R2Y; + if (sc->range) + cfg |= SCALER_CFG_CSC_Y_OFFSET_DST; + else + cfg &= ~SCALER_CFG_CSC_Y_OFFSET_DST; + } + sc_write(cfg, SCALER_CFG); + + if (itu_709) { + if (sc->range) + csc_eq_val = sc_csc_list[csc_type]->wide_709; + else + csc_eq_val = sc_csc_list[csc_type]->narrow_709; + } else { + if (sc->range) + csc_eq_val = sc_csc_list[csc_type]->wide_601; + else + csc_eq_val = sc_csc_list[csc_type]->narrow_601; + } + } + + for (i = 0, j = 0; i < SC_COEF_PHASE; i++, j += 4) { + cfg = sc_read(SCALER_CSC_COEF00 + j); + cfg &= ~SCALER_CSC_COEF_MASK; + cfg |= csc_eq_val[i]; + sc_write(cfg, SCALER_CSC_COEF00 + j); + } + + return 0; +} + +static void sc_set_scaler_ratio(struct sc_context *ctx, struct sc_scaler *sc) +{ + u32 cfg; + + DRM_INFO("%s:hratio[%ld]vratio[%ld]\n", + __func__, sc->hratio, sc->vratio); + + cfg = sc_read(SCALER_H_RATIO); + cfg &= ~SCALER_RATIO_MASK; + cfg |= sc->hratio; + sc_write(cfg, SCALER_H_RATIO); + + cfg = sc_read(SCALER_V_RATIO); + cfg &= ~SCALER_RATIO_MASK; + cfg |= sc->vratio; + sc_write(cfg, SCALER_V_RATIO); +} + +static void sc_set_h_coef(struct sc_context *ctx, int coef) +{ + u32 phase, tab, cnt = 0; + u32 cfg, val_h, val_l; + + DRM_INFO("%s:coef[%d]\n", __func__, coef); + + for (phase = 0; phase < SC_COEF_PHASE; phase++) { + for (tab = SC_COEF_H_8T; tab > 0; tab -= 2, cnt++) { + val_h = sc_coef_8t[coef][phase][tab - 1] & 0x1FF; + val_l = sc_coef_8t[coef][phase][tab - 2] & 0x1FF; + cfg = (val_h << 16) | (val_l << 0); + sc_write(cfg, SCALER_YHCOEF + cnt * 0x4); + sc_write(cfg, SCALER_CHCOEF + cnt * 0x4); + } + } +} + +static void sc_set_v_coef(struct sc_context *ctx, int coef) +{ + u32 phase, tab, cnt = 0; + u32 cfg, val_h, val_l; + + DRM_INFO("%s:coef[%d]\n", __func__, coef); + + for (phase = 0; phase < SC_COEF_PHASE; phase++) { + for (tab = SC_COEF_V_4T; tab > 0; tab -= 2, cnt++) { + val_h = sc_coef_4t[coef][phase][tab - 1] & 0x1FF; + val_l = sc_coef_4t[coef][phase][tab - 2] & 0x1FF; + cfg = (val_h << 16) | (val_l << 0); + sc_write(cfg, SCALER_YVCOEF + cnt * 0x4); + sc_write(cfg, SCALER_CVCOEF + cnt * 0x4); + } + } +} + +static int sc_get_scale_filter(u32 ratio) +{ + int filter; + + if (ratio <= 65536) + filter = 0; /* 8:8 or zoom-in */ + else if (ratio <= 74898) + filter = 1; /* 8:7 zoom-out */ + else if (ratio <= 87381) + filter = 2; /* 8:6 zoom-out */ + else if (ratio <= 104857) + filter = 3; /* 8:5 zoom-out */ + else if (ratio <= 131072) + filter = 4; /* 8:4 zoom-out */ + else if (ratio <= 174762) + filter = 5; /* 8:3 zoom-out */ + else + filter = 6; /* 8:2 zoom-out */ + + return filter; +} + +static int sc_set_scaler_coef(struct sc_context *ctx, struct sc_scaler *sc) +{ + int hcoef, vcoef; + + DRM_INFO("%s\n", __func__); + + hcoef = sc_get_scale_filter(sc->hratio); + vcoef = sc_get_scale_filter(sc->vratio); + + sc_set_h_coef(ctx, hcoef); + sc_set_v_coef(ctx, vcoef); + + return 0; +} + +static int sc_set_scaler(struct sc_context *ctx, struct sc_scaler *sc, + struct tdm_pos *src, struct tdm_pos *dst) +{ + DRM_INFO("%s\n", __func__); + + if (ctx->rotation) { + sc->hratio = SC_RATIO(src->h, dst->w); + sc->vratio = SC_RATIO(src->w, dst->h); + } else{ + sc->hratio = SC_RATIO(src->w, dst->w); + sc->vratio = SC_RATIO(src->h, dst->h); + } + + DRM_INFO("%s:hratio[%ld]vratio[%ld]\n", + __func__, sc->hratio, sc->vratio); + + sc_set_scaler_coef(ctx, &ctx->sc); + sc_set_scaler_ratio(ctx, &ctx->sc); + + return 0; +} + +static int sc_dst_set_size(struct device *dev, int swap, + struct tdm_pos *pos, struct tdm_sz *sz) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_pos img_pos = *pos; + u32 cfg; + + DRM_INFO("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", + __func__, swap, pos->x, pos->y, pos->w, pos->h); + + if (swap) { + img_pos.w = pos->h; + img_pos.h = pos->w; + } + + /* pixel offset */ + cfg = (SCALER_DST_X(pos->x) | + SCALER_DST_Y(pos->y)); + sc_write(cfg, SCALER_DST_POS); + + /* scaled size */ + cfg = (SCALER_DST_W(pos->w) | SCALER_DST_H(pos->h)); + sc_write(cfg, SCALER_DST_WH); + + DRM_INFO("%s:hsize[%d]vsize[%d]\n", + __func__, sz->hsize, sz->vsize); + + /* span size */ + cfg = sc_read(SCALER_DST_SPAN); + cfg &= ~(SCALER_DST_CSPAN_MASK | + SCALER_DST_YSPAN_MASK); + + cfg |= sz->hsize; + + if (ctx->nplanar[TDM_OPS_DST] == 2) + cfg |= sz->hsize << 16; + + if (ctx->nplanar[TDM_OPS_DST] == 3) + cfg |= (sz->hsize >> 1) << 16; + + sc_write(cfg, SCALER_DST_SPAN); + + return 0; +} + +static int sc_dst_set_addr(struct device *dev, + struct tdm_pp_buf_info *buf_info, u32 buf_id, + enum tdm_pp_buf_type buf_type) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + struct tdm_pp_cmd_node *c_node = ppdrv->c_node; + struct tdm_pp_property *property; + struct tdm_pp_config *config; + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + + DRM_INFO("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + property->prop_id, buf_id, buf_type); + + /* Set current buf_id */ + ctx->cur_buf_id[TDM_OPS_DST] = buf_id; + + /* address register set */ + switch (buf_type) { + case PP_BUF_ENQUEUE: + config = &property->config[TDM_OPS_DST]; + + sc_write(buf_info->base[TDM_PLANAR_Y], + SCALER_DST_Y_BASE); + sc_write(buf_info->base[TDM_PLANAR_CB], + SCALER_DST_CB_BASE); + sc_write(buf_info->base[TDM_PLANAR_CR], + SCALER_DST_CR_BASE); + break; + case PP_BUF_DEQUEUE: + default: + /* bypass */ + break; + } + + return 0; +} + +static struct tdm_pp_ops sc_dst_ops = { + .set_fmt = sc_dst_set_fmt, + .set_transf = sc_dst_set_transf, + .set_size = sc_dst_set_size, + .set_addr = sc_dst_set_addr, +}; + +#ifdef DEBUG +static void sc_print_reg(struct sc_context *ctx) +{ + struct resource *res = ctx->regs_res; + char buf[256]; + int i, pos = 0; + u32 cfg; + + pos += sprintf(buf+pos, "0x%.8x | ", res->start); + for (i = 1; i < SC_MAX_REG + 1; i++) { + cfg = sc_read((i-1) * sizeof(u32)); + pos += sprintf(buf+pos, "0x%.8x ", cfg); + if (i % 4 == 0) { + DRM_INFO("%s\n", buf); + pos = 0; + memset(buf, 0x0, 256); + pos += sprintf(buf+pos, "0x%.8x | ", + res->start + (i * sizeof(u32))); + } + } + + DRM_INFO("\n"); +} +#endif + +static int sc_clk_ctrl(struct sc_context *ctx, bool enable) +{ + int ret = 0; + + DRM_INFO("%s:enable[%d]\n", __func__, enable); + + if (enable) { + if (!IS_ERR(ctx->clk_chld) && !IS_ERR(ctx->clk_parn)) { + ret = clk_set_parent(ctx->clk_chld, ctx->clk_parn); + if (ret) + DRM_ERROR("set_parent:ret[%d]\n", ret); + } + + if (!IS_ERR(ctx->pclk)) { + ret = clk_prepare_enable(ctx->pclk); + if (ret) + DRM_ERROR("enable pclk:ret[%d]\n", ret); + } + + if (!IS_ERR(ctx->aclk)) { + ret = clk_prepare_enable(ctx->aclk); + if (ret) + DRM_ERROR("enable aclk:ret[%d]\n", ret); + } + } else { + if (!IS_ERR(ctx->aclk)) + clk_disable_unprepare(ctx->aclk); + if (!IS_ERR(ctx->pclk)) + clk_disable_unprepare(ctx->pclk); + } + + return ret; +} + +static irqreturn_t sc_irq_handler(int irq, void *dev_id) +{ + struct sc_context *ctx = dev_id; + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + struct tdm_pp_cmd_node *c_node = ppdrv->c_node; + struct tdm_pp_property *property = &c_node->property; + struct tdm_pp_event_work *event_work = + c_node->event_work; + u32 cfg; + int *buf_id = ctx->cur_buf_id; + + DRM_INFO("%s\n", __func__); + +#ifdef DEBUG + sc_print_reg(ctx); +#endif + + cfg = sc_read(SCALER_INT_STATUS); + cfg |= SCALER_INT_STATUS_FRAME_END; + sc_write(cfg, SCALER_INT_STATUS); + + if (c_node->state == PP_STATE_STOP) { + DRM_ERROR("invalid state:prop_id[%d]\n", property->prop_id); + return IRQ_HANDLED; + } + + DRM_INFO("%s:src buf_id[%d]dst buf_id[%d]\n", __func__, + buf_id[TDM_OPS_SRC], buf_id[TDM_OPS_DST]); + + event_work->ppdrv = ppdrv; + event_work->buf_id[TDM_OPS_SRC] = buf_id[TDM_OPS_SRC]; + event_work->buf_id[TDM_OPS_DST] = buf_id[TDM_OPS_DST]; + queue_work(ppdrv->event_workq, &event_work->work); + + return IRQ_HANDLED; +} + +static int sc_init_capability(struct tdm_ppdrv *ppdrv) +{ + return 0; +} + +static inline bool sc_check_drm_flip(enum tdm_flip flip) +{ + switch (flip) { + case TDM_FLIP_NONE: + case TDM_FLIP_VERTICAL: + case TDM_FLIP_HORIZONTAL: + case TDM_FLIP_BOTH: + return true; + default: + DRM_INFO("%s:invalid flip\n", __func__); + return false; + } +} + +static inline bool sc_check_fmt_limit(struct tdm_pp_property *property) +{ + struct tdm_pp_config *src_config = + &property->config[TDM_OPS_SRC]; + struct tdm_pp_config *dst_config = + &property->config[TDM_OPS_DST]; + struct tdm_pos src_pos = src_config->pos; + struct tdm_pos dst_pos = dst_config->pos; + unsigned int h_ratio, v_ratio, i; + + for_each_pp_ops(i) { + if ((property->config[i].fmt == TDM_DEGREE_90) || + (property->config[i].fmt == TDM_DEGREE_270)) + swap(src_pos.w, src_pos.h); + } + + h_ratio = SC_RATIO(src_pos.w, dst_pos.w); + v_ratio = SC_RATIO(src_pos.h, dst_pos.h); + + if ((h_ratio > SC_DOWN_MIN) || + (h_ratio < SC_UP_MAX)) { + DRM_INFO("%s:width scaling is out of range\n", __func__); + return false; + } + + if ((v_ratio > SC_DOWN_MIN) || + (v_ratio < SC_UP_MAX)) { + DRM_INFO("%s:height scaling is out of range\n", __func__); + return false; + } + + return true; +} + +static int sc_ppdrv_check_property(struct device *dev, + struct tdm_pp_property *property) +{ + return 0; +} + +static int sc_ppdrv_reset(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + struct sc_scaler *sc = &ctx->sc; + int ret; + + DRM_INFO("%s\n", __func__); + + /* reset h/w block */ + ret = sc_sw_reset(ctx); + if (ret < 0) { + dev_err(dev, "failed to reset hardware.\n"); + return ret; + } + + /* scaler setting */ + memset(&ctx->sc, 0x0, sizeof(ctx->sc)); + sc->range = true; + + return 0; +} + +static int sc_check_prepare(struct sc_context *ctx) +{ + DRM_INFO("%s\n", __func__); + + return 0; +} + +static int sc_ppdrv_start(struct device *dev, enum tdm_pp_cmd cmd) +{ + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + struct tdm_pp_cmd_node *c_node = ppdrv->c_node; + struct tdm_pp_property *property; + struct tdm_pp_config *config; + struct tdm_pos img_pos[TDM_OPS_MAX]; + u32 fmt[TDM_OPS_MAX]; + u32 cfg; + int ret, i; + + DRM_INFO("%s:cmd[%d]\n", __func__, cmd); + + if (!c_node) { + DRM_ERROR("failed to get c_node.\n"); + return -EINVAL; + } + + property = &c_node->property; + + ret = sc_check_prepare(ctx); + if (ret) { + dev_err(dev, "failed to check prepare.\n"); + return ret; + } + + sc_handle_irq(ctx, true); + + for_each_pp_ops(i) { + config = &property->config[i]; + img_pos[i] = config->pos; + fmt[i] = config->fmt; + } + + switch (cmd) { + case PP_CMD_M2M: + /* bypass */ + break; + default: + ret = -EINVAL; + dev_err(dev, "invalid operations.\n"); + return ret; + } + + /* set scaler */ + ret = sc_set_scaler(ctx, &ctx->sc, + &img_pos[TDM_OPS_SRC], + &img_pos[TDM_OPS_DST]); + if (ret) { + dev_err(dev, "failed to set precalser.\n"); + return ret; + } + + /* set coefficient */ + sc_set_csc_coef(ctx, &ctx->sc, fmt, img_pos[TDM_OPS_DST].w); + + cfg = sc_read(SCALER_CFG); + cfg |= SCALER_CFG_START_CMD; + sc_write(cfg, SCALER_CFG); + + return 0; +} + +static void sc_ppdrv_stop(struct device *dev, enum tdm_pp_cmd cmd) +{ + struct sc_context *ctx = get_sc_context(dev); + u32 cfg; + + DRM_INFO("%s:cmd[%d]\n", __func__, cmd); + + switch (cmd) { + case PP_CMD_M2M: + /* bypass */ + break; + default: + dev_err(dev, "invalid operations.\n"); + break; + } + + sc_handle_irq(ctx, false); + + cfg = sc_read(SCALER_CFG); + cfg &= ~SCALER_CFG_START_CMD; + sc_write(cfg, SCALER_CFG); +} + +static int sc_clk_get(struct sc_context *ctx, struct device *dev) +{ + ctx->aclk = devm_clk_get(dev, "gate"); + if (IS_ERR(ctx->aclk)) { + if (PTR_ERR(ctx->aclk) != -ENOENT) { + DRM_ERROR("failed to get gate clock: %ld\n", + PTR_ERR(ctx->aclk)); + return PTR_ERR(ctx->aclk); + } + DRM_INFO("%s:gate clock is not present\n", __func__); + } + + ctx->pclk = devm_clk_get(dev, "gate2"); + if (IS_ERR(ctx->pclk)) { + if (PTR_ERR(ctx->pclk) != -ENOENT) { + DRM_ERROR("failed to get gate2 clock: %ld\n", + PTR_ERR(ctx->pclk)); + clk_put(ctx->aclk); + return PTR_ERR(ctx->pclk); + } + DRM_INFO("%s:gate2 clock is not present\n", __func__); + } + + ctx->clk_chld = devm_clk_get(dev, "mux_user"); + if (IS_ERR(ctx->clk_chld)) { + if (PTR_ERR(ctx->clk_chld) != -ENOENT) { + DRM_ERROR("failed to get mux_user clock: %ld\n", + PTR_ERR(ctx->clk_chld)); + clk_put(ctx->pclk); + clk_put(ctx->aclk); + return PTR_ERR(ctx->clk_chld); + } + DRM_INFO("%s:mux_user clock is not present\n", __func__); + } + + if (!IS_ERR(ctx->clk_chld)) { + ctx->clk_parn = devm_clk_get(dev, "mux_src"); + if (IS_ERR(ctx->clk_parn)) { + DRM_ERROR("failed to get mux_src clock: %ld\n", + PTR_ERR(ctx->clk_parn)); + clk_put(ctx->clk_chld); + clk_put(ctx->pclk); + clk_put(ctx->aclk); + return PTR_ERR(ctx->clk_parn); + } + } else + ctx->clk_parn = ERR_PTR(-ENOENT); + + return 0; +} + +static int sc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sc_context *ctx; + struct resource *res; + struct tdm_ppdrv *ppdrv; + int ret = -EINVAL; + + DRM_INFO("%s\n", __func__); + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + /* resource memory */ + ctx->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ctx->regs_res) { + DRM_ERROR("failed to find registers.\n"); + ret = -ENOENT; + goto err_ctx; + } + + ctx->regs = devm_ioremap_resource(dev, ctx->regs_res); + if (!ctx->regs) { + DRM_ERROR("failed to map registers.\n"); + ret = -ENXIO; + goto err_ctx; + } + + /* resource irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + DRM_ERROR("failed to request irq resource.\n"); + goto err_get_regs; + } + + ctx->irq = res->start; + ret = request_threaded_irq(ctx->irq, NULL, sc_irq_handler, + IRQF_ONESHOT, "drm_sc", ctx); + if (ret < 0) { + DRM_ERROR("failed to request irq.\n"); + goto err_get_regs; + } + + ret = sc_clk_get(ctx, dev); + if (ret) { + DRM_ERROR("failed to clk get.\n"); + goto err_get_irq; + } + + /* ToDo: iommu enable */ + ppdrv = &ctx->ppdrv; + ppdrv->dev = dev; + ppdrv->ops[TDM_OPS_SRC] = &sc_src_ops; + ppdrv->ops[TDM_OPS_DST] = &sc_dst_ops; + ppdrv->check_property = sc_ppdrv_check_property; + ppdrv->reset = sc_ppdrv_reset; + ppdrv->start = sc_ppdrv_start; + ppdrv->stop = sc_ppdrv_stop; + ret = sc_init_capability(ppdrv); + if (ret < 0) { + DRM_ERROR("failed to init property list.\n"); + goto err_clk; + } + + DRM_INFO("%s:ppdrv[%p]\n", __func__, ppdrv); + + platform_set_drvdata(pdev, ctx); + pm_runtime_enable(dev); + + ret = iovmm_activate(dev); + if (ret < 0) { + DRM_ERROR("failed to activate vmm\n"); + goto err_vmm_activate; + } + + ret = tdm_ppdrv_register(ppdrv); + if (ret < 0) { + DRM_ERROR("failed to register drm sc device.\n"); + goto err_ppdrv_register; + } + + DRM_INFO("%s:drm sc registered successfully.\n", __func__); + + return 0; + +err_ppdrv_register: + iovmm_deactivate(dev); +err_vmm_activate: + pm_runtime_disable(dev); +err_clk: + if (ctx->clk_parn) + clk_put(ctx->clk_parn); + if (ctx->clk_chld) + clk_put(ctx->clk_chld); + if (ctx->pclk) + clk_put(ctx->pclk); + if (ctx->aclk) + clk_put(ctx->aclk); +err_get_irq: + free_irq(ctx->irq, ctx); +err_get_regs: + devm_iounmap(dev, ctx->regs); +err_ctx: + devm_kfree(dev, ctx); + return ret; +} + +static int sc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sc_context *ctx = get_sc_context(dev); + struct tdm_ppdrv *ppdrv = &ctx->ppdrv; + + tdm_ppdrv_unregister(ppdrv); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + free_irq(ctx->irq, ctx); + devm_iounmap(dev, ctx->regs); + + clk_put(ctx->clk_parn); + clk_put(ctx->clk_chld); + clk_put(ctx->pclk); + clk_put(ctx->aclk); + + devm_kfree(dev, ctx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sc_suspend(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_INFO("%s\n", __func__); + + if (pm_runtime_suspended(dev)) + return 0; + + return sc_clk_ctrl(ctx, false); +} + +static int sc_resume(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_INFO("%s\n", __func__); + + if (!pm_runtime_suspended(dev)) + return sc_clk_ctrl(ctx, true); + + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int sc_runtime_suspend(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_INFO("%s\n", __func__); + + return sc_clk_ctrl(ctx, false); +} + +static int sc_runtime_resume(struct device *dev) +{ + struct sc_context *ctx = get_sc_context(dev); + + DRM_INFO("%s\n", __func__); + + return sc_clk_ctrl(ctx, true); +} +#endif + +static const struct dev_pm_ops sc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sc_suspend, sc_resume) + SET_RUNTIME_PM_OPS(sc_runtime_suspend, sc_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id tdm_pp_msc_dt_match[] = { + { .compatible = "samsung,exynos5-scaler",}, + {} +}; +MODULE_DEVICE_TABLE(of, tdm_pp_msc_dt_match); +#endif + +struct platform_driver pp_msc_driver = { + .probe = sc_probe, + .remove = sc_remove, + .driver = { + .name = "tdm-pp-sc", + .owner = THIS_MODULE, + .pm = &sc_pm_ops, +#ifdef CONFIG_OF + .of_match_table = tdm_pp_msc_dt_match, +#endif + }, +}; + + diff --git a/drivers/gpu/drm/tgm/tdm_pp_msc.h b/drivers/gpu/drm/tgm/tdm_pp_msc.h new file mode 100644 index 00000000000..8470fd2116e --- /dev/null +++ b/drivers/gpu/drm/tgm/tdm_pp_msc.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Authors: + * Jinyoung Jeon + * Jonggab Park + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _TDM_PP_MSC_H_ +#define _TDM_PP_MSC_H_ + +/* ToDo */ + +#endif /* _TDM_PP_MSC_H_ */ + diff --git a/drivers/gpu/drm/tgm/tdm_pp_msc_regs.h b/drivers/gpu/drm/tgm/tdm_pp_msc_regs.h new file mode 100644 index 00000000000..f561c956f35 --- /dev/null +++ b/drivers/gpu/drm/tgm/tdm_pp_msc_regs.h @@ -0,0 +1,162 @@ +/* linux/drivers/gpu/drm/exynos/regs-sc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Register definition file for Samsung Scaler driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef REGS_SC_H_ +#define REGS_SC_H_ + +/* Status */ +#define SCALER_STATUS 0x00 + +/* Configuration */ +#define SCALER_CFG 0x04 +#define SCALER_CFG_FILL_EN (1 << 24) +#define SCALER_CFG_BL_DIV_ALPHA_EN (1 << 17) +#define SCALER_CFG_BLEND_EN (1 << 16) +#define SCALER_CFG_CSC_Y_OFFSET_SRC (1 << 10) +#define SCALER_CFG_CSC_Y_OFFSET_DST (1 << 9) +#define SCALER_CFG_SOFT_RST (1 << 1) +#define SCALER_CFG_START_CMD (1 << 0) + +/* Interrupt */ +#define SCALER_INT_EN 0x08 +#define SCALER_INT_EN_FRAME_END (1 << 0) +#define SCALER_INT_EN_ALL_v3 0x82ffffff + +#define SCALER_INT_STATUS 0x0c +#define SCALER_INT_STATUS_FRAME_END (1 << 0) + +#define SCALER_SRC_CFG 0x10 + +/* Source Image Configuration */ +#define SCALER_CFG_TILE_EN (1 << 10) +#define SCALER_CFG_VHALF_PHASE_EN (1 << 9) +#define SCALER_CFG_BIG_ENDIAN (1 << 8) +#define SCALER_CFG_SWAP_MASK (3 << 5) +#define SCALER_CFG_BYTE_SWAP (1 << 5) +#define SCALER_CFG_HWORD_SWAP (2 << 5) +#define SCALER_CFG_BYTE_HWORD_SWAP (3 << 5) +#define SCALER_CFG_FMT_MASK (0x1f << 0) +#define SCALER_CFG_FMT_YCBCR420_2P (0 << 0) +#define SCALER_CFG_FMT_YUYV (0xa << 0) +#define SCALER_CFG_FMT_UYVY (0xb << 0) +#define SCALER_CFG_FMT_YVYU (9 << 0) +#define SCALER_CFG_FMT_YCBCR422_2P (2 << 0) +#define SCALER_CFG_FMT_YCBCR444_2P (3 << 0) +#define SCALER_CFG_FMT_RGB565 (4 << 0) +#define SCALER_CFG_FMT_ARGB1555 (5 << 0) +#define SCALER_CFG_FMT_ARGB4444 (0xc << 0) +#define SCALER_CFG_FMT_ARGB8888 (6 << 0) +#define SCALER_CFG_FMT_RGBA8888 (0xe << 0) +#define SCALER_CFG_FMT_P_ARGB8888 (7 << 0) +#define SCALER_CFG_FMT_L8A8 (0xd << 0) +#define SCALER_CFG_FMT_L8 (0xf << 0) +#define SCALER_CFG_FMT_YCRCB420_2P (0x10 << 0) +#define SCALER_CFG_FMT_YCRCB422_2P (0x12 << 0) +#define SCALER_CFG_FMT_YCRCB444_2P (0x13 << 0) +#define SCALER_CFG_FMT_YCBCR420_3P (0x14 << 0) +#define SCALER_CFG_FMT_YCBCR422_3P (0x16 << 0) +#define SCALER_CFG_FMT_YCBCR444_3P (0x17 << 0) + +/* Source Y Base Address */ +#define SCALER_SRC_Y_BASE 0x14 +#define SCALER_SRC_CB_BASE 0x18 +#define SCALER_SRC_CR_BASE 0x294 +#define SCALER_SRC_SPAN 0x1c +#define SCALER_SRC_CSPAN_MASK (0xffff << 16) +#define SCALER_SRC_YSPAN_MASK (0xffff << 0) + +#define SCALER_SRC_Y_POS 0x20 +#define SCALER_SRC_YX(x) ((x) << 18) +#define SCALER_SRC_YY(x) ((x) << 2) + +#define SCALER_SRC_WH 0x24 +#define SCALER_SRC_W(x) ((x) << 16) +#define SCALER_SRC_H(x) ((x) << 0) + +#define SCALER_SRC_C_POS 0x28 + +#define SCALER_DST_CFG 0x30 +#define SCALER_DST_Y_BASE 0x34 +#define SCALER_DST_CB_BASE 0x38 +#define SCALER_DST_CR_BASE 0x298 +#define SCALER_DST_SPAN 0x3c +#define SCALER_DST_CSPAN_MASK (0xffff << 16) +#define SCALER_DST_YSPAN_MASK (0xffff << 0) + +#define SCALER_DST_WH 0x40 +#define SCALER_DST_W(x) ((x) << 16) +#define SCALER_DST_H(x) ((x) << 0) + +#define SCALER_DST_POS 0x44 +#define SCALER_DST_X(x) ((x) << 16) +#define SCALER_DST_Y(x) ((x) << 0) + +#define SCALER_H_RATIO 0x50 +#define SCALER_V_RATIO 0x54 +#define SCALER_RATIO_MASK (0x7ffff << 0) + +#define SCALER_ROT_CFG 0x58 +#define SCALER_ROT_MASK (3 << 0) +#define SCALER_FLIP_MASK (3 << 2) +#define SCALER_FLIP_X_EN (1 << 3) +#define SCALER_FLIP_Y_EN (1 << 2) +#define SCALER_ROT_90 (1 << 0) +#define SCALER_ROT_180 (2 << 0) +#define SCALER_ROT_270 (3 << 0) + +#define SCALER_LAT_CON 0x5c + +#define SCALER_YHCOEF 0x60 +#define SCALER_YVCOEF 0xf0 +#define SCALER_CHCOEF 0x140 +#define SCALER_CVCOEF 0x1d0 + +#define SCALER_CSC_COEF00 0x220 +#define SCALER_CSC_COEF10 0x224 +#define SCALER_CSC_COEF20 0x228 +#define SCALER_CSC_COEF01 0x22c +#define SCALER_CSC_COEF11 0x230 +#define SCALER_CSC_COEF21 0x234 +#define SCALER_CSC_COEF02 0x238 +#define SCALER_CSC_COEF12 0x23c +#define SCALER_CSC_COEF22 0x240 +#define SCALER_CSC_COEF_MASK (0xfff << 0) + +#define SCALER_DITH_CFG 0x250 +#define SCALER_DITH_R_MASK (3 << 6) +#define SCALER_DITH_G_MASK (3 << 3) +#define SCALER_DITH_B_MASK (3 << 0) +#define SCALER_DITH_R_SHIFT (6) +#define SCALER_DITH_G_SHIFT (3) +#define SCALER_DITH_B_SHIFT (0) + +#define SCALER_CRC_COLOR01 0x270 +#define SCALER_CRC_COLOR23 0x274 +#define SCALER_CYCLE_COUNT 0x278 + +#define SCALER_SRC_BLEND_COLOR 0x280 +#define SCALER_SRC_BLEND_ALPHA 0x284 +#define SCALER_DST_BLEND_COLOR 0x288 +#define SCALER_DST_BLEND_ALPHA 0x28c +#define SCALER_SEL_INV_MASK (1 << 31) +#define SCALER_SEL_MASK (2 << 29) +#define SCALER_OP_SEL_INV_MASK (1 << 28) +#define SCALER_OP_SEL_MASK (0xf << 24) +#define SCALER_SEL_INV_SHIFT (31) +#define SCALER_SEL_SHIFT (29) +#define SCALER_OP_SEL_INV_SHIFT (28) +#define SCALER_OP_SEL_SHIFT (24) + +#define SCALER_FILL_COLOR 0x290 + +#endif /* REGS_GSC_H_ */ + diff --git a/drivers/gpu/drm/tgm/tgm_drv.c b/drivers/gpu/drm/tgm/tgm_drv.c index ab9de261339..196674baaa1 100644 --- a/drivers/gpu/drm/tgm/tgm_drv.c +++ b/drivers/gpu/drm/tgm/tgm_drv.c @@ -604,6 +604,11 @@ static int __init tgm_drv_init(void) DRM_DEBUG("%s\n", __func__); +#ifdef CONFIG_DRM_TDM_PP_MSC + ret = platform_driver_register(&pp_msc_driver); + if (ret < 0) + return ret; +#endif #ifdef CONFIG_DRM_TDM_PP ret = platform_driver_register(&pp_driver); if (ret < 0) @@ -620,6 +625,9 @@ out_tgm_drv: #ifdef CONFIG_DRM_TDM_PP platform_driver_unregister(&pp_driver); out_pp_driver: +#endif +#ifdef CONFIG_DRM_TDM_PP_MSC + platform_driver_unregister(&pp_msc_driver); #endif return ret; } @@ -633,6 +641,10 @@ static void __exit tgm_drv_exit(void) #ifdef CONFIG_DRM_TDM_PP platform_driver_unregister(&pp_driver); #endif + +#ifdef CONFIG_DRM_TDM_PP_MSC + platform_driver_unregister(&pp_msc_driver); +#endif } diff --git a/drivers/gpu/drm/tgm/tgm_drv.h b/drivers/gpu/drm/tgm/tgm_drv.h index 03beed2a0ef..ae7e68e2441 100644 --- a/drivers/gpu/drm/tgm/tgm_drv.h +++ b/drivers/gpu/drm/tgm/tgm_drv.h @@ -60,4 +60,5 @@ int tgm_subdrv_open(struct drm_device *dev, struct drm_file *file); void tgm_subdrv_close(struct drm_device *dev, struct drm_file *file); extern struct platform_driver pp_driver; +extern struct platform_driver pp_msc_driver; #endif /* _TGM_DRV_H_ */ -- 2.34.1