summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/exynos
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /drivers/gpu/drm/exynos
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/gpu/drm/exynos')
-rw-r--r--drivers/gpu/drm/exynos/Kconfig22
-rw-r--r--drivers/gpu/drm/exynos/Makefile2
-rw-r--r--drivers/gpu/drm/exynos/exynos5433_drm_decon.c660
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c173
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c21
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c201
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c26
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c429
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h49
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c539
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c35
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c39
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c323
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gsc.c6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_iommu.c21
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_iommu.h11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c81
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_mic.c490
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c137
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.h11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c125
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c39
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c139
26 files changed, 2193 insertions, 1392 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 0a6780367..43003c4ad 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -24,16 +24,22 @@ config DRM_EXYNOS_FIMD
help
Choose this option if you want to use Exynos FIMD for DRM.
-config DRM_EXYNOS7_DECON
- bool "Exynos DRM DECON"
+config DRM_EXYNOS5433_DECON
+ bool "Exynos5433 DRM DECON"
depends on DRM_EXYNOS
+ help
+ Choose this option if you want to use Exynos5433 DECON for DRM.
+
+config DRM_EXYNOS7_DECON
+ bool "Exynos7 DRM DECON"
+ depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
help
Choose this option if you want to use Exynos DECON for DRM.
config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
+ depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
select DRM_PANEL
default n
help
@@ -41,7 +47,7 @@ config DRM_EXYNOS_DPI
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
+ depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON)
select DRM_MIPI_DSI
select DRM_PANEL
default n
@@ -50,7 +56,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
+ depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
select DRM_PANEL
help
@@ -97,3 +103,9 @@ config DRM_EXYNOS_GSC
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM
help
Choose this option if you want to use Exynos GSC for DRM.
+
+config DRM_EXYNOS_MIC
+ bool "Exynos DRM MIC"
+ depends on (DRM_EXYNOS && DRM_EXYNOS5433_DECON)
+ help
+ Choose this option if you want to use Exynos MIC for DRM.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index cc90679cf..7de0b1084 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -10,6 +10,7 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
+exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON) += exynos5433_drm_decon.o
exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON) += exynos7_drm_decon.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o
@@ -21,5 +22,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o
exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
new file mode 100644
index 000000000..8b1225f24
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
@@ -0,0 +1,660 @@
+/* drivers/gpu/drm/exynos5433_drm_decon.c
+ *
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors:
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ * Hyungwon Hwang <human.hwang@samsung.com>
+ *
+ * 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 Foundationr
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+
+#include <video/exynos5433_decon.h>
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_plane.h"
+#include "exynos_drm_iommu.h"
+
+#define WINDOWS_NR 3
+#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
+
+struct decon_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct exynos_drm_crtc *crtc;
+ struct exynos_drm_plane planes[WINDOWS_NR];
+ void __iomem *addr;
+ struct clk *clks[6];
+ unsigned int default_win;
+ unsigned long irq_flags;
+ int pipe;
+ bool suspended;
+
+#define BIT_CLKS_ENABLED 0
+#define BIT_IRQS_ENABLED 1
+ unsigned long enabled;
+ bool i80_if;
+ atomic_t win_updated;
+};
+
+static const char * const decon_clks_name[] = {
+ "aclk_decon",
+ "aclk_smmu_decon0x",
+ "aclk_xiu_decon0x",
+ "pclk_smmu_decon0x",
+ "sclk_decon_vclk",
+ "sclk_decon_eclk",
+};
+
+static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ u32 val;
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (test_and_set_bit(0, &ctx->irq_flags)) {
+ val = VIDINTCON0_INTEN;
+ if (ctx->i80_if)
+ val |= VIDINTCON0_FRAMEDONE;
+ else
+ val |= VIDINTCON0_INTFRMEN;
+
+ writel(val, ctx->addr + DECON_VIDINTCON0);
+ }
+
+ return 0;
+}
+
+static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ writel(0, ctx->addr + DECON_VIDINTCON0);
+}
+
+static void decon_setup_trigger(struct decon_context *ctx)
+{
+ u32 val = TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
+ TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN;
+ writel(val, ctx->addr + DECON_TRIGCON);
+}
+
+static void decon_commit(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ struct drm_display_mode *mode = &crtc->base.mode;
+ u32 val;
+
+ if (ctx->suspended)
+ return;
+
+ /* enable clock gate */
+ val = CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F;
+ writel(val, ctx->addr + DECON_CMU);
+
+ /* lcd on and use command if */
+ val = VIDOUT_LCD_ON;
+ if (ctx->i80_if)
+ val |= VIDOUT_COMMAND_IF;
+ else
+ val |= VIDOUT_RGB_IF;
+ writel(val, ctx->addr + DECON_VIDOUTCON0);
+
+ val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
+ VIDTCON2_HOZVAL(mode->hdisplay - 1);
+ writel(val, ctx->addr + DECON_VIDTCON2);
+
+ if (!ctx->i80_if) {
+ val = VIDTCON00_VBPD_F(
+ mode->crtc_vtotal - mode->crtc_vsync_end) |
+ VIDTCON00_VFPD_F(
+ mode->crtc_vsync_start - mode->crtc_vdisplay);
+ writel(val, ctx->addr + DECON_VIDTCON00);
+
+ val = VIDTCON01_VSPW_F(
+ mode->crtc_vsync_end - mode->crtc_vsync_start);
+ writel(val, ctx->addr + DECON_VIDTCON01);
+
+ val = VIDTCON10_HBPD_F(
+ mode->crtc_htotal - mode->crtc_hsync_end) |
+ VIDTCON10_HFPD_F(
+ mode->crtc_hsync_start - mode->crtc_hdisplay);
+ writel(val, ctx->addr + DECON_VIDTCON10);
+
+ val = VIDTCON11_HSPW_F(
+ mode->crtc_hsync_end - mode->crtc_hsync_start);
+ writel(val, ctx->addr + DECON_VIDTCON11);
+ }
+
+ decon_setup_trigger(ctx);
+
+ /* enable output and display signal */
+ val = VIDCON0_ENVID | VIDCON0_ENVID_F;
+ writel(val, ctx->addr + DECON_VIDCON0);
+}
+
+#define COORDINATE_X(x) (((x) & 0xfff) << 12)
+#define COORDINATE_Y(x) ((x) & 0xfff)
+#define OFFSIZE(x) (((x) & 0x3fff) << 14)
+#define PAGEWIDTH(x) ((x) & 0x3fff)
+
+static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
+{
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+ unsigned long val;
+
+ val = readl(ctx->addr + DECON_WINCONx(win));
+ val &= ~WINCONx_BPPMODE_MASK;
+
+ switch (plane->pixel_format) {
+ case DRM_FORMAT_XRGB1555:
+ val |= WINCONx_BPPMODE_16BPP_I1555;
+ val |= WINCONx_HAWSWP_F;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case DRM_FORMAT_RGB565:
+ val |= WINCONx_BPPMODE_16BPP_565;
+ val |= WINCONx_HAWSWP_F;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ val |= WINCONx_BPPMODE_24BPP_888;
+ val |= WINCONx_WSWP_F;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ val |= WINCONx_BPPMODE_32BPP_A8888;
+ val |= WINCONx_WSWP_F | WINCONx_BLD_PIX_F | WINCONx_ALPHA_SEL_F;
+ val |= WINCONx_BURSTLEN_16WORD;
+ break;
+ default:
+ DRM_ERROR("Proper pixel format is not set\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("bpp = %u\n", plane->bpp);
+
+ /*
+ * In case of exynos, setting dma-burst to 16Word causes permanent
+ * tearing for very small buffers, e.g. cursor buffer. Burst Mode
+ * switching which is based on plane size is not recommended as
+ * plane size varies a lot towards the end of the screen and rapid
+ * movement causes unstable DMA which results into iommu crash/tear.
+ */
+
+ if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+ val &= ~WINCONx_BURSTLEN_MASK;
+ val |= WINCONx_BURSTLEN_8WORD;
+ }
+
+ writel(val, ctx->addr + DECON_WINCONx(win));
+}
+
+static void decon_shadow_protect_win(struct decon_context *ctx, int win,
+ bool protect)
+{
+ u32 val;
+
+ val = readl(ctx->addr + DECON_SHADOWCON);
+
+ if (protect)
+ val |= SHADOWCON_Wx_PROTECT(win);
+ else
+ val &= ~SHADOWCON_Wx_PROTECT(win);
+
+ writel(val, ctx->addr + DECON_SHADOWCON);
+}
+
+static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+{
+ struct decon_context *ctx = crtc->ctx;
+ struct exynos_drm_plane *plane;
+ u32 val;
+
+ if (win < 0 || win >= WINDOWS_NR)
+ return;
+
+ plane = &ctx->planes[win];
+
+ if (ctx->suspended)
+ return;
+
+ decon_shadow_protect_win(ctx, win, true);
+
+ val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y);
+ writel(val, ctx->addr + DECON_VIDOSDxA(win));
+
+ val = COORDINATE_X(plane->crtc_x + plane->crtc_width - 1) |
+ COORDINATE_Y(plane->crtc_y + plane->crtc_height - 1);
+ writel(val, ctx->addr + DECON_VIDOSDxB(win));
+
+ val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
+ VIDOSD_Wx_ALPHA_B_F(0x0);
+ writel(val, ctx->addr + DECON_VIDOSDxC(win));
+
+ val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
+ VIDOSD_Wx_ALPHA_B_F(0x0);
+ writel(val, ctx->addr + DECON_VIDOSDxD(win));
+
+ writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win));
+
+ val = plane->dma_addr[0] + plane->pitch * plane->crtc_height;
+ writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
+
+ val = OFFSIZE(plane->pitch - plane->crtc_width * (plane->bpp >> 3))
+ | PAGEWIDTH(plane->crtc_width * (plane->bpp >> 3));
+ writel(val, ctx->addr + DECON_VIDW0xADD2(win));
+
+ decon_win_set_pixfmt(ctx, win);
+
+ /* window enable */
+ val = readl(ctx->addr + DECON_WINCONx(win));
+ val |= WINCONx_ENWIN_F;
+ writel(val, ctx->addr + DECON_WINCONx(win));
+
+ decon_shadow_protect_win(ctx, win, false);
+
+ /* standalone update */
+ val = readl(ctx->addr + DECON_UPDATE);
+ val |= STANDALONE_UPDATE_F;
+ writel(val, ctx->addr + DECON_UPDATE);
+
+ if (ctx->i80_if)
+ atomic_set(&ctx->win_updated, 1);
+}
+
+static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
+{
+ struct decon_context *ctx = crtc->ctx;
+ struct exynos_drm_plane *plane;
+ u32 val;
+
+ if (win < 0 || win >= WINDOWS_NR)
+ return;
+
+ plane = &ctx->planes[win];
+
+ if (ctx->suspended)
+ return;
+
+ decon_shadow_protect_win(ctx, win, true);
+
+ /* window disable */
+ val = readl(ctx->addr + DECON_WINCONx(win));
+ val &= ~WINCONx_ENWIN_F;
+ writel(val, ctx->addr + DECON_WINCONx(win));
+
+ decon_shadow_protect_win(ctx, win, false);
+
+ /* standalone update */
+ val = readl(ctx->addr + DECON_UPDATE);
+ val |= STANDALONE_UPDATE_F;
+ writel(val, ctx->addr + DECON_UPDATE);
+}
+
+static void decon_swreset(struct decon_context *ctx)
+{
+ unsigned int tries;
+
+ writel(0, ctx->addr + DECON_VIDCON0);
+ for (tries = 2000; tries; --tries) {
+ if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_STOP_STATUS)
+ break;
+ udelay(10);
+ }
+
+ WARN(tries == 0, "failed to disable DECON\n");
+
+ writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0);
+ for (tries = 2000; tries; --tries) {
+ if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_SWRESET)
+ break;
+ udelay(10);
+ }
+
+ WARN(tries == 0, "failed to software reset DECON\n");
+}
+
+static void decon_enable(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int ret;
+ int i;
+
+ if (!ctx->suspended)
+ return;
+
+ ctx->suspended = false;
+
+ pm_runtime_get_sync(ctx->dev);
+
+ for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
+ ret = clk_prepare_enable(ctx->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ set_bit(BIT_CLKS_ENABLED, &ctx->enabled);
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ decon_enable_vblank(ctx->crtc);
+
+ decon_commit(ctx->crtc);
+
+ return;
+err:
+ while (--i >= 0)
+ clk_disable_unprepare(ctx->clks[i]);
+
+ ctx->suspended = true;
+}
+
+static void decon_disable(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int i;
+
+ if (ctx->suspended)
+ return;
+
+ /*
+ * We need to make sure that all windows are disabled before we
+ * suspend that connector. Otherwise we might try to scan from
+ * a destroyed buffer later.
+ */
+ for (i = 0; i < WINDOWS_NR; i++)
+ decon_win_disable(crtc, i);
+
+ decon_swreset(ctx);
+
+ for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
+ clk_disable_unprepare(ctx->clks[i]);
+
+ clear_bit(BIT_CLKS_ENABLED, &ctx->enabled);
+
+ pm_runtime_put_sync(ctx->dev);
+
+ ctx->suspended = true;
+}
+
+void decon_te_irq_handler(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ u32 val;
+
+ if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
+ return;
+
+ if (atomic_add_unless(&ctx->win_updated, -1, 0)) {
+ /* trigger */
+ val = readl(ctx->addr + DECON_TRIGCON);
+ val |= TRIGCON_SWTRIGCMD;
+ writel(val, ctx->addr + DECON_TRIGCON);
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+}
+
+static void decon_clear_channels(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int win, i, ret;
+ u32 val;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
+ ret = clk_prepare_enable(ctx->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ for (win = 0; win < WINDOWS_NR; win++) {
+ /* shadow update disable */
+ val = readl(ctx->addr + DECON_SHADOWCON);
+ val |= SHADOWCON_Wx_PROTECT(win);
+ writel(val, ctx->addr + DECON_SHADOWCON);
+
+ /* window disable */
+ val = readl(ctx->addr + DECON_WINCONx(win));
+ val &= ~WINCONx_ENWIN_F;
+ writel(val, ctx->addr + DECON_WINCONx(win));
+
+ /* shadow update enable */
+ val = readl(ctx->addr + DECON_SHADOWCON);
+ val &= ~SHADOWCON_Wx_PROTECT(win);
+ writel(val, ctx->addr + DECON_SHADOWCON);
+
+ /* standalone update */
+ val = readl(ctx->addr + DECON_UPDATE);
+ val |= STANDALONE_UPDATE_F;
+ writel(val, ctx->addr + DECON_UPDATE);
+ }
+ /* TODO: wait for possible vsync */
+ msleep(50);
+
+err:
+ while (--i >= 0)
+ clk_disable_unprepare(ctx->clks[i]);
+}
+
+static struct exynos_drm_crtc_ops decon_crtc_ops = {
+ .enable = decon_enable,
+ .disable = decon_disable,
+ .commit = decon_commit,
+ .enable_vblank = decon_enable_vblank,
+ .disable_vblank = decon_disable_vblank,
+ .commit = decon_commit,
+ .win_commit = decon_win_commit,
+ .win_disable = decon_win_disable,
+ .te_handler = decon_te_irq_handler,
+ .clear_channels = decon_clear_channels,
+};
+
+static int decon_bind(struct device *dev, struct device *master, void *data)
+{
+ struct decon_context *ctx = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ struct exynos_drm_private *priv = drm_dev->dev_private;
+ struct exynos_drm_plane *exynos_plane;
+ enum drm_plane_type type;
+ unsigned int zpos;
+ int ret;
+
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = priv->pipe++;
+
+ for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
+ type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
+ DRM_PLANE_TYPE_OVERLAY;
+ ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
+ 1 << ctx->pipe, type, zpos);
+ if (ret)
+ return ret;
+ }
+
+ exynos_plane = &ctx->planes[ctx->default_win];
+ ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
+ ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
+ &decon_crtc_ops, ctx);
+ if (IS_ERR(ctx->crtc)) {
+ ret = PTR_ERR(ctx->crtc);
+ goto err;
+ }
+
+ ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ priv->pipe--;
+ return ret;
+}
+
+static void decon_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct decon_context *ctx = dev_get_drvdata(dev);
+
+ decon_disable(ctx->crtc);
+
+ /* detach this sub driver from iommu mapping if supported. */
+ if (is_drm_iommu_supported(ctx->drm_dev))
+ drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
+}
+
+static const struct component_ops decon_component_ops = {
+ .bind = decon_bind,
+ .unbind = decon_unbind,
+};
+
+static irqreturn_t decon_vsync_irq_handler(int irq, void *dev_id)
+{
+ struct decon_context *ctx = dev_id;
+ u32 val;
+
+ if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
+ goto out;
+
+ val = readl(ctx->addr + DECON_VIDINTCON1);
+ if (val & VIDINTCON1_INTFRMPEND) {
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+
+ /* clear */
+ writel(VIDINTCON1_INTFRMPEND, ctx->addr + DECON_VIDINTCON1);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id)
+{
+ struct decon_context *ctx = dev_id;
+ u32 val;
+
+ if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
+ goto out;
+
+ val = readl(ctx->addr + DECON_VIDINTCON1);
+ if (val & VIDINTCON1_INTFRMDONEPEND) {
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+
+ /* clear */
+ writel(VIDINTCON1_INTFRMDONEPEND,
+ ctx->addr + DECON_VIDINTCON1);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int exynos5433_decon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct decon_context *ctx;
+ struct resource *res;
+ int ret;
+ int i;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->default_win = 0;
+ ctx->suspended = true;
+ ctx->dev = dev;
+ if (of_get_child_by_name(dev->of_node, "i80-if-timings"))
+ ctx->i80_if = true;
+
+ for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
+ struct clk *clk;
+
+ clk = devm_clk_get(ctx->dev, decon_clks_name[i]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ctx->clks[i] = clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "cannot find IO resource\n");
+ return -ENXIO;
+ }
+
+ ctx->addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->addr)) {
+ dev_err(dev, "ioremap failed\n");
+ return PTR_ERR(ctx->addr);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ ctx->i80_if ? "lcd_sys" : "vsync");
+ if (!res) {
+ dev_err(dev, "cannot find IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = devm_request_irq(dev, res->start, ctx->i80_if ?
+ decon_lcd_sys_irq_handler : decon_vsync_irq_handler, 0,
+ "drm_decon", ctx);
+ if (ret < 0) {
+ dev_err(dev, "lcd_sys irq request failed\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ctx);
+
+ pm_runtime_enable(dev);
+
+ ret = component_add(dev, &decon_component_ops);
+ if (ret)
+ goto err_disable_pm_runtime;
+
+ return 0;
+
+err_disable_pm_runtime:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int exynos5433_decon_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ component_del(&pdev->dev, &decon_component_ops);
+
+ return 0;
+}
+
+static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
+ { .compatible = "samsung,exynos5433-decon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
+
+struct platform_driver exynos5433_decon_driver = {
+ .probe = exynos5433_decon_probe,
+ .remove = exynos5433_decon_remove,
+ .driver = {
+ .name = "exynos5433-decon",
+ .of_match_table = exynos5433_decon_driver_dt_match,
+ },
+};
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 6714e5b19..362532afd 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -89,8 +89,9 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
-static void decon_clear_channel(struct decon_context *ctx)
+static void decon_clear_channels(struct exynos_drm_crtc *crtc)
{
+ struct decon_context *ctx = crtc->ctx;
unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -120,27 +121,16 @@ static int decon_ctx_initialize(struct decon_context *ctx,
struct drm_device *drm_dev)
{
struct exynos_drm_private *priv = drm_dev->dev_private;
+ int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
- /* attach this sub driver to iommu mapping if supported. */
- if (is_drm_iommu_supported(ctx->drm_dev)) {
- int ret;
-
- /*
- * If any channel is already active, iommu will throw
- * a PAGE FAULT when enabled. So clear any channel if enabled.
- */
- decon_clear_channel(ctx);
- ret = drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
- if (ret) {
- DRM_ERROR("drm_iommu_attach failed.\n");
- return ret;
- }
- }
+ ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, ctx->dev);
+ if (ret)
+ priv->pipe--;
- return 0;
+ return ret;
}
static void decon_ctx_remove(struct decon_context *ctx)
@@ -175,7 +165,7 @@ static bool decon_mode_fixup(struct exynos_drm_crtc *crtc,
static void decon_commit(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
- struct drm_display_mode *mode = &crtc->base.mode;
+ struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
u32 val, clkdiv;
if (ctx->suspended)
@@ -395,7 +385,7 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
{
struct decon_context *ctx = crtc->ctx;
- struct drm_display_mode *mode = &crtc->base.mode;
+ struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
struct exynos_drm_plane *plane;
int padding;
unsigned long val, alpha;
@@ -410,11 +400,8 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
- /* If suspended, enable this on resume */
- if (ctx->suspended) {
- plane->resume = true;
+ if (ctx->suspended)
return;
- }
/*
* SHADOWCON/PRTCON register is used for enabling timing.
@@ -506,8 +493,6 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
val = readl(ctx->regs + DECON_UPDATE);
val |= DECON_UPDATE_STANDALONE_F;
writel(val, ctx->regs + DECON_UPDATE);
-
- plane->enabled = true;
}
static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
@@ -521,11 +506,8 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
- if (ctx->suspended) {
- /* do not resume this window*/
- plane->resume = false;
+ if (ctx->suspended)
return;
- }
/* protect windows */
decon_shadow_protect_win(ctx, win, true);
@@ -541,49 +523,6 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
val = readl(ctx->regs + DECON_UPDATE);
val |= DECON_UPDATE_STANDALONE_F;
writel(val, ctx->regs + DECON_UPDATE);
-
- plane->enabled = false;
-}
-
-static void decon_window_suspend(struct decon_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- plane->resume = plane->enabled;
- if (plane->enabled)
- decon_win_disable(ctx->crtc, i);
- }
-}
-
-static void decon_window_resume(struct decon_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- plane->enabled = plane->resume;
- plane->resume = false;
- }
-}
-
-static void decon_apply(struct decon_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- if (plane->enabled)
- decon_win_commit(ctx->crtc, i);
- else
- decon_win_disable(ctx->crtc, i);
- }
-
- decon_commit(ctx->crtc);
}
static void decon_init(struct decon_context *ctx)
@@ -603,12 +542,13 @@ static void decon_init(struct decon_context *ctx)
writel(VIDCON1_VCLK_HOLD, ctx->regs + VIDCON1(0));
}
-static int decon_poweron(struct decon_context *ctx)
+static void decon_enable(struct exynos_drm_crtc *crtc)
{
+ struct decon_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
- return 0;
+ return;
ctx->suspended = false;
@@ -617,68 +557,51 @@ static int decon_poweron(struct decon_context *ctx)
ret = clk_prepare_enable(ctx->pclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
- goto pclk_err;
+ return;
}
ret = clk_prepare_enable(ctx->aclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
- goto aclk_err;
+ return;
}
ret = clk_prepare_enable(ctx->eclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
- goto eclk_err;
+ return;
}
ret = clk_prepare_enable(ctx->vclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
- goto vclk_err;
+ return;
}
decon_init(ctx);
/* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags)) {
- ret = decon_enable_vblank(ctx->crtc);
- if (ret) {
- DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
- goto err;
- }
- }
-
- decon_window_resume(ctx);
-
- decon_apply(ctx);
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ decon_enable_vblank(ctx->crtc);
- return 0;
-
-err:
- clk_disable_unprepare(ctx->vclk);
-vclk_err:
- clk_disable_unprepare(ctx->eclk);
-eclk_err:
- clk_disable_unprepare(ctx->aclk);
-aclk_err:
- clk_disable_unprepare(ctx->pclk);
-pclk_err:
- ctx->suspended = true;
- return ret;
+ decon_commit(ctx->crtc);
}
-static int decon_poweroff(struct decon_context *ctx)
+static void decon_disable(struct exynos_drm_crtc *crtc)
{
+ struct decon_context *ctx = crtc->ctx;
+ int i;
+
if (ctx->suspended)
- return 0;
+ return;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
- decon_window_suspend(ctx);
+ for (i = 0; i < WINDOWS_NR; i++)
+ decon_win_disable(crtc, i);
clk_disable_unprepare(ctx->vclk);
clk_disable_unprepare(ctx->eclk);
@@ -688,30 +611,11 @@ static int decon_poweroff(struct decon_context *ctx)
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
- return 0;
-}
-
-static void decon_dpms(struct exynos_drm_crtc *crtc, int mode)
-{
- DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- decon_poweron(crtc->ctx);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- decon_poweroff(crtc->ctx);
- break;
- default:
- DRM_DEBUG_KMS("unspecified mode %d\n", mode);
- break;
- }
}
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
- .dpms = decon_dpms,
+ .enable = decon_enable,
+ .disable = decon_disable,
.mode_fixup = decon_mode_fixup,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
@@ -719,6 +623,7 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.wait_for_vblank = decon_wait_for_vblank,
.win_commit = decon_win_commit,
.win_disable = decon_win_disable,
+ .clear_channels = decon_clear_channels,
};
@@ -796,7 +701,7 @@ static void decon_unbind(struct device *dev, struct device *master,
{
struct decon_context *ctx = dev_get_drvdata(dev);
- decon_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
+ decon_disable(ctx->crtc);
if (ctx->display)
exynos_dpi_remove(ctx->display);
@@ -824,11 +729,6 @@ static int decon_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
- ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
- EXYNOS_DISPLAY_TYPE_LCD);
- if (ret)
- return ret;
-
ctx->dev = dev;
ctx->suspended = true;
@@ -838,10 +738,8 @@ static int decon_probe(struct platform_device *pdev)
of_node_put(i80_if_timings);
ctx->regs = of_iomap(dev->of_node, 0);
- if (!ctx->regs) {
- ret = -ENOMEM;
- goto err_del_component;
- }
+ if (!ctx->regs)
+ return -ENOMEM;
ctx->pclk = devm_clk_get(dev, "pclk_decon0");
if (IS_ERR(ctx->pclk)) {
@@ -911,8 +809,6 @@ err_disable_pm_runtime:
err_iounmap:
iounmap(ctx->regs);
-err_del_component:
- exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
@@ -925,7 +821,6 @@ static int decon_remove(struct platform_device *pdev)
iounmap(ctx->regs);
component_del(&pdev->dev, &decon_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 30feb7d06..172b8002a 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -28,8 +28,8 @@
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
-#include <drm/bridge/ptn3460.h>
#include "exynos_dp_core.h"
@@ -953,10 +953,13 @@ static void exynos_dp_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dp_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
+ .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = exynos_dp_detect,
.destroy = exynos_dp_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dp_get_modes(struct drm_connector *connector)
@@ -1329,7 +1332,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *panel_node, *bridge_node, *endpoint;
struct exynos_dp_device *dp;
- int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
@@ -1340,11 +1342,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
dp->display.ops = &exynos_dp_display_ops;
platform_set_drvdata(pdev, dp);
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- dp->display.type);
- if (ret)
- return ret;
-
panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) {
dp->panel = of_drm_find_panel(panel_node);
@@ -1365,18 +1362,12 @@ static int exynos_dp_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- ret = component_add(&pdev->dev, &exynos_dp_ops);
- if (ret)
- exynos_drm_component_del(&pdev->dev,
- EXYNOS_DEVICE_TYPE_CONNECTOR);
-
- return ret;
+ return component_add(&pdev->dev, &exynos_dp_ops);
}
static int exynos_dp_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_dp_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 9006b947e..644b4b76e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -14,57 +14,47 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_plane.h"
-static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
-
- if (exynos_crtc->dpms == mode) {
- DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ if (exynos_crtc->enabled)
return;
- }
-
- if (mode > DRM_MODE_DPMS_ON) {
- /* wait for the completion of page flip. */
- if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
- (exynos_crtc->event == NULL), HZ/20))
- exynos_crtc->event = NULL;
- drm_crtc_vblank_off(crtc);
- }
-
- if (exynos_crtc->ops->dpms)
- exynos_crtc->ops->dpms(exynos_crtc, mode);
- exynos_crtc->dpms = mode;
+ if (exynos_crtc->ops->enable)
+ exynos_crtc->ops->enable(exynos_crtc);
- if (mode == DRM_MODE_DPMS_ON)
- drm_crtc_vblank_on(crtc);
-}
+ exynos_crtc->enabled = true;
-static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
-{
- /* drm framework doesn't check NULL. */
+ drm_crtc_vblank_on(crtc);
}
-static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
+static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct exynos_drm_plane *exynos_plane = to_exynos_plane(crtc->primary);
- exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+ if (!exynos_crtc->enabled)
+ return;
- if (exynos_crtc->ops->win_commit)
- exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
+ /* wait for the completion of page flip. */
+ if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
+ (exynos_crtc->event == NULL), HZ/20))
+ exynos_crtc->event = NULL;
- if (exynos_crtc->ops->commit)
- exynos_crtc->ops->commit(exynos_crtc);
+ drm_crtc_vblank_off(crtc);
+
+ if (exynos_crtc->ops->disable)
+ exynos_crtc->ops->disable(exynos_crtc);
+
+ exynos_crtc->enabled = false;
}
static bool
@@ -81,145 +71,38 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
-static int
-exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode, int x, int y,
- struct drm_framebuffer *old_fb)
+static void
+exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
- struct drm_framebuffer *fb = crtc->primary->fb;
- unsigned int crtc_w;
- unsigned int crtc_h;
- int ret;
-
- /*
- * copy the mode data adjusted by mode_fixup() into crtc->mode
- * so that hardware can be seet to proper mode.
- */
- memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
-
- ret = exynos_check_plane(crtc->primary, fb);
- if (ret < 0)
- return ret;
-
- crtc_w = fb->width - x;
- crtc_h = fb->height - y;
- exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
- crtc_w, crtc_h, x, y, crtc_w, crtc_h);
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- return 0;
+ if (exynos_crtc->ops->commit)
+ exynos_crtc->ops->commit(exynos_crtc);
}
-static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
+static void exynos_crtc_atomic_begin(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct drm_framebuffer *fb = crtc->primary->fb;
- unsigned int crtc_w;
- unsigned int crtc_h;
- /* when framebuffer changing is requested, crtc's dpms should be on */
- if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
- DRM_ERROR("failed framebuffer changing request.\n");
- return -EPERM;
+ if (crtc->state->event) {
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+ exynos_crtc->event = crtc->state->event;
}
-
- crtc_w = fb->width - x;
- crtc_h = fb->height - y;
-
- return exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
- crtc_w, crtc_h, x, y, crtc_w, crtc_h);
}
-static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
+static void exynos_crtc_atomic_flush(struct drm_crtc *crtc)
{
- struct drm_plane *plane;
- int ret;
-
- exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-
- drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
- if (plane->crtc != crtc)
- continue;
-
- ret = plane->funcs->disable_plane(plane);
- if (ret)
- DRM_ERROR("Failed to disable plane %d\n", ret);
- }
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
- .dpms = exynos_drm_crtc_dpms,
- .prepare = exynos_drm_crtc_prepare,
- .commit = exynos_drm_crtc_commit,
- .mode_fixup = exynos_drm_crtc_mode_fixup,
- .mode_set = exynos_drm_crtc_mode_set,
- .mode_set_base = exynos_drm_crtc_mode_set_base,
+ .enable = exynos_drm_crtc_enable,
.disable = exynos_drm_crtc_disable,
+ .mode_fixup = exynos_drm_crtc_mode_fixup,
+ .mode_set_nofb = exynos_drm_crtc_mode_set_nofb,
+ .atomic_begin = exynos_crtc_atomic_begin,
+ .atomic_flush = exynos_crtc_atomic_flush,
};
-static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- struct drm_pending_vblank_event *event,
- uint32_t page_flip_flags)
-{
- struct drm_device *dev = crtc->dev;
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct drm_framebuffer *old_fb = crtc->primary->fb;
- unsigned int crtc_w, crtc_h;
- int ret;
-
- /* when the page flip is requested, crtc's dpms should be on */
- if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
- DRM_ERROR("failed page flip request.\n");
- return -EINVAL;
- }
-
- if (!event)
- return -EINVAL;
-
- spin_lock_irq(&dev->event_lock);
- if (exynos_crtc->event) {
- ret = -EBUSY;
- goto out;
- }
-
- ret = drm_vblank_get(dev, exynos_crtc->pipe);
- if (ret) {
- DRM_DEBUG("failed to acquire vblank counter\n");
- goto out;
- }
-
- exynos_crtc->event = event;
- spin_unlock_irq(&dev->event_lock);
-
- /*
- * the pipe from user always is 0 so we can set pipe number
- * of current owner to event.
- */
- event->pipe = exynos_crtc->pipe;
-
- crtc->primary->fb = fb;
- crtc_w = fb->width - crtc->x;
- crtc_h = fb->height - crtc->y;
- ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
- crtc_w, crtc_h, crtc->x, crtc->y,
- crtc_w, crtc_h);
- if (ret) {
- crtc->primary->fb = old_fb;
- spin_lock_irq(&dev->event_lock);
- exynos_crtc->event = NULL;
- drm_vblank_put(dev, exynos_crtc->pipe);
- spin_unlock_irq(&dev->event_lock);
- return ret;
- }
-
- return 0;
-
-out:
- spin_unlock_irq(&dev->event_lock);
- return ret;
-}
-
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
@@ -232,9 +115,12 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
- .set_config = drm_crtc_helper_set_config,
- .page_flip = exynos_drm_crtc_page_flip,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
.destroy = exynos_drm_crtc_destroy,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
@@ -255,7 +141,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
- exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->pipe = pipe;
exynos_crtc->type = type;
exynos_crtc->ops = ops;
@@ -286,7 +171,7 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
- if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
+ if (!exynos_crtc->enabled)
return -EPERM;
if (exynos_crtc->ops->enable_vblank)
@@ -301,7 +186,7 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
- if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
+ if (!exynos_crtc->enabled)
return;
if (exynos_crtc->ops->disable_vblank)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 37678cf44..7cb6595c1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -13,6 +13,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
+#include <drm/drm_atomic_helper.h>
#include <linux/regulator/consumer.h>
@@ -59,10 +60,13 @@ static void exynos_dpi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dpi_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
+ .dpms = drm_atomic_helper_connector_dpms,
.detect = exynos_dpi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = exynos_dpi_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dpi_get_modes(struct drm_connector *connector)
@@ -309,33 +313,19 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
ctx->dev = dev;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
- ret = exynos_drm_component_add(dev,
- EXYNOS_DEVICE_TYPE_CONNECTOR,
- ctx->display.type);
- if (ret)
- return ERR_PTR(ret);
-
ret = exynos_dpi_parse_dt(ctx);
if (ret < 0) {
devm_kfree(dev, ctx);
- goto err_del_component;
+ return NULL;
}
if (ctx->panel_node) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
- if (!ctx->panel) {
- exynos_drm_component_del(dev,
- EXYNOS_DEVICE_TYPE_CONNECTOR);
+ if (!ctx->panel)
return ERR_PTR(-EPROBE_DEFER);
- }
}
return &ctx->display;
-
-err_del_component:
- exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
-
- return NULL;
}
int exynos_dpi_remove(struct exynos_drm_display *display)
@@ -347,7 +337,5 @@ int exynos_dpi_remove(struct exynos_drm_display *display)
if (ctx->panel)
drm_panel_detach(ctx->panel);
- exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 8ac465208..63a68c60a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -38,19 +38,6 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-static struct platform_device *exynos_drm_pdev;
-
-static DEFINE_MUTEX(drm_component_lock);
-static LIST_HEAD(drm_component_list);
-
-struct component_dev {
- struct list_head list;
- struct device *crtc_dev;
- struct device *conn_dev;
- enum exynos_drm_output_type out_type;
- unsigned int dev_type_flag;
-};
-
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
@@ -98,6 +85,8 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_cleanup_vblank;
+ drm_mode_config_reset(dev);
+
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
@@ -348,190 +337,29 @@ static const struct dev_pm_ops exynos_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
};
-int exynos_drm_component_add(struct device *dev,
- enum exynos_drm_device_type dev_type,
- enum exynos_drm_output_type out_type)
-{
- struct component_dev *cdev;
-
- if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
- dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
- DRM_ERROR("invalid device type.\n");
- return -EINVAL;
- }
-
- mutex_lock(&drm_component_lock);
-
- /*
- * Make sure to check if there is a component which has two device
- * objects, for connector and for encoder/connector.
- * It should make sure that crtc and encoder/connector drivers are
- * ready before exynos drm core binds them.
- */
- list_for_each_entry(cdev, &drm_component_list, list) {
- if (cdev->out_type == out_type) {
- /*
- * If crtc and encoder/connector device objects are
- * added already just return.
- */
- if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
- EXYNOS_DEVICE_TYPE_CONNECTOR)) {
- mutex_unlock(&drm_component_lock);
- return 0;
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
- cdev->crtc_dev = dev;
- cdev->dev_type_flag |= dev_type;
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
- cdev->conn_dev = dev;
- cdev->dev_type_flag |= dev_type;
- }
-
- mutex_unlock(&drm_component_lock);
- return 0;
- }
- }
-
- mutex_unlock(&drm_component_lock);
-
- cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
- if (!cdev)
- return -ENOMEM;
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
- cdev->crtc_dev = dev;
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
- cdev->conn_dev = dev;
-
- cdev->out_type = out_type;
- cdev->dev_type_flag = dev_type;
-
- mutex_lock(&drm_component_lock);
- list_add_tail(&cdev->list, &drm_component_list);
- mutex_unlock(&drm_component_lock);
-
- return 0;
-}
-
-void exynos_drm_component_del(struct device *dev,
- enum exynos_drm_device_type dev_type)
-{
- struct component_dev *cdev, *next;
-
- mutex_lock(&drm_component_lock);
-
- list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
- if (cdev->crtc_dev == dev) {
- cdev->crtc_dev = NULL;
- cdev->dev_type_flag &= ~dev_type;
- }
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
- if (cdev->conn_dev == dev) {
- cdev->conn_dev = NULL;
- cdev->dev_type_flag &= ~dev_type;
- }
- }
-
- /*
- * Release cdev object only in case that both of crtc and
- * encoder/connector device objects are NULL.
- */
- if (!cdev->crtc_dev && !cdev->conn_dev) {
- list_del(&cdev->list);
- kfree(cdev);
- }
- }
-
- mutex_unlock(&drm_component_lock);
-}
-
-static int compare_dev(struct device *dev, void *data)
-{
- return dev == (struct device *)data;
-}
-
-static struct component_match *exynos_drm_match_add(struct device *dev)
-{
- struct component_match *match = NULL;
- struct component_dev *cdev;
- unsigned int attach_cnt = 0;
-
- mutex_lock(&drm_component_lock);
-
- /* Do not retry to probe if there is no any kms driver regitered. */
- if (list_empty(&drm_component_list)) {
- mutex_unlock(&drm_component_lock);
- return ERR_PTR(-ENODEV);
- }
-
- list_for_each_entry(cdev, &drm_component_list, list) {
- /*
- * Add components to master only in case that crtc and
- * encoder/connector device objects exist.
- */
- if (!cdev->crtc_dev || !cdev->conn_dev)
- continue;
-
- attach_cnt++;
-
- mutex_unlock(&drm_component_lock);
-
- /*
- * fimd and dpi modules have same device object so add
- * only crtc device object in this case.
- */
- if (cdev->crtc_dev == cdev->conn_dev) {
- component_match_add(dev, &match, compare_dev,
- cdev->crtc_dev);
- goto out_lock;
- }
-
- /*
- * Do not chage below call order.
- * crtc device first should be added to master because
- * connector/encoder need pipe number of crtc when they
- * are created.
- */
- component_match_add(dev, &match, compare_dev, cdev->crtc_dev);
- component_match_add(dev, &match, compare_dev, cdev->conn_dev);
-
-out_lock:
- mutex_lock(&drm_component_lock);
- }
-
- mutex_unlock(&drm_component_lock);
-
- return attach_cnt ? match : ERR_PTR(-EPROBE_DEFER);
-}
-
-static int exynos_drm_bind(struct device *dev)
-{
- return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
-}
-
-static void exynos_drm_unbind(struct device *dev)
-{
- drm_put_dev(dev_get_drvdata(dev));
-}
-
-static const struct component_master_ops exynos_drm_ops = {
- .bind = exynos_drm_bind,
- .unbind = exynos_drm_unbind,
-};
+/* forward declaration */
+static struct platform_driver exynos_drm_platform_driver;
+/*
+ * Connector drivers should not be placed before associated crtc drivers,
+ * because connector requires pipe number of its crtc during initialization.
+ */
static struct platform_driver *const exynos_drm_kms_drivers[] = {
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ &vidi_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
&fimd_driver,
#endif
+#ifdef CONFIG_DRM_EXYNOS5433_DECON
+ &exynos5433_decon_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS7_DECON
&decon_driver,
#endif
+#ifdef CONFIG_DRM_EXYNOS_MIC
+ &mic_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS_DP
&dp_driver,
#endif
@@ -560,6 +388,59 @@ static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_IPP
&ipp_driver,
#endif
+ &exynos_drm_platform_driver,
+};
+
+static struct platform_driver *const exynos_drm_drv_with_simple_dev[] = {
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ &vidi_driver,
+#endif
+#ifdef CONFIG_DRM_EXYNOS_IPP
+ &ipp_driver,
+#endif
+ &exynos_drm_platform_driver,
+};
+#define PDEV_COUNT ARRAY_SIZE(exynos_drm_drv_with_simple_dev)
+
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
+
+static struct component_match *exynos_drm_match_add(struct device *dev)
+{
+ struct component_match *match = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
+ struct device_driver *drv = &exynos_drm_kms_drivers[i]->driver;
+ struct device *p = NULL, *d;
+
+ while ((d = bus_find_device(&platform_bus_type, p, drv,
+ (void *)platform_bus_type.match))) {
+ put_device(p);
+ component_match_add(dev, &match, compare_dev, d);
+ p = d;
+ }
+ put_device(p);
+ }
+
+ return match ?: ERR_PTR(-ENODEV);
+}
+
+static int exynos_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
+}
+
+static void exynos_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops exynos_drm_ops = {
+ .bind = exynos_drm_bind,
+ .unbind = exynos_drm_unbind,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
@@ -570,9 +451,8 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
match = exynos_drm_match_add(&pdev->dev);
- if (IS_ERR(match)) {
+ if (IS_ERR(match))
return PTR_ERR(match);
- }
return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
match);
@@ -584,13 +464,6 @@ static int exynos_drm_platform_remove(struct platform_device *pdev)
return 0;
}
-static const char * const strings[] = {
- "samsung,exynos3",
- "samsung,exynos4",
- "samsung,exynos5",
- "samsung,exynos7",
-};
-
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
@@ -600,101 +473,125 @@ static struct platform_driver exynos_drm_platform_driver = {
},
};
-static int exynos_drm_init(void)
+static struct platform_device *exynos_drm_pdevs[PDEV_COUNT];
+
+static void exynos_drm_unregister_devices(void)
{
- bool is_exynos = false;
- int ret, i, j;
+ int i = PDEV_COUNT;
- /*
- * Register device object only in case of Exynos SoC.
- *
- * Below codes resolves temporarily infinite loop issue incurred
- * by Exynos drm driver when using multi-platform kernel.
- * So these codes will be replaced with more generic way later.
- */
- for (i = 0; i < ARRAY_SIZE(strings); i++) {
- if (of_machine_is_compatible(strings[i])) {
- is_exynos = true;
- break;
- }
+ while (--i >= 0) {
+ platform_device_unregister(exynos_drm_pdevs[i]);
+ exynos_drm_pdevs[i] = NULL;
}
+}
- if (!is_exynos)
- return -ENODEV;
+static int exynos_drm_register_devices(void)
+{
+ int i;
- exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
- NULL, 0);
- if (IS_ERR(exynos_drm_pdev))
- return PTR_ERR(exynos_drm_pdev);
+ for (i = 0; i < PDEV_COUNT; ++i) {
+ struct platform_driver *d = exynos_drm_drv_with_simple_dev[i];
+ struct platform_device *pdev =
+ platform_device_register_simple(d->driver.name, -1,
+ NULL, 0);
- ret = exynos_drm_probe_vidi();
- if (ret < 0)
- goto err_unregister_pd;
+ if (!IS_ERR(pdev)) {
+ exynos_drm_pdevs[i] = pdev;
+ continue;
+ }
+ while (--i >= 0) {
+ platform_device_unregister(exynos_drm_pdevs[i]);
+ exynos_drm_pdevs[i] = NULL;
+ }
- for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
- ret = platform_driver_register(exynos_drm_kms_drivers[i]);
- if (ret < 0)
- goto err_unregister_kms_drivers;
+ return PTR_ERR(pdev);
}
- for (j = 0; j < ARRAY_SIZE(exynos_drm_non_kms_drivers); ++j) {
- ret = platform_driver_register(exynos_drm_non_kms_drivers[j]);
- if (ret < 0)
- goto err_unregister_non_kms_drivers;
- }
+ return 0;
+}
-#ifdef CONFIG_DRM_EXYNOS_IPP
- ret = exynos_platform_device_ipp_register();
- if (ret < 0)
- goto err_unregister_non_kms_drivers;
-#endif
+static void exynos_drm_unregister_drivers(struct platform_driver * const *drv,
+ int count)
+{
+ while (--count >= 0)
+ platform_driver_unregister(drv[count]);
+}
- ret = platform_driver_register(&exynos_drm_platform_driver);
- if (ret)
- goto err_unregister_resources;
+static int exynos_drm_register_drivers(struct platform_driver * const *drv,
+ int count)
+{
+ int i, ret;
- return 0;
+ for (i = 0; i < count; ++i) {
+ ret = platform_driver_register(drv[i]);
+ if (!ret)
+ continue;
-err_unregister_resources:
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-#endif
+ while (--i >= 0)
+ platform_driver_unregister(drv[i]);
-err_unregister_non_kms_drivers:
- while (--j >= 0)
- platform_driver_unregister(exynos_drm_non_kms_drivers[j]);
+ return ret;
+ }
-err_unregister_kms_drivers:
- while (--i >= 0)
- platform_driver_unregister(exynos_drm_kms_drivers[i]);
+ return 0;
+}
- exynos_drm_remove_vidi();
+static inline int exynos_drm_register_kms_drivers(void)
+{
+ return exynos_drm_register_drivers(exynos_drm_kms_drivers,
+ ARRAY_SIZE(exynos_drm_kms_drivers));
+}
-err_unregister_pd:
- platform_device_unregister(exynos_drm_pdev);
+static inline int exynos_drm_register_non_kms_drivers(void)
+{
+ return exynos_drm_register_drivers(exynos_drm_non_kms_drivers,
+ ARRAY_SIZE(exynos_drm_non_kms_drivers));
+}
- return ret;
+static inline void exynos_drm_unregister_kms_drivers(void)
+{
+ exynos_drm_unregister_drivers(exynos_drm_kms_drivers,
+ ARRAY_SIZE(exynos_drm_kms_drivers));
}
-static void exynos_drm_exit(void)
+static inline void exynos_drm_unregister_non_kms_drivers(void)
{
- int i;
+ exynos_drm_unregister_drivers(exynos_drm_non_kms_drivers,
+ ARRAY_SIZE(exynos_drm_non_kms_drivers));
+}
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-#endif
+static int exynos_drm_init(void)
+{
+ int ret;
- for (i = ARRAY_SIZE(exynos_drm_non_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_non_kms_drivers[i]);
+ ret = exynos_drm_register_devices();
+ if (ret)
+ return ret;
- for (i = ARRAY_SIZE(exynos_drm_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_kms_drivers[i]);
+ ret = exynos_drm_register_kms_drivers();
+ if (ret)
+ goto err_unregister_pdevs;
- platform_driver_unregister(&exynos_drm_platform_driver);
+ ret = exynos_drm_register_non_kms_drivers();
+ if (ret)
+ goto err_unregister_kms_drivers;
+
+ return 0;
+
+err_unregister_kms_drivers:
+ exynos_drm_unregister_kms_drivers();
- exynos_drm_remove_vidi();
+err_unregister_pdevs:
+ exynos_drm_unregister_devices();
- platform_device_unregister(exynos_drm_pdev);
+ return ret;
+}
+
+static void exynos_drm_exit(void)
+{
+ exynos_drm_unregister_non_kms_drivers();
+ exynos_drm_unregister_kms_drivers();
+ exynos_drm_unregister_devices();
}
module_init(exynos_drm_init);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 29e3fb78c..dd00f160c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -25,13 +25,6 @@
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc, base)
#define to_exynos_plane(x) container_of(x, struct exynos_drm_plane, base)
-/* This enumerates device type. */
-enum exynos_drm_device_type {
- EXYNOS_DEVICE_TYPE_NONE,
- EXYNOS_DEVICE_TYPE_CRTC,
- EXYNOS_DEVICE_TYPE_CONNECTOR,
-};
-
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
@@ -71,8 +64,6 @@ enum exynos_drm_output_type {
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @zpos: order of overlay layer(z position).
- * @enabled: enabled or not.
- * @resume: to resume or not.
*
* this structure is common to exynos SoC and its contents would be copied
* to hardware specific overlay info.
@@ -101,9 +92,6 @@ struct exynos_drm_plane {
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos;
-
- bool enabled:1;
- bool resume:1;
};
/*
@@ -157,7 +145,8 @@ struct exynos_drm_display {
/*
* Exynos drm crtc ops
*
- * @dpms: control device power.
+ * @enable: enable the device
+ * @disable: disable the device
* @mode_fixup: fix mode data before applying it
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
@@ -175,7 +164,8 @@ struct exynos_drm_display {
*/
struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
- void (*dpms)(struct exynos_drm_crtc *crtc, int mode);
+ void (*enable)(struct exynos_drm_crtc *crtc);
+ void (*disable)(struct exynos_drm_crtc *crtc);
bool (*mode_fixup)(struct exynos_drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
@@ -187,6 +177,7 @@ struct exynos_drm_crtc_ops {
void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos);
void (*te_handler)(struct exynos_drm_crtc *crtc);
void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
+ void (*clear_channels)(struct exynos_drm_crtc *crtc);
};
/*
@@ -201,7 +192,7 @@ struct exynos_drm_crtc_ops {
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occurred through
* this pipe value.
- * @dpms: store the crtc dpms value
+ * @enabled: if the crtc is enabled or not
* @event: vblank event that is currently queued for flip
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the crtc's implementation specific context
@@ -210,7 +201,7 @@ struct exynos_drm_crtc {
struct drm_crtc base;
enum exynos_drm_output_type type;
unsigned int pipe;
- unsigned int dpms;
+ bool enabled;
wait_queue_head_t pending_flip_queue;
struct drm_pending_vblank_event *event;
const struct exynos_drm_crtc_ops *ops;
@@ -293,15 +284,6 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
-#ifdef CONFIG_DRM_EXYNOS_IPP
-int exynos_platform_device_ipp_register(void);
-void exynos_platform_device_ipp_unregister(void);
-#else
-static inline int exynos_platform_device_ipp_register(void) { return 0; }
-static inline void exynos_platform_device_ipp_unregister(void) {}
-#endif
-
-
#ifdef CONFIG_DRM_EXYNOS_DPI
struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct exynos_drm_display *display);
@@ -314,26 +296,12 @@ static inline int exynos_dpi_remove(struct exynos_drm_display *display)
}
#endif
-#ifdef CONFIG_DRM_EXYNOS_VIDI
-int exynos_drm_probe_vidi(void);
-void exynos_drm_remove_vidi(void);
-#else
-static inline int exynos_drm_probe_vidi(void) { return 0; }
-static inline void exynos_drm_remove_vidi(void) {}
-#endif
-
/* This function creates a encoder and a connector, and initializes them. */
int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_display *display);
-int exynos_drm_component_add(struct device *dev,
- enum exynos_drm_device_type dev_type,
- enum exynos_drm_output_type out_type);
-
-void exynos_drm_component_del(struct device *dev,
- enum exynos_drm_device_type dev_type);
-
extern struct platform_driver fimd_driver;
+extern struct platform_driver exynos5433_decon_driver;
extern struct platform_driver decon_driver;
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
@@ -346,4 +314,5 @@ extern struct platform_driver fimc_driver;
extern struct platform_driver rotator_driver;
extern struct platform_driver gsc_driver;
extern struct platform_driver ipp_driver;
+extern struct platform_driver mic_driver;
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 04927153b..0e58b36cb 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -14,12 +14,14 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
+#include <drm/drm_atomic_helper.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/irq.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
@@ -33,38 +35,6 @@
/* returns true iff both arguments logically differs */
#define NEQV(a, b) (!(a) ^ !(b))
-#define DSIM_STATUS_REG 0x0 /* Status register */
-#define DSIM_SWRST_REG 0x4 /* Software reset register */
-#define DSIM_CLKCTRL_REG 0x8 /* Clock control register */
-#define DSIM_TIMEOUT_REG 0xc /* Time out register */
-#define DSIM_CONFIG_REG 0x10 /* Configuration register */
-#define DSIM_ESCMODE_REG 0x14 /* Escape mode register */
-
-/* Main display image resolution register */
-#define DSIM_MDRESOL_REG 0x18
-#define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */
-#define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */
-#define DSIM_MSYNC_REG 0x24 /* Main display sync area register */
-
-/* Sub display image resolution register */
-#define DSIM_SDRESOL_REG 0x28
-#define DSIM_INTSRC_REG 0x2c /* Interrupt source register */
-#define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */
-#define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */
-#define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */
-#define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */
-#define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */
-#define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */
-
-/* FIFO memory AC characteristic register */
-#define DSIM_PLLCTRL_REG 0x4c /* PLL control register */
-#define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */
-#define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */
-#define DSIM_PHYCTRL_REG 0x5c
-#define DSIM_PHYTIMING_REG 0x64
-#define DSIM_PHYTIMING1_REG 0x68
-#define DSIM_PHYTIMING2_REG 0x6c
-
/* DSIM_STATUS */
#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
#define DSIM_STOP_STATE_CLK (1 << 8)
@@ -128,8 +98,8 @@
/* DSIM_MDRESOL */
#define DSIM_MAIN_STAND_BY (1 << 31)
-#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16)
-#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0)
+#define DSIM_MAIN_VRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 16)
+#define DSIM_MAIN_HRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 0)
/* DSIM_MVPORCH */
#define DSIM_CMD_ALLOW(x) ((x) << 28)
@@ -163,6 +133,7 @@
#define DSIM_INT_PLL_STABLE (1 << 31)
#define DSIM_INT_SW_RST_RELEASE (1 << 30)
#define DSIM_INT_SFR_FIFO_EMPTY (1 << 29)
+#define DSIM_INT_SFR_HDR_FIFO_EMPTY (1 << 28)
#define DSIM_INT_BTA (1 << 25)
#define DSIM_INT_FRAME_DONE (1 << 24)
#define DSIM_INT_RX_TIMEOUT (1 << 21)
@@ -211,6 +182,8 @@
/* DSIM_PHYCTRL */
#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0)
+#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP (1 << 30)
+#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP (1 << 14)
/* DSIM_PHYTIMING */
#define DSIM_PHYTIMING_LPX(x) ((x) << 8)
@@ -234,6 +207,18 @@
#define DSI_XFER_TIMEOUT_MS 100
#define DSI_RX_FIFO_EMPTY 0x30800002
+#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
+
+#define REG_ADDR(dsi, reg_idx) ((dsi)->reg_base + \
+ dsi->driver_data->reg_ofs[(reg_idx)])
+#define DSI_WRITE(dsi, reg_idx, val) writel((val), \
+ REG_ADDR((dsi), (reg_idx)))
+#define DSI_READ(dsi, reg_idx) readl(REG_ADDR((dsi), (reg_idx)))
+
+static char *clk_names[5] = { "bus_clk", "sclk_mipi",
+ "phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
+ "sclk_rgb_vclk_to_dsim0" };
+
enum exynos_dsi_transfer_type {
EXYNOS_DSI_TX,
EXYNOS_DSI_RX,
@@ -259,12 +244,18 @@ struct exynos_dsi_transfer {
#define DSIM_STATE_ENABLED BIT(0)
#define DSIM_STATE_INITIALIZED BIT(1)
#define DSIM_STATE_CMD_LPM BIT(2)
+#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3)
struct exynos_dsi_driver_data {
+ unsigned int *reg_ofs;
unsigned int plltmr_reg;
-
unsigned int has_freqband:1;
unsigned int has_clklane_stop:1;
+ unsigned int num_clks;
+ unsigned int max_freq;
+ unsigned int wait_for_reset;
+ unsigned int num_bits_resol;
+ unsigned int *reg_values;
};
struct exynos_dsi {
@@ -277,8 +268,7 @@ struct exynos_dsi {
void __iomem *reg_base;
struct phy *phy;
- struct clk *pll_clk;
- struct clk *bus_clk;
+ struct clk **clks;
struct regulator_bulk_data supplies[2];
int irq;
int te_gpio;
@@ -299,6 +289,7 @@ struct exynos_dsi {
struct list_head transfer_list;
struct exynos_dsi_driver_data *driver_data;
+ struct device_node *bridge_node;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
@@ -309,25 +300,186 @@ static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d)
return container_of(d, struct exynos_dsi, display);
}
+enum reg_idx {
+ DSIM_STATUS_REG, /* Status register */
+ DSIM_SWRST_REG, /* Software reset register */
+ DSIM_CLKCTRL_REG, /* Clock control register */
+ DSIM_TIMEOUT_REG, /* Time out register */
+ DSIM_CONFIG_REG, /* Configuration register */
+ DSIM_ESCMODE_REG, /* Escape mode register */
+ DSIM_MDRESOL_REG,
+ DSIM_MVPORCH_REG, /* Main display Vporch register */
+ DSIM_MHPORCH_REG, /* Main display Hporch register */
+ DSIM_MSYNC_REG, /* Main display sync area register */
+ DSIM_INTSRC_REG, /* Interrupt source register */
+ DSIM_INTMSK_REG, /* Interrupt mask register */
+ DSIM_PKTHDR_REG, /* Packet Header FIFO register */
+ DSIM_PAYLOAD_REG, /* Payload FIFO register */
+ DSIM_RXFIFO_REG, /* Read FIFO register */
+ DSIM_FIFOCTRL_REG, /* FIFO status and control register */
+ DSIM_PLLCTRL_REG, /* PLL control register */
+ DSIM_PHYCTRL_REG,
+ DSIM_PHYTIMING_REG,
+ DSIM_PHYTIMING1_REG,
+ DSIM_PHYTIMING2_REG,
+ NUM_REGS
+};
+static unsigned int exynos_reg_ofs[] = {
+ [DSIM_STATUS_REG] = 0x00,
+ [DSIM_SWRST_REG] = 0x04,
+ [DSIM_CLKCTRL_REG] = 0x08,
+ [DSIM_TIMEOUT_REG] = 0x0c,
+ [DSIM_CONFIG_REG] = 0x10,
+ [DSIM_ESCMODE_REG] = 0x14,
+ [DSIM_MDRESOL_REG] = 0x18,
+ [DSIM_MVPORCH_REG] = 0x1c,
+ [DSIM_MHPORCH_REG] = 0x20,
+ [DSIM_MSYNC_REG] = 0x24,
+ [DSIM_INTSRC_REG] = 0x2c,
+ [DSIM_INTMSK_REG] = 0x30,
+ [DSIM_PKTHDR_REG] = 0x34,
+ [DSIM_PAYLOAD_REG] = 0x38,
+ [DSIM_RXFIFO_REG] = 0x3c,
+ [DSIM_FIFOCTRL_REG] = 0x44,
+ [DSIM_PLLCTRL_REG] = 0x4c,
+ [DSIM_PHYCTRL_REG] = 0x5c,
+ [DSIM_PHYTIMING_REG] = 0x64,
+ [DSIM_PHYTIMING1_REG] = 0x68,
+ [DSIM_PHYTIMING2_REG] = 0x6c,
+};
+
+static unsigned int exynos5433_reg_ofs[] = {
+ [DSIM_STATUS_REG] = 0x04,
+ [DSIM_SWRST_REG] = 0x0C,
+ [DSIM_CLKCTRL_REG] = 0x10,
+ [DSIM_TIMEOUT_REG] = 0x14,
+ [DSIM_CONFIG_REG] = 0x18,
+ [DSIM_ESCMODE_REG] = 0x1C,
+ [DSIM_MDRESOL_REG] = 0x20,
+ [DSIM_MVPORCH_REG] = 0x24,
+ [DSIM_MHPORCH_REG] = 0x28,
+ [DSIM_MSYNC_REG] = 0x2C,
+ [DSIM_INTSRC_REG] = 0x34,
+ [DSIM_INTMSK_REG] = 0x38,
+ [DSIM_PKTHDR_REG] = 0x3C,
+ [DSIM_PAYLOAD_REG] = 0x40,
+ [DSIM_RXFIFO_REG] = 0x44,
+ [DSIM_FIFOCTRL_REG] = 0x4C,
+ [DSIM_PLLCTRL_REG] = 0x94,
+ [DSIM_PHYCTRL_REG] = 0xA4,
+ [DSIM_PHYTIMING_REG] = 0xB4,
+ [DSIM_PHYTIMING1_REG] = 0xB8,
+ [DSIM_PHYTIMING2_REG] = 0xBC,
+};
+
+enum reg_value_idx {
+ RESET_TYPE,
+ PLL_TIMER,
+ STOP_STATE_CNT,
+ PHYCTRL_ULPS_EXIT,
+ PHYCTRL_VREG_LP,
+ PHYCTRL_SLEW_UP,
+ PHYTIMING_LPX,
+ PHYTIMING_HS_EXIT,
+ PHYTIMING_CLK_PREPARE,
+ PHYTIMING_CLK_ZERO,
+ PHYTIMING_CLK_POST,
+ PHYTIMING_CLK_TRAIL,
+ PHYTIMING_HS_PREPARE,
+ PHYTIMING_HS_ZERO,
+ PHYTIMING_HS_TRAIL
+};
+
+static unsigned int reg_values[] = {
+ [RESET_TYPE] = DSIM_SWRST,
+ [PLL_TIMER] = 500,
+ [STOP_STATE_CNT] = 0xf,
+ [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af),
+ [PHYCTRL_VREG_LP] = 0,
+ [PHYCTRL_SLEW_UP] = 0,
+ [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06),
+ [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b),
+ [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07),
+ [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27),
+ [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d),
+ [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08),
+ [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09),
+ [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d),
+ [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b),
+};
+
+static unsigned int exynos5433_reg_values[] = {
+ [RESET_TYPE] = DSIM_FUNCRST,
+ [PLL_TIMER] = 22200,
+ [STOP_STATE_CNT] = 0xa,
+ [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190),
+ [PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP,
+ [PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP,
+ [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07),
+ [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c),
+ [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
+ [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d),
+ [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
+ [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09),
+ [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b),
+ [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10),
+ [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c),
+};
+
static struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
+ .reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x50,
.has_freqband = 1,
.has_clklane_stop = 1,
+ .num_clks = 2,
+ .max_freq = 1000,
+ .wait_for_reset = 1,
+ .num_bits_resol = 11,
+ .reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
+ .reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x50,
.has_freqband = 1,
.has_clklane_stop = 1,
+ .num_clks = 2,
+ .max_freq = 1000,
+ .wait_for_reset = 1,
+ .num_bits_resol = 11,
+ .reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = {
+ .reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x58,
.has_clklane_stop = 1,
+ .num_clks = 2,
+ .max_freq = 1000,
+ .wait_for_reset = 1,
+ .num_bits_resol = 11,
+ .reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
+ .reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x58,
+ .num_clks = 2,
+ .max_freq = 1000,
+ .wait_for_reset = 1,
+ .num_bits_resol = 11,
+ .reg_values = reg_values,
+};
+
+static struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
+ .reg_ofs = exynos5433_reg_ofs,
+ .plltmr_reg = 0xa0,
+ .has_clklane_stop = 1,
+ .num_clks = 5,
+ .max_freq = 1500,
+ .wait_for_reset = 0,
+ .num_bits_resol = 12,
+ .reg_values = exynos5433_reg_values,
};
static struct of_device_id exynos_dsi_of_match[] = {
@@ -339,6 +491,8 @@ static struct of_device_id exynos_dsi_of_match[] = {
.data = &exynos4415_dsi_driver_data },
{ .compatible = "samsung,exynos5410-mipi-dsi",
.data = &exynos5_dsi_driver_data },
+ { .compatible = "samsung,exynos5433-mipi-dsi",
+ .data = &exynos5433_dsi_driver_data },
{ }
};
@@ -361,8 +515,10 @@ static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
static void exynos_dsi_reset(struct exynos_dsi *dsi)
{
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+
reinit_completion(&dsi->completed);
- writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+ DSI_WRITE(dsi, DSIM_SWRST_REG, driver_data->reg_values[RESET_TYPE]);
}
#ifndef MHZ
@@ -372,6 +528,7 @@ static void exynos_dsi_reset(struct exynos_dsi *dsi)
static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
{
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned long best_freq = 0;
u32 min_delta = 0xffffffff;
u8 p_min, p_max;
@@ -395,7 +552,8 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
tmp = (u64)_m * fin;
do_div(tmp, _p);
- if (tmp < 500 * MHZ || tmp > 1000 * MHZ)
+ if (tmp < 500 * MHZ ||
+ tmp > driver_data->max_freq * MHZ)
continue;
tmp = (u64)_m * fin;
@@ -431,16 +589,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
u16 m;
u32 reg;
- clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate);
-
- fin = clk_get_rate(dsi->pll_clk);
- if (!fin) {
- dev_err(dsi->dev, "failed to get PLL clock frequency\n");
- return 0;
- }
-
- dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin);
-
+ fin = dsi->pll_clk_rate;
fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
if (!fout) {
dev_err(dsi->dev,
@@ -449,7 +598,8 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
}
dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
- writel(500, dsi->reg_base + driver_data->plltmr_reg);
+ writel(driver_data->reg_values[PLL_TIMER],
+ dsi->reg_base + driver_data->plltmr_reg);
reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
@@ -471,7 +621,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
reg |= DSIM_FREQ_BAND(band);
}
- writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+ DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg);
timeout = 1000;
do {
@@ -479,7 +629,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
dev_err(dsi->dev, "PLL failed to stabilize\n");
return 0;
}
- reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+ reg = DSI_READ(dsi, DSIM_STATUS_REG);
} while ((reg & DSIM_PLL_STABLE) == 0);
return fout;
@@ -509,7 +659,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
hs_clk, byte_clk, esc_clk);
- reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+ reg = DSI_READ(dsi, DSIM_CLKCTRL_REG);
reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
| DSIM_BYTE_CLK_SRC_MASK);
@@ -519,7 +669,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
| DSIM_BYTE_CLK_SRC(0)
| DSIM_TX_REQUEST_HSCLK;
- writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+ DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg);
return 0;
}
@@ -527,22 +677,24 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ unsigned int *reg_values = driver_data->reg_values;
u32 reg;
if (driver_data->has_freqband)
return;
/* B D-PHY: D-PHY Master & Slave Analog Block control */
- reg = DSIM_PHYCTRL_ULPS_EXIT(0x0af);
- writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
+ reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] |
+ reg_values[PHYCTRL_SLEW_UP];
+ DSI_WRITE(dsi, DSIM_PHYCTRL_REG, reg);
/*
* T LPX: Transmitted length of any Low-Power state period
* T HS-EXIT: Time that the transmitter drives LP-11 following a HS
* burst
*/
- reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
+ reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT];
+ DSI_WRITE(dsi, DSIM_PHYTIMING_REG, reg);
/*
* T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
@@ -557,11 +709,12 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
* T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
* the last payload clock bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
- DSIM_PHYTIMING1_CLK_ZERO(0x27) |
- DSIM_PHYTIMING1_CLK_POST(0x0d) |
- DSIM_PHYTIMING1_CLK_TRAIL(0x08);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
+ reg = reg_values[PHYTIMING_CLK_PREPARE] |
+ reg_values[PHYTIMING_CLK_ZERO] |
+ reg_values[PHYTIMING_CLK_POST] |
+ reg_values[PHYTIMING_CLK_TRAIL];
+
+ DSI_WRITE(dsi, DSIM_PHYTIMING1_REG, reg);
/*
* T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
@@ -572,23 +725,31 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
* T HS-TRAIL: Time that the transmitter drives the flipped differential
* state after last payload data bit of a HS transmission burst
*/
- reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
- DSIM_PHYTIMING2_HS_TRAIL(0x0b);
- writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
+ reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] |
+ reg_values[PHYTIMING_HS_TRAIL];
+ DSI_WRITE(dsi, DSIM_PHYTIMING2_REG, reg);
}
static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
{
u32 reg;
- reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+ reg = DSI_READ(dsi, DSIM_CLKCTRL_REG);
reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
- writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+ DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg);
- reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG);
+ reg = DSI_READ(dsi, DSIM_PLLCTRL_REG);
reg &= ~DSIM_PLL_EN;
- writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+ DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg);
+}
+
+static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
+{
+ u32 reg = DSI_READ(dsi, DSIM_CONFIG_REG);
+ reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
+ DSIM_LANE_EN(lane));
+ DSI_WRITE(dsi, DSIM_CONFIG_REG, reg);
}
static int exynos_dsi_init_link(struct exynos_dsi *dsi)
@@ -599,15 +760,14 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
u32 lanes_mask;
/* Initialize FIFO pointers */
- reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+ reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG);
reg &= ~0x1f;
- writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+ DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg);
usleep_range(9000, 11000);
reg |= 0x1f;
- writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
-
+ DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg);
usleep_range(9000, 11000);
/* DSI configuration */
@@ -664,17 +824,6 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
return -EINVAL;
}
- reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1);
-
- writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
-
- reg |= DSIM_LANE_EN_CLK;
- writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
-
- lanes_mask = BIT(dsi->lanes) - 1;
- reg |= DSIM_LANE_EN(lanes_mask);
- writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
-
/*
* Use non-continuous clock mode if the periparal wants and
* host controller supports
@@ -686,8 +835,11 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
if (driver_data->has_clklane_stop &&
dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
reg |= DSIM_CLKLANE_STOP;
- writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
}
+ DSI_WRITE(dsi, DSIM_CONFIG_REG, reg);
+
+ lanes_mask = BIT(dsi->lanes) - 1;
+ exynos_dsi_enable_lane(dsi, lanes_mask);
/* Check clock and data lane state are stop state */
timeout = 100;
@@ -697,19 +849,19 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
return -EFAULT;
}
- reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+ reg = DSI_READ(dsi, DSIM_STATUS_REG);
if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
!= DSIM_STOP_STATE_DAT(lanes_mask))
continue;
} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
- reg = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+ reg = DSI_READ(dsi, DSIM_ESCMODE_REG);
reg &= ~DSIM_STOP_STATE_CNT_MASK;
- reg |= DSIM_STOP_STATE_CNT(0xf);
- writel(reg, dsi->reg_base + DSIM_ESCMODE_REG);
+ reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
+ DSI_WRITE(dsi, DSIM_ESCMODE_REG, reg);
reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
- writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG);
+ DSI_WRITE(dsi, DSIM_TIMEOUT_REG, reg);
return 0;
}
@@ -717,25 +869,27 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
{
struct videomode *vm = &dsi->vm;
+ unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
u32 reg;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
reg = DSIM_CMD_ALLOW(0xf)
| DSIM_STABLE_VFP(vm->vfront_porch)
| DSIM_MAIN_VBP(vm->vback_porch);
- writel(reg, dsi->reg_base + DSIM_MVPORCH_REG);
+ DSI_WRITE(dsi, DSIM_MVPORCH_REG, reg);
reg = DSIM_MAIN_HFP(vm->hfront_porch)
| DSIM_MAIN_HBP(vm->hback_porch);
- writel(reg, dsi->reg_base + DSIM_MHPORCH_REG);
+ DSI_WRITE(dsi, DSIM_MHPORCH_REG, reg);
reg = DSIM_MAIN_VSA(vm->vsync_len)
| DSIM_MAIN_HSA(vm->hsync_len);
- writel(reg, dsi->reg_base + DSIM_MSYNC_REG);
+ DSI_WRITE(dsi, DSIM_MSYNC_REG, reg);
}
+ reg = DSIM_MAIN_HRESOL(vm->hactive, num_bits_resol) |
+ DSIM_MAIN_VRESOL(vm->vactive, num_bits_resol);
- reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive);
- writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+ DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg);
dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive);
}
@@ -744,12 +898,12 @@ static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
{
u32 reg;
- reg = readl(dsi->reg_base + DSIM_MDRESOL_REG);
+ reg = DSI_READ(dsi, DSIM_MDRESOL_REG);
if (enable)
reg |= DSIM_MAIN_STAND_BY;
else
reg &= ~DSIM_MAIN_STAND_BY;
- writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+ DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg);
}
static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
@@ -757,7 +911,7 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
int timeout = 2000;
do {
- u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+ u32 reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG);
if (!(reg & DSIM_SFR_HEADER_FULL))
return 0;
@@ -771,22 +925,21 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
{
- u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+ u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG);
if (lpm)
v |= DSIM_CMD_LPDT_LP;
else
v &= ~DSIM_CMD_LPDT_LP;
- writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
+ DSI_WRITE(dsi, DSIM_ESCMODE_REG, v);
}
static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
{
- u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
-
+ u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG);
v |= DSIM_FORCE_BTA;
- writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
+ DSI_WRITE(dsi, DSIM_ESCMODE_REG, v);
}
static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
@@ -810,7 +963,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
while (length >= 4) {
reg = (payload[3] << 24) | (payload[2] << 16)
| (payload[1] << 8) | payload[0];
- writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+ DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg);
payload += 4;
length -= 4;
}
@@ -825,7 +978,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
/* Fall through */
case 1:
reg |= payload[0];
- writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+ DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg);
break;
case 0:
/* Do nothing */
@@ -848,7 +1001,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
dsi->state ^= DSIM_STATE_CMD_LPM;
}
- writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+ DSI_WRITE(dsi, DSIM_PKTHDR_REG, reg);
if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
exynos_dsi_force_bta(dsi);
@@ -864,7 +1017,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
u32 reg;
if (first) {
- reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
switch (reg & 0x3f) {
case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
@@ -903,7 +1056,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
/* Receive payload */
while (length >= 4) {
- reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
payload[0] = (reg >> 0) & 0xff;
payload[1] = (reg >> 8) & 0xff;
payload[2] = (reg >> 16) & 0xff;
@@ -913,7 +1066,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
}
if (length) {
- reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
switch (length) {
case 3:
payload[2] = (reg >> 16) & 0xff;
@@ -932,7 +1085,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
clear_fifo:
length = DSI_RX_FIFO_SIZE / 4;
do {
- reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+ reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
if (reg == DSI_RX_FIFO_EMPTY)
break;
} while (--length);
@@ -1088,23 +1241,26 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
struct exynos_dsi *dsi = dev_id;
u32 status;
- status = readl(dsi->reg_base + DSIM_INTSRC_REG);
+ status = DSI_READ(dsi, DSIM_INTSRC_REG);
if (!status) {
static unsigned long int j;
if (printk_timed_ratelimit(&j, 500))
dev_warn(dsi->dev, "spurious interrupt\n");
return IRQ_HANDLED;
}
- writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+ DSI_WRITE(dsi, DSIM_INTSRC_REG, status);
if (status & DSIM_INT_SW_RST_RELEASE) {
- u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY);
- writel(mask, dsi->reg_base + DSIM_INTMSK_REG);
+ u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
+ DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_FRAME_DONE |
+ DSIM_INT_RX_ECC_ERR | DSIM_INT_SW_RST_RELEASE);
+ DSI_WRITE(dsi, DSIM_INTMSK_REG, mask);
complete(&dsi->completed);
return IRQ_HANDLED;
}
- if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY)))
+ if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
+ DSIM_INT_FRAME_DONE | DSIM_INT_PLL_STABLE)))
return IRQ_HANDLED;
if (exynos_dsi_transfer_finish(dsi))
@@ -1118,7 +1274,7 @@ static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
struct drm_encoder *encoder = dsi->display.encoder;
- if (dsi->state & DSIM_STATE_ENABLED)
+ if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)
exynos_drm_crtc_te_handler(encoder->crtc);
return IRQ_HANDLED;
@@ -1142,10 +1298,17 @@ static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
static int exynos_dsi_init(struct exynos_dsi *dsi)
{
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+
exynos_dsi_reset(dsi);
exynos_dsi_enable_irq(dsi);
+
+ if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
+ exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1);
+
exynos_dsi_enable_clock(dsi);
- exynos_dsi_wait_for_reset(dsi);
+ if (driver_data->wait_for_reset)
+ exynos_dsi_wait_for_reset(dsi);
exynos_dsi_set_phy_ctrl(dsi);
exynos_dsi_init_link(dsi);
@@ -1164,15 +1327,15 @@ static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
goto out;
}
- ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio");
+ ret = gpio_request(dsi->te_gpio, "te_gpio");
if (ret) {
dev_err(dsi->dev, "gpio request failed with %d\n", ret);
goto out;
}
te_gpio_irq = gpio_to_irq(dsi->te_gpio);
-
irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
+
ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
IRQF_TRIGGER_RISING, "TE", dsi);
if (ret) {
@@ -1251,6 +1414,9 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
struct exynos_dsi_transfer xfer;
int ret;
+ if (!(dsi->state & DSIM_STATE_ENABLED))
+ return -EINVAL;
+
if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
ret = exynos_dsi_init(dsi);
if (ret)
@@ -1294,7 +1460,8 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = {
static int exynos_dsi_poweron(struct exynos_dsi *dsi)
{
- int ret;
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ int ret, i;
ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
if (ret < 0) {
@@ -1302,31 +1469,23 @@ static int exynos_dsi_poweron(struct exynos_dsi *dsi)
return ret;
}
- ret = clk_prepare_enable(dsi->bus_clk);
- if (ret < 0) {
- dev_err(dsi->dev, "cannot enable bus clock %d\n", ret);
- goto err_bus_clk;
- }
-
- ret = clk_prepare_enable(dsi->pll_clk);
- if (ret < 0) {
- dev_err(dsi->dev, "cannot enable pll clock %d\n", ret);
- goto err_pll_clk;
+ for (i = 0; i < driver_data->num_clks; i++) {
+ ret = clk_prepare_enable(dsi->clks[i]);
+ if (ret < 0)
+ goto err_clk;
}
ret = phy_power_on(dsi->phy);
if (ret < 0) {
dev_err(dsi->dev, "cannot enable phy %d\n", ret);
- goto err_phy;
+ goto err_clk;
}
return 0;
-err_phy:
- clk_disable_unprepare(dsi->pll_clk);
-err_pll_clk:
- clk_disable_unprepare(dsi->bus_clk);
-err_bus_clk:
+err_clk:
+ while (--i > -1)
+ clk_disable_unprepare(dsi->clks[i]);
regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
return ret;
@@ -1334,7 +1493,8 @@ err_bus_clk:
static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
{
- int ret;
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ int ret, i;
usleep_range(10000, 20000);
@@ -1350,8 +1510,8 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
phy_power_off(dsi->phy);
- clk_disable_unprepare(dsi->pll_clk);
- clk_disable_unprepare(dsi->bus_clk);
+ for (i = driver_data->num_clks - 1; i > -1; i--)
+ clk_disable_unprepare(dsi->clks[i]);
ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
if (ret < 0)
@@ -1369,8 +1529,11 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
if (ret < 0)
return ret;
+ dsi->state |= DSIM_STATE_ENABLED;
+
ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
+ dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi);
return ret;
}
@@ -1378,8 +1541,6 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
- dsi->state |= DSIM_STATE_ENABLED;
-
ret = drm_panel_enable(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
@@ -1389,6 +1550,8 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
return ret;
}
+ dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
+
return 0;
}
@@ -1397,12 +1560,15 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
if (!(dsi->state & DSIM_STATE_ENABLED))
return;
+ dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+
drm_panel_disable(dsi->panel);
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
- exynos_dsi_poweroff(dsi);
dsi->state &= ~DSIM_STATE_ENABLED;
+
+ exynos_dsi_poweroff(dsi);
}
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
@@ -1457,10 +1623,13 @@ static void exynos_dsi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dsi_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
+ .dpms = drm_atomic_helper_connector_dpms,
.detect = exynos_dsi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = exynos_dsi_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dsi_get_modes(struct drm_connector *connector)
@@ -1627,7 +1796,22 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency",
&dsi->esc_clk_rate);
+ if (ret < 0)
+ goto end;
+
+ of_node_put(ep);
+
+ ep = of_graph_get_next_endpoint(node, NULL);
+ if (!ep) {
+ ret = -ENXIO;
+ goto end;
+ }
+ dsi->bridge_node = of_graph_get_remote_port_parent(ep);
+ if (!dsi->bridge_node) {
+ ret = -ENXIO;
+ goto end;
+ }
end:
of_node_put(ep);
@@ -1640,6 +1824,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
struct exynos_drm_display *display = dev_get_drvdata(dev);
struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_device *drm_dev = data;
+ struct drm_bridge *bridge;
int ret;
ret = exynos_drm_create_enc_conn(drm_dev, display);
@@ -1649,6 +1834,12 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
return ret;
}
+ bridge = of_drm_find_bridge(dsi->bridge_node);
+ if (bridge) {
+ display->encoder->bridge = bridge;
+ drm_bridge_attach(drm_dev, bridge);
+ }
+
return mipi_dsi_host_register(&dsi->dsi_host);
}
@@ -1673,7 +1864,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct resource *res;
struct exynos_dsi *dsi;
- int ret;
+ int ret, i;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
@@ -1682,11 +1873,6 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD;
dsi->display.ops = &exynos_dsi_display_ops;
- ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- dsi->display.type);
- if (ret)
- return ret;
-
/* To be checked as invalid one */
dsi->te_gpio = -ENOENT;
@@ -1702,7 +1888,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
ret = exynos_dsi_parse_dt(dsi);
if (ret)
- goto err_del_component;
+ return ret;
dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio";
@@ -1713,40 +1899,44 @@ static int exynos_dsi_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- dsi->pll_clk = devm_clk_get(dev, "pll_clk");
- if (IS_ERR(dsi->pll_clk)) {
- dev_info(dev, "failed to get dsi pll input clock\n");
- ret = PTR_ERR(dsi->pll_clk);
- goto err_del_component;
- }
+ dsi->clks = devm_kzalloc(dev,
+ sizeof(*dsi->clks) * dsi->driver_data->num_clks,
+ GFP_KERNEL);
+ if (!dsi->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < dsi->driver_data->num_clks; i++) {
+ dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
+ if (IS_ERR(dsi->clks[i])) {
+ if (strcmp(clk_names[i], "sclk_mipi") == 0) {
+ strcpy(clk_names[i], OLD_SCLK_MIPI_CLK_NAME);
+ i--;
+ continue;
+ }
- dsi->bus_clk = devm_clk_get(dev, "bus_clk");
- if (IS_ERR(dsi->bus_clk)) {
- dev_info(dev, "failed to get dsi bus clock\n");
- ret = PTR_ERR(dsi->bus_clk);
- goto err_del_component;
+ dev_info(dev, "failed to get the clock: %s\n",
+ clk_names[i]);
+ return PTR_ERR(dsi->clks[i]);
+ }
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dsi->reg_base)) {
dev_err(dev, "failed to remap io region\n");
- ret = PTR_ERR(dsi->reg_base);
- goto err_del_component;
+ return PTR_ERR(dsi->reg_base);
}
dsi->phy = devm_phy_get(dev, "dsim");
if (IS_ERR(dsi->phy)) {
dev_info(dev, "failed to get dsim phy\n");
- ret = PTR_ERR(dsi->phy);
- goto err_del_component;
+ return PTR_ERR(dsi->phy);
}
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) {
dev_err(dev, "failed to request dsi irq resource\n");
- ret = dsi->irq;
- goto err_del_component;
+ return dsi->irq;
}
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
@@ -1755,26 +1945,17 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dev_name(dev), dsi);
if (ret) {
dev_err(dev, "failed to request dsi irq\n");
- goto err_del_component;
+ return ret;
}
platform_set_drvdata(pdev, &dsi->display);
- ret = component_add(dev, &exynos_dsi_component_ops);
- if (ret)
- goto err_del_component;
-
- return ret;
-
-err_del_component:
- exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
- return ret;
+ return component_add(dev, &exynos_dsi_component_ops);
}
static int exynos_dsi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_dsi_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 57de0bdc5..7b89fd520 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -32,17 +32,6 @@ struct exynos_drm_encoder {
struct exynos_drm_display *display;
};
-static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display *display = exynos_encoder->display;
-
- DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
-
- if (display->ops->dpms)
- display->ops->dpms(display, mode);
-}
-
static bool
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
@@ -76,12 +65,7 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
display->ops->mode_set(display, adjusted_mode);
}
-static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
-{
- /* drm framework doesn't check NULL. */
-}
-
-static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
+static void exynos_drm_encoder_enable(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
@@ -95,24 +79,17 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
{
- struct drm_plane *plane;
- struct drm_device *dev = encoder->dev;
-
- exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display *display = exynos_encoder->display;
- /* all planes connected to this encoder should be also disabled. */
- drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
- if (plane->crtc && (plane->crtc == encoder->crtc))
- plane->funcs->disable_plane(plane);
- }
+ if (display->ops->dpms)
+ display->ops->dpms(display, DRM_MODE_DPMS_OFF);
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
- .dpms = exynos_drm_encoder_dpms,
.mode_fixup = exynos_drm_encoder_mode_fixup,
.mode_set = exynos_drm_encoder_mode_set,
- .prepare = exynos_drm_encoder_prepare,
- .commit = exynos_drm_encoder_commit,
+ .enable = exynos_drm_encoder_enable,
.disable = exynos_drm_encoder_disable,
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 142eb4e3f..2b6320e6e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -16,6 +16,8 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <uapi/drm/exynos_drm.h>
#include "exynos_drm_drv.h"
@@ -265,9 +267,46 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
exynos_drm_fbdev_init(dev);
}
+static int exynos_atomic_commit(struct drm_device *dev,
+ struct drm_atomic_state *state,
+ bool async)
+{
+ int ret;
+
+ ret = drm_atomic_helper_prepare_planes(dev, state);
+ if (ret)
+ return ret;
+
+ /* This is the point of no return */
+
+ drm_atomic_helper_swap_state(dev, state);
+
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+ drm_atomic_helper_commit_modeset_enables(dev, state);
+
+ /*
+ * Exynos can't update planes with CRTCs and encoders disabled,
+ * its updates routines, specially for FIMD, requires the clocks
+ * to be enabled. So it is necessary to handle the modeset operations
+ * *before* the commit_planes() step, this way it will always
+ * have the relevant clocks enabled to perform the update.
+ */
+
+ drm_atomic_helper_commit_planes(dev, state);
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+ drm_atomic_state_free(state);
+
+ return 0;
+}
+
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = exynos_atomic_commit,
};
void exynos_drm_mode_config_init(struct drm_device *dev)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index e71e331f0..e0b085b4b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -275,9 +275,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
}
- /* disable all the possible outputs/crtcs before entering KMS mode */
- drm_helper_disable_unused_functions(dev);
-
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
index 842d6b8dc..2a652359a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
@@ -1745,7 +1745,6 @@ static int fimc_probe(struct platform_device *pdev)
spin_lock_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
- pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = exynos_drm_ippdrv_register(ippdrv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index a0edab833..794e56c87 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -196,6 +196,62 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
+static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
+{
+ struct fimd_context *ctx = crtc->ctx;
+ u32 val;
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (!test_and_set_bit(0, &ctx->irq_flags)) {
+ val = readl(ctx->regs + VIDINTCON0);
+
+ val |= VIDINTCON0_INT_ENABLE;
+
+ if (ctx->i80_if) {
+ val |= VIDINTCON0_INT_I80IFDONE;
+ val |= VIDINTCON0_INT_SYSMAINCON;
+ val &= ~VIDINTCON0_INT_SYSSUBCON;
+ } else {
+ val |= VIDINTCON0_INT_FRAME;
+
+ val &= ~VIDINTCON0_FRAMESEL0_MASK;
+ val |= VIDINTCON0_FRAMESEL0_VSYNC;
+ val &= ~VIDINTCON0_FRAMESEL1_MASK;
+ val |= VIDINTCON0_FRAMESEL1_NONE;
+ }
+
+ writel(val, ctx->regs + VIDINTCON0);
+ }
+
+ return 0;
+}
+
+static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
+{
+ struct fimd_context *ctx = crtc->ctx;
+ u32 val;
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags)) {
+ val = readl(ctx->regs + VIDINTCON0);
+
+ val &= ~VIDINTCON0_INT_ENABLE;
+
+ if (ctx->i80_if) {
+ val &= ~VIDINTCON0_INT_I80IFDONE;
+ val &= ~VIDINTCON0_INT_SYSMAINCON;
+ val &= ~VIDINTCON0_INT_SYSSUBCON;
+ } else
+ val &= ~VIDINTCON0_INT_FRAME;
+
+ writel(val, ctx->regs + VIDINTCON0);
+ }
+}
+
static void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
@@ -242,12 +298,19 @@ static void fimd_enable_shadow_channel_path(struct fimd_context *ctx,
writel(val, ctx->regs + SHADOWCON);
}
-static void fimd_clear_channel(struct fimd_context *ctx)
+static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
{
+ struct fimd_context *ctx = crtc->ctx;
unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
+ /* Hardware is in unknown state, so ensure it gets enabled properly */
+ pm_runtime_get_sync(ctx->dev);
+
+ clk_prepare_enable(ctx->bus_clk);
+ clk_prepare_enable(ctx->lcd_clk);
+
/* Check if any channel is enabled. */
for (win = 0; win < WINDOWS_NR; win++) {
u32 val = readl(ctx->regs + WINCON(win));
@@ -265,36 +328,24 @@ static void fimd_clear_channel(struct fimd_context *ctx)
/* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled) {
- unsigned int state = ctx->suspended;
+ int pipe = ctx->pipe;
- ctx->suspended = 0;
- fimd_wait_for_vblank(ctx->crtc);
- ctx->suspended = state;
- }
-}
+ /* ensure that vblank interrupt won't be reported to core */
+ ctx->suspended = false;
+ ctx->pipe = -1;
-static int fimd_iommu_attach_devices(struct fimd_context *ctx,
- struct drm_device *drm_dev)
-{
-
- /* attach this sub driver to iommu mapping if supported. */
- if (is_drm_iommu_supported(ctx->drm_dev)) {
- int ret;
-
- /*
- * If any channel is already active, iommu will throw
- * a PAGE FAULT when enabled. So clear any channel if enabled.
- */
- fimd_clear_channel(ctx);
- ret = drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
- if (ret) {
- DRM_ERROR("drm_iommu_attach failed.\n");
- return ret;
- }
+ fimd_enable_vblank(ctx->crtc);
+ fimd_wait_for_vblank(ctx->crtc);
+ fimd_disable_vblank(ctx->crtc);
+ ctx->suspended = true;
+ ctx->pipe = pipe;
}
- return 0;
+ clk_disable_unprepare(ctx->lcd_clk);
+ clk_disable_unprepare(ctx->bus_clk);
+
+ pm_runtime_put(ctx->dev);
}
static void fimd_iommu_detach_devices(struct fimd_context *ctx)
@@ -337,7 +388,7 @@ static bool fimd_mode_fixup(struct exynos_drm_crtc *crtc,
static void fimd_commit(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
- struct drm_display_mode *mode = &crtc->base.mode;
+ struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 val, clkdiv;
@@ -434,61 +485,6 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
writel(val, ctx->regs + VIDCON0);
}
-static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
-{
- struct fimd_context *ctx = crtc->ctx;
- u32 val;
-
- if (ctx->suspended)
- return -EPERM;
-
- if (!test_and_set_bit(0, &ctx->irq_flags)) {
- val = readl(ctx->regs + VIDINTCON0);
-
- val |= VIDINTCON0_INT_ENABLE;
-
- if (ctx->i80_if) {
- val |= VIDINTCON0_INT_I80IFDONE;
- val |= VIDINTCON0_INT_SYSMAINCON;
- val &= ~VIDINTCON0_INT_SYSSUBCON;
- } else {
- val |= VIDINTCON0_INT_FRAME;
-
- val &= ~VIDINTCON0_FRAMESEL0_MASK;
- val |= VIDINTCON0_FRAMESEL0_VSYNC;
- val &= ~VIDINTCON0_FRAMESEL1_MASK;
- val |= VIDINTCON0_FRAMESEL1_NONE;
- }
-
- writel(val, ctx->regs + VIDINTCON0);
- }
-
- return 0;
-}
-
-static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
-{
- struct fimd_context *ctx = crtc->ctx;
- u32 val;
-
- if (ctx->suspended)
- return;
-
- if (test_and_clear_bit(0, &ctx->irq_flags)) {
- val = readl(ctx->regs + VIDINTCON0);
-
- val &= ~VIDINTCON0_INT_ENABLE;
-
- if (ctx->i80_if) {
- val &= ~VIDINTCON0_INT_I80IFDONE;
- val &= ~VIDINTCON0_INT_SYSMAINCON;
- val &= ~VIDINTCON0_INT_SYSSUBCON;
- } else
- val &= ~VIDINTCON0_INT_FRAME;
-
- writel(val, ctx->regs + VIDINTCON0);
- }
-}
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
{
@@ -634,11 +630,8 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
- /* If suspended, enable this on resume */
- if (ctx->suspended) {
- plane->resume = true;
+ if (ctx->suspended)
return;
- }
/*
* SHADOWCON/PRTCON register is used for enabling timing.
@@ -728,8 +721,6 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
/* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
- plane->enabled = true;
-
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
}
@@ -744,11 +735,8 @@ static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
- if (ctx->suspended) {
- /* do not resume this window*/
- plane->resume = false;
+ if (ctx->suspended)
return;
- }
/* protect windows */
fimd_shadow_protect_win(ctx, win, true);
@@ -760,57 +748,15 @@ static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
/* unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
-
- plane->enabled = false;
-}
-
-static void fimd_window_suspend(struct fimd_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- plane->resume = plane->enabled;
- if (plane->enabled)
- fimd_win_disable(ctx->crtc, i);
- }
-}
-
-static void fimd_window_resume(struct fimd_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- plane->enabled = plane->resume;
- plane->resume = false;
- }
}
-static void fimd_apply(struct fimd_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- if (plane->enabled)
- fimd_win_commit(ctx->crtc, i);
- else
- fimd_win_disable(ctx->crtc, i);
- }
-
- fimd_commit(ctx->crtc);
-}
-
-static int fimd_poweron(struct fimd_context *ctx)
+static void fimd_enable(struct exynos_drm_crtc *crtc)
{
+ struct fimd_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
- return 0;
+ return;
ctx->suspended = false;
@@ -819,50 +765,43 @@ static int fimd_poweron(struct fimd_context *ctx)
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
- goto bus_clk_err;
+ return;
}
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
- goto lcd_clk_err;
+ return;
}
/* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags)) {
- ret = fimd_enable_vblank(ctx->crtc);
- if (ret) {
- DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
- goto enable_vblank_err;
- }
- }
-
- fimd_window_resume(ctx);
-
- fimd_apply(ctx);
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ fimd_enable_vblank(ctx->crtc);
- return 0;
-
-enable_vblank_err:
- clk_disable_unprepare(ctx->lcd_clk);
-lcd_clk_err:
- clk_disable_unprepare(ctx->bus_clk);
-bus_clk_err:
- ctx->suspended = true;
- return ret;
+ fimd_commit(ctx->crtc);
}
-static int fimd_poweroff(struct fimd_context *ctx)
+static void fimd_disable(struct exynos_drm_crtc *crtc)
{
+ struct fimd_context *ctx = crtc->ctx;
+ int i;
+
if (ctx->suspended)
- return 0;
+ return;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
- fimd_window_suspend(ctx);
+ for (i = 0; i < WINDOWS_NR; i++)
+ fimd_win_disable(crtc, i);
+
+ fimd_enable_vblank(crtc);
+ fimd_wait_for_vblank(crtc);
+ fimd_disable_vblank(crtc);
+
+ writel(0, ctx->regs + VIDCON0);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
@@ -870,26 +809,6 @@ static int fimd_poweroff(struct fimd_context *ctx)
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
- return 0;
-}
-
-static void fimd_dpms(struct exynos_drm_crtc *crtc, int mode)
-{
- DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- fimd_poweron(crtc->ctx);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- fimd_poweroff(crtc->ctx);
- break;
- default:
- DRM_DEBUG_KMS("unspecified mode %d\n", mode);
- break;
- }
}
static void fimd_trigger(struct device *dev)
@@ -964,7 +883,8 @@ static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
}
static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
- .dpms = fimd_dpms,
+ .enable = fimd_enable,
+ .disable = fimd_disable,
.mode_fixup = fimd_mode_fixup,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
@@ -974,6 +894,7 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.win_disable = fimd_win_disable,
.te_handler = fimd_te_handler,
.clock_enable = fimd_dp_clock_enable,
+ .clear_channels = fimd_clear_channels,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
@@ -1043,7 +964,11 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
- return fimd_iommu_attach_devices(ctx, drm_dev);
+ ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev);
+ if (ret)
+ priv->pipe--;
+
+ return ret;
}
static void fimd_unbind(struct device *dev, struct device *master,
@@ -1051,7 +976,7 @@ static void fimd_unbind(struct device *dev, struct device *master,
{
struct fimd_context *ctx = dev_get_drvdata(dev);
- fimd_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
+ fimd_disable(ctx->crtc);
fimd_iommu_detach_devices(ctx);
@@ -1079,11 +1004,6 @@ static int fimd_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
- ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
- EXYNOS_DISPLAY_TYPE_LCD);
- if (ret)
- return ret;
-
ctx->dev = dev;
ctx->suspended = true;
ctx->driver_data = drm_fimd_get_driver_data(pdev);
@@ -1134,38 +1054,33 @@ static int fimd_probe(struct platform_device *pdev)
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
- ret = PTR_ERR(ctx->bus_clk);
- goto err_del_component;
+ return PTR_ERR(ctx->bus_clk);
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
- ret = PTR_ERR(ctx->lcd_clk);
- goto err_del_component;
+ return PTR_ERR(ctx->lcd_clk);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctx->regs)) {
- ret = PTR_ERR(ctx->regs);
- goto err_del_component;
- }
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
ctx->i80_if ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "irq request failed.\n");
- ret = -ENXIO;
- goto err_del_component;
+ return -ENXIO;
}
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
- goto err_del_component;
+ return ret;
}
init_waitqueue_head(&ctx->wait_vsync_queue);
@@ -1175,8 +1090,7 @@ static int fimd_probe(struct platform_device *pdev)
ctx->display = exynos_dpi_probe(dev);
if (IS_ERR(ctx->display)) {
- ret = PTR_ERR(ctx->display);
- goto err_del_component;
+ return PTR_ERR(ctx->display);
}
pm_runtime_enable(dev);
@@ -1190,8 +1104,6 @@ static int fimd_probe(struct platform_device *pdev)
err_disable_pm_runtime:
pm_runtime_disable(dev);
-err_del_component:
- exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
@@ -1200,7 +1112,6 @@ static int fimd_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &fimd_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 308173cb4..6f42e2248 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -61,7 +61,6 @@ struct exynos_drm_gem_buf {
* or at framebuffer creation.
* @size: size requested from user, in bytes and this size is aligned
* in page unit.
- * @vma: a pointer to vm_area.
* @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transferred to user as kms_bo.handle so
@@ -71,7 +70,6 @@ struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_gem_buf *buffer;
unsigned long size;
- struct vm_area_struct *vma;
unsigned int flags;
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index 8040ed2a8..f1c6b76c1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -593,8 +593,7 @@ static int gsc_src_set_transf(struct device *dev,
gsc_write(cfg, GSC_IN_CON);
- ctx->rotation = cfg &
- (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0;
+ ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;
*swap = ctx->rotation;
return 0;
@@ -857,8 +856,7 @@ static int gsc_dst_set_transf(struct device *dev,
gsc_write(cfg, GSC_IN_CON);
- ctx->rotation = cfg &
- (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0;
+ ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0;
*swap = ctx->rotation;
return 0;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c
index b32b291f8..d4ec7465e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c
@@ -100,6 +100,9 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
+ if (subdrv_dev->archdata.mapping)
+ arm_iommu_detach_device(subdrv_dev);
+
ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping);
if (ret < 0) {
DRM_DEBUG_KMS("failed iommu attach.\n");
@@ -114,8 +117,8 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
* If iommu attach succeeded, the sub driver would have dma_ops
* for iommu and also all sub drivers have same dma_ops.
*/
- if (!dev->archdata.dma_ops)
- dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops;
+ if (get_dma_ops(dev) == get_dma_ops(NULL))
+ set_dma_ops(dev, get_dma_ops(subdrv_dev));
return 0;
}
@@ -141,3 +144,17 @@ void drm_iommu_detach_device(struct drm_device *drm_dev,
iommu_detach_device(mapping->domain, subdrv_dev);
drm_release_iommu_mapping(drm_dev);
}
+
+int drm_iommu_attach_device_if_possible(struct exynos_drm_crtc *exynos_crtc,
+ struct drm_device *drm_dev, struct device *subdrv_dev)
+{
+ int ret = 0;
+
+ if (is_drm_iommu_supported(drm_dev)) {
+ if (exynos_crtc->ops->clear_channels)
+ exynos_crtc->ops->clear_channels(exynos_crtc);
+ return drm_iommu_attach_device(drm_dev, subdrv_dev);
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index 35d25889b..8341c7a47 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -38,6 +38,10 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
#endif
}
+int drm_iommu_attach_device_if_possible(
+ struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev,
+ struct device *subdrv_dev);
+
#else
static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
@@ -65,5 +69,12 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
return false;
}
+static inline int drm_iommu_attach_device_if_possible(
+ struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev,
+ struct device *subdrv_dev)
+{
+ return 0;
+}
+
#endif
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index b7f1cbc46..67e5451e0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -45,9 +45,6 @@
#define get_ipp_context(dev) platform_get_drvdata(to_platform_device(dev))
#define ipp_is_m2m_cmd(c) (c == IPP_CMD_M2M)
-/* platform device pointer for ipp device. */
-static struct platform_device *exynos_drm_ipp_pdev;
-
/*
* A structure of event.
*
@@ -102,30 +99,6 @@ static LIST_HEAD(exynos_drm_ippdrv_list);
static DEFINE_MUTEX(exynos_drm_ippdrv_lock);
static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list);
-int exynos_platform_device_ipp_register(void)
-{
- struct platform_device *pdev;
-
- if (exynos_drm_ipp_pdev)
- return -EEXIST;
-
- pdev = platform_device_register_simple("exynos-drm-ipp", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- exynos_drm_ipp_pdev = pdev;
-
- return 0;
-}
-
-void exynos_platform_device_ipp_unregister(void)
-{
- if (exynos_drm_ipp_pdev) {
- platform_device_unregister(exynos_drm_ipp_pdev);
- exynos_drm_ipp_pdev = NULL;
- }
-}
-
int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
{
mutex_lock(&exynos_drm_ippdrv_lock);
@@ -482,12 +455,11 @@ static int ipp_validate_mem_node(struct drm_device *drm_dev,
{
struct drm_exynos_ipp_config *ipp_cfg;
unsigned int num_plane;
- unsigned long min_size, size;
- unsigned int bpp;
+ unsigned long size, buf_size = 0, plane_size, img_size = 0;
+ unsigned int bpp, width, height;
int i;
- /* The property id should already be varified */
- ipp_cfg = &c_node->property.config[m_node->prop_id];
+ ipp_cfg = &c_node->property.config[m_node->ops_id];
num_plane = drm_format_num_planes(ipp_cfg->fmt);
/**
@@ -498,20 +470,45 @@ static int ipp_validate_mem_node(struct drm_device *drm_dev,
* but it seems more than enough
*/
for (i = 0; i < num_plane; ++i) {
- if (!m_node->buf_info.handles[i]) {
- DRM_ERROR("invalid handle for plane %d\n", i);
- return -EINVAL;
- }
+ width = ipp_cfg->sz.hsize;
+ height = ipp_cfg->sz.vsize;
bpp = drm_format_plane_cpp(ipp_cfg->fmt, i);
- min_size = (ipp_cfg->sz.hsize * ipp_cfg->sz.vsize * bpp) >> 3;
- size = exynos_drm_gem_get_size(drm_dev,
- m_node->buf_info.handles[i],
- c_node->filp);
- if (min_size > size) {
- DRM_ERROR("invalid size for plane %d\n", i);
- return -EINVAL;
+
+ /*
+ * The result of drm_format_plane_cpp() for chroma planes must
+ * be used with drm_format_xxxx_chroma_subsampling() for
+ * correct result.
+ */
+ if (i > 0) {
+ width /= drm_format_horz_chroma_subsampling(
+ ipp_cfg->fmt);
+ height /= drm_format_vert_chroma_subsampling(
+ ipp_cfg->fmt);
}
+ plane_size = width * height * bpp;
+ img_size += plane_size;
+
+ if (m_node->buf_info.handles[i]) {
+ size = exynos_drm_gem_get_size(drm_dev,
+ m_node->buf_info.handles[i],
+ c_node->filp);
+ if (plane_size > size) {
+ DRM_ERROR(
+ "buffer %d is smaller than required\n",
+ i);
+ return -EINVAL;
+ }
+
+ buf_size += size;
+ }
+ }
+
+ if (buf_size < img_size) {
+ DRM_ERROR("size of buffers(%lu) is smaller than image(%lu)\n",
+ buf_size, img_size);
+ return -EINVAL;
}
+
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c
new file mode 100644
index 000000000..8994eab56
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors:
+ * Hyungwon Hwang <human.hwang@samsung.com>
+ *
+ * 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 Foundationr
+ */
+
+#include <linux/platform_device.h>
+#include <video/of_videomode.h>
+#include <linux/of_address.h>
+#include <video/videomode.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/clk.h>
+#include <drm/drmP.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+/* Sysreg registers for MIC */
+#define DSD_CFG_MUX 0x1004
+#define MIC0_RGB_MUX (1 << 0)
+#define MIC0_I80_MUX (1 << 1)
+#define MIC0_ON_MUX (1 << 5)
+
+/* MIC registers */
+#define MIC_OP 0x0
+#define MIC_IP_VER 0x0004
+#define MIC_V_TIMING_0 0x0008
+#define MIC_V_TIMING_1 0x000C
+#define MIC_IMG_SIZE 0x0010
+#define MIC_INPUT_TIMING_0 0x0014
+#define MIC_INPUT_TIMING_1 0x0018
+#define MIC_2D_OUTPUT_TIMING_0 0x001C
+#define MIC_2D_OUTPUT_TIMING_1 0x0020
+#define MIC_2D_OUTPUT_TIMING_2 0x0024
+#define MIC_3D_OUTPUT_TIMING_0 0x0028
+#define MIC_3D_OUTPUT_TIMING_1 0x002C
+#define MIC_3D_OUTPUT_TIMING_2 0x0030
+#define MIC_CORE_PARA_0 0x0034
+#define MIC_CORE_PARA_1 0x0038
+#define MIC_CTC_CTRL 0x0040
+#define MIC_RD_DATA 0x0044
+
+#define MIC_UPD_REG (1 << 31)
+#define MIC_ON_REG (1 << 30)
+#define MIC_TD_ON_REG (1 << 29)
+#define MIC_BS_CHG_OUT (1 << 16)
+#define MIC_VIDEO_TYPE(x) (((x) & 0xf) << 12)
+#define MIC_PSR_EN (1 << 5)
+#define MIC_SW_RST (1 << 4)
+#define MIC_ALL_RST (1 << 3)
+#define MIC_CORE_VER_CONTROL (1 << 2)
+#define MIC_MODE_SEL_COMMAND_MODE (1 << 1)
+#define MIC_MODE_SEL_MASK (1 << 1)
+#define MIC_CORE_EN (1 << 0)
+
+#define MIC_V_PULSE_WIDTH(x) (((x) & 0x3fff) << 16)
+#define MIC_V_PERIOD_LINE(x) ((x) & 0x3fff)
+
+#define MIC_VBP_SIZE(x) (((x) & 0x3fff) << 16)
+#define MIC_VFP_SIZE(x) ((x) & 0x3fff)
+
+#define MIC_IMG_V_SIZE(x) (((x) & 0x3fff) << 16)
+#define MIC_IMG_H_SIZE(x) ((x) & 0x3fff)
+
+#define MIC_H_PULSE_WIDTH_IN(x) (((x) & 0x3fff) << 16)
+#define MIC_H_PERIOD_PIXEL_IN(x) ((x) & 0x3fff)
+
+#define MIC_HBP_SIZE_IN(x) (((x) & 0x3fff) << 16)
+#define MIC_HFP_SIZE_IN(x) ((x) & 0x3fff)
+
+#define MIC_H_PULSE_WIDTH_2D(x) (((x) & 0x3fff) << 16)
+#define MIC_H_PERIOD_PIXEL_2D(x) ((x) & 0x3fff)
+
+#define MIC_HBP_SIZE_2D(x) (((x) & 0x3fff) << 16)
+#define MIC_HFP_SIZE_2D(x) ((x) & 0x3fff)
+
+#define MIC_BS_SIZE_2D(x) ((x) & 0x3fff)
+
+enum {
+ ENDPOINT_DECON_NODE,
+ ENDPOINT_DSI_NODE,
+ NUM_ENDPOINTS
+};
+
+static char *clk_names[] = { "pclk_mic0", "sclk_rgb_vclk_to_mic0" };
+#define NUM_CLKS ARRAY_SIZE(clk_names)
+static DEFINE_MUTEX(mic_mutex);
+
+struct exynos_mic {
+ struct device *dev;
+ void __iomem *reg;
+ struct regmap *sysreg;
+ struct clk *clks[NUM_CLKS];
+
+ bool i80_mode;
+ struct videomode vm;
+ struct drm_encoder *encoder;
+ struct drm_bridge bridge;
+
+ bool enabled;
+};
+
+static void mic_set_path(struct exynos_mic *mic, bool enable)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(mic->sysreg, DSD_CFG_MUX, &val);
+ if (ret) {
+ DRM_ERROR("mic: Failed to read system register\n");
+ return;
+ }
+
+ if (enable) {
+ if (mic->i80_mode)
+ val |= MIC0_I80_MUX;
+ else
+ val |= MIC0_RGB_MUX;
+
+ val |= MIC0_ON_MUX;
+ } else
+ val &= ~(MIC0_RGB_MUX | MIC0_I80_MUX | MIC0_ON_MUX);
+
+ regmap_write(mic->sysreg, DSD_CFG_MUX, val);
+ if (ret)
+ DRM_ERROR("mic: Failed to read system register\n");
+}
+
+static int mic_sw_reset(struct exynos_mic *mic)
+{
+ unsigned int retry = 100;
+ int ret;
+
+ writel(MIC_SW_RST, mic->reg + MIC_OP);
+
+ while (retry-- > 0) {
+ ret = readl(mic->reg + MIC_OP);
+ if (!(ret & MIC_SW_RST))
+ return 0;
+
+ udelay(10);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void mic_set_porch_timing(struct exynos_mic *mic)
+{
+ struct videomode vm = mic->vm;
+ u32 reg;
+
+ reg = MIC_V_PULSE_WIDTH(vm.vsync_len) +
+ MIC_V_PERIOD_LINE(vm.vsync_len + vm.vactive +
+ vm.vback_porch + vm.vfront_porch);
+ writel(reg, mic->reg + MIC_V_TIMING_0);
+
+ reg = MIC_VBP_SIZE(vm.vback_porch) +
+ MIC_VFP_SIZE(vm.vfront_porch);
+ writel(reg, mic->reg + MIC_V_TIMING_1);
+
+ reg = MIC_V_PULSE_WIDTH(vm.hsync_len) +
+ MIC_V_PERIOD_LINE(vm.hsync_len + vm.hactive +
+ vm.hback_porch + vm.hfront_porch);
+ writel(reg, mic->reg + MIC_INPUT_TIMING_0);
+
+ reg = MIC_VBP_SIZE(vm.hback_porch) +
+ MIC_VFP_SIZE(vm.hfront_porch);
+ writel(reg, mic->reg + MIC_INPUT_TIMING_1);
+}
+
+static void mic_set_img_size(struct exynos_mic *mic)
+{
+ struct videomode *vm = &mic->vm;
+ u32 reg;
+
+ reg = MIC_IMG_H_SIZE(vm->hactive) +
+ MIC_IMG_V_SIZE(vm->vactive);
+
+ writel(reg, mic->reg + MIC_IMG_SIZE);
+}
+
+static void mic_set_output_timing(struct exynos_mic *mic)
+{
+ struct videomode vm = mic->vm;
+ u32 reg, bs_size_2d;
+
+ DRM_DEBUG("w: %u, h: %u\n", vm.hactive, vm.vactive);
+ bs_size_2d = ((vm.hactive >> 2) << 1) + (vm.vactive % 4);
+ reg = MIC_BS_SIZE_2D(bs_size_2d);
+ writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_2);
+
+ if (!mic->i80_mode) {
+ reg = MIC_H_PULSE_WIDTH_2D(vm.hsync_len) +
+ MIC_H_PERIOD_PIXEL_2D(vm.hsync_len + bs_size_2d +
+ vm.hback_porch + vm.hfront_porch);
+ writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_0);
+
+ reg = MIC_HBP_SIZE_2D(vm.hback_porch) +
+ MIC_H_PERIOD_PIXEL_2D(vm.hfront_porch);
+ writel(reg, mic->reg + MIC_2D_OUTPUT_TIMING_1);
+ }
+}
+
+static void mic_set_reg_on(struct exynos_mic *mic, bool enable)
+{
+ u32 reg = readl(mic->reg + MIC_OP);
+
+ if (enable) {
+ reg &= ~(MIC_MODE_SEL_MASK | MIC_CORE_VER_CONTROL | MIC_PSR_EN);
+ reg |= (MIC_CORE_EN | MIC_BS_CHG_OUT | MIC_ON_REG);
+
+ reg &= ~MIC_MODE_SEL_COMMAND_MODE;
+ if (mic->i80_mode)
+ reg |= MIC_MODE_SEL_COMMAND_MODE;
+ } else {
+ reg &= ~MIC_CORE_EN;
+ }
+
+ reg |= MIC_UPD_REG;
+ writel(reg, mic->reg + MIC_OP);
+}
+
+static struct device_node *get_remote_node(struct device_node *from, int reg)
+{
+ struct device_node *endpoint = NULL, *remote_node = NULL;
+
+ endpoint = of_graph_get_endpoint_by_regs(from, reg, -1);
+ if (!endpoint) {
+ DRM_ERROR("mic: Failed to find remote port from %s",
+ from->full_name);
+ goto exit;
+ }
+
+ remote_node = of_graph_get_remote_port_parent(endpoint);
+ if (!remote_node) {
+ DRM_ERROR("mic: Failed to find remote port parent from %s",
+ from->full_name);
+ goto exit;
+ }
+
+exit:
+ of_node_put(endpoint);
+ return remote_node;
+}
+
+static int parse_dt(struct exynos_mic *mic)
+{
+ int ret = 0, i, j;
+ struct device_node *remote_node;
+ struct device_node *nodes[3];
+
+ /*
+ * The order of endpoints does matter.
+ * The first node must be for decon and the second one must be for dsi.
+ */
+ for (i = 0, j = 0; i < NUM_ENDPOINTS; i++) {
+ remote_node = get_remote_node(mic->dev->of_node, i);
+ if (!remote_node) {
+ ret = -EPIPE;
+ goto exit;
+ }
+ nodes[j++] = remote_node;
+
+ switch (i) {
+ case ENDPOINT_DECON_NODE:
+ /* decon node */
+ if (of_get_child_by_name(remote_node,
+ "i80-if-timings"))
+ mic->i80_mode = 1;
+
+ break;
+ case ENDPOINT_DSI_NODE:
+ /* panel node */
+ remote_node = get_remote_node(remote_node, 1);
+ if (!remote_node) {
+ ret = -EPIPE;
+ goto exit;
+ }
+ nodes[j++] = remote_node;
+
+ ret = of_get_videomode(remote_node,
+ &mic->vm, 0);
+ if (ret) {
+ DRM_ERROR("mic: failed to get videomode");
+ goto exit;
+ }
+
+ break;
+ default:
+ DRM_ERROR("mic: Unknown endpoint from MIC");
+ break;
+ }
+ }
+
+exit:
+ while (--j > -1)
+ of_node_put(nodes[j]);
+
+ return ret;
+}
+
+void mic_disable(struct drm_bridge *bridge) { }
+
+void mic_post_disable(struct drm_bridge *bridge)
+{
+ struct exynos_mic *mic = bridge->driver_private;
+ int i;
+
+ mutex_lock(&mic_mutex);
+ if (!mic->enabled)
+ goto already_disabled;
+
+ mic_set_path(mic, 0);
+
+ for (i = NUM_CLKS - 1; i > -1; i--)
+ clk_disable_unprepare(mic->clks[i]);
+
+ mic->enabled = 0;
+
+already_disabled:
+ mutex_unlock(&mic_mutex);
+}
+
+void mic_pre_enable(struct drm_bridge *bridge)
+{
+ struct exynos_mic *mic = bridge->driver_private;
+ int ret, i;
+
+ mutex_lock(&mic_mutex);
+ if (mic->enabled)
+ goto already_enabled;
+
+ for (i = 0; i < NUM_CLKS; i++) {
+ ret = clk_prepare_enable(mic->clks[i]);
+ if (ret < 0) {
+ DRM_ERROR("Failed to enable clock (%s)\n",
+ clk_names[i]);
+ goto turn_off_clks;
+ }
+ }
+
+ mic_set_path(mic, 1);
+
+ ret = mic_sw_reset(mic);
+ if (ret) {
+ DRM_ERROR("Failed to reset\n");
+ goto turn_off_clks;
+ }
+
+ if (!mic->i80_mode)
+ mic_set_porch_timing(mic);
+ mic_set_img_size(mic);
+ mic_set_output_timing(mic);
+ mic_set_reg_on(mic, 1);
+ mic->enabled = 1;
+ mutex_unlock(&mic_mutex);
+
+ return;
+
+turn_off_clks:
+ while (--i > -1)
+ clk_disable_unprepare(mic->clks[i]);
+already_enabled:
+ mutex_unlock(&mic_mutex);
+}
+
+void mic_enable(struct drm_bridge *bridge) { }
+
+void mic_destroy(struct drm_bridge *bridge)
+{
+ struct exynos_mic *mic = bridge->driver_private;
+ int i;
+
+ mutex_lock(&mic_mutex);
+ if (!mic->enabled)
+ goto already_disabled;
+
+ for (i = NUM_CLKS - 1; i > -1; i--)
+ clk_disable_unprepare(mic->clks[i]);
+
+already_disabled:
+ mutex_unlock(&mic_mutex);
+}
+
+struct drm_bridge_funcs mic_bridge_funcs = {
+ .disable = mic_disable,
+ .post_disable = mic_post_disable,
+ .pre_enable = mic_pre_enable,
+ .enable = mic_enable,
+};
+
+int exynos_mic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_mic *mic;
+ struct resource res;
+ int ret, i;
+
+ mic = devm_kzalloc(dev, sizeof(*mic), GFP_KERNEL);
+ if (!mic) {
+ DRM_ERROR("mic: Failed to allocate memory for MIC object\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mic->dev = dev;
+
+ ret = parse_dt(mic);
+ if (ret)
+ goto err;
+
+ ret = of_address_to_resource(dev->of_node, 0, &res);
+ if (ret) {
+ DRM_ERROR("mic: Failed to get mem region for MIC\n");
+ goto err;
+ }
+ mic->reg = devm_ioremap(dev, res.start, resource_size(&res));
+ if (!mic->reg) {
+ DRM_ERROR("mic: Failed to remap for MIC\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mic->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,disp-syscon");
+ if (IS_ERR(mic->sysreg)) {
+ DRM_ERROR("mic: Failed to get system register.\n");
+ goto err;
+ }
+
+ mic->bridge.funcs = &mic_bridge_funcs;
+ mic->bridge.of_node = dev->of_node;
+ mic->bridge.driver_private = mic;
+ ret = drm_bridge_add(&mic->bridge);
+ if (ret) {
+ DRM_ERROR("mic: Failed to add MIC to the global bridge list\n");
+ goto err;
+ }
+
+ for (i = 0; i < NUM_CLKS; i++) {
+ mic->clks[i] = of_clk_get_by_name(dev->of_node, clk_names[i]);
+ if (IS_ERR(mic->clks[i])) {
+ DRM_ERROR("mic: Failed to get clock (%s)\n",
+ clk_names[i]);
+ ret = PTR_ERR(mic->clks[i]);
+ goto err;
+ }
+ }
+
+ DRM_DEBUG_KMS("MIC has been probed\n");
+
+err:
+ return ret;
+}
+
+static int exynos_mic_remove(struct platform_device *pdev)
+{
+ struct exynos_mic *mic = platform_get_drvdata(pdev);
+ int i;
+
+ drm_bridge_remove(&mic->bridge);
+
+ for (i = NUM_CLKS - 1; i > -1; i--)
+ clk_put(mic->clks[i]);
+
+ return 0;
+}
+
+static const struct of_device_id exynos_mic_of_match[] = {
+ { .compatible = "samsung,exynos5433-mic" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, exynos_mic_of_match);
+
+struct platform_driver mic_driver = {
+ .probe = exynos_mic_probe,
+ .remove = exynos_mic_remove,
+ .driver = {
+ .name = "exynos-mic",
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_mic_of_match,
+ },
+};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index b1180fbe7..a729980d3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -13,6 +13,7 @@
#include <drm/exynos_drm.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
@@ -61,42 +62,21 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last)
return size;
}
-int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
-{
- struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
- int nr;
- int i;
-
- nr = exynos_drm_fb_get_buf_cnt(fb);
- for (i = 0; i < nr; i++) {
- struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
-
- if (!buffer) {
- DRM_DEBUG_KMS("buffer is null\n");
- return -EFAULT;
- }
-
- exynos_plane->dma_addr[i] = buffer->dma_addr + fb->offsets[i];
-
- DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
- i, (unsigned long)exynos_plane->dma_addr[i]);
- }
-
- return 0;
-}
-
-void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h)
+static void exynos_plane_mode_set(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
unsigned int actual_w;
unsigned int actual_h;
- actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
- actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
+ actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
+ actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
if (crtc_x < 0) {
if (actual_w)
@@ -132,10 +112,10 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
exynos_plane->crtc_height = actual_h;
/* set drm mode data. */
- exynos_plane->mode_width = crtc->mode.hdisplay;
- exynos_plane->mode_height = crtc->mode.vdisplay;
- exynos_plane->refresh = crtc->mode.vrefresh;
- exynos_plane->scan_flag = crtc->mode.flags;
+ exynos_plane->mode_width = mode->hdisplay;
+ exynos_plane->mode_height = mode->vdisplay;
+ exynos_plane->refresh = mode->vrefresh;
+ exynos_plane->scan_flag = mode->flags;
DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
exynos_plane->crtc_x, exynos_plane->crtc_y,
@@ -144,48 +124,83 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
plane->crtc = crtc;
}
-int
-exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h)
+static struct drm_plane_funcs exynos_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int exynos_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
{
+ struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
+ int nr;
+ int i;
+
+ if (!state->fb)
+ return 0;
+
+ nr = exynos_drm_fb_get_buf_cnt(state->fb);
+ for (i = 0; i < nr; i++) {
+ struct exynos_drm_gem_buf *buffer =
+ exynos_drm_fb_buffer(state->fb, i);
+
+ if (!buffer) {
+ DRM_DEBUG_KMS("buffer is null\n");
+ return -EFAULT;
+ }
+
+ exynos_plane->dma_addr[i] = buffer->dma_addr +
+ state->fb->offsets[i];
+
+ DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
+ i, (unsigned long)exynos_plane->dma_addr[i]);
+ }
+
+ return 0;
+}
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+static void exynos_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc);
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
- int ret;
- ret = exynos_check_plane(plane, fb);
- if (ret < 0)
- return ret;
+ if (!state->crtc)
+ return;
- exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
- crtc_w, crtc_h, src_x >> 16, src_y >> 16,
- src_w >> 16, src_h >> 16);
+ exynos_plane_mode_set(plane, state->crtc, state->fb,
+ state->crtc_x, state->crtc_y,
+ state->crtc_w, state->crtc_h,
+ state->src_x >> 16, state->src_y >> 16,
+ state->src_w >> 16, state->src_h >> 16);
if (exynos_crtc->ops->win_commit)
exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
-
- return 0;
}
-static int exynos_disable_plane(struct drm_plane *plane)
+static void exynos_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc);
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc);
- if (exynos_crtc && exynos_crtc->ops->win_disable)
+ if (!old_state->crtc)
+ return;
+
+ if (exynos_crtc->ops->win_disable)
exynos_crtc->ops->win_disable(exynos_crtc,
exynos_plane->zpos);
-
- return 0;
}
-static struct drm_plane_funcs exynos_plane_funcs = {
- .update_plane = exynos_update_plane,
- .disable_plane = exynos_disable_plane,
- .destroy = drm_plane_cleanup,
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+ .atomic_check = exynos_plane_atomic_check,
+ .atomic_update = exynos_plane_atomic_update,
+ .atomic_disable = exynos_plane_atomic_disable,
};
static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
@@ -223,6 +238,8 @@ int exynos_plane_init(struct drm_device *dev,
return err;
}
+ drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
+
exynos_plane->zpos = zpos;
if (type == DRM_PLANE_TYPE_OVERLAY)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h
index f360590d1..8c88ae983 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h
@@ -9,17 +9,6 @@
*
*/
-int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb);
-void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h);
-int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h);
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 1b3479a8d..3413393d8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -20,6 +20,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
@@ -130,78 +131,34 @@ static void vidi_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
- plane->enabled = true;
-
DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr);
if (ctx->vblank_on)
schedule_work(&ctx->work);
}
-static void vidi_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
+static void vidi_enable(struct exynos_drm_crtc *crtc)
{
struct vidi_context *ctx = crtc->ctx;
- struct exynos_drm_plane *plane;
-
- if (win < 0 || win >= WINDOWS_NR)
- return;
-
- plane = &ctx->planes[win];
- plane->enabled = false;
-
- /* TODO. */
-}
-
-static int vidi_power_on(struct vidi_context *ctx, bool enable)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
- if (enable != false && enable != true)
- return -EINVAL;
-
- if (enable) {
- ctx->suspended = false;
+ mutex_lock(&ctx->lock);
- /* if vblank was enabled status, enable it again. */
- if (test_and_clear_bit(0, &ctx->irq_flags))
- vidi_enable_vblank(ctx->crtc);
+ ctx->suspended = false;
- for (i = 0; i < WINDOWS_NR; i++) {
- plane = &ctx->planes[i];
- if (plane->enabled)
- vidi_win_commit(ctx->crtc, i);
- }
- } else {
- ctx->suspended = true;
- }
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ vidi_enable_vblank(ctx->crtc);
- return 0;
+ mutex_unlock(&ctx->lock);
}
-static void vidi_dpms(struct exynos_drm_crtc *crtc, int mode)
+static void vidi_disable(struct exynos_drm_crtc *crtc)
{
struct vidi_context *ctx = crtc->ctx;
- DRM_DEBUG_KMS("%d\n", mode);
-
mutex_lock(&ctx->lock);
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- vidi_power_on(ctx, true);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- vidi_power_on(ctx, false);
- break;
- default:
- DRM_DEBUG_KMS("unspecified mode %d\n", mode);
- break;
- }
+ ctx->suspended = true;
mutex_unlock(&ctx->lock);
}
@@ -218,11 +175,11 @@ static int vidi_ctx_initialize(struct vidi_context *ctx,
}
static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
- .dpms = vidi_dpms,
+ .enable = vidi_enable,
+ .disable = vidi_disable,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
.win_commit = vidi_win_commit,
- .win_disable = vidi_win_disable,
};
static void vidi_fake_vblank_handler(struct work_struct *work)
@@ -384,10 +341,13 @@ static void vidi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs vidi_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
+ .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = vidi_detect,
.destroy = vidi_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int vidi_get_modes(struct drm_connector *connector)
@@ -520,16 +480,6 @@ static int vidi_probe(struct platform_device *pdev)
ctx->default_win = 0;
ctx->pdev = pdev;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
- EXYNOS_DISPLAY_TYPE_VIDI);
- if (ret)
- return ret;
-
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- ctx->display.type);
- if (ret)
- goto err_del_crtc_component;
-
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
mutex_init(&ctx->lock);
@@ -539,7 +489,7 @@ static int vidi_probe(struct platform_device *pdev)
ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0) {
DRM_ERROR("failed to create connection sysfs.\n");
- goto err_del_conn_component;
+ return ret;
}
ret = component_add(&pdev->dev, &vidi_component_ops);
@@ -550,10 +500,6 @@ static int vidi_probe(struct platform_device *pdev)
err_remove_file:
device_remove_file(&pdev->dev, &dev_attr_connection);
-err_del_conn_component:
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
-err_del_crtc_component:
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
@@ -570,8 +516,6 @@ static int vidi_remove(struct platform_device *pdev)
}
component_del(&pdev->dev, &vidi_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
@@ -584,38 +528,3 @@ struct platform_driver vidi_driver = {
.owner = THIS_MODULE,
},
};
-
-int exynos_drm_probe_vidi(void)
-{
- struct platform_device *pdev;
- int ret;
-
- pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- ret = platform_driver_register(&vidi_driver);
- if (ret) {
- platform_device_unregister(pdev);
- return ret;
- }
-
- return ret;
-}
-
-static int exynos_drm_remove_vidi_device(struct device *dev, void *data)
-{
- platform_device_unregister(to_platform_device(dev));
-
- return 0;
-}
-
-void exynos_drm_remove_vidi(void)
-{
- int ret = driver_for_each_device(&vidi_driver.driver, NULL, NULL,
- exynos_drm_remove_vidi_device);
- /* silence compiler warning */
- (void)ret;
-
- platform_driver_unregister(&vidi_driver);
-}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 5eba971f3..4a00990e4 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -17,6 +17,7 @@
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
#include "regs-hdmi.h"
@@ -1050,16 +1051,20 @@ static void hdmi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs hdmi_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
+ .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = hdmi_detect,
.destroy = hdmi_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int hdmi_get_modes(struct drm_connector *connector)
{
struct hdmi_context *hdata = ctx_from_connector(connector);
struct edid *edid;
+ int ret;
if (!hdata->ddc_adpt)
return -ENODEV;
@@ -1075,7 +1080,11 @@ static int hdmi_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(connector, edid);
- return drm_add_edid_modes(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+
+ kfree(edid);
+
+ return ret;
}
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
@@ -2123,8 +2132,8 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
*/
if (crtc)
funcs = crtc->helper_private;
- if (funcs && funcs->dpms)
- (*funcs->dpms)(crtc, mode);
+ if (funcs && funcs->disable)
+ (*funcs->disable)(crtc);
hdmi_poweroff(hdata);
break;
@@ -2356,20 +2365,13 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->display.type = EXYNOS_DISPLAY_TYPE_HDMI;
hdata->display.ops = &hdmi_display_ops;
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
- hdata->display.type);
- if (ret)
- return ret;
-
mutex_init(&hdata->hdmi_mutex);
platform_set_drvdata(pdev, hdata);
match = of_match_node(hdmi_match_types, dev->of_node);
- if (!match) {
- ret = -ENODEV;
- goto err_del_component;
- }
+ if (!match)
+ return -ENODEV;
drv_data = (struct hdmi_driver_data *)match->data;
hdata->type = drv_data->type;
@@ -2389,13 +2391,13 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hdata->regs)) {
ret = PTR_ERR(hdata->regs);
- goto err_del_component;
+ return ret;
}
ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
if (ret) {
DRM_ERROR("failed to request HPD gpio\n");
- goto err_del_component;
+ return ret;
}
ddc_node = hdmi_legacy_ddc_dt_binding(dev);
@@ -2406,8 +2408,7 @@ static int hdmi_probe(struct platform_device *pdev)
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
- ret = -ENODEV;
- goto err_del_component;
+ return -ENODEV;
}
out_get_ddc_adpt:
@@ -2491,9 +2492,6 @@ err_hdmiphy:
err_ddc:
put_device(&hdata->ddc_adpt->dev);
-err_del_component:
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
-
return ret;
}
@@ -2513,7 +2511,6 @@ static int hdmi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &hdmi_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 8874c1fcb..4706b5690 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -718,6 +718,10 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
/* handling VSYNC */
if (val & MXR_INT_STATUS_VSYNC) {
+ /* vsync interrupt use different bit for read and clear */
+ val |= MXR_INT_CLEAR_VSYNC;
+ val &= ~MXR_INT_STATUS_VSYNC;
+
/* interlace scan need to check shadow register */
if (ctx->interlace) {
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
@@ -743,11 +747,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
out:
/* clear interrupts */
- if (~val & MXR_INT_EN_VSYNC) {
- /* vsync interrupt use different bit for read and clear */
- val &= ~MXR_INT_EN_VSYNC;
- val |= MXR_INT_CLEAR_VSYNC;
- }
mixer_reg_write(res, MXR_INT_STATUS, val);
spin_unlock(&res->reg_slock);
@@ -882,10 +881,12 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
}
}
- if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
- return 0;
+ ret = drm_iommu_attach_device_if_possible(mixer_ctx->crtc, drm_dev,
+ mixer_ctx->dev);
+ if (ret)
+ priv->pipe--;
- return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
+ return ret;
}
static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
@@ -905,8 +906,8 @@ static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
}
/* enable vsync interrupt */
- mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
- MXR_INT_EN_VSYNC);
+ mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
+ mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
return 0;
}
@@ -916,7 +917,13 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
struct mixer_context *mixer_ctx = crtc->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
+ if (!mixer_ctx->powered) {
+ mixer_ctx->int_en &= MXR_INT_EN_VSYNC;
+ return;
+ }
+
/* disable vsync interrupt */
+ mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
}
@@ -937,8 +944,6 @@ static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
vp_video_buffer(mixer_ctx, win);
else
mixer_graph_buffer(mixer_ctx, win);
-
- mixer_ctx->planes[win].enabled = true;
}
static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
@@ -952,7 +957,6 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
mutex_unlock(&mixer_ctx->mixer_mutex);
- mixer_ctx->planes[win].resume = false;
return;
}
mutex_unlock(&mixer_ctx->mixer_mutex);
@@ -964,8 +968,6 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
mixer_vsync_set_update(mixer_ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
-
- mixer_ctx->planes[win].enabled = false;
}
static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
@@ -1000,36 +1002,11 @@ static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
}
-static void mixer_window_suspend(struct mixer_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < MIXER_WIN_NR; i++) {
- plane = &ctx->planes[i];
- plane->resume = plane->enabled;
- mixer_win_disable(ctx->crtc, i);
- }
- mixer_wait_for_vblank(ctx->crtc);
-}
-
-static void mixer_window_resume(struct mixer_context *ctx)
-{
- struct exynos_drm_plane *plane;
- int i;
-
- for (i = 0; i < MIXER_WIN_NR; i++) {
- plane = &ctx->planes[i];
- plane->enabled = plane->resume;
- plane->resume = false;
- if (plane->enabled)
- mixer_win_commit(ctx->crtc, i);
- }
-}
-
-static void mixer_poweron(struct mixer_context *ctx)
+static void mixer_enable(struct exynos_drm_crtc *crtc)
{
+ struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res;
+ int ret;
mutex_lock(&ctx->mixer_mutex);
if (ctx->powered) {
@@ -1041,12 +1018,32 @@ static void mixer_poweron(struct mixer_context *ctx)
pm_runtime_get_sync(ctx->dev);
- clk_prepare_enable(res->mixer);
- clk_prepare_enable(res->hdmi);
+ ret = clk_prepare_enable(res->mixer);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
+ return;
+ }
+ ret = clk_prepare_enable(res->hdmi);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
+ return;
+ }
if (ctx->vp_enabled) {
- clk_prepare_enable(res->vp);
- if (ctx->has_sclk)
- clk_prepare_enable(res->sclk_mixer);
+ ret = clk_prepare_enable(res->vp);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
+ ret);
+ return;
+ }
+ if (ctx->has_sclk) {
+ ret = clk_prepare_enable(res->sclk_mixer);
+ if (ret < 0) {
+ DRM_ERROR("Failed to prepare_enable the " \
+ "sclk_mixer clk [%d]\n",
+ ret);
+ return;
+ }
+ }
}
mutex_lock(&ctx->mixer_mutex);
@@ -1055,15 +1052,17 @@ static void mixer_poweron(struct mixer_context *ctx)
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
+ if (ctx->int_en & MXR_INT_EN_VSYNC)
+ mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);
-
- mixer_window_resume(ctx);
}
-static void mixer_poweroff(struct mixer_context *ctx)
+static void mixer_disable(struct exynos_drm_crtc *crtc)
{
+ struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res;
+ int i;
mutex_lock(&ctx->mixer_mutex);
if (!ctx->powered) {
@@ -1074,7 +1073,9 @@ static void mixer_poweroff(struct mixer_context *ctx)
mixer_stop(ctx);
mixer_regs_dump(ctx);
- mixer_window_suspend(ctx);
+
+ for (i = 0; i < MIXER_WIN_NR; i++)
+ mixer_win_disable(crtc, i);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
@@ -1093,23 +1094,6 @@ static void mixer_poweroff(struct mixer_context *ctx)
pm_runtime_put_sync(ctx->dev);
}
-static void mixer_dpms(struct exynos_drm_crtc *crtc, int mode)
-{
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- mixer_poweron(crtc->ctx);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- mixer_poweroff(crtc->ctx);
- break;
- default:
- DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
- break;
- }
-}
-
/* Only valid for Mixer version 16.0.33.0 */
int mixer_check_mode(struct drm_display_mode *mode)
{
@@ -1131,7 +1115,8 @@ int mixer_check_mode(struct drm_display_mode *mode)
}
static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
- .dpms = mixer_dpms,
+ .enable = mixer_enable,
+ .disable = mixer_disable,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
.wait_for_vblank = mixer_wait_for_vblank,
@@ -1280,18 +1265,9 @@ static int mixer_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
- EXYNOS_DISPLAY_TYPE_HDMI);
- if (ret)
- return ret;
-
ret = component_add(&pdev->dev, &mixer_component_ops);
- if (ret) {
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
- return ret;
- }
-
- pm_runtime_enable(dev);
+ if (!ret)
+ pm_runtime_enable(dev);
return ret;
}
@@ -1301,7 +1277,6 @@ static int mixer_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &mixer_component_ops);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}