summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/gpu/drm/exynos
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/gpu/drm/exynos
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/gpu/drm/exynos')
-rw-r--r--kernel/drivers/gpu/drm/exynos/Kconfig74
-rw-r--r--kernel/drivers/gpu/drm/exynos/Makefile12
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos5433_drm_decon.c689
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos7_drm_decon.c321
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_dp_core.c163
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_dp_core.h3
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_buf.c186
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_buf.h33
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_core.c42
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.c259
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.h10
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c286
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h20
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_dpi.c137
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_drv.c651
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_drv.h186
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_dsi.c671
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.c197
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.h23
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_fb.c150
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_fb.h17
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_fbdev.c139
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_fimc.c53
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_fimd.c516
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_g2d.c157
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_gem.c574
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_gem.h81
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_gsc.c34
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.c16
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.h4
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_ipp.c97
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_mic.c490
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_plane.c177
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_plane.h14
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_rotator.c2
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_drm_vidi.c257
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_hdmi.c1453
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_mixer.c363
-rw-r--r--kernel/drivers/gpu/drm/exynos/exynos_mixer.h20
-rw-r--r--kernel/drivers/gpu/drm/exynos/regs-hdmi.h33
40 files changed, 4070 insertions, 4540 deletions
diff --git a/kernel/drivers/gpu/drm/exynos/Kconfig b/kernel/drivers/gpu/drm/exynos/Kconfig
index 0a6780367..96e86cf44 100644
--- a/kernel/drivers/gpu/drm/exynos/Kconfig
+++ b/kernel/drivers/gpu/drm/exynos/Kconfig
@@ -11,37 +11,59 @@ config DRM_EXYNOS
Choose this option if you have a Samsung SoC EXYNOS chipset.
If M is selected the module will be called exynosdrm.
+if DRM_EXYNOS
+
config DRM_EXYNOS_IOMMU
bool
- depends on DRM_EXYNOS && EXYNOS_IOMMU && ARM_DMA_USE_IOMMU
+ depends on EXYNOS_IOMMU && ARM_DMA_USE_IOMMU
default y
+comment "CRTCs"
+
config DRM_EXYNOS_FIMD
- bool "Exynos DRM FIMD"
- depends on DRM_EXYNOS && !FB_S3C
+ bool "FIMD"
+ depends on !FB_S3C
select FB_MODE_HELPERS
select MFD_SYSCON
help
Choose this option if you want to use Exynos FIMD for DRM.
+config DRM_EXYNOS5433_DECON
+ bool "DECON on Exynos5433"
+ help
+ Choose this option if you want to use Exynos5433 DECON for DRM.
+
config DRM_EXYNOS7_DECON
- bool "Exynos DRM DECON"
- depends on DRM_EXYNOS
+ bool "DECON on Exynos7"
+ depends on !FB_S3C
select FB_MODE_HELPERS
help
Choose this option if you want to use Exynos DECON for DRM.
+config DRM_EXYNOS_MIXER
+ bool "Mixer"
+ depends on !VIDEO_SAMSUNG_S5P_TV
+ help
+ Choose this option if you want to use Exynos Mixer for DRM.
+
+config DRM_EXYNOS_VIDI
+ bool "Virtual Display"
+ help
+ Choose this option if you want to use Exynos VIDI for DRM.
+
+comment "Encoders and Bridges"
+
config DRM_EXYNOS_DPI
- bool "EXYNOS DRM parallel output support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
+ bool "Parallel output"
+ depends on DRM_EXYNOS_FIMD
select DRM_PANEL
default n
help
This enables support for Exynos parallel output.
config DRM_EXYNOS_DSI
- bool "EXYNOS DRM MIPI-DSI driver support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
+ bool "MIPI-DSI host"
+ depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON
select DRM_MIPI_DSI
select DRM_PANEL
default n
@@ -49,51 +71,55 @@ config DRM_EXYNOS_DSI
This enables support for Exynos MIPI-DSI device.
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)
+ bool "Display Port"
+ depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON
default DRM_EXYNOS
select DRM_PANEL
help
This enables support for DP device.
config DRM_EXYNOS_HDMI
- bool "Exynos DRM HDMI"
- depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
+ bool "HDMI"
+ depends on !VIDEO_SAMSUNG_S5P_TV && (DRM_EXYNOS_MIXER || DRM_EXYNOS5433_DECON)
help
Choose this option if you want to use Exynos HDMI for DRM.
-config DRM_EXYNOS_VIDI
- bool "Exynos DRM Virtual Display"
- depends on DRM_EXYNOS
+config DRM_EXYNOS_MIC
+ bool "Mobile Image Compressor"
+ depends on DRM_EXYNOS5433_DECON
help
- Choose this option if you want to use Exynos VIDI for DRM.
+ Choose this option if you want to use Exynos MIC for DRM.
+
+comment "Sub-drivers"
config DRM_EXYNOS_G2D
- bool "Exynos DRM G2D"
- depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D
+ bool "G2D"
+ depends on !VIDEO_SAMSUNG_S5P_G2D
+ select FRAME_VECTOR
help
Choose this option if you want to use Exynos G2D for DRM.
config DRM_EXYNOS_IPP
- bool "Exynos DRM IPP"
- depends on DRM_EXYNOS
+ bool "Image Post Processor"
help
Choose this option if you want to use IPP feature for DRM.
config DRM_EXYNOS_FIMC
- bool "Exynos DRM FIMC"
+ bool "FIMC"
depends on DRM_EXYNOS_IPP && MFD_SYSCON
help
Choose this option if you want to use Exynos FIMC for DRM.
config DRM_EXYNOS_ROTATOR
- bool "Exynos DRM Rotator"
+ bool "Rotator"
depends on DRM_EXYNOS_IPP
help
Choose this option if you want to use Exynos Rotator for DRM.
config DRM_EXYNOS_GSC
- bool "Exynos DRM GSC"
+ bool "GScaler"
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM
help
Choose this option if you want to use Exynos GSC for DRM.
+
+endif
diff --git a/kernel/drivers/gpu/drm/exynos/Makefile b/kernel/drivers/gpu/drm/exynos/Makefile
index cc90679cf..6496532aa 100644
--- a/kernel/drivers/gpu/drm/exynos/Makefile
+++ b/kernel/drivers/gpu/drm/exynos/Makefile
@@ -3,23 +3,25 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
-exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \
- exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
- exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
- exynos_drm_plane.o exynos_drm_dmabuf.o
+exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fbdev.o \
+ exynos_drm_fb.o exynos_drm_gem.o exynos_drm_core.o \
+ exynos_drm_plane.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
exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER) += exynos_mixer.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
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/kernel/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/kernel/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
new file mode 100644
index 000000000..fbe1b3174
--- /dev/null
+++ b/kernel/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
@@ -0,0 +1,689 @@
+/* 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_device.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 CURSOR_WIN 2
+#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
+
+static const char * const decon_clks_name[] = {
+ "pclk",
+ "aclk_decon",
+ "aclk_smmu_decon0x",
+ "aclk_xiu_decon0x",
+ "pclk_smmu_decon0x",
+ "sclk_decon_vclk",
+ "sclk_decon_eclk",
+};
+
+enum decon_iftype {
+ IFTYPE_RGB,
+ IFTYPE_I80,
+ IFTYPE_HDMI
+};
+
+enum decon_flag_bits {
+ BIT_CLKS_ENABLED,
+ BIT_IRQS_ENABLED,
+ BIT_WIN_UPDATED,
+ BIT_SUSPENDED
+};
+
+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[ARRAY_SIZE(decon_clks_name)];
+ int pipe;
+ unsigned long flags;
+ enum decon_iftype out_type;
+ int first_win;
+};
+
+static const uint32_t decon_formats[] = {
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
+static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask,
+ u32 val)
+{
+ val = (val & mask) | (readl(ctx->addr + reg) & ~mask);
+ writel(val, ctx->addr + reg);
+}
+
+static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ u32 val;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return -EPERM;
+
+ if (test_and_set_bit(BIT_IRQS_ENABLED, &ctx->flags)) {
+ val = VIDINTCON0_INTEN;
+ if (ctx->out_type == IFTYPE_I80)
+ 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 (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ if (test_and_clear_bit(BIT_IRQS_ENABLED, &ctx->flags))
+ writel(0, ctx->addr + DECON_VIDINTCON0);
+}
+
+static void decon_setup_trigger(struct decon_context *ctx)
+{
+ u32 val = (ctx->out_type != IFTYPE_HDMI)
+ ? TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
+ TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN
+ : TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
+ TRIGCON_HWTRIGMASK_I80_RGB | TRIGCON_HWTRIGEN_I80_RGB;
+ 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 *m = &crtc->base.mode;
+ u32 val;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ if (ctx->out_type == IFTYPE_HDMI) {
+ m->crtc_hsync_start = m->crtc_hdisplay + 10;
+ m->crtc_hsync_end = m->crtc_htotal - 92;
+ m->crtc_vsync_start = m->crtc_vdisplay + 1;
+ m->crtc_vsync_end = m->crtc_vsync_start + 1;
+ }
+
+ decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID, 0);
+
+ /* 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->out_type == IFTYPE_I80)
+ val |= VIDOUT_COMMAND_IF;
+ else
+ val |= VIDOUT_RGB_IF;
+ writel(val, ctx->addr + DECON_VIDOUTCON0);
+
+ val = VIDTCON2_LINEVAL(m->vdisplay - 1) |
+ VIDTCON2_HOZVAL(m->hdisplay - 1);
+ writel(val, ctx->addr + DECON_VIDTCON2);
+
+ if (ctx->out_type != IFTYPE_I80) {
+ val = VIDTCON00_VBPD_F(
+ m->crtc_vtotal - m->crtc_vsync_end - 1) |
+ VIDTCON00_VFPD_F(
+ m->crtc_vsync_start - m->crtc_vdisplay - 1);
+ writel(val, ctx->addr + DECON_VIDTCON00);
+
+ val = VIDTCON01_VSPW_F(
+ m->crtc_vsync_end - m->crtc_vsync_start - 1);
+ writel(val, ctx->addr + DECON_VIDTCON01);
+
+ val = VIDTCON10_HBPD_F(
+ m->crtc_htotal - m->crtc_hsync_end - 1) |
+ VIDTCON10_HFPD_F(
+ m->crtc_hsync_start - m->crtc_hdisplay - 1);
+ writel(val, ctx->addr + DECON_VIDTCON10);
+
+ val = VIDTCON11_HSPW_F(
+ m->crtc_hsync_end - m->crtc_hsync_start - 1);
+ writel(val, ctx->addr + DECON_VIDTCON11);
+ }
+
+ decon_setup_trigger(ctx);
+
+ /* enable output and display signal */
+ decon_set_bits(ctx, DECON_VIDCON0, VIDCON0_ENVID | VIDCON0_ENVID_F, ~0);
+}
+
+static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
+ struct drm_framebuffer *fb)
+{
+ unsigned long val;
+
+ val = readl(ctx->addr + DECON_WINCONx(win));
+ val &= ~WINCONx_BPPMODE_MASK;
+
+ switch (fb->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", fb->bits_per_pixel);
+
+ /*
+ * 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 (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)
+{
+ decon_set_bits(ctx, DECON_SHADOWCON, SHADOWCON_Wx_PROTECT(win),
+ protect ? ~0 : 0);
+}
+
+static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct decon_context *ctx = crtc->ctx;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ decon_shadow_protect_win(ctx, plane->zpos, true);
+}
+
+#define BIT_VAL(x, e, s) (((x) & ((1 << ((e) - (s) + 1)) - 1)) << (s))
+#define COORDINATE_X(x) BIT_VAL((x), 23, 12)
+#define COORDINATE_Y(x) BIT_VAL((x), 11, 0)
+
+static void decon_update_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct decon_context *ctx = crtc->ctx;
+ struct drm_plane_state *state = plane->base.state;
+ unsigned int win = plane->zpos;
+ unsigned int bpp = state->fb->bits_per_pixel >> 3;
+ unsigned int pitch = state->fb->pitches[0];
+ u32 val;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ 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_w - 1) |
+ COORDINATE_Y(plane->crtc_y + plane->crtc_h - 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] + pitch * plane->crtc_h;
+ writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
+
+ if (ctx->out_type != IFTYPE_HDMI)
+ val = BIT_VAL(pitch - plane->crtc_w * bpp, 27, 14)
+ | BIT_VAL(plane->crtc_w * bpp, 13, 0);
+ else
+ val = BIT_VAL(pitch - plane->crtc_w * bpp, 29, 15)
+ | BIT_VAL(plane->crtc_w * bpp, 14, 0);
+ writel(val, ctx->addr + DECON_VIDW0xADD2(win));
+
+ decon_win_set_pixfmt(ctx, win, state->fb);
+
+ /* window enable */
+ decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
+
+ /* standalone update */
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+}
+
+static void decon_disable_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct decon_context *ctx = crtc->ctx;
+ unsigned int win = plane->zpos;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ decon_shadow_protect_win(ctx, win, true);
+
+ /* window disable */
+ decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
+
+ decon_shadow_protect_win(ctx, win, false);
+
+ /* standalone update */
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+}
+
+static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct decon_context *ctx = crtc->ctx;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ decon_shadow_protect_win(ctx, plane->zpos, false);
+
+ if (ctx->out_type == IFTYPE_I80)
+ set_bit(BIT_WIN_UPDATED, &ctx->flags);
+}
+
+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");
+
+ if (ctx->out_type != IFTYPE_HDMI)
+ return;
+
+ writel(VIDCON0_CLKVALUP | VIDCON0_VLCKFREE, ctx->addr + DECON_VIDCON0);
+ decon_set_bits(ctx, DECON_CMU,
+ CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F, ~0);
+ writel(VIDCON1_VCLK_RUN_VDEN_DISABLE, ctx->addr + DECON_VIDCON1);
+ writel(CRCCTRL_CRCEN | CRCCTRL_CRCSTART_F | CRCCTRL_CRCCLKEN,
+ ctx->addr + DECON_CRCCTRL);
+ decon_setup_trigger(ctx);
+}
+
+static void decon_enable(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int ret;
+ int i;
+
+ if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags))
+ return;
+
+ 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->flags);
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(BIT_IRQS_ENABLED, &ctx->flags))
+ decon_enable_vblank(ctx->crtc);
+
+ decon_commit(ctx->crtc);
+
+ return;
+err:
+ while (--i >= 0)
+ clk_disable_unprepare(ctx->clks[i]);
+
+ set_bit(BIT_SUSPENDED, &ctx->flags);
+}
+
+static void decon_disable(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int i;
+
+ if (test_bit(BIT_SUSPENDED, &ctx->flags))
+ 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 = ctx->first_win; i < WINDOWS_NR; i++)
+ decon_disable_plane(crtc, &ctx->planes[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->flags);
+
+ pm_runtime_put_sync(ctx->dev);
+
+ set_bit(BIT_SUSPENDED, &ctx->flags);
+}
+
+void decon_te_irq_handler(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+
+ if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
+ return;
+
+ if (test_and_clear_bit(BIT_WIN_UPDATED, &ctx->flags))
+ decon_set_bits(ctx, DECON_TRIGCON, TRIGCON_SWTRIGCMD, ~0);
+
+ drm_crtc_handle_vblank(&ctx->crtc->base);
+}
+
+static void decon_clear_channels(struct exynos_drm_crtc *crtc)
+{
+ struct decon_context *ctx = crtc->ctx;
+ int win, i, ret;
+
+ 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++) {
+ decon_shadow_protect_win(ctx, win, true);
+ decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, 0);
+ decon_shadow_protect_win(ctx, win, false);
+ decon_set_bits(ctx, DECON_UPDATE, STANDALONE_UPDATE_F, ~0);
+ }
+ /* 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,
+ .atomic_begin = decon_atomic_begin,
+ .update_plane = decon_update_plane,
+ .disable_plane = decon_disable_plane,
+ .atomic_flush = decon_atomic_flush,
+ .te_handler = decon_te_irq_handler,
+};
+
+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 exynos_drm_output_type out_type;
+ enum drm_plane_type type;
+ unsigned int win;
+ int ret;
+
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = priv->pipe++;
+
+ for (win = ctx->first_win; win < WINDOWS_NR; win++) {
+ int tmp = (win == ctx->first_win) ? 0 : win;
+
+ type = exynos_plane_get_type(tmp, CURSOR_WIN);
+ ret = exynos_plane_init(drm_dev, &ctx->planes[win],
+ 1 << ctx->pipe, type, decon_formats,
+ ARRAY_SIZE(decon_formats), win);
+ if (ret)
+ return ret;
+ }
+
+ exynos_plane = &ctx->planes[ctx->first_win];
+ out_type = (ctx->out_type == IFTYPE_HDMI) ? EXYNOS_DISPLAY_TYPE_HDMI
+ : EXYNOS_DISPLAY_TYPE_LCD;
+ ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
+ ctx->pipe, out_type,
+ &decon_crtc_ops, ctx);
+ if (IS_ERR(ctx->crtc)) {
+ ret = PTR_ERR(ctx->crtc);
+ goto err;
+ }
+
+ decon_clear_channels(ctx->crtc);
+
+ ret = drm_iommu_attach_device(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. */
+ 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_irq_handler(int irq, void *dev_id)
+{
+ struct decon_context *ctx = dev_id;
+ u32 val;
+ int win;
+
+ if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
+ goto out;
+
+ val = readl(ctx->addr + DECON_VIDINTCON1);
+ val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND;
+
+ if (val) {
+ for (win = ctx->first_win; win < WINDOWS_NR ; win++) {
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+
+ if (!plane->pending_fb)
+ continue;
+
+ exynos_drm_crtc_finish_update(ctx->crtc, plane);
+ }
+
+ /* clear */
+ writel(val, ctx->addr + DECON_VIDINTCON1);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
+ {
+ .compatible = "samsung,exynos5433-decon",
+ .data = (void *)IFTYPE_RGB
+ },
+ {
+ .compatible = "samsung,exynos5433-decon-tv",
+ .data = (void *)IFTYPE_HDMI
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
+
+static int exynos5433_decon_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id;
+ 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;
+
+ __set_bit(BIT_SUSPENDED, &ctx->flags);
+ ctx->dev = dev;
+
+ of_id = of_match_device(exynos5433_decon_driver_dt_match, &pdev->dev);
+ ctx->out_type = (enum decon_iftype)of_id->data;
+
+ if (ctx->out_type == IFTYPE_HDMI)
+ ctx->first_win = 1;
+ else if (of_get_child_by_name(dev->of_node, "i80-if-timings"))
+ ctx->out_type = IFTYPE_I80;
+
+ 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->out_type == IFTYPE_I80) ? "lcd_sys" : "vsync");
+ if (!res) {
+ dev_err(dev, "cannot find IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = devm_request_irq(dev, res->start, decon_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;
+}
+
+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/kernel/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/kernel/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 6714e5b19..ead2b16e2 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -37,10 +37,10 @@
* DECON stands for Display and Enhancement controller.
*/
-#define DECON_DEFAULT_FRAMERATE 60
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
#define WINDOWS_NR 2
+#define CURSOR_WIN 1
struct decon_context {
struct device *dev;
@@ -52,7 +52,6 @@ struct decon_context {
struct clk *eclk;
struct clk *vclk;
void __iomem *regs;
- unsigned int default_win;
unsigned long irq_flags;
bool i80_if;
bool suspended;
@@ -61,7 +60,7 @@ struct decon_context {
atomic_t wait_vsync_event;
struct exynos_drm_panel_info panel;
- struct exynos_drm_display *display;
+ struct drm_encoder *encoder;
};
static const struct of_device_id decon_driver_dt_match[] = {
@@ -70,6 +69,18 @@ static const struct of_device_id decon_driver_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, decon_driver_dt_match);
+static const uint32_t decon_formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888,
+};
+
static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
@@ -89,8 +100,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,34 +132,24 @@ 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;
- }
- }
+ decon_clear_channels(ctx->crtc);
- return 0;
+ ret = drm_iommu_attach_device(drm_dev, ctx->dev);
+ if (ret)
+ priv->pipe--;
+
+ return ret;
}
static void decon_ctx_remove(struct decon_context *ctx)
{
/* 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);
+ drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
}
static u32 decon_calc_clkdiv(struct decon_context *ctx,
@@ -162,20 +164,10 @@ static u32 decon_calc_clkdiv(struct decon_context *ctx,
return (clkdiv < 0x100) ? clkdiv : 0xff;
}
-static bool decon_mode_fixup(struct exynos_drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- if (adjusted_mode->vrefresh == 0)
- adjusted_mode->vrefresh = DECON_DEFAULT_FRAMERATE;
-
- return true;
-}
-
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)
@@ -281,16 +273,16 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
}
}
-static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
+static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
+ struct drm_framebuffer *fb)
{
- struct exynos_drm_plane *plane = &ctx->planes[win];
unsigned long val;
int padding;
val = readl(ctx->regs + WINCON(win));
val &= ~WINCONx_BPPMODE_MASK;
- switch (plane->pixel_format) {
+ switch (fb->pixel_format) {
case DRM_FORMAT_RGB565:
val |= WINCONx_BPPMODE_16BPP_565;
val |= WINCONx_BURSTLEN_16WORD;
@@ -339,7 +331,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
break;
}
- DRM_DEBUG_KMS("bpp = %d\n", plane->bpp);
+ DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
/*
* In case of exynos, setting dma-burst to 16Word causes permanent
@@ -349,8 +341,8 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
* movement causes unstable DMA which results into iommu crash/tear.
*/
- padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
- if (plane->fb_width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+ padding = (fb->pitches[0] / (fb->bits_per_pixel >> 3)) - fb->width;
+ if (fb->width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) {
val &= ~WINCONx_BURSTLEN_MASK;
val |= WINCONx_BURSTLEN_8WORD;
}
@@ -392,29 +384,33 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
writel(val, ctx->regs + SHADOWCON);
}
-static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
- struct drm_display_mode *mode = &crtc->base.mode;
- struct exynos_drm_plane *plane;
- int padding;
- unsigned long val, alpha;
- unsigned int last_x;
- unsigned int last_y;
if (ctx->suspended)
return;
- if (win < 0 || win >= WINDOWS_NR)
- return;
+ decon_shadow_protect_win(ctx, plane->zpos, true);
+}
- plane = &ctx->planes[win];
+static void decon_update_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct decon_context *ctx = crtc->ctx;
+ struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
+ struct drm_plane_state *state = plane->base.state;
+ int padding;
+ unsigned long val, alpha;
+ unsigned int last_x;
+ unsigned int last_y;
+ unsigned int win = plane->zpos;
+ unsigned int bpp = state->fb->bits_per_pixel >> 3;
+ unsigned int pitch = state->fb->pitches[0];
- /* If suspended, enable this on resume */
- if (ctx->suspended) {
- plane->resume = true;
+ if (ctx->suspended)
return;
- }
/*
* SHADOWCON/PRTCON register is used for enabling timing.
@@ -426,18 +422,15 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
* is set.
*/
- /* protect windows */
- decon_shadow_protect_win(ctx, win, true);
-
/* buffer start address */
val = (unsigned long)plane->dma_addr[0];
writel(val, ctx->regs + VIDW_BUF_START(win));
- padding = (plane->pitch / (plane->bpp >> 3)) - plane->fb_width;
+ padding = (pitch / bpp) - state->fb->width;
/* buffer size */
- writel(plane->fb_width + padding, ctx->regs + VIDW_WHOLE_X(win));
- writel(plane->fb_height, ctx->regs + VIDW_WHOLE_Y(win));
+ writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
+ writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win));
/* offset from the start of the buffer to read */
writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win));
@@ -446,25 +439,25 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
DRM_DEBUG_KMS("start addr = 0x%lx\n",
(unsigned long)val);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
- plane->crtc_width, plane->crtc_height);
+ plane->crtc_w, plane->crtc_h);
/*
* OSD position.
* In case the window layout goes of LCD layout, DECON fails.
*/
- if ((plane->crtc_x + plane->crtc_width) > mode->hdisplay)
- plane->crtc_x = mode->hdisplay - plane->crtc_width;
- if ((plane->crtc_y + plane->crtc_height) > mode->vdisplay)
- plane->crtc_y = mode->vdisplay - plane->crtc_height;
+ if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay)
+ plane->crtc_x = mode->hdisplay - plane->crtc_w;
+ if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay)
+ plane->crtc_y = mode->vdisplay - plane->crtc_h;
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
VIDOSDxA_TOPLEFT_Y(plane->crtc_y);
writel(val, ctx->regs + VIDOSD_A(win));
- last_x = plane->crtc_x + plane->crtc_width;
+ last_x = plane->crtc_x + plane->crtc_w;
if (last_x)
last_x--;
- last_y = plane->crtc_y + plane->crtc_height;
+ last_y = plane->crtc_y + plane->crtc_h;
if (last_y)
last_y--;
@@ -488,7 +481,7 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
writel(alpha, ctx->regs + VIDOSD_D(win));
- decon_win_set_pixfmt(ctx, win);
+ decon_win_set_pixfmt(ctx, win, state->fb);
/* hardware window 0 doesn't support color key. */
if (win != 0)
@@ -506,26 +499,17 @@ 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)
+static void decon_disable_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct decon_context *ctx = crtc->ctx;
- struct exynos_drm_plane *plane;
+ unsigned int win = plane->zpos;
u32 val;
- if (win < 0 || win >= WINDOWS_NR)
- return;
-
- 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);
@@ -535,55 +519,20 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win));
- /* unprotect windows */
- decon_shadow_protect_win(ctx, win, false);
-
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)
+static void decon_atomic_flush(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
- 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;
+ struct decon_context *ctx = crtc->ctx;
- 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);
- }
+ if (ctx->suspended)
+ return;
- decon_commit(ctx->crtc);
+ decon_shadow_protect_win(ctx, plane->zpos, false);
}
static void decon_init(struct decon_context *ctx)
@@ -603,12 +552,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 +567,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_disable_plane(crtc, &ctx->planes[i]);
clk_disable_unprepare(ctx->vclk);
clk_disable_unprepare(ctx->eclk);
@@ -688,37 +621,19 @@ 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,
- .mode_fixup = decon_mode_fixup,
+ .enable = decon_enable,
+ .disable = decon_disable,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
.wait_for_vblank = decon_wait_for_vblank,
- .win_commit = decon_win_commit,
- .win_disable = decon_win_disable,
+ .atomic_begin = decon_atomic_begin,
+ .update_plane = decon_update_plane,
+ .disable_plane = decon_disable_plane,
+ .atomic_flush = decon_atomic_flush,
};
@@ -726,6 +641,7 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
{
struct decon_context *ctx = (struct decon_context *)dev_id;
u32 val, clear_bit;
+ int win;
val = readl(ctx->regs + VIDINTCON1);
@@ -738,8 +654,15 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
goto out;
if (!ctx->i80_if) {
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ drm_crtc_handle_vblank(&ctx->crtc->base);
+ for (win = 0 ; win < WINDOWS_NR ; win++) {
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+
+ if (!plane->pending_fb)
+ continue;
+
+ exynos_drm_crtc_finish_update(ctx->crtc, plane);
+ }
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
@@ -767,15 +690,15 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
}
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
- type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
- DRM_PLANE_TYPE_OVERLAY;
+ type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
- 1 << ctx->pipe, type, zpos);
+ 1 << ctx->pipe, type, decon_formats,
+ ARRAY_SIZE(decon_formats), zpos);
if (ret)
return ret;
}
- exynos_plane = &ctx->planes[ctx->default_win];
+ exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
&decon_crtc_ops, ctx);
@@ -784,8 +707,8 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(ctx->crtc);
}
- if (ctx->display)
- exynos_drm_create_enc_conn(drm_dev, ctx->display);
+ if (ctx->encoder)
+ exynos_dpi_bind(drm_dev, ctx->encoder);
return 0;
@@ -796,10 +719,10 @@ 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);
+ if (ctx->encoder)
+ exynos_dpi_remove(ctx->encoder);
decon_ctx_remove(ctx);
}
@@ -824,11 +747,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 +756,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)) {
@@ -891,9 +807,9 @@ static int decon_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- ctx->display = exynos_dpi_probe(dev);
- if (IS_ERR(ctx->display)) {
- ret = PTR_ERR(ctx->display);
+ ctx->encoder = exynos_dpi_probe(dev);
+ if (IS_ERR(ctx->encoder)) {
+ ret = PTR_ERR(ctx->encoder);
goto err_iounmap;
}
@@ -911,8 +827,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 +839,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/kernel/drivers/gpu/drm/exynos/exynos_dp_core.c b/kernel/drivers/gpu/drm/exynos/exynos_dp_core.c
index 30feb7d06..124fb9a56 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -28,23 +28,24 @@
#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"
+#include "exynos_drm_crtc.h"
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
connector)
static inline struct exynos_drm_crtc *dp_to_crtc(struct exynos_dp_device *dp)
{
- return to_exynos_crtc(dp->encoder->crtc);
+ return to_exynos_crtc(dp->encoder.crtc);
}
-static inline struct exynos_dp_device *
-display_to_dp(struct exynos_drm_display *d)
+static inline struct exynos_dp_device *encoder_to_dp(
+ struct drm_encoder *e)
{
- return container_of(d, struct exynos_dp_device, display);
+ return container_of(e, struct exynos_dp_device, encoder);
}
struct bridge_init {
@@ -795,9 +796,6 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
/* Configure video slave mode */
exynos_dp_enable_video_master(dp, 0);
- /* Enable video */
- exynos_dp_start_video(dp);
-
timeout_loop = 0;
for (;;) {
@@ -891,9 +889,9 @@ static void exynos_dp_hotplug(struct work_struct *work)
drm_helper_hpd_irq_event(dp->drm_dev);
}
-static void exynos_dp_commit(struct exynos_drm_display *display)
+static void exynos_dp_commit(struct drm_encoder *encoder)
{
- struct exynos_dp_device *dp = display_to_dp(display);
+ struct exynos_dp_device *dp = encoder_to_dp(encoder);
int ret;
/* Keep the panel disabled while we configure video */
@@ -938,6 +936,9 @@ static void exynos_dp_commit(struct exynos_drm_display *display)
if (drm_panel_enable(dp->panel))
DRM_ERROR("failed to enable the panel\n");
}
+
+ /* Enable video */
+ exynos_dp_start_video(dp);
}
static enum drm_connector_status exynos_dp_detect(
@@ -953,10 +954,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)
@@ -991,7 +995,7 @@ static struct drm_encoder *exynos_dp_best_encoder(
{
struct exynos_dp_device *dp = ctx_from_connector(connector);
- return dp->encoder;
+ return &dp->encoder;
}
static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
@@ -1016,15 +1020,12 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
return 0;
}
-static int exynos_dp_create_connector(struct exynos_drm_display *display,
- struct drm_encoder *encoder)
+static int exynos_dp_create_connector(struct drm_encoder *encoder)
{
- struct exynos_dp_device *dp = display_to_dp(display);
+ struct exynos_dp_device *dp = encoder_to_dp(encoder);
struct drm_connector *connector = &dp->connector;
int ret;
- dp->encoder = encoder;
-
/* Pre-empt DP connector creation if there's a bridge */
if (dp->bridge) {
ret = exynos_drm_attach_lcd_bridge(dp, encoder);
@@ -1051,20 +1052,22 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
return ret;
}
-static void exynos_dp_phy_init(struct exynos_dp_device *dp)
+static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- if (dp->phy)
- phy_power_on(dp->phy);
+ return true;
}
-static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
+static void exynos_dp_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- if (dp->phy)
- phy_power_off(dp->phy);
}
-static void exynos_dp_poweron(struct exynos_dp_device *dp)
+static void exynos_dp_enable(struct drm_encoder *encoder)
{
+ struct exynos_dp_device *dp = encoder_to_dp(encoder);
struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
@@ -1081,14 +1084,17 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)
crtc->ops->clock_enable(dp_to_crtc(dp), true);
clk_prepare_enable(dp->clock);
- exynos_dp_phy_init(dp);
+ phy_power_on(dp->phy);
exynos_dp_init_dp(dp);
enable_irq(dp->irq);
- exynos_dp_commit(&dp->display);
+ exynos_dp_commit(&dp->encoder);
+
+ dp->dpms_mode = DRM_MODE_DPMS_ON;
}
-static void exynos_dp_poweroff(struct exynos_dp_device *dp)
+static void exynos_dp_disable(struct drm_encoder *encoder)
{
+ struct exynos_dp_device *dp = encoder_to_dp(encoder);
struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
@@ -1103,7 +1109,7 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
- exynos_dp_phy_exit(dp);
+ phy_power_off(dp->phy);
clk_disable_unprepare(dp->clock);
if (crtc->ops->clock_enable)
@@ -1113,31 +1119,19 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)
if (drm_panel_unprepare(dp->panel))
DRM_ERROR("failed to turnoff the panel\n");
}
-}
-
-static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
-{
- struct exynos_dp_device *dp = display_to_dp(display);
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- exynos_dp_poweron(dp);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- exynos_dp_poweroff(dp);
- break;
- default:
- break;
- }
- dp->dpms_mode = mode;
+ dp->dpms_mode = DRM_MODE_DPMS_OFF;
}
-static struct exynos_drm_display_ops exynos_dp_display_ops = {
- .create_connector = exynos_dp_create_connector,
- .dpms = exynos_dp_dpms,
- .commit = exynos_dp_commit,
+static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
+ .mode_fixup = exynos_dp_mode_fixup,
+ .mode_set = exynos_dp_mode_set,
+ .enable = exynos_dp_enable,
+ .disable = exynos_dp_disable,
+};
+
+static struct drm_encoder_funcs exynos_dp_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
@@ -1216,9 +1210,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data;
+ struct drm_encoder *encoder = &dp->encoder;
struct resource *res;
unsigned int irq_flags;
- int ret = 0;
+ int pipe, ret = 0;
dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1294,7 +1289,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
- exynos_dp_phy_init(dp);
+ phy_power_on(dp->phy);
exynos_dp_init_dp(dp);
@@ -1308,7 +1303,28 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
dp->drm_dev = drm_dev;
- return exynos_drm_create_enc_conn(drm_dev, &dp->display);
+ pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+ EXYNOS_DISPLAY_TYPE_LCD);
+ if (pipe < 0)
+ return pipe;
+
+ encoder->possible_crtcs = 1 << pipe;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
+
+ ret = exynos_dp_create_connector(encoder);
+ if (ret) {
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
+ drm_encoder_cleanup(encoder);
+ return ret;
+ }
+
+ return 0;
}
static void exynos_dp_unbind(struct device *dev, struct device *master,
@@ -1316,7 +1332,7 @@ static void exynos_dp_unbind(struct device *dev, struct device *master,
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
- exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
+ exynos_dp_disable(&dp->encoder);
}
static const struct component_ops exynos_dp_ops = {
@@ -1329,22 +1345,14 @@ 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);
if (!dp)
return -ENOMEM;
- dp->display.type = EXYNOS_DISPLAY_TYPE_LCD;
- 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,44 +1373,16 @@ 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;
}
-#ifdef CONFIG_PM_SLEEP
-static int exynos_dp_suspend(struct device *dev)
-{
- struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
- exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
- return 0;
-}
-
-static int exynos_dp_resume(struct device *dev)
-{
- struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
- exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_ON);
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops exynos_dp_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume)
-};
-
static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
@@ -1415,7 +1395,6 @@ struct platform_driver dp_driver = {
.driver = {
.name = "exynos-dp",
.owner = THIS_MODULE,
- .pm = &exynos_dp_pm_ops,
.of_match_table = exynos_dp_match,
},
};
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_dp_core.h b/kernel/drivers/gpu/drm/exynos/exynos_dp_core.h
index a4e799679..e413b6f7b 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -147,11 +147,10 @@ struct link_train {
};
struct exynos_dp_device {
- struct exynos_drm_display display;
+ struct drm_encoder encoder;
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
- struct drm_encoder *encoder;
struct drm_panel *panel;
struct drm_bridge *bridge;
struct clk *clock;
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.c
deleted file mode 100644
index 24994ba10..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/* exynos_drm_buf.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Author: Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/exynos_drm.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_drm_gem.h"
-#include "exynos_drm_buf.h"
-#include "exynos_drm_iommu.h"
-
-static int lowlevel_buffer_allocate(struct drm_device *dev,
- unsigned int flags, struct exynos_drm_gem_buf *buf)
-{
- int ret = 0;
- enum dma_attr attr;
- unsigned int nr_pages;
-
- if (buf->dma_addr) {
- DRM_DEBUG_KMS("already allocated.\n");
- return 0;
- }
-
- init_dma_attrs(&buf->dma_attrs);
-
- /*
- * if EXYNOS_BO_CONTIG, fully physically contiguous memory
- * region will be allocated else physically contiguous
- * as possible.
- */
- if (!(flags & EXYNOS_BO_NONCONTIG))
- dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &buf->dma_attrs);
-
- /*
- * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping
- * else cachable mapping.
- */
- if (flags & EXYNOS_BO_WC || !(flags & EXYNOS_BO_CACHABLE))
- attr = DMA_ATTR_WRITE_COMBINE;
- else
- attr = DMA_ATTR_NON_CONSISTENT;
-
- dma_set_attr(attr, &buf->dma_attrs);
- dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->dma_attrs);
-
- nr_pages = buf->size >> PAGE_SHIFT;
-
- if (!is_drm_iommu_supported(dev)) {
- dma_addr_t start_addr;
- unsigned int i = 0;
-
- buf->pages = drm_calloc_large(nr_pages, sizeof(struct page *));
- if (!buf->pages) {
- DRM_ERROR("failed to allocate pages.\n");
- return -ENOMEM;
- }
-
- buf->cookie = dma_alloc_attrs(dev->dev,
- buf->size,
- &buf->dma_addr, GFP_KERNEL,
- &buf->dma_attrs);
- if (!buf->cookie) {
- DRM_ERROR("failed to allocate buffer.\n");
- ret = -ENOMEM;
- goto err_free;
- }
-
- start_addr = buf->dma_addr;
- while (i < nr_pages) {
- buf->pages[i] = phys_to_page(start_addr);
- start_addr += PAGE_SIZE;
- i++;
- }
- } else {
-
- buf->pages = dma_alloc_attrs(dev->dev, buf->size,
- &buf->dma_addr, GFP_KERNEL,
- &buf->dma_attrs);
- if (!buf->pages) {
- DRM_ERROR("failed to allocate buffer.\n");
- return -ENOMEM;
- }
- }
-
- buf->sgt = drm_prime_pages_to_sg(buf->pages, nr_pages);
- if (IS_ERR(buf->sgt)) {
- DRM_ERROR("failed to get sg table.\n");
- ret = PTR_ERR(buf->sgt);
- goto err_free_attrs;
- }
-
- DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
- (unsigned long)buf->dma_addr,
- buf->size);
-
- return ret;
-
-err_free_attrs:
- dma_free_attrs(dev->dev, buf->size, buf->pages,
- (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
- buf->dma_addr = (dma_addr_t)NULL;
-err_free:
- if (!is_drm_iommu_supported(dev))
- drm_free_large(buf->pages);
-
- return ret;
-}
-
-static void lowlevel_buffer_deallocate(struct drm_device *dev,
- unsigned int flags, struct exynos_drm_gem_buf *buf)
-{
- if (!buf->dma_addr) {
- DRM_DEBUG_KMS("dma_addr is invalid.\n");
- return;
- }
-
- DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
- (unsigned long)buf->dma_addr,
- buf->size);
-
- sg_free_table(buf->sgt);
-
- kfree(buf->sgt);
- buf->sgt = NULL;
-
- if (!is_drm_iommu_supported(dev)) {
- dma_free_attrs(dev->dev, buf->size, buf->cookie,
- (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
- drm_free_large(buf->pages);
- } else
- dma_free_attrs(dev->dev, buf->size, buf->pages,
- (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
-
- buf->dma_addr = (dma_addr_t)NULL;
-}
-
-struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
- unsigned int size)
-{
- struct exynos_drm_gem_buf *buffer;
-
- DRM_DEBUG_KMS("desired size = 0x%x\n", size);
-
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer)
- return NULL;
-
- buffer->size = size;
- return buffer;
-}
-
-void exynos_drm_fini_buf(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
-{
- kfree(buffer);
- buffer = NULL;
-}
-
-int exynos_drm_alloc_buf(struct drm_device *dev,
- struct exynos_drm_gem_buf *buf, unsigned int flags)
-{
-
- /*
- * allocate memory region and set the memory information
- * to vaddr and dma_addr of a buffer object.
- */
- if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-void exynos_drm_free_buf(struct drm_device *dev,
- unsigned int flags, struct exynos_drm_gem_buf *buffer)
-{
-
- lowlevel_buffer_deallocate(dev, flags, buffer);
-}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.h
deleted file mode 100644
index a6412f196..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_buf.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* exynos_drm_buf.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Author: Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_BUF_H_
-#define _EXYNOS_DRM_BUF_H_
-
-/* create and initialize buffer object. */
-struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
- unsigned int size);
-
-/* destroy buffer object. */
-void exynos_drm_fini_buf(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer);
-
-/* allocate physical memory region and setup sgt. */
-int exynos_drm_alloc_buf(struct drm_device *dev,
- struct exynos_drm_gem_buf *buf,
- unsigned int flags);
-
-/* release physical memory region, and sgt. */
-void exynos_drm_free_buf(struct drm_device *dev,
- unsigned int flags,
- struct exynos_drm_gem_buf *buffer);
-
-#endif
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_core.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_core.c
index 4c9f972ea..7f55ba677 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -15,46 +15,10 @@
#include <drm/drmP.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
-#include "exynos_drm_encoder.h"
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
-int exynos_drm_create_enc_conn(struct drm_device *dev,
- struct exynos_drm_display *display)
-{
- struct drm_encoder *encoder;
- int ret;
- unsigned long possible_crtcs = 0;
-
- ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
- if (ret < 0)
- return ret;
-
- possible_crtcs |= 1 << ret;
-
- /* create and initialize a encoder for this sub driver. */
- encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
- if (!encoder) {
- DRM_ERROR("failed to create encoder\n");
- return -EFAULT;
- }
-
- display->encoder = encoder;
-
- ret = display->ops->create_connector(display, encoder);
- if (ret) {
- DRM_ERROR("failed to create connector ret = %d\n", ret);
- goto err_destroy_encoder;
- }
-
- return 0;
-
-err_destroy_encoder:
- encoder->funcs->destroy(encoder);
- return ret;
-}
-
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
if (!subdrv)
@@ -64,7 +28,6 @@ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
@@ -75,7 +38,6 @@ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
int exynos_drm_device_subdrv_probe(struct drm_device *dev)
{
@@ -105,7 +67,6 @@ int exynos_drm_device_subdrv_probe(struct drm_device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe);
int exynos_drm_device_subdrv_remove(struct drm_device *dev)
{
@@ -123,7 +84,6 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
{
@@ -147,7 +107,6 @@ err:
}
return ret;
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
{
@@ -158,4 +117,3 @@ void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
subdrv->close(dev, subdrv->dev, file);
}
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 9006b947e..e69357172 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -14,212 +14,97 @@
#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->ops->enable)
+ exynos_crtc->ops->enable(exynos_crtc);
- if (exynos_crtc->dpms == mode) {
- DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
- 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 (mode == DRM_MODE_DPMS_ON)
- drm_crtc_vblank_on(crtc);
+ drm_crtc_vblank_on(crtc);
}
-static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
-{
- /* drm framework doesn't check NULL. */
-}
-
-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);
+ drm_crtc_vblank_off(crtc);
- if (exynos_crtc->ops->win_commit)
- exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
-
- if (exynos_crtc->ops->commit)
- exynos_crtc->ops->commit(exynos_crtc);
+ if (exynos_crtc->ops->disable)
+ exynos_crtc->ops->disable(exynos_crtc);
}
-static bool
-exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void
+exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- if (exynos_crtc->ops->mode_fixup)
- return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
- adjusted_mode);
-
- return true;
+ if (exynos_crtc->ops->commit)
+ exynos_crtc->ops->commit(exynos_crtc);
}
-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 int exynos_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
{
- 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));
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- ret = exynos_check_plane(crtc->primary, fb);
- if (ret < 0)
- return ret;
+ if (!state->enable)
+ return 0;
- 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);
+ if (exynos_crtc->ops->atomic_check)
+ return exynos_crtc->ops->atomic_check(exynos_crtc, state);
return 0;
}
-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 drm_crtc_state *old_crtc_state)
{
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;
- }
+ struct drm_plane *plane;
- crtc_w = fb->width - x;
- crtc_h = fb->height - y;
+ exynos_crtc->event = crtc->state->event;
- return exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
- crtc_w, crtc_h, x, y, crtc_w, crtc_h);
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
+
+ if (exynos_crtc->ops->atomic_begin)
+ exynos_crtc->ops->atomic_begin(exynos_crtc,
+ exynos_plane);
+ }
}
-static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
+static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
{
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_plane *plane;
- int ret;
- exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
- 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);
+ if (exynos_crtc->ops->atomic_flush)
+ exynos_crtc->ops->atomic_flush(exynos_crtc,
+ exynos_plane);
}
}
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_set_nofb = exynos_drm_crtc_mode_set_nofb,
+ .atomic_check = exynos_crtc_atomic_check,
+ .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 +117,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,
@@ -253,14 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
if (!exynos_crtc)
return ERR_PTR(-ENOMEM);
- 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;
exynos_crtc->ctx = ctx;
+ init_waitqueue_head(&exynos_crtc->wait_update);
+
crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc;
@@ -280,52 +167,52 @@ err_crtc:
return ERR_PTR(ret);
}
-int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
- if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
- return -EPERM;
-
if (exynos_crtc->ops->enable_vblank)
- exynos_crtc->ops->enable_vblank(exynos_crtc);
+ return exynos_crtc->ops->enable_vblank(exynos_crtc);
return 0;
}
-void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
- if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
- return;
-
if (exynos_crtc->ops->disable_vblank)
exynos_crtc->ops->disable_vblank(exynos_crtc);
}
-void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
{
- struct exynos_drm_private *dev_priv = dev->dev_private;
- struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
+ wait_event_timeout(exynos_crtc->wait_update,
+ (atomic_read(&exynos_crtc->pending_update) == 0),
+ msecs_to_jiffies(50));
+}
+
+void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
+ struct exynos_drm_plane *exynos_plane)
+{
+ struct drm_crtc *crtc = &exynos_crtc->base;
unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags);
- if (exynos_crtc->event) {
+ exynos_plane->pending_fb = NULL;
- drm_send_vblank_event(dev, -1, exynos_crtc->event);
- drm_vblank_put(dev, pipe);
- wake_up(&exynos_crtc->pending_flip_queue);
+ if (atomic_dec_and_test(&exynos_crtc->pending_update))
+ wake_up(&exynos_crtc->wait_update);
- }
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ if (exynos_crtc->event)
+ drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
exynos_crtc->event = NULL;
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
@@ -352,7 +239,7 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
}
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
- unsigned int out_type)
+ enum exynos_drm_output_type out_type)
{
struct drm_crtc *crtc;
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 0f3aa7081..f9f365bd0 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -23,14 +23,16 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
enum exynos_drm_output_type type,
const struct exynos_drm_crtc_ops *ops,
void *context);
-int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
-void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
-void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
+void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc);
+void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
+ struct exynos_drm_plane *exynos_plane);
void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb);
/* This function gets pipe value to crtc device matched with out_type. */
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
- unsigned int out_type);
+ enum exynos_drm_output_type out_type);
/*
* This function calls the crtc device(manager)'s te_handler() callback
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
deleted file mode 100644
index cd485c091..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/* exynos_drm_dmabuf.c
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- * Author: Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/exynos_drm.h>
-#include "exynos_drm_dmabuf.h"
-#include "exynos_drm_drv.h"
-#include "exynos_drm_gem.h"
-
-#include <linux/dma-buf.h>
-
-struct exynos_drm_dmabuf_attachment {
- struct sg_table sgt;
- enum dma_data_direction dir;
- bool is_mapped;
-};
-
-static struct exynos_drm_gem_obj *dma_buf_to_obj(struct dma_buf *buf)
-{
- return to_exynos_gem_obj(buf->priv);
-}
-
-static int exynos_gem_attach_dma_buf(struct dma_buf *dmabuf,
- struct device *dev,
- struct dma_buf_attachment *attach)
-{
- struct exynos_drm_dmabuf_attachment *exynos_attach;
-
- exynos_attach = kzalloc(sizeof(*exynos_attach), GFP_KERNEL);
- if (!exynos_attach)
- return -ENOMEM;
-
- exynos_attach->dir = DMA_NONE;
- attach->priv = exynos_attach;
-
- return 0;
-}
-
-static void exynos_gem_detach_dma_buf(struct dma_buf *dmabuf,
- struct dma_buf_attachment *attach)
-{
- struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv;
- struct sg_table *sgt;
-
- if (!exynos_attach)
- return;
-
- sgt = &exynos_attach->sgt;
-
- if (exynos_attach->dir != DMA_NONE)
- dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
- exynos_attach->dir);
-
- sg_free_table(sgt);
- kfree(exynos_attach);
- attach->priv = NULL;
-}
-
-static struct sg_table *
- exynos_gem_map_dma_buf(struct dma_buf_attachment *attach,
- enum dma_data_direction dir)
-{
- struct exynos_drm_dmabuf_attachment *exynos_attach = attach->priv;
- struct exynos_drm_gem_obj *gem_obj = dma_buf_to_obj(attach->dmabuf);
- struct drm_device *dev = gem_obj->base.dev;
- struct exynos_drm_gem_buf *buf;
- struct scatterlist *rd, *wr;
- struct sg_table *sgt = NULL;
- unsigned int i;
- int nents, ret;
-
- /* just return current sgt if already requested. */
- if (exynos_attach->dir == dir && exynos_attach->is_mapped)
- return &exynos_attach->sgt;
-
- buf = gem_obj->buffer;
- if (!buf) {
- DRM_ERROR("buffer is null.\n");
- return ERR_PTR(-ENOMEM);
- }
-
- sgt = &exynos_attach->sgt;
-
- ret = sg_alloc_table(sgt, buf->sgt->orig_nents, GFP_KERNEL);
- if (ret) {
- DRM_ERROR("failed to alloc sgt.\n");
- return ERR_PTR(-ENOMEM);
- }
-
- mutex_lock(&dev->struct_mutex);
-
- rd = buf->sgt->sgl;
- wr = sgt->sgl;
- for (i = 0; i < sgt->orig_nents; ++i) {
- sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
- rd = sg_next(rd);
- wr = sg_next(wr);
- }
-
- if (dir != DMA_NONE) {
- nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
- if (!nents) {
- DRM_ERROR("failed to map sgl with iommu.\n");
- sg_free_table(sgt);
- sgt = ERR_PTR(-EIO);
- goto err_unlock;
- }
- }
-
- exynos_attach->is_mapped = true;
- exynos_attach->dir = dir;
- attach->priv = exynos_attach;
-
- DRM_DEBUG_PRIME("buffer size = 0x%lx\n", buf->size);
-
-err_unlock:
- mutex_unlock(&dev->struct_mutex);
- return sgt;
-}
-
-static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
- struct sg_table *sgt,
- enum dma_data_direction dir)
-{
- /* Nothing to do. */
-}
-
-static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
- unsigned long page_num)
-{
- /* TODO */
-
- return NULL;
-}
-
-static void exynos_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
- unsigned long page_num,
- void *addr)
-{
- /* TODO */
-}
-
-static void *exynos_gem_dmabuf_kmap(struct dma_buf *dma_buf,
- unsigned long page_num)
-{
- /* TODO */
-
- return NULL;
-}
-
-static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
- unsigned long page_num, void *addr)
-{
- /* TODO */
-}
-
-static int exynos_gem_dmabuf_mmap(struct dma_buf *dma_buf,
- struct vm_area_struct *vma)
-{
- return -ENOTTY;
-}
-
-static struct dma_buf_ops exynos_dmabuf_ops = {
- .attach = exynos_gem_attach_dma_buf,
- .detach = exynos_gem_detach_dma_buf,
- .map_dma_buf = exynos_gem_map_dma_buf,
- .unmap_dma_buf = exynos_gem_unmap_dma_buf,
- .kmap = exynos_gem_dmabuf_kmap,
- .kmap_atomic = exynos_gem_dmabuf_kmap_atomic,
- .kunmap = exynos_gem_dmabuf_kunmap,
- .kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic,
- .mmap = exynos_gem_dmabuf_mmap,
- .release = drm_gem_dmabuf_release,
-};
-
-struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,
- struct drm_gem_object *obj, int flags)
-{
- struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
- DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
-
- exp_info.ops = &exynos_dmabuf_ops;
- exp_info.size = exynos_gem_obj->base.size;
- exp_info.flags = flags;
- exp_info.priv = obj;
-
- return dma_buf_export(&exp_info);
-}
-
-struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
- struct dma_buf *dma_buf)
-{
- struct dma_buf_attachment *attach;
- struct sg_table *sgt;
- struct scatterlist *sgl;
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct exynos_drm_gem_buf *buffer;
- int ret;
-
- /* is this one of own objects? */
- if (dma_buf->ops == &exynos_dmabuf_ops) {
- struct drm_gem_object *obj;
-
- obj = dma_buf->priv;
-
- /* is it from our device? */
- if (obj->dev == drm_dev) {
- /*
- * Importing dmabuf exported from out own gem increases
- * refcount on gem itself instead of f_count of dmabuf.
- */
- drm_gem_object_reference(obj);
- return obj;
- }
- }
-
- attach = dma_buf_attach(dma_buf, drm_dev->dev);
- if (IS_ERR(attach))
- return ERR_PTR(-EINVAL);
-
- get_dma_buf(dma_buf);
-
- sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
- if (IS_ERR(sgt)) {
- ret = PTR_ERR(sgt);
- goto err_buf_detach;
- }
-
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer) {
- ret = -ENOMEM;
- goto err_unmap_attach;
- }
-
- exynos_gem_obj = exynos_drm_gem_init(drm_dev, dma_buf->size);
- if (!exynos_gem_obj) {
- ret = -ENOMEM;
- goto err_free_buffer;
- }
-
- sgl = sgt->sgl;
-
- buffer->size = dma_buf->size;
- buffer->dma_addr = sg_dma_address(sgl);
-
- if (sgt->nents == 1) {
- /* always physically continuous memory if sgt->nents is 1. */
- exynos_gem_obj->flags |= EXYNOS_BO_CONTIG;
- } else {
- /*
- * this case could be CONTIG or NONCONTIG type but for now
- * sets NONCONTIG.
- * TODO. we have to find a way that exporter can notify
- * the type of its own buffer to importer.
- */
- exynos_gem_obj->flags |= EXYNOS_BO_NONCONTIG;
- }
-
- exynos_gem_obj->buffer = buffer;
- buffer->sgt = sgt;
- exynos_gem_obj->base.import_attach = attach;
-
- DRM_DEBUG_PRIME("dma_addr = %pad, size = 0x%lx\n", &buffer->dma_addr,
- buffer->size);
-
- return &exynos_gem_obj->base;
-
-err_free_buffer:
- kfree(buffer);
- buffer = NULL;
-err_unmap_attach:
- dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
-err_buf_detach:
- dma_buf_detach(dma_buf, attach);
- dma_buf_put(dma_buf);
-
- return ERR_PTR(ret);
-}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h
deleted file mode 100644
index 886de9ff4..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_dmabuf.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* exynos_drm_dmabuf.h
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- * Author: Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_DMABUF_H_
-#define _EXYNOS_DRM_DMABUF_H_
-
-struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,
- struct drm_gem_object *obj, int flags);
-
-struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
- struct dma_buf *dma_buf);
-#endif
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 37678cf44..c748b8790 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -13,32 +13,31 @@
#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>
#include <video/of_videomode.h>
#include <video/videomode.h>
-#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
struct exynos_dpi {
- struct exynos_drm_display display;
+ struct drm_encoder encoder;
struct device *dev;
struct device_node *panel_node;
struct drm_panel *panel;
struct drm_connector connector;
- struct drm_encoder *encoder;
struct videomode *vm;
- int dpms_mode;
};
#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
-static inline struct exynos_dpi *display_to_dpi(struct exynos_drm_display *d)
+static inline struct exynos_dpi *encoder_to_dpi(struct drm_encoder *e)
{
- return container_of(d, struct exynos_dpi, display);
+ return container_of(e, struct exynos_dpi, encoder);
}
static enum drm_connector_status
@@ -59,10 +58,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)
@@ -95,7 +97,7 @@ exynos_dpi_best_encoder(struct drm_connector *connector)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
- return ctx->encoder;
+ return &ctx->encoder;
}
static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
@@ -103,15 +105,12 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
.best_encoder = exynos_dpi_best_encoder,
};
-static int exynos_dpi_create_connector(struct exynos_drm_display *display,
- struct drm_encoder *encoder)
+static int exynos_dpi_create_connector(struct drm_encoder *encoder)
{
- struct exynos_dpi *ctx = display_to_dpi(display);
+ struct exynos_dpi *ctx = encoder_to_dpi(encoder);
struct drm_connector *connector = &ctx->connector;
int ret;
- ctx->encoder = encoder;
-
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector,
@@ -129,46 +128,48 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
return 0;
}
-static void exynos_dpi_poweron(struct exynos_dpi *ctx)
+static bool exynos_dpi_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void exynos_dpi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void exynos_dpi_enable(struct drm_encoder *encoder)
{
+ struct exynos_dpi *ctx = encoder_to_dpi(encoder);
+
if (ctx->panel) {
drm_panel_prepare(ctx->panel);
drm_panel_enable(ctx->panel);
}
}
-static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
+static void exynos_dpi_disable(struct drm_encoder *encoder)
{
+ struct exynos_dpi *ctx = encoder_to_dpi(encoder);
+
if (ctx->panel) {
drm_panel_disable(ctx->panel);
drm_panel_unprepare(ctx->panel);
}
}
-static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
-{
- struct exynos_dpi *ctx = display_to_dpi(display);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- if (ctx->dpms_mode != DRM_MODE_DPMS_ON)
- exynos_dpi_poweron(ctx);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- if (ctx->dpms_mode == DRM_MODE_DPMS_ON)
- exynos_dpi_poweroff(ctx);
- break;
- default:
- break;
- }
- ctx->dpms_mode = mode;
-}
+static struct drm_encoder_helper_funcs exynos_dpi_encoder_helper_funcs = {
+ .mode_fixup = exynos_dpi_mode_fixup,
+ .mode_set = exynos_dpi_mode_set,
+ .enable = exynos_dpi_enable,
+ .disable = exynos_dpi_disable,
+};
-static struct exynos_drm_display_ops exynos_dpi_display_ops = {
- .create_connector = exynos_dpi_create_connector,
- .dpms = exynos_dpi_dpms
+static struct drm_encoder_funcs exynos_dpi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
/* of_* functions will be removed after merge of of_graph patches */
@@ -295,7 +296,34 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return 0;
}
-struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
+int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
+{
+ int ret;
+
+ ret = exynos_drm_crtc_get_pipe_from_type(dev, EXYNOS_DISPLAY_TYPE_LCD);
+ if (ret < 0)
+ return ret;
+
+ encoder->possible_crtcs = 1 << ret;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);
+
+ ret = exynos_dpi_create_connector(encoder);
+ if (ret) {
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
+ drm_encoder_cleanup(encoder);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct drm_encoder *exynos_dpi_probe(struct device *dev)
{
struct exynos_dpi *ctx;
int ret;
@@ -304,50 +332,31 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
if (!ctx)
return ERR_PTR(-ENOMEM);
- ctx->display.type = EXYNOS_DISPLAY_TYPE_LCD;
- ctx->display.ops = &exynos_dpi_display_ops;
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;
+ return &ctx->encoder;
}
-int exynos_dpi_remove(struct exynos_drm_display *display)
+int exynos_dpi_remove(struct drm_encoder *encoder)
{
- struct exynos_dpi *ctx = display_to_dpi(display);
+ struct exynos_dpi *ctx = encoder_to_dpi(encoder);
- exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF);
+ exynos_dpi_disable(&ctx->encoder);
if (ctx->panel)
drm_panel_detach(ctx->panel);
- exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
-
return 0;
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 8ac465208..2c6019d6a 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -13,6 +13,8 @@
#include <linux/pm_runtime.h>
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <linux/component.h>
@@ -21,13 +23,11 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
-#include "exynos_drm_encoder.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h"
-#include "exynos_drm_dmabuf.h"
#include "exynos_drm_g2d.h"
#include "exynos_drm_ipp.h"
#include "exynos_drm_iommu.h"
@@ -38,28 +38,112 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-static struct platform_device *exynos_drm_pdev;
+struct exynos_atomic_commit {
+ struct work_struct work;
+ struct drm_device *dev;
+ struct drm_atomic_state *state;
+ u32 crtcs;
+};
+
+static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int i, ret;
-static DEFINE_MUTEX(drm_component_lock);
-static LIST_HEAD(drm_component_list);
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-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;
-};
+ if (!crtc->state->enable)
+ continue;
+
+ ret = drm_crtc_vblank_get(crtc);
+ if (ret)
+ continue;
+
+ exynos_drm_crtc_wait_pending_update(exynos_crtc);
+ drm_crtc_vblank_put(crtc);
+ }
+}
+
+static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
+{
+ struct drm_device *dev = commit->dev;
+ struct exynos_drm_private *priv = dev->dev_private;
+ struct drm_atomic_state *state = commit->state;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ struct drm_plane_state *plane_state;
+ struct drm_crtc_state *crtc_state;
+ int i;
+
+ 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.
+ */
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+
+ atomic_set(&exynos_crtc->pending_update, 0);
+ }
+
+ for_each_plane_in_state(state, plane, plane_state, i) {
+ struct exynos_drm_crtc *exynos_crtc =
+ to_exynos_crtc(plane->crtc);
+
+ if (!plane->crtc)
+ continue;
+
+ atomic_inc(&exynos_crtc->pending_update);
+ }
+
+ drm_atomic_helper_commit_planes(dev, state, false);
+
+ exynos_atomic_wait_for_commit(state);
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+ drm_atomic_state_free(state);
+
+ spin_lock(&priv->lock);
+ priv->pending &= ~commit->crtcs;
+ spin_unlock(&priv->lock);
+
+ wake_up_all(&priv->wait);
+
+ kfree(commit);
+}
+
+static void exynos_drm_atomic_work(struct work_struct *work)
+{
+ struct exynos_atomic_commit *commit = container_of(work,
+ struct exynos_atomic_commit, work);
+
+ exynos_atomic_commit_complete(commit);
+}
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
- int ret;
+ struct drm_encoder *encoder;
+ unsigned int clone_mask;
+ int cnt, ret;
private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
if (!private)
return -ENOMEM;
+ init_waitqueue_head(&private->wait);
+ spin_lock_init(&private->lock);
+
dev_set_drvdata(dev->dev, dev);
dev->dev_private = (void *)private;
@@ -80,7 +164,13 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
exynos_drm_mode_config_init(dev);
/* setup possible_clones. */
- exynos_drm_encoder_setup(dev);
+ cnt = 0;
+ clone_mask = 0;
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ clone_mask |= (1 << (cnt++));
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ encoder->possible_clones = clone_mask;
platform_set_drvdata(dev->platformdev, dev);
@@ -98,6 +188,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.
@@ -154,6 +246,65 @@ static int exynos_drm_unload(struct drm_device *dev)
return 0;
}
+static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs)
+{
+ bool pending;
+
+ spin_lock(&priv->lock);
+ pending = priv->pending & crtcs;
+ spin_unlock(&priv->lock);
+
+ return pending;
+}
+
+int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
+ bool async)
+{
+ struct exynos_drm_private *priv = dev->dev_private;
+ struct exynos_atomic_commit *commit;
+ int i, ret;
+
+ commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+ if (!commit)
+ return -ENOMEM;
+
+ ret = drm_atomic_helper_prepare_planes(dev, state);
+ if (ret) {
+ kfree(commit);
+ return ret;
+ }
+
+ /* This is the point of no return */
+
+ INIT_WORK(&commit->work, exynos_drm_atomic_work);
+ commit->dev = dev;
+ commit->state = state;
+
+ /* Wait until all affected CRTCs have completed previous commits and
+ * mark them as pending.
+ */
+ for (i = 0; i < dev->mode_config.num_crtc; ++i) {
+ if (state->crtcs[i])
+ commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
+ }
+
+ wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
+
+ spin_lock(&priv->lock);
+ priv->pending |= commit->crtcs;
+ spin_unlock(&priv->lock);
+
+ drm_atomic_helper_swap_state(dev, state);
+
+ if (async)
+ schedule_work(&commit->work);
+ else
+ exynos_atomic_commit_complete(commit);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_connector *connector;
@@ -190,6 +341,7 @@ static int exynos_drm_resume(struct drm_device *dev)
return 0;
}
+#endif
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
@@ -253,25 +405,25 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = {
static const struct drm_ioctl_desc exynos_ioctls[] = {
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET,
- exynos_drm_gem_get_ioctl, DRM_UNLOCKED),
- DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
- vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER,
- exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST,
- exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC,
- exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY,
- exynos_drm_ipp_get_property, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY,
- exynos_drm_ipp_set_property, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF,
- exynos_drm_ipp_queue_buf, DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL,
- exynos_drm_ipp_cmd_ctrl, DRM_UNLOCKED | DRM_AUTH),
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl,
+ DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, vidi_connection_ioctl,
+ DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, exynos_g2d_set_cmdlist_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
};
static const struct file_operations exynos_drm_driver_fops = {
@@ -288,17 +440,16 @@ static const struct file_operations exynos_drm_driver_fops = {
};
static struct drm_driver exynos_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
+ | DRIVER_ATOMIC | DRIVER_RENDER,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
- .suspend = exynos_drm_suspend,
- .resume = exynos_drm_resume,
.open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
.set_busid = drm_platform_set_busid,
- .get_vblank_counter = drm_vblank_count,
+ .get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
.gem_free_object = exynos_drm_gem_free_object,
@@ -308,8 +459,12 @@ static struct drm_driver exynos_drm_driver = {
.dumb_destroy = drm_gem_dumb_destroy,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_export = exynos_dmabuf_prime_export,
- .gem_prime_import = exynos_dmabuf_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = exynos_drm_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table,
+ .gem_prime_vmap = exynos_drm_gem_prime_vmap,
+ .gem_prime_vunmap = exynos_drm_gem_prime_vunmap,
.ioctls = exynos_ioctls,
.num_ioctls = ARRAY_SIZE(exynos_ioctls),
.fops = &exynos_drm_driver_fops,
@@ -348,200 +503,41 @@ 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_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
#ifdef CONFIG_DRM_EXYNOS_DSI
&dsi_driver,
#endif
-#ifdef CONFIG_DRM_EXYNOS_HDMI
+#ifdef CONFIG_DRM_EXYNOS_MIXER
&mixer_driver,
+#endif
+#ifdef CONFIG_DRM_EXYNOS_HDMI
&hdmi_driver,
#endif
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ &vidi_driver,
+#endif
};
static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
@@ -560,6 +556,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 +619,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 +632,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 +641,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;
+
+ ret = exynos_drm_register_devices();
+ if (ret)
+ return ret;
+
+ ret = exynos_drm_register_kms_drivers();
+ if (ret)
+ goto err_unregister_pdevs;
+
+ ret = exynos_drm_register_non_kms_drivers();
+ if (ret)
+ goto err_unregister_kms_drivers;
- for (i = ARRAY_SIZE(exynos_drm_non_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_non_kms_drivers[i]);
+ return 0;
- for (i = ARRAY_SIZE(exynos_drm_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_kms_drivers[i]);
+err_unregister_kms_drivers:
+ exynos_drm_unregister_kms_drivers();
- platform_driver_unregister(&exynos_drm_platform_driver);
+err_unregister_pdevs:
+ exynos_drm_unregister_devices();
- exynos_drm_remove_vidi();
+ return ret;
+}
- platform_device_unregister(exynos_drm_pdev);
+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/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 29e3fb78c..f1eda7fa4 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -22,16 +22,11 @@
#define MAX_PLANE 5
#define MAX_FB_BUFFER 4
+#define DEFAULT_WIN 0
+
#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,
@@ -51,28 +46,17 @@ enum exynos_drm_output_type {
* - the unit is screen coordinates.
* @src_y: offset y on a framebuffer to be displayed.
* - the unit is screen coordinates.
- * @src_width: width of a partial image to be displayed from framebuffer.
- * @src_height: height of a partial image to be displayed from framebuffer.
- * @fb_width: width of a framebuffer.
- * @fb_height: height of a framebuffer.
+ * @src_w: width of a partial image to be displayed from framebuffer.
+ * @src_h: height of a partial image to be displayed from framebuffer.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
- * @crtc_width: window width to be displayed (hardware screen).
- * @crtc_height: window height to be displayed (hardware screen).
- * @mode_width: width of screen mode.
- * @mode_height: height of screen mode.
+ * @crtc_w: window width to be displayed (hardware screen).
+ * @crtc_h: window height to be displayed (hardware screen).
* @h_ratio: horizontal scaling ratio, 16.16 fixed point
* @v_ratio: vertical scaling ratio, 16.16 fixed point
- * @refresh: refresh rate.
- * @scan_flag: interlace or progressive way.
- * (it could be DRM_MODE_FLAG_*)
- * @bpp: pixel size.(in bit)
- * @pixel_format: fourcc pixel format of this overlay
* @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.
@@ -82,90 +66,34 @@ struct exynos_drm_plane {
struct drm_plane base;
unsigned int src_x;
unsigned int src_y;
- unsigned int src_width;
- unsigned int src_height;
- unsigned int fb_width;
- unsigned int fb_height;
+ unsigned int src_w;
+ unsigned int src_h;
unsigned int crtc_x;
unsigned int crtc_y;
- unsigned int crtc_width;
- unsigned int crtc_height;
- unsigned int mode_width;
- unsigned int mode_height;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
unsigned int h_ratio;
unsigned int v_ratio;
- unsigned int refresh;
- unsigned int scan_flag;
- unsigned int bpp;
- unsigned int pitch;
- uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos;
-
- bool enabled:1;
- bool resume:1;
-};
-
-/*
- * Exynos DRM Display Structure.
- * - this structure is common to analog tv, digital tv and lcd panel.
- *
- * @create_connector: initialize and register a new connector
- * @remove: cleans up the display for removal
- * @mode_fixup: fix mode data comparing to hw specific display mode.
- * @mode_set: convert drm_display_mode to hw specific display mode and
- * would be called by encoder->mode_set().
- * @check_mode: check if mode is valid or not.
- * @dpms: display device on or off.
- * @commit: apply changes to hw
- */
-struct exynos_drm_display;
-struct exynos_drm_display_ops {
- int (*create_connector)(struct exynos_drm_display *display,
- struct drm_encoder *encoder);
- void (*remove)(struct exynos_drm_display *display);
- void (*mode_fixup)(struct exynos_drm_display *display,
- struct drm_connector *connector,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode);
- void (*mode_set)(struct exynos_drm_display *display,
- struct drm_display_mode *mode);
- int (*check_mode)(struct exynos_drm_display *display,
- struct drm_display_mode *mode);
- void (*dpms)(struct exynos_drm_display *display, int mode);
- void (*commit)(struct exynos_drm_display *display);
-};
-
-/*
- * Exynos drm display structure, maps 1:1 with an encoder/connector
- *
- * @list: the list entry for this manager
- * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
- * @encoder: encoder object this display maps to
- * @connector: connector object this display maps to
- * @ops: pointer to callbacks for exynos drm specific functionality
- * @ctx: A pointer to the display's implementation specific context
- */
-struct exynos_drm_display {
- struct list_head list;
- enum exynos_drm_output_type type;
- struct drm_encoder *encoder;
- struct drm_connector *connector;
- struct exynos_drm_display_ops *ops;
+ struct drm_framebuffer *pending_fb;
};
/*
* Exynos drm crtc ops
*
- * @dpms: control device power.
- * @mode_fixup: fix mode data before applying it
+ * @enable: enable the device
+ * @disable: disable the device
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
* @wait_for_vblank: wait for vblank interrupt to make sure that
* hardware overlay is updated.
- * @win_commit: apply hardware specific overlay data to registers.
- * @win_disable: disable hardware specific overlay.
+ * @atomic_check: validate state
+ * @atomic_begin: prepare a window to receive a update
+ * @atomic_flush: mark the end of a window update
+ * @update_plane: apply hardware specific overlay data to registers.
+ * @disable_plane: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect
* synchronization signal if there is a page flip request.
* @clock_enable: optional function enabling/disabling display domain clock,
@@ -175,16 +103,22 @@ struct exynos_drm_display {
*/
struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
- void (*dpms)(struct exynos_drm_crtc *crtc, int mode);
- bool (*mode_fixup)(struct exynos_drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode);
+ void (*enable)(struct exynos_drm_crtc *crtc);
+ void (*disable)(struct exynos_drm_crtc *crtc);
void (*commit)(struct exynos_drm_crtc *crtc);
int (*enable_vblank)(struct exynos_drm_crtc *crtc);
void (*disable_vblank)(struct exynos_drm_crtc *crtc);
void (*wait_for_vblank)(struct exynos_drm_crtc *crtc);
- void (*win_commit)(struct exynos_drm_crtc *crtc, unsigned int zpos);
- void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos);
+ int (*atomic_check)(struct exynos_drm_crtc *crtc,
+ struct drm_crtc_state *state);
+ void (*atomic_begin)(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane);
+ void (*update_plane)(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane);
+ void (*disable_plane)(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane);
+ void (*atomic_flush)(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane);
void (*te_handler)(struct exynos_drm_crtc *crtc);
void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
};
@@ -201,8 +135,10 @@ 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
+ * @wait_update: wait all pending planes updates to finish
+ * @pending_update: number of pending plane updates in this crtc
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the crtc's implementation specific context
*/
@@ -210,9 +146,9 @@ struct exynos_drm_crtc {
struct drm_crtc base;
enum exynos_drm_output_type type;
unsigned int pipe;
- unsigned int dpms;
- wait_queue_head_t pending_flip_queue;
struct drm_pending_vblank_event *event;
+ wait_queue_head_t wait_update;
+ atomic_t pending_update;
const struct exynos_drm_crtc_ops *ops;
void *ctx;
};
@@ -238,6 +174,9 @@ struct drm_exynos_file_private {
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
* @pipe: the pipe number for this crtc/manager.
+ * @pending: the crtcs that have pending updates to finish
+ * @lock: protect access to @pending
+ * @wait: wait an atomic commit to finish
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
@@ -253,6 +192,11 @@ struct exynos_drm_private {
unsigned long da_space_size;
unsigned int pipe;
+
+ /* for atomic commit */
+ u32 pending;
+ spinlock_t lock;
+ wait_queue_head_t wait;
};
/*
@@ -293,47 +237,30 @@ 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);
+struct drm_encoder *exynos_dpi_probe(struct device *dev);
+int exynos_dpi_remove(struct drm_encoder *encoder);
+int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder);
#else
-static inline struct exynos_drm_display *
+static inline struct drm_encoder *
exynos_dpi_probe(struct device *dev) { return NULL; }
-static inline int exynos_dpi_remove(struct exynos_drm_display *display)
+static inline int exynos_dpi_remove(struct drm_encoder *encoder)
+{
+ return 0;
+}
+static inline int exynos_dpi_bind(struct drm_device *dev,
+ struct drm_encoder *encoder)
{
return 0;
}
#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);
+int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
+ bool async);
-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 +273,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/kernel/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 04927153b..12b03b364 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/kernel/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,16 +244,22 @@ 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 {
- struct exynos_drm_display display;
+ struct drm_encoder encoder;
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
struct device_node *panel_node;
@@ -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,35 +289,197 @@ 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)
#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
-static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d)
+static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
{
- return container_of(d, struct exynos_dsi, display);
+ return container_of(e, struct exynos_dsi, encoder);
}
+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))
@@ -1116,9 +1272,9 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
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;
+ struct drm_encoder *encoder = &dsi->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,79 +1510,66 @@ 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)
dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
}
-static int exynos_dsi_enable(struct exynos_dsi *dsi)
+static void exynos_dsi_enable(struct drm_encoder *encoder)
{
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
int ret;
if (dsi->state & DSIM_STATE_ENABLED)
- return 0;
+ return;
ret = exynos_dsi_poweron(dsi);
if (ret < 0)
- return ret;
+ return;
+
+ 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;
+ return;
}
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;
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi);
- return ret;
+ return;
}
- return 0;
+ dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
}
-static void exynos_dsi_disable(struct exynos_dsi *dsi)
+static void exynos_dsi_disable(struct drm_encoder *encoder)
{
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+
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;
-}
-static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
-{
- struct exynos_dsi *dsi = display_to_dsi(display);
-
- if (dsi->panel) {
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- exynos_dsi_enable(dsi);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- exynos_dsi_disable(dsi);
- break;
- default:
- break;
- }
- }
+ exynos_dsi_poweroff(dsi);
}
static enum drm_connector_status
@@ -1435,10 +1582,10 @@ exynos_dsi_detect(struct drm_connector *connector, bool force)
if (dsi->panel)
drm_panel_attach(dsi->panel, &dsi->connector);
} else if (!dsi->panel_node) {
- struct exynos_drm_display *display;
+ struct drm_encoder *encoder;
- display = platform_get_drvdata(to_platform_device(dsi->dev));
- exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF);
+ encoder = platform_get_drvdata(to_platform_device(dsi->dev));
+ exynos_dsi_disable(encoder);
drm_panel_detach(dsi->panel);
dsi->panel = NULL;
}
@@ -1457,10 +1604,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)
@@ -1478,7 +1628,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector)
{
struct exynos_dsi *dsi = connector_to_dsi(connector);
- return dsi->display.encoder;
+ return &dsi->encoder;
}
static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
@@ -1486,10 +1636,9 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
.best_encoder = exynos_dsi_best_encoder,
};
-static int exynos_dsi_create_connector(struct exynos_drm_display *display,
- struct drm_encoder *encoder)
+static int exynos_dsi_create_connector(struct drm_encoder *encoder)
{
- struct exynos_dsi *dsi = display_to_dsi(display);
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct drm_connector *connector = &dsi->connector;
int ret;
@@ -1510,26 +1659,40 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
return 0;
}
-static void exynos_dsi_mode_set(struct exynos_drm_display *display,
- struct drm_display_mode *mode)
+static bool exynos_dsi_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct exynos_dsi *dsi = display_to_dsi(display);
- struct videomode *vm = &dsi->vm;
+ return true;
+}
- vm->hactive = mode->hdisplay;
- vm->vactive = mode->vdisplay;
- vm->vfront_porch = mode->vsync_start - mode->vdisplay;
- vm->vback_porch = mode->vtotal - mode->vsync_end;
- vm->vsync_len = mode->vsync_end - mode->vsync_start;
- vm->hfront_porch = mode->hsync_start - mode->hdisplay;
- vm->hback_porch = mode->htotal - mode->hsync_end;
- vm->hsync_len = mode->hsync_end - mode->hsync_start;
+static void exynos_dsi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+ struct videomode *vm = &dsi->vm;
+ struct drm_display_mode *m = adjusted_mode;
+
+ vm->hactive = m->hdisplay;
+ vm->vactive = m->vdisplay;
+ vm->vfront_porch = m->vsync_start - m->vdisplay;
+ vm->vback_porch = m->vtotal - m->vsync_end;
+ vm->vsync_len = m->vsync_end - m->vsync_start;
+ vm->hfront_porch = m->hsync_start - m->hdisplay;
+ vm->hback_porch = m->htotal - m->hsync_end;
+ vm->hsync_len = m->hsync_end - m->hsync_start;
}
-static struct exynos_drm_display_ops exynos_dsi_display_ops = {
- .create_connector = exynos_dsi_create_connector,
+static struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
+ .mode_fixup = exynos_dsi_mode_fixup,
.mode_set = exynos_dsi_mode_set,
- .dpms = exynos_dsi_dpms
+ .enable = exynos_dsi_enable,
+ .disable = exynos_dsi_disable,
+};
+
+static struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
@@ -1627,7 +1790,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);
@@ -1637,28 +1815,48 @@ end:
static int exynos_dsi_bind(struct device *dev, struct device *master,
void *data)
{
- struct exynos_drm_display *display = dev_get_drvdata(dev);
- struct exynos_dsi *dsi = display_to_dsi(display);
+ struct drm_encoder *encoder = dev_get_drvdata(dev);
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct drm_device *drm_dev = data;
+ struct drm_bridge *bridge;
int ret;
- ret = exynos_drm_create_enc_conn(drm_dev, display);
+ ret = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+ EXYNOS_DISPLAY_TYPE_LCD);
+ if (ret < 0)
+ return ret;
+
+ encoder->possible_crtcs = 1 << ret;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
+
+ ret = exynos_dsi_create_connector(encoder);
if (ret) {
- DRM_ERROR("Encoder create [%d] failed with %d\n",
- display->type, ret);
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
+ drm_encoder_cleanup(encoder);
return ret;
}
+ bridge = of_drm_find_bridge(dsi->bridge_node);
+ if (bridge) {
+ drm_bridge_attach(drm_dev, bridge);
+ }
+
return mipi_dsi_host_register(&dsi->dsi_host);
}
static void exynos_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
- struct exynos_drm_display *display = dev_get_drvdata(dev);
- struct exynos_dsi *dsi = display_to_dsi(display);
+ struct drm_encoder *encoder = dev_get_drvdata(dev);
+ struct exynos_dsi *dsi = encoder_to_dsi(encoder);
- exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF);
+ exynos_dsi_disable(encoder);
mipi_dsi_host_unregister(&dsi->dsi_host);
}
@@ -1673,20 +1871,12 @@ 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)
return -ENOMEM;
- 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 +1892,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 +1903,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 +1949,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;
+ platform_set_drvdata(pdev, &dsi->encoder);
- 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/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.c
deleted file mode 100644
index 57de0bdc5..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/* exynos_drm_encoder.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Joonyoung Shim <jy0922.shim@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_drm_encoder.h"
-
-#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
- drm_encoder)
-
-/*
- * exynos specific encoder structure.
- *
- * @drm_encoder: encoder object.
- * @display: the display structure that maps to this encoder
- */
-struct exynos_drm_encoder {
- struct drm_encoder 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,
- struct drm_display_mode *adjusted_mode)
-{
- struct drm_device *dev = encoder->dev;
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display *display = exynos_encoder->display;
- struct drm_connector *connector;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->encoder != encoder)
- continue;
-
- if (display->ops->mode_fixup)
- display->ops->mode_fixup(display, connector, mode,
- adjusted_mode);
- }
-
- return true;
-}
-
-static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display *display = exynos_encoder->display;
-
- if (display->ops->mode_set)
- 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)
-{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display *display = exynos_encoder->display;
-
- if (display->ops->dpms)
- display->ops->dpms(display, DRM_MODE_DPMS_ON);
-
- if (display->ops->commit)
- display->ops->commit(display);
-}
-
-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);
-
- /* 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);
- }
-}
-
-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,
- .disable = exynos_drm_encoder_disable,
-};
-
-static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
-{
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
-
- drm_encoder_cleanup(encoder);
- kfree(exynos_encoder);
-}
-
-static struct drm_encoder_funcs exynos_encoder_funcs = {
- .destroy = exynos_drm_encoder_destroy,
-};
-
-static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
-{
- struct drm_encoder *clone;
- struct drm_device *dev = encoder->dev;
- struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
- struct exynos_drm_display *display = exynos_encoder->display;
- unsigned int clone_mask = 0;
- int cnt = 0;
-
- list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
- switch (display->type) {
- case EXYNOS_DISPLAY_TYPE_LCD:
- case EXYNOS_DISPLAY_TYPE_HDMI:
- case EXYNOS_DISPLAY_TYPE_VIDI:
- clone_mask |= (1 << (cnt++));
- break;
- default:
- continue;
- }
- }
-
- return clone_mask;
-}
-
-void exynos_drm_encoder_setup(struct drm_device *dev)
-{
- struct drm_encoder *encoder;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
- encoder->possible_clones = exynos_drm_encoder_clones(encoder);
-}
-
-struct drm_encoder *
-exynos_drm_encoder_create(struct drm_device *dev,
- struct exynos_drm_display *display,
- unsigned long possible_crtcs)
-{
- struct drm_encoder *encoder;
- struct exynos_drm_encoder *exynos_encoder;
-
- if (!possible_crtcs)
- return NULL;
-
- exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
- if (!exynos_encoder)
- return NULL;
-
- exynos_encoder->display = display;
- encoder = &exynos_encoder->drm_encoder;
- encoder->possible_crtcs = possible_crtcs;
-
- DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
-
- drm_encoder_init(dev, encoder, &exynos_encoder_funcs,
- DRM_MODE_ENCODER_TMDS);
-
- drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs);
-
- DRM_DEBUG_KMS("encoder has been created\n");
-
- return encoder;
-}
-
-struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder)
-{
- return to_exynos_encoder(encoder)->display;
-}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.h
deleted file mode 100644
index 26305d8dd..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Joonyoung Shim <jy0922.shim@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_ENCODER_H_
-#define _EXYNOS_DRM_ENCODER_H_
-
-void exynos_drm_encoder_setup(struct drm_device *dev);
-struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
- struct exynos_drm_display *mgr,
- unsigned long possible_crtcs);
-struct exynos_drm_display *exynos_drm_get_display(struct drm_encoder *encoder);
-
-#endif
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 142eb4e3f..fcea28bdb 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -16,12 +16,13 @@
#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"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h"
-#include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
#include "exynos_drm_crtc.h"
@@ -31,17 +32,15 @@
* exynos specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
- * @buf_cnt: a buffer count to drm framebuffer.
- * @exynos_gem_obj: array of exynos specific gem object containing a gem object.
+ * @exynos_gem: array of exynos specific gem object containing a gem object.
*/
struct exynos_drm_fb {
- struct drm_framebuffer fb;
- unsigned int buf_cnt;
- struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER];
+ struct drm_framebuffer fb;
+ struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
};
static int check_fb_gem_memory_type(struct drm_device *drm_dev,
- struct exynos_drm_gem_obj *exynos_gem_obj)
+ struct exynos_drm_gem *exynos_gem)
{
unsigned int flags;
@@ -52,7 +51,7 @@ static int check_fb_gem_memory_type(struct drm_device *drm_dev,
if (is_drm_iommu_supported(drm_dev))
return 0;
- flags = exynos_gem_obj->flags;
+ flags = exynos_gem->flags;
/*
* without iommu support, not support physically non-continuous memory
@@ -76,13 +75,13 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
drm_framebuffer_cleanup(fb);
- for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem_obj); i++) {
+ for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem); i++) {
struct drm_gem_object *obj;
- if (exynos_fb->exynos_gem_obj[i] == NULL)
+ if (exynos_fb->exynos_gem[i] == NULL)
continue;
- obj = &exynos_fb->exynos_gem_obj[i]->base;
+ obj = &exynos_fb->exynos_gem[i]->base;
drm_gem_object_unreference_unlocked(obj);
}
@@ -96,12 +95,8 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
- /* This fb should have only one gem object. */
- if (WARN_ON(exynos_fb->buf_cnt != 1))
- return -EINVAL;
-
return drm_gem_handle_create(file_priv,
- &exynos_fb->exynos_gem_obj[0]->base, handle);
+ &exynos_fb->exynos_gem[0]->base, handle);
}
static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
@@ -120,138 +115,95 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
.dirty = exynos_drm_fb_dirty,
};
-void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
- unsigned int cnt)
-{
- struct exynos_drm_fb *exynos_fb;
-
- exynos_fb = to_exynos_fb(fb);
-
- exynos_fb->buf_cnt = cnt;
-}
-
-unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb)
-{
- struct exynos_drm_fb *exynos_fb;
-
- exynos_fb = to_exynos_fb(fb);
-
- return exynos_fb->buf_cnt;
-}
-
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object *obj)
+ struct exynos_drm_gem **exynos_gem,
+ int count)
{
struct exynos_drm_fb *exynos_fb;
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ int i;
int ret;
- exynos_gem_obj = to_exynos_gem_obj(obj);
-
- ret = check_fb_gem_memory_type(dev, exynos_gem_obj);
- if (ret < 0)
- return ERR_PTR(ret);
-
exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
if (!exynos_fb)
return ERR_PTR(-ENOMEM);
+ for (i = 0; i < count; i++) {
+ ret = check_fb_gem_memory_type(dev, exynos_gem[i]);
+ if (ret < 0)
+ goto err;
+
+ exynos_fb->exynos_gem[i] = exynos_gem[i];
+ }
+
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
- exynos_fb->exynos_gem_obj[0] = exynos_gem_obj;
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
- if (ret) {
- kfree(exynos_fb);
+ if (ret < 0) {
DRM_ERROR("failed to initialize framebuffer\n");
- return ERR_PTR(ret);
+ goto err;
}
return &exynos_fb->fb;
+
+err:
+ kfree(exynos_fb);
+ return ERR_PTR(ret);
}
static struct drm_framebuffer *
exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
{
+ struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
struct drm_gem_object *obj;
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct exynos_drm_fb *exynos_fb;
- int i, ret;
-
- exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
- if (!exynos_fb)
- return ERR_PTR(-ENOMEM);
-
- obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
- if (!obj) {
- DRM_ERROR("failed to lookup gem object\n");
- ret = -ENOENT;
- goto err_free;
- }
-
- drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
- exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
- exynos_fb->buf_cnt = drm_format_num_planes(mode_cmd->pixel_format);
-
- DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt);
+ struct drm_framebuffer *fb;
+ int i;
+ int ret;
- for (i = 1; i < exynos_fb->buf_cnt; i++) {
+ for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
obj = drm_gem_object_lookup(dev, file_priv,
- mode_cmd->handles[i]);
+ mode_cmd->handles[i]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
ret = -ENOENT;
- exynos_fb->buf_cnt = i;
- goto err_unreference;
+ goto err;
}
- exynos_gem_obj = to_exynos_gem_obj(obj);
- exynos_fb->exynos_gem_obj[i] = exynos_gem_obj;
-
- ret = check_fb_gem_memory_type(dev, exynos_gem_obj);
- if (ret < 0)
- goto err_unreference;
+ exynos_gem[i] = to_exynos_gem(obj);
}
- ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
- if (ret) {
- DRM_ERROR("failed to init framebuffer.\n");
- goto err_unreference;
+ fb = exynos_drm_framebuffer_init(dev, mode_cmd, exynos_gem, i);
+ if (IS_ERR(fb)) {
+ ret = PTR_ERR(fb);
+ goto err;
}
- return &exynos_fb->fb;
+ return fb;
-err_unreference:
- for (i = 0; i < exynos_fb->buf_cnt; i++) {
- struct drm_gem_object *obj;
+err:
+ while (i--)
+ drm_gem_object_unreference_unlocked(&exynos_gem[i]->base);
- obj = &exynos_fb->exynos_gem_obj[i]->base;
- if (obj)
- drm_gem_object_unreference_unlocked(obj);
- }
-err_free:
- kfree(exynos_fb);
return ERR_PTR(ret);
}
-struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
- int index)
+struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
- struct exynos_drm_gem_buf *buffer;
+ struct exynos_drm_gem *exynos_gem;
if (index >= MAX_FB_BUFFER)
return NULL;
- buffer = exynos_fb->exynos_gem_obj[index]->buffer;
- if (!buffer)
+ exynos_gem = exynos_fb->exynos_gem[index];
+ if (!exynos_gem)
return NULL;
- DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr);
+ DRM_DEBUG_KMS("dma_addr: 0x%lx\n", (unsigned long)exynos_gem->dma_addr);
- return buffer;
+ return exynos_gem;
}
static void exynos_drm_output_poll_changed(struct drm_device *dev)
@@ -268,6 +220,8 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
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/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.h
index 517471b37..726a2d443 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_fb.h
@@ -14,22 +14,17 @@
#ifndef _EXYNOS_DRM_FB_H_
#define _EXYNOS_DRM_FB_H
+#include "exynos_drm_gem.h"
+
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object *obj);
+ struct exynos_drm_gem **exynos_gem,
+ int count);
-/* get memory information of a drm framebuffer */
-struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
- int index);
+/* get gem object of a drm framebuffer */
+struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index);
void exynos_drm_mode_config_init(struct drm_device *dev);
-/* set a buffer count to drm framebuffer. */
-void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb,
- unsigned int cnt);
-
-/* get a buffer count to drm framebuffer. */
-unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb);
-
#endif
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index e71e331f0..f6118baa8 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -21,7 +21,6 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h"
-#include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
#define MAX_CONNECTOR 4
@@ -31,8 +30,8 @@
drm_fb_helper)
struct exynos_drm_fbdev {
- struct drm_fb_helper drm_fb_helper;
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_fb_helper drm_fb_helper;
+ struct exynos_drm_gem *exynos_gem;
};
static int exynos_drm_fb_mmap(struct fb_info *info,
@@ -40,8 +39,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info,
{
struct drm_fb_helper *helper = info->par;
struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
- struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj;
- struct exynos_drm_gem_buf *buffer = exynos_gem_obj->buffer;
+ struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
unsigned long vm_size;
int ret;
@@ -49,11 +47,12 @@ static int exynos_drm_fb_mmap(struct fb_info *info,
vm_size = vma->vm_end - vma->vm_start;
- if (vm_size > buffer->size)
+ if (vm_size > exynos_gem->size)
return -EINVAL;
- ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
- buffer->dma_addr, buffer->size, &buffer->dma_attrs);
+ ret = dma_mmap_attrs(helper->dev->dev, vma, exynos_gem->pages,
+ exynos_gem->dma_addr, exynos_gem->size,
+ &exynos_gem->dma_attrs);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
@@ -65,9 +64,9 @@ static int exynos_drm_fb_mmap(struct fb_info *info,
static struct fb_ops exynos_drm_fb_ops = {
.owner = THIS_MODULE,
.fb_mmap = exynos_drm_fb_mmap,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
+ .fb_fillrect = drm_fb_helper_cfb_fillrect,
+ .fb_copyarea = drm_fb_helper_cfb_copyarea,
+ .fb_imageblit = drm_fb_helper_cfb_imageblit,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_blank = drm_fb_helper_blank,
@@ -76,42 +75,42 @@ static struct fb_ops exynos_drm_fb_ops = {
};
static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes,
- struct drm_framebuffer *fb)
+ struct drm_fb_helper_surface_size *sizes,
+ struct exynos_drm_gem *exynos_gem)
{
- struct fb_info *fbi = helper->fbdev;
- struct exynos_drm_gem_buf *buffer;
+ struct fb_info *fbi;
+ struct drm_framebuffer *fb = helper->fb;
unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
unsigned int nr_pages;
unsigned long offset;
+ fbi = drm_fb_helper_alloc_fbi(helper);
+ if (IS_ERR(fbi)) {
+ DRM_ERROR("failed to allocate fb info.\n");
+ return PTR_ERR(fbi);
+ }
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &exynos_drm_fb_ops;
+
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
- /* RGB formats use only one buffer */
- buffer = exynos_drm_fb_buffer(fb, 0);
- if (!buffer) {
- DRM_DEBUG_KMS("buffer is null.\n");
- return -EFAULT;
- }
-
- nr_pages = buffer->size >> PAGE_SHIFT;
+ nr_pages = exynos_gem->size >> PAGE_SHIFT;
- buffer->kvaddr = (void __iomem *) vmap(buffer->pages,
- nr_pages, VM_MAP,
- pgprot_writecombine(PAGE_KERNEL));
- if (!buffer->kvaddr) {
+ exynos_gem->kvaddr = (void __iomem *) vmap(exynos_gem->pages, nr_pages,
+ VM_MAP, pgprot_writecombine(PAGE_KERNEL));
+ if (!exynos_gem->kvaddr) {
DRM_ERROR("failed to map pages to kernel space.\n");
+ drm_fb_helper_release_fbi(helper);
return -EIO;
}
- /* buffer count to framebuffer always is 1 at booting time. */
- exynos_drm_fb_set_buf_cnt(fb, 1);
-
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
- fbi->screen_base = buffer->kvaddr + offset;
+ fbi->screen_base = exynos_gem->kvaddr + offset;
fbi->screen_size = size;
fbi->fix.smem_len = size;
@@ -122,9 +121,8 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
struct drm_device *dev = helper->dev;
- struct fb_info *fbi;
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
struct platform_device *pdev = dev->platformdev;
unsigned long size;
@@ -142,69 +140,46 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
mutex_lock(&dev->struct_mutex);
- fbi = framebuffer_alloc(0, &pdev->dev);
- if (!fbi) {
- DRM_ERROR("failed to allocate fb info.\n");
- ret = -ENOMEM;
- goto out;
- }
-
size = mode_cmd.pitches[0] * mode_cmd.height;
- exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size);
+ exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size);
/*
* If physically contiguous memory allocation fails and if IOMMU is
* supported then try to get buffer from non physically contiguous
* memory area.
*/
- if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
+ if (IS_ERR(exynos_gem) && is_drm_iommu_supported(dev)) {
dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
- exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG,
- size);
+ exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG,
+ size);
}
- if (IS_ERR(exynos_gem_obj)) {
- ret = PTR_ERR(exynos_gem_obj);
- goto err_release_framebuffer;
+ if (IS_ERR(exynos_gem)) {
+ ret = PTR_ERR(exynos_gem);
+ goto out;
}
- exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
+ exynos_fbdev->exynos_gem = exynos_gem;
- helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
- &exynos_gem_obj->base);
+ helper->fb =
+ exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
if (IS_ERR(helper->fb)) {
DRM_ERROR("failed to create drm framebuffer.\n");
ret = PTR_ERR(helper->fb);
goto err_destroy_gem;
}
- helper->fbdev = fbi;
-
- fbi->par = helper;
- fbi->flags = FBINFO_FLAG_DEFAULT;
- fbi->fbops = &exynos_drm_fb_ops;
-
- ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
- if (ret) {
- DRM_ERROR("failed to allocate cmap.\n");
- goto err_destroy_framebuffer;
- }
-
- ret = exynos_drm_fbdev_update(helper, sizes, helper->fb);
+ ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
if (ret < 0)
- goto err_dealloc_cmap;
+ goto err_destroy_framebuffer;
mutex_unlock(&dev->struct_mutex);
return ret;
-err_dealloc_cmap:
- fb_dealloc_cmap(&fbi->cmap);
err_destroy_framebuffer:
drm_framebuffer_cleanup(helper->fb);
err_destroy_gem:
- exynos_drm_gem_destroy(exynos_gem_obj);
-err_release_framebuffer:
- framebuffer_release(fbi);
+ exynos_drm_gem_destroy(exynos_gem);
/*
* if failed, all resources allocated above would be released by
@@ -275,9 +250,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");
@@ -300,11 +272,11 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,
struct drm_fb_helper *fb_helper)
{
struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper);
- struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
struct drm_framebuffer *fb;
- if (exynos_gem_obj->buffer->kvaddr)
- vunmap(exynos_gem_obj->buffer->kvaddr);
+ if (exynos_gem->kvaddr)
+ vunmap(exynos_gem->kvaddr);
/* release drm framebuffer and real buffer */
if (fb_helper->fb && fb_helper->fb->funcs) {
@@ -315,21 +287,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,
}
}
- /* release linux framebuffer */
- if (fb_helper->fbdev) {
- struct fb_info *info;
- int ret;
-
- info = fb_helper->fbdev;
- ret = unregister_framebuffer(info);
- if (ret < 0)
- DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
-
- if (info->cmap.len)
- fb_dealloc_cmap(&info->cmap);
-
- framebuffer_release(info);
- }
+ drm_fb_helper_unregister_fbi(fb_helper);
+ drm_fb_helper_release_fbi(fb_helper);
drm_fb_helper_fini(fb_helper);
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_fimc.c
index 842d6b8dc..c747824f3 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_fimc.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_fimc.c
@@ -466,7 +466,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
EXYNOS_MSCTRL_C_INT_IN_2PLANE);
break;
default:
- dev_err(ippdrv->dev, "inavlid source yuv order 0x%x.\n", fmt);
+ dev_err(ippdrv->dev, "invalid source yuv order 0x%x.\n", fmt);
return -EINVAL;
}
@@ -513,7 +513,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
cfg |= EXYNOS_MSCTRL_INFORMAT_YCBCR420;
break;
default:
- dev_err(ippdrv->dev, "inavlid source format 0x%x.\n", fmt);
+ dev_err(ippdrv->dev, "invalid source format 0x%x.\n", fmt);
return -EINVAL;
}
@@ -578,7 +578,7 @@ static int fimc_src_set_transf(struct device *dev,
cfg1 &= ~EXYNOS_MSCTRL_FLIP_Y_MIRROR;
break;
default:
- dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree);
+ dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
return -EINVAL;
}
@@ -701,7 +701,7 @@ static int fimc_src_set_addr(struct device *dev,
property->prop_id, buf_id, buf_type);
if (buf_id > FIMC_MAX_SRC) {
- dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id);
+ dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id);
return -ENOMEM;
}
@@ -812,7 +812,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
cfg |= EXYNOS_CIOCTRL_YCBCR_2PLANE;
break;
default:
- dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt);
+ dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
return -EINVAL;
}
@@ -865,7 +865,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
cfg |= EXYNOS_CITRGFMT_OUTFORMAT_YCBCR420;
break;
default:
- dev_err(ippdrv->dev, "inavlid target format 0x%x.\n",
+ dev_err(ippdrv->dev, "invalid target format 0x%x.\n",
fmt);
return -EINVAL;
}
@@ -929,7 +929,7 @@ static int fimc_dst_set_transf(struct device *dev,
cfg &= ~EXYNOS_CITRGFMT_FLIP_Y_MIRROR;
break;
default:
- dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree);
+ dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
return -EINVAL;
}
@@ -1160,7 +1160,7 @@ static int fimc_dst_set_addr(struct device *dev,
property->prop_id, buf_id, buf_type);
if (buf_id > FIMC_MAX_DST) {
- dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id);
+ dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id);
return -ENOMEM;
}
@@ -1206,23 +1206,6 @@ static struct exynos_drm_ipp_ops fimc_dst_ops = {
.set_addr = fimc_dst_set_addr,
};
-static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable)
-{
- DRM_DEBUG_KMS("enable[%d]\n", enable);
-
- if (enable) {
- clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
- clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]);
- ctx->suspended = false;
- } else {
- clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]);
- clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]);
- ctx->suspended = true;
- }
-
- return 0;
-}
-
static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
{
struct fimc_context *ctx = dev_id;
@@ -1745,7 +1728,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);
@@ -1781,6 +1763,24 @@ static int fimc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable)
+{
+ DRM_DEBUG_KMS("enable[%d]\n", enable);
+
+ if (enable) {
+ clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
+ clk_prepare_enable(ctx->clocks[FIMC_CLK_WB_A]);
+ ctx->suspended = false;
+ } else {
+ clk_disable_unprepare(ctx->clocks[FIMC_CLK_GATE]);
+ clk_disable_unprepare(ctx->clocks[FIMC_CLK_WB_A]);
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_PM_SLEEP
static int fimc_suspend(struct device *dev)
{
@@ -1807,7 +1807,6 @@ static int fimc_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM
static int fimc_runtime_suspend(struct device *dev)
{
struct fimc_context *ctx = get_fimc_context(dev);
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index a0edab833..bd75c1531 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -41,7 +41,6 @@
* CPU Interface.
*/
-#define FIMD_DEFAULT_FRAMERATE 60
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
/* position control register for hardware window 0, 2 ~ 4.*/
@@ -59,6 +58,7 @@
#define VIDWnALPHA1(win) (VIDW_ALPHA + 0x04 + (win) * 8)
#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
+#define VIDWx_BUF_START_S(win, buf) (VIDW_BUF_START_S(buf) + (win) * 8)
#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
@@ -87,6 +87,7 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
+#define CURSOR_WIN 4
struct fimd_driver_data {
unsigned int timing_base;
@@ -153,7 +154,6 @@ struct fimd_context {
struct clk *lcd_clk;
void __iomem *regs;
struct regmap *sysreg;
- unsigned int default_win;
unsigned long irq_flags;
u32 vidcon0;
u32 vidcon1;
@@ -169,7 +169,7 @@ struct fimd_context {
struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data;
- struct exynos_drm_display *display;
+ struct drm_encoder *encoder;
};
static const struct of_device_id fimd_driver_dt_match[] = {
@@ -187,6 +187,14 @@ static const struct of_device_id fimd_driver_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
+static const uint32_t fimd_formats[] = {
+ DRM_FORMAT_C8,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
static inline struct fimd_driver_data *drm_fimd_get_driver_data(
struct platform_device *pdev)
{
@@ -196,6 +204,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 +306,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,43 +336,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;
- }
-}
-
-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;
+ /* ensure that vblank interrupt won't be reported to core */
+ ctx->suspended = false;
+ ctx->pipe = -1;
- /*
- * 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);
-static void fimd_iommu_detach_devices(struct fimd_context *ctx)
-{
- /* 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);
+ pm_runtime_put(ctx->dev);
}
static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
@@ -324,20 +376,10 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
return (clkdiv < 0x100) ? clkdiv : 0xff;
}
-static bool fimd_mode_fixup(struct exynos_drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- if (adjusted_mode->vrefresh == 0)
- adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
-
- return true;
-}
-
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,65 +476,10 @@ 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)
+static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
+ struct drm_framebuffer *fb)
{
- 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)
-{
- struct exynos_drm_plane *plane = &ctx->planes[win];
unsigned long val;
val = WINCONx_ENWIN;
@@ -502,11 +489,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
* So the request format is ARGB8888 then change it to XRGB8888.
*/
if (ctx->driver_data->has_limited_fmt && !win) {
- if (plane->pixel_format == DRM_FORMAT_ARGB8888)
- plane->pixel_format = DRM_FORMAT_XRGB8888;
+ if (fb->pixel_format == DRM_FORMAT_ARGB8888)
+ fb->pixel_format = DRM_FORMAT_XRGB8888;
}
- switch (plane->pixel_format) {
+ switch (fb->pixel_format) {
case DRM_FORMAT_C8:
val |= WINCON0_BPPMODE_8BPP_PALETTE;
val |= WINCONx_BURSTLEN_8WORD;
@@ -542,7 +529,7 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
break;
}
- DRM_DEBUG_KMS("bpp = %d\n", plane->bpp);
+ DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
/*
* In case of exynos, setting dma-burst to 16Word causes permanent
@@ -552,7 +539,7 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
* movement causes unstable DMA which results into iommu crash/tear.
*/
- if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+ if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
val &= ~WINCONx_BURSTLEN_MASK;
val |= WINCONx_BURSTLEN_4WORD;
}
@@ -602,6 +589,16 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
{
u32 reg, bits, val;
+ /*
+ * SHADOWCON/PRTCON register is used for enabling timing.
+ *
+ * for example, once only width value of a register is set,
+ * if the dma is started then fimd hardware could malfunction so
+ * with protect window setting, the register fields with prefix '_F'
+ * wouldn't be updated at vsync also but updated once unprotect window
+ * is set.
+ */
+
if (ctx->driver_data->has_shadowcon) {
reg = SHADOWCON;
bits = SHADOWCON_WINx_PROTECT(win);
@@ -618,44 +615,45 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
writel(val, ctx->regs + reg);
}
-static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+static void fimd_atomic_begin(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct fimd_context *ctx = crtc->ctx;
- struct exynos_drm_plane *plane;
- dma_addr_t dma_addr;
- unsigned long val, size, offset;
- unsigned int last_x, last_y, buf_offsize, line_size;
if (ctx->suspended)
return;
- if (win < 0 || win >= WINDOWS_NR)
- return;
+ fimd_shadow_protect_win(ctx, plane->zpos, true);
+}
- plane = &ctx->planes[win];
+static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct fimd_context *ctx = crtc->ctx;
- /* If suspended, enable this on resume */
- if (ctx->suspended) {
- plane->resume = true;
+ if (ctx->suspended)
return;
- }
- /*
- * SHADOWCON/PRTCON register is used for enabling timing.
- *
- * for example, once only width value of a register is set,
- * if the dma is started then fimd hardware could malfunction so
- * with protect window setting, the register fields with prefix '_F'
- * wouldn't be updated at vsync also but updated once unprotect window
- * is set.
- */
+ fimd_shadow_protect_win(ctx, plane->zpos, false);
+}
- /* protect windows */
- fimd_shadow_protect_win(ctx, win, true);
+static void fimd_update_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
+{
+ struct fimd_context *ctx = crtc->ctx;
+ struct drm_plane_state *state = plane->base.state;
+ dma_addr_t dma_addr;
+ unsigned long val, size, offset;
+ unsigned int last_x, last_y, buf_offsize, line_size;
+ unsigned int win = plane->zpos;
+ unsigned int bpp = state->fb->bits_per_pixel >> 3;
+ unsigned int pitch = state->fb->pitches[0];
+ if (ctx->suspended)
+ return;
- offset = plane->src_x * (plane->bpp >> 3);
- offset += plane->src_y * plane->pitch;
+ offset = plane->src_x * bpp;
+ offset += plane->src_y * pitch;
/* buffer start address */
dma_addr = plane->dma_addr[0] + offset;
@@ -663,18 +661,18 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
- size = plane->pitch * plane->crtc_height;
+ size = pitch * plane->crtc_h;
val = (unsigned long)(dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)dma_addr, val, size);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
- plane->crtc_width, plane->crtc_height);
+ plane->crtc_w, plane->crtc_h);
/* buffer size */
- buf_offsize = plane->pitch - (plane->crtc_width * (plane->bpp >> 3));
- line_size = plane->crtc_width * (plane->bpp >> 3);
+ buf_offsize = pitch - (plane->crtc_w * bpp);
+ line_size = plane->crtc_w * bpp;
val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
@@ -688,10 +686,10 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y);
writel(val, ctx->regs + VIDOSD_A(win));
- last_x = plane->crtc_x + plane->crtc_width;
+ last_x = plane->crtc_x + plane->crtc_w;
if (last_x)
last_x--;
- last_y = plane->crtc_y + plane->crtc_height;
+ last_y = plane->crtc_y + plane->crtc_h;
if (last_y)
last_y--;
@@ -708,13 +706,13 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
u32 offset = VIDOSD_D(win);
if (win == 0)
offset = VIDOSD_C(win);
- val = plane->crtc_width * plane->crtc_height;
+ val = plane->crtc_w * plane->crtc_h;
writel(val, ctx->regs + offset);
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
}
- fimd_win_set_pixfmt(ctx, win);
+ fimd_win_set_pixfmt(ctx, win, state->fb);
/* hardware window 0 doesn't support color key. */
if (win != 0)
@@ -725,92 +723,32 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
if (ctx->driver_data->has_shadowcon)
fimd_enable_shadow_channel_path(ctx, win, true);
- /* 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);
}
-static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
+static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct fimd_context *ctx = crtc->ctx;
- struct exynos_drm_plane *plane;
+ unsigned int win = plane->zpos;
- if (win < 0 || win >= WINDOWS_NR)
- return;
-
- 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);
fimd_enable_video_output(ctx, win, false);
if (ctx->driver_data->has_shadowcon)
fimd_enable_shadow_channel_path(ctx, win, false);
-
- /* 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 +757,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);
-
- return 0;
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ fimd_enable_vblank(ctx->crtc);
-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_disable_plane(crtc, &ctx->planes[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 +801,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)
@@ -943,7 +854,7 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)
}
if (test_bit(0, &ctx->irq_flags))
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ drm_crtc_handle_vblank(&ctx->crtc->base);
}
static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
@@ -960,18 +871,20 @@ static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
return;
val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
- writel(DP_MIE_CLK_DP_ENABLE, ctx->regs + DP_MIE_CLKCON);
+ writel(val, ctx->regs + DP_MIE_CLKCON);
}
static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
- .dpms = fimd_dpms,
- .mode_fixup = fimd_mode_fixup,
+ .enable = fimd_enable,
+ .disable = fimd_disable,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
.wait_for_vblank = fimd_wait_for_vblank,
- .win_commit = fimd_win_commit,
- .win_disable = fimd_win_disable,
+ .atomic_begin = fimd_atomic_begin,
+ .update_plane = fimd_update_plane,
+ .disable_plane = fimd_disable_plane,
+ .atomic_flush = fimd_atomic_flush,
.te_handler = fimd_te_handler,
.clock_enable = fimd_dp_clock_enable,
};
@@ -979,7 +892,8 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
- u32 val, clear_bit;
+ u32 val, clear_bit, start, start_s;
+ int win;
val = readl(ctx->regs + VIDINTCON1);
@@ -991,15 +905,25 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
if (ctx->pipe < 0 || !ctx->drm_dev)
goto out;
- if (ctx->i80_if) {
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ if (!ctx->i80_if)
+ drm_crtc_handle_vblank(&ctx->crtc->base);
+
+ for (win = 0 ; win < WINDOWS_NR ; win++) {
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+ if (!plane->pending_fb)
+ continue;
+
+ start = readl(ctx->regs + VIDWx_BUF_START(win, 0));
+ start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0));
+ if (start == start_s)
+ exynos_drm_crtc_finish_update(ctx->crtc, plane);
+ }
+
+ if (ctx->i80_if) {
/* Exits triggering mode */
atomic_set(&ctx->triggering, 0);
} else {
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
-
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
@@ -1025,25 +949,32 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
ctx->pipe = priv->pipe++;
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
- type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
- DRM_PLANE_TYPE_OVERLAY;
+ type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
- 1 << ctx->pipe, type, zpos);
+ 1 << ctx->pipe, type, fimd_formats,
+ ARRAY_SIZE(fimd_formats), zpos);
if (ret)
return ret;
}
- exynos_plane = &ctx->planes[ctx->default_win];
+ exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
&fimd_crtc_ops, ctx);
if (IS_ERR(ctx->crtc))
return PTR_ERR(ctx->crtc);
- if (ctx->display)
- exynos_drm_create_enc_conn(drm_dev, ctx->display);
+ if (ctx->encoder)
+ exynos_dpi_bind(drm_dev, ctx->encoder);
+
+ if (is_drm_iommu_supported(drm_dev))
+ fimd_clear_channels(ctx->crtc);
+
+ ret = drm_iommu_attach_device(drm_dev, dev);
+ if (ret)
+ priv->pipe--;
- return fimd_iommu_attach_devices(ctx, drm_dev);
+ return ret;
}
static void fimd_unbind(struct device *dev, struct device *master,
@@ -1051,12 +982,12 @@ 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);
+ drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
- if (ctx->display)
- exynos_dpi_remove(ctx->display);
+ if (ctx->encoder)
+ exynos_dpi_remove(ctx->encoder);
}
static const struct component_ops fimd_component_ops = {
@@ -1079,11 +1010,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 +1060,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);
@@ -1173,11 +1094,9 @@ static int fimd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- ctx->display = exynos_dpi_probe(dev);
- if (IS_ERR(ctx->display)) {
- ret = PTR_ERR(ctx->display);
- goto err_del_component;
- }
+ ctx->encoder = exynos_dpi_probe(dev);
+ if (IS_ERR(ctx->encoder))
+ return PTR_ERR(ctx->encoder);
pm_runtime_enable(dev);
@@ -1190,8 +1109,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 +1117,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/kernel/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 81a250830..c17efdb23 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -48,11 +48,13 @@
/* registers for base address */
#define G2D_SRC_BASE_ADDR 0x0304
+#define G2D_SRC_STRIDE_REG 0x0308
#define G2D_SRC_COLOR_MODE 0x030C
#define G2D_SRC_LEFT_TOP 0x0310
#define G2D_SRC_RIGHT_BOTTOM 0x0314
#define G2D_SRC_PLANE2_BASE_ADDR 0x0318
#define G2D_DST_BASE_ADDR 0x0404
+#define G2D_DST_STRIDE_REG 0x0408
#define G2D_DST_COLOR_MODE 0x040C
#define G2D_DST_LEFT_TOP 0x0410
#define G2D_DST_RIGHT_BOTTOM 0x0414
@@ -148,6 +150,7 @@ struct g2d_cmdlist {
* A structure of buffer description
*
* @format: color format
+ * @stride: buffer stride/pitch in bytes
* @left_x: the x coordinates of left top corner
* @top_y: the y coordinates of left top corner
* @right_x: the x coordinates of right bottom corner
@@ -156,6 +159,7 @@ struct g2d_cmdlist {
*/
struct g2d_buf_desc {
unsigned int format;
+ unsigned int stride;
unsigned int left_x;
unsigned int top_y;
unsigned int right_x;
@@ -190,10 +194,8 @@ struct g2d_cmdlist_userptr {
dma_addr_t dma_addr;
unsigned long userptr;
unsigned long size;
- struct page **pages;
- unsigned int npages;
+ struct frame_vector *vec;
struct sg_table *sgt;
- struct vm_area_struct *vma;
atomic_t refcount;
bool in_pool;
bool out_of_list;
@@ -363,6 +365,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,
{
struct g2d_cmdlist_userptr *g2d_userptr =
(struct g2d_cmdlist_userptr *)obj;
+ struct page **pages;
if (!obj)
return;
@@ -382,19 +385,21 @@ out:
exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,
DMA_BIDIRECTIONAL);
- exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
- g2d_userptr->npages,
- g2d_userptr->vma);
+ pages = frame_vector_pages(g2d_userptr->vec);
+ if (!IS_ERR(pages)) {
+ int i;
- exynos_gem_put_vma(g2d_userptr->vma);
+ for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++)
+ set_page_dirty_lock(pages[i]);
+ }
+ put_vaddr_frames(g2d_userptr->vec);
+ frame_vector_destroy(g2d_userptr->vec);
if (!g2d_userptr->out_of_list)
list_del_init(&g2d_userptr->list);
sg_free_table(g2d_userptr->sgt);
kfree(g2d_userptr->sgt);
-
- drm_free_large(g2d_userptr->pages);
kfree(g2d_userptr);
}
@@ -408,9 +413,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct g2d_cmdlist_userptr *g2d_userptr;
struct g2d_data *g2d;
- struct page **pages;
struct sg_table *sgt;
- struct vm_area_struct *vma;
unsigned long start, end;
unsigned int npages, offset;
int ret;
@@ -456,65 +459,40 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
return ERR_PTR(-ENOMEM);
atomic_set(&g2d_userptr->refcount, 1);
+ g2d_userptr->size = size;
start = userptr & PAGE_MASK;
offset = userptr & ~PAGE_MASK;
end = PAGE_ALIGN(userptr + size);
npages = (end - start) >> PAGE_SHIFT;
- g2d_userptr->npages = npages;
-
- pages = drm_calloc_large(npages, sizeof(struct page *));
- if (!pages) {
- DRM_ERROR("failed to allocate pages.\n");
+ g2d_userptr->vec = frame_vector_create(npages);
+ if (!g2d_userptr->vec) {
ret = -ENOMEM;
goto err_free;
}
- down_read(&current->mm->mmap_sem);
- vma = find_vma(current->mm, userptr);
- if (!vma) {
- up_read(&current->mm->mmap_sem);
- DRM_ERROR("failed to get vm region.\n");
+ ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec);
+ if (ret != npages) {
+ DRM_ERROR("failed to get user pages from userptr.\n");
+ if (ret < 0)
+ goto err_destroy_framevec;
ret = -EFAULT;
- goto err_free_pages;
+ goto err_put_framevec;
}
-
- if (vma->vm_end < userptr + size) {
- up_read(&current->mm->mmap_sem);
- DRM_ERROR("vma is too small.\n");
+ if (frame_vector_to_pages(g2d_userptr->vec) < 0) {
ret = -EFAULT;
- goto err_free_pages;
- }
-
- g2d_userptr->vma = exynos_gem_get_vma(vma);
- if (!g2d_userptr->vma) {
- up_read(&current->mm->mmap_sem);
- DRM_ERROR("failed to copy vma.\n");
- ret = -ENOMEM;
- goto err_free_pages;
- }
-
- g2d_userptr->size = size;
-
- ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK,
- npages, pages, vma);
- if (ret < 0) {
- up_read(&current->mm->mmap_sem);
- DRM_ERROR("failed to get user pages from userptr.\n");
- goto err_put_vma;
+ goto err_put_framevec;
}
- up_read(&current->mm->mmap_sem);
- g2d_userptr->pages = pages;
-
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) {
ret = -ENOMEM;
- goto err_free_userptr;
+ goto err_put_framevec;
}
- ret = sg_alloc_table_from_pages(sgt, pages, npages, offset,
- size, GFP_KERNEL);
+ ret = sg_alloc_table_from_pages(sgt,
+ frame_vector_pages(g2d_userptr->vec),
+ npages, offset, size, GFP_KERNEL);
if (ret < 0) {
DRM_ERROR("failed to get sgt from pages.\n");
goto err_free_sgt;
@@ -549,16 +527,11 @@ err_sg_free_table:
err_free_sgt:
kfree(sgt);
-err_free_userptr:
- exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
- g2d_userptr->npages,
- g2d_userptr->vma);
-
-err_put_vma:
- exynos_gem_put_vma(g2d_userptr->vma);
+err_put_framevec:
+ put_vaddr_frames(g2d_userptr->vec);
-err_free_pages:
- drm_free_large(pages);
+err_destroy_framevec:
+ frame_vector_destroy(g2d_userptr->vec);
err_free:
kfree(g2d_userptr);
@@ -589,6 +562,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)
switch (reg_offset) {
case G2D_SRC_BASE_ADDR:
+ case G2D_SRC_STRIDE_REG:
case G2D_SRC_COLOR_MODE:
case G2D_SRC_LEFT_TOP:
case G2D_SRC_RIGHT_BOTTOM:
@@ -598,6 +572,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)
reg_type = REG_TYPE_SRC_PLANE2;
break;
case G2D_DST_BASE_ADDR:
+ case G2D_DST_STRIDE_REG:
case G2D_DST_COLOR_MODE:
case G2D_DST_LEFT_TOP:
case G2D_DST_RIGHT_BOTTOM:
@@ -652,8 +627,8 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,
enum g2d_reg_type reg_type,
unsigned long size)
{
- unsigned int width, height;
- unsigned long area;
+ int width, height;
+ unsigned long bpp, last_pos;
/*
* check source and destination buffers only.
@@ -662,22 +637,37 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,
if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST)
return true;
- width = buf_desc->right_x - buf_desc->left_x;
+ /* This check also makes sure that right_x > left_x. */
+ width = (int)buf_desc->right_x - (int)buf_desc->left_x;
if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) {
- DRM_ERROR("width[%u] is out of range!\n", width);
+ DRM_ERROR("width[%d] is out of range!\n", width);
return false;
}
- height = buf_desc->bottom_y - buf_desc->top_y;
+ /* This check also makes sure that bottom_y > top_y. */
+ height = (int)buf_desc->bottom_y - (int)buf_desc->top_y;
if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) {
- DRM_ERROR("height[%u] is out of range!\n", height);
+ DRM_ERROR("height[%d] is out of range!\n", height);
return false;
}
- area = (unsigned long)width * (unsigned long)height *
- g2d_get_buf_bpp(buf_desc->format);
- if (area > size) {
- DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size);
+ bpp = g2d_get_buf_bpp(buf_desc->format);
+
+ /* Compute the position of the last byte that the engine accesses. */
+ last_pos = ((unsigned long)buf_desc->bottom_y - 1) *
+ (unsigned long)buf_desc->stride +
+ (unsigned long)buf_desc->right_x * bpp - 1;
+
+ /*
+ * Since right_x > left_x and bottom_y > top_y we already know
+ * that the first_pos < last_pos (first_pos being the position
+ * of the first byte the engine accesses), it just remains to
+ * check if last_pos is smaller then the buffer size.
+ */
+
+ if (last_pos >= size) {
+ DRM_ERROR("last engine access position [%lu] "
+ "is out of range [%lu]!\n", last_pos, size);
return false;
}
@@ -973,8 +963,6 @@ static int g2d_check_reg_offset(struct device *dev,
goto err;
reg_type = g2d_get_reg_type(reg_offset);
- if (reg_type == REG_TYPE_NONE)
- goto err;
/* check userptr buffer type. */
if ((cmdlist->data[index] & ~0x7fffffff) >> 31) {
@@ -983,14 +971,22 @@ static int g2d_check_reg_offset(struct device *dev,
} else
buf_info->types[reg_type] = BUF_TYPE_GEM;
break;
+ case G2D_SRC_STRIDE_REG:
+ case G2D_DST_STRIDE_REG:
+ if (for_addr)
+ goto err;
+
+ reg_type = g2d_get_reg_type(reg_offset);
+
+ buf_desc = &buf_info->descs[reg_type];
+ buf_desc->stride = cmdlist->data[index + 1];
+ break;
case G2D_SRC_COLOR_MODE:
case G2D_DST_COLOR_MODE:
if (for_addr)
goto err;
reg_type = g2d_get_reg_type(reg_offset);
- if (reg_type == REG_TYPE_NONE)
- goto err;
buf_desc = &buf_info->descs[reg_type];
value = cmdlist->data[index + 1];
@@ -1003,8 +999,6 @@ static int g2d_check_reg_offset(struct device *dev,
goto err;
reg_type = g2d_get_reg_type(reg_offset);
- if (reg_type == REG_TYPE_NONE)
- goto err;
buf_desc = &buf_info->descs[reg_type];
value = cmdlist->data[index + 1];
@@ -1018,8 +1012,6 @@ static int g2d_check_reg_offset(struct device *dev,
goto err;
reg_type = g2d_get_reg_type(reg_offset);
- if (reg_type == REG_TYPE_NONE)
- goto err;
buf_desc = &buf_info->descs[reg_type];
value = cmdlist->data[index + 1];
@@ -1067,7 +1059,6 @@ int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data,
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl);
int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
@@ -1238,7 +1229,6 @@ err:
g2d_put_cmdlist(g2d, node);
return ret;
}
-EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl);
int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
@@ -1301,7 +1291,6 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
out:
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl);
static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
@@ -1319,9 +1308,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
return ret;
}
- if (!is_drm_iommu_supported(drm_dev))
- return 0;
-
ret = drm_iommu_attach_device(drm_dev, dev);
if (ret < 0) {
dev_err(dev, "failed to enable iommu.\n");
@@ -1334,9 +1320,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
static void g2d_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
- if (!is_drm_iommu_supported(drm_dev))
- return;
-
drm_iommu_detach_device(drm_dev, dev);
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 0d5b9698d..252eb3014 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -13,98 +13,115 @@
#include <drm/drm_vma_manager.h>
#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
-#include "exynos_drm_buf.h"
#include "exynos_drm_iommu.h"
-static unsigned int convert_to_vm_err_msg(int msg)
+static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
{
- unsigned int out_msg;
+ struct drm_device *dev = exynos_gem->base.dev;
+ enum dma_attr attr;
+ unsigned int nr_pages;
+ struct sg_table sgt;
+ int ret = -ENOMEM;
+
+ if (exynos_gem->dma_addr) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return 0;
+ }
- switch (msg) {
- case 0:
- case -ERESTARTSYS:
- case -EINTR:
- out_msg = VM_FAULT_NOPAGE;
- break;
+ init_dma_attrs(&exynos_gem->dma_attrs);
- case -ENOMEM:
- out_msg = VM_FAULT_OOM;
- break;
+ /*
+ * if EXYNOS_BO_CONTIG, fully physically contiguous memory
+ * region will be allocated else physically contiguous
+ * as possible.
+ */
+ if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG))
+ dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &exynos_gem->dma_attrs);
- default:
- out_msg = VM_FAULT_SIGBUS;
- break;
+ /*
+ * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping
+ * else cachable mapping.
+ */
+ if (exynos_gem->flags & EXYNOS_BO_WC ||
+ !(exynos_gem->flags & EXYNOS_BO_CACHABLE))
+ attr = DMA_ATTR_WRITE_COMBINE;
+ else
+ attr = DMA_ATTR_NON_CONSISTENT;
+
+ dma_set_attr(attr, &exynos_gem->dma_attrs);
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &exynos_gem->dma_attrs);
+
+ nr_pages = exynos_gem->size >> PAGE_SHIFT;
+
+ exynos_gem->pages = drm_calloc_large(nr_pages, sizeof(struct page *));
+ if (!exynos_gem->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ return -ENOMEM;
}
- return out_msg;
-}
+ exynos_gem->cookie = dma_alloc_attrs(dev->dev, exynos_gem->size,
+ &exynos_gem->dma_addr, GFP_KERNEL,
+ &exynos_gem->dma_attrs);
+ if (!exynos_gem->cookie) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ goto err_free;
+ }
-static int check_gem_flags(unsigned int flags)
-{
- if (flags & ~(EXYNOS_BO_MASK)) {
- DRM_ERROR("invalid flags.\n");
- return -EINVAL;
+ ret = dma_get_sgtable_attrs(dev->dev, &sgt, exynos_gem->cookie,
+ exynos_gem->dma_addr, exynos_gem->size,
+ &exynos_gem->dma_attrs);
+ if (ret < 0) {
+ DRM_ERROR("failed to get sgtable.\n");
+ goto err_dma_free;
}
- return 0;
-}
+ if (drm_prime_sg_to_page_addr_arrays(&sgt, exynos_gem->pages, NULL,
+ nr_pages)) {
+ DRM_ERROR("invalid sgtable.\n");
+ ret = -EINVAL;
+ goto err_sgt_free;
+ }
-static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj,
- struct vm_area_struct *vma)
-{
- DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
+ sg_free_table(&sgt);
- /* non-cachable as default. */
- if (obj->flags & EXYNOS_BO_CACHABLE)
- vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
- else if (obj->flags & EXYNOS_BO_WC)
- vma->vm_page_prot =
- pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
- else
- vma->vm_page_prot =
- pgprot_noncached(vm_get_page_prot(vma->vm_flags));
-}
+ DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)exynos_gem->dma_addr, exynos_gem->size);
-static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
-{
- /* TODO */
+ return 0;
+
+err_sgt_free:
+ sg_free_table(&sgt);
+err_dma_free:
+ dma_free_attrs(dev->dev, exynos_gem->size, exynos_gem->cookie,
+ exynos_gem->dma_addr, &exynos_gem->dma_attrs);
+err_free:
+ drm_free_large(exynos_gem->pages);
- return roundup(size, PAGE_SIZE);
+ return ret;
}
-static int exynos_drm_gem_map_buf(struct drm_gem_object *obj,
- struct vm_area_struct *vma,
- unsigned long f_vaddr,
- pgoff_t page_offset)
+static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem)
{
- struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
- struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
- struct scatterlist *sgl;
- unsigned long pfn;
- int i;
-
- if (!buf->sgt)
- return -EINTR;
+ struct drm_device *dev = exynos_gem->base.dev;
- if (page_offset >= (buf->size >> PAGE_SHIFT)) {
- DRM_ERROR("invalid page offset\n");
- return -EINVAL;
+ if (!exynos_gem->dma_addr) {
+ DRM_DEBUG_KMS("dma_addr is invalid.\n");
+ return;
}
- sgl = buf->sgt->sgl;
- for_each_sg(buf->sgt->sgl, sgl, buf->sgt->nents, i) {
- if (page_offset < (sgl->length >> PAGE_SHIFT))
- break;
- page_offset -= (sgl->length >> PAGE_SHIFT);
- }
+ DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)exynos_gem->dma_addr, exynos_gem->size);
- pfn = __phys_to_pfn(sg_phys(sgl)) + page_offset;
+ dma_free_attrs(dev->dev, exynos_gem->size, exynos_gem->cookie,
+ (dma_addr_t)exynos_gem->dma_addr,
+ &exynos_gem->dma_attrs);
- return vm_insert_mixed(vma, f_vaddr, pfn);
+ drm_free_large(exynos_gem->pages);
}
static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
@@ -129,13 +146,9 @@ static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
return 0;
}
-void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
+void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem)
{
- struct drm_gem_object *obj;
- struct exynos_drm_gem_buf *buf;
-
- obj = &exynos_gem_obj->base;
- buf = exynos_gem_obj->buffer;
+ struct drm_gem_object *obj = &exynos_gem->base;
DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count);
@@ -146,28 +159,21 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
* once dmabuf's refcount becomes 0.
*/
if (obj->import_attach)
- goto out;
-
- exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
-
-out:
- exynos_drm_fini_buf(obj->dev, buf);
- exynos_gem_obj->buffer = NULL;
-
- drm_gem_free_mmap_offset(obj);
+ drm_prime_gem_destroy(obj, exynos_gem->sgt);
+ else
+ exynos_drm_free_buf(exynos_gem);
/* release file pointer to gem object. */
drm_gem_object_release(obj);
- kfree(exynos_gem_obj);
- exynos_gem_obj = NULL;
+ kfree(exynos_gem);
}
unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
@@ -176,103 +182,97 @@ unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
return 0;
}
- exynos_gem_obj = to_exynos_gem_obj(obj);
+ exynos_gem = to_exynos_gem(obj);
drm_gem_object_unreference_unlocked(obj);
- return exynos_gem_obj->buffer->size;
+ return exynos_gem->size;
}
-
-struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
- unsigned long size)
+static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev,
+ unsigned long size)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
struct drm_gem_object *obj;
int ret;
- exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
- if (!exynos_gem_obj)
- return NULL;
+ exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL);
+ if (!exynos_gem)
+ return ERR_PTR(-ENOMEM);
- exynos_gem_obj->size = size;
- obj = &exynos_gem_obj->base;
+ exynos_gem->size = size;
+ obj = &exynos_gem->base;
ret = drm_gem_object_init(dev, obj, size);
if (ret < 0) {
DRM_ERROR("failed to initialize gem object\n");
- kfree(exynos_gem_obj);
- return NULL;
+ kfree(exynos_gem);
+ return ERR_PTR(ret);
+ }
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret < 0) {
+ drm_gem_object_release(obj);
+ kfree(exynos_gem);
+ return ERR_PTR(ret);
}
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
- return exynos_gem_obj;
+ return exynos_gem;
}
-struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned int flags,
- unsigned long size)
+struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev,
+ unsigned int flags,
+ unsigned long size)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct exynos_drm_gem_buf *buf;
+ struct exynos_drm_gem *exynos_gem;
int ret;
+ if (flags & ~(EXYNOS_BO_MASK)) {
+ DRM_ERROR("invalid flags.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
if (!size) {
DRM_ERROR("invalid size.\n");
return ERR_PTR(-EINVAL);
}
- size = roundup_gem_size(size, flags);
-
- ret = check_gem_flags(flags);
- if (ret)
- return ERR_PTR(ret);
-
- buf = exynos_drm_init_buf(dev, size);
- if (!buf)
- return ERR_PTR(-ENOMEM);
-
- exynos_gem_obj = exynos_drm_gem_init(dev, size);
- if (!exynos_gem_obj) {
- ret = -ENOMEM;
- goto err_fini_buf;
- }
+ size = roundup(size, PAGE_SIZE);
- exynos_gem_obj->buffer = buf;
+ exynos_gem = exynos_drm_gem_init(dev, size);
+ if (IS_ERR(exynos_gem))
+ return exynos_gem;
/* set memory type and cache attribute from user side. */
- exynos_gem_obj->flags = flags;
-
- ret = exynos_drm_alloc_buf(dev, buf, flags);
- if (ret < 0)
- goto err_gem_fini;
+ exynos_gem->flags = flags;
- return exynos_gem_obj;
+ ret = exynos_drm_alloc_buf(exynos_gem);
+ if (ret < 0) {
+ drm_gem_object_release(&exynos_gem->base);
+ kfree(exynos_gem);
+ return ERR_PTR(ret);
+ }
-err_gem_fini:
- drm_gem_object_release(&exynos_gem_obj->base);
- kfree(exynos_gem_obj);
-err_fini_buf:
- exynos_drm_fini_buf(dev, buf);
- return ERR_PTR(ret);
+ return exynos_gem;
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_create *args = data;
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
int ret;
- exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
- if (IS_ERR(exynos_gem_obj))
- return PTR_ERR(exynos_gem_obj);
+ exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size);
+ if (IS_ERR(exynos_gem))
+ return PTR_ERR(exynos_gem);
- ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
- &args->handle);
+ ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv,
+ &args->handle);
if (ret) {
- exynos_drm_gem_destroy(exynos_gem_obj);
+ exynos_drm_gem_destroy(exynos_gem);
return ret;
}
@@ -283,7 +283,7 @@ dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle);
@@ -292,9 +292,9 @@ dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
return ERR_PTR(-EINVAL);
}
- exynos_gem_obj = to_exynos_gem_obj(obj);
+ exynos_gem = to_exynos_gem(obj);
- return &exynos_gem_obj->buffer->dma_addr;
+ return &exynos_gem->dma_addr;
}
void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
@@ -318,11 +318,10 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
drm_gem_object_unreference_unlocked(obj);
}
-int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
+static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem,
struct vm_area_struct *vma)
{
- struct drm_device *drm_dev = exynos_gem_obj->base.dev;
- struct exynos_drm_gem_buf *buffer;
+ struct drm_device *drm_dev = exynos_gem->base.dev;
unsigned long vm_size;
int ret;
@@ -331,19 +330,13 @@ int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
vm_size = vma->vm_end - vma->vm_start;
- /*
- * a buffer contains information to physically continuous memory
- * allocated by user request or at framebuffer creation.
- */
- buffer = exynos_gem_obj->buffer;
-
/* check if user-requested size is valid. */
- if (vm_size > buffer->size)
+ if (vm_size > exynos_gem->size)
return -EINVAL;
- ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
- buffer->dma_addr, buffer->size,
- &buffer->dma_attrs);
+ ret = dma_mmap_attrs(drm_dev->dev, vma, exynos_gem->pages,
+ exynos_gem->dma_addr, exynos_gem->size,
+ &exynos_gem->dma_attrs);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
@@ -354,7 +347,8 @@ int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
-{ struct exynos_drm_gem_obj *exynos_gem_obj;
+{
+ struct exynos_drm_gem *exynos_gem;
struct drm_exynos_gem_info *args = data;
struct drm_gem_object *obj;
@@ -367,10 +361,10 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- exynos_gem_obj = to_exynos_gem_obj(obj);
+ exynos_gem = to_exynos_gem(obj);
- args->flags = exynos_gem_obj->flags;
- args->size = exynos_gem_obj->size;
+ args->flags = exynos_gem->flags;
+ args->size = exynos_gem->size;
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
@@ -378,103 +372,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
return 0;
}
-struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
-{
- struct vm_area_struct *vma_copy;
-
- vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
- if (!vma_copy)
- return NULL;
-
- if (vma->vm_ops && vma->vm_ops->open)
- vma->vm_ops->open(vma);
-
- if (vma->vm_file)
- get_file(vma->vm_file);
-
- memcpy(vma_copy, vma, sizeof(*vma));
-
- vma_copy->vm_mm = NULL;
- vma_copy->vm_next = NULL;
- vma_copy->vm_prev = NULL;
-
- return vma_copy;
-}
-
-void exynos_gem_put_vma(struct vm_area_struct *vma)
-{
- if (!vma)
- return;
-
- if (vma->vm_ops && vma->vm_ops->close)
- vma->vm_ops->close(vma);
-
- if (vma->vm_file)
- fput(vma->vm_file);
-
- kfree(vma);
-}
-
-int exynos_gem_get_pages_from_userptr(unsigned long start,
- unsigned int npages,
- struct page **pages,
- struct vm_area_struct *vma)
-{
- int get_npages;
-
- /* the memory region mmaped with VM_PFNMAP. */
- if (vma_is_io(vma)) {
- unsigned int i;
-
- for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
- unsigned long pfn;
- int ret = follow_pfn(vma, start, &pfn);
- if (ret)
- return ret;
-
- pages[i] = pfn_to_page(pfn);
- }
-
- if (i != npages) {
- DRM_ERROR("failed to get user_pages.\n");
- return -EINVAL;
- }
-
- return 0;
- }
-
- get_npages = get_user_pages(current, current->mm, start,
- npages, 1, 1, pages, NULL);
- get_npages = max(get_npages, 0);
- if (get_npages != npages) {
- DRM_ERROR("failed to get user_pages.\n");
- while (get_npages)
- put_page(pages[--get_npages]);
- return -EFAULT;
- }
-
- return 0;
-}
-
-void exynos_gem_put_pages_to_userptr(struct page **pages,
- unsigned int npages,
- struct vm_area_struct *vma)
-{
- if (!vma_is_io(vma)) {
- unsigned int i;
-
- for (i = 0; i < npages; i++) {
- set_page_dirty_lock(pages[i]);
-
- /*
- * undo the reference we took when populating
- * the table.
- */
- put_page(pages[i]);
- }
- }
-}
-
int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir)
@@ -503,23 +400,15 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
void exynos_drm_gem_free_object(struct drm_gem_object *obj)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct exynos_drm_gem_buf *buf;
-
- exynos_gem_obj = to_exynos_gem_obj(obj);
- buf = exynos_gem_obj->buffer;
-
- if (obj->import_attach)
- drm_prime_gem_destroy(obj, buf->sgt);
-
- exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
+ exynos_drm_gem_destroy(to_exynos_gem(obj));
}
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
+ unsigned int flags;
int ret;
/*
@@ -531,25 +420,21 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
- if (is_drm_iommu_supported(dev)) {
- exynos_gem_obj = exynos_drm_gem_create(dev,
- EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
- args->size);
- } else {
- exynos_gem_obj = exynos_drm_gem_create(dev,
- EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
- args->size);
- }
+ if (is_drm_iommu_supported(dev))
+ flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC;
+ else
+ flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC;
- if (IS_ERR(exynos_gem_obj)) {
+ exynos_gem = exynos_drm_gem_create(dev, flags, args->size);
+ if (IS_ERR(exynos_gem)) {
dev_warn(dev->dev, "FB allocation failed.\n");
- return PTR_ERR(exynos_gem_obj);
+ return PTR_ERR(exynos_gem);
}
- ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
- &args->handle);
+ ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv,
+ &args->handle);
if (ret) {
- exynos_drm_gem_destroy(exynos_gem_obj);
+ exynos_drm_gem_destroy(exynos_gem);
return ret;
}
@@ -578,14 +463,9 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
goto unlock;
}
- ret = drm_gem_create_mmap_offset(obj);
- if (ret)
- goto out;
-
*offset = drm_vma_node_offset_addr(&obj->vma_node);
DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
-out:
drm_gem_object_unreference(obj);
unlock:
mutex_unlock(&dev->struct_mutex);
@@ -595,29 +475,39 @@ unlock:
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *obj = vma->vm_private_data;
- struct drm_device *dev = obj->dev;
- unsigned long f_vaddr;
+ struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj);
+ unsigned long pfn;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
- f_vaddr = (unsigned long)vmf->virtual_address;
-
- mutex_lock(&dev->struct_mutex);
- ret = exynos_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
- if (ret < 0)
- DRM_ERROR("failed to map a buffer with user.\n");
+ if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) {
+ DRM_ERROR("invalid page offset\n");
+ ret = -EINVAL;
+ goto out;
+ }
- mutex_unlock(&dev->struct_mutex);
+ pfn = page_to_pfn(exynos_gem->pages[page_offset]);
+ ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
- return convert_to_vm_err_msg(ret);
+out:
+ switch (ret) {
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
}
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem *exynos_gem;
struct drm_gem_object *obj;
int ret;
@@ -629,15 +519,21 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
}
obj = vma->vm_private_data;
- exynos_gem_obj = to_exynos_gem_obj(obj);
+ exynos_gem = to_exynos_gem(obj);
- ret = check_gem_flags(exynos_gem_obj->flags);
- if (ret)
- goto err_close_vm;
+ DRM_DEBUG_KMS("flags = 0x%x\n", exynos_gem->flags);
- update_vm_cache_attr(exynos_gem_obj, vma);
+ /* non-cachable as default. */
+ if (exynos_gem->flags & EXYNOS_BO_CACHABLE)
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ else if (exynos_gem->flags & EXYNOS_BO_WC)
+ vma->vm_page_prot =
+ pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+ else
+ vma->vm_page_prot =
+ pgprot_noncached(vm_get_page_prot(vma->vm_flags));
- ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);
+ ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma);
if (ret)
goto err_close_vm;
@@ -645,7 +541,81 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
err_close_vm:
drm_gem_vm_close(vma);
- drm_gem_free_mmap_offset(obj);
return ret;
}
+
+/* low-level interface prime helpers */
+struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj);
+ int npages;
+
+ npages = exynos_gem->size >> PAGE_SHIFT;
+
+ return drm_prime_pages_to_sg(exynos_gem->pages, npages);
+}
+
+struct drm_gem_object *
+exynos_drm_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt)
+{
+ struct exynos_drm_gem *exynos_gem;
+ int npages;
+ int ret;
+
+ exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size);
+ if (IS_ERR(exynos_gem)) {
+ ret = PTR_ERR(exynos_gem);
+ return ERR_PTR(ret);
+ }
+
+ exynos_gem->dma_addr = sg_dma_address(sgt->sgl);
+
+ npages = exynos_gem->size >> PAGE_SHIFT;
+ exynos_gem->pages = drm_malloc_ab(npages, sizeof(struct page *));
+ if (!exynos_gem->pages) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem->pages, NULL,
+ npages);
+ if (ret < 0)
+ goto err_free_large;
+
+ exynos_gem->sgt = sgt;
+
+ if (sgt->nents == 1) {
+ /* always physically continuous memory if sgt->nents is 1. */
+ exynos_gem->flags |= EXYNOS_BO_CONTIG;
+ } else {
+ /*
+ * this case could be CONTIG or NONCONTIG type but for now
+ * sets NONCONTIG.
+ * TODO. we have to find a way that exporter can notify
+ * the type of its own buffer to importer.
+ */
+ exynos_gem->flags |= EXYNOS_BO_NONCONTIG;
+ }
+
+ return &exynos_gem->base;
+
+err_free_large:
+ drm_free_large(exynos_gem->pages);
+err:
+ drm_gem_object_release(&exynos_gem->base);
+ kfree(exynos_gem);
+ return ERR_PTR(ret);
+}
+
+void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ return NULL;
+}
+
+void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ /* Nothing to do */
+}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 308173cb4..37ab8b282 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -14,41 +14,11 @@
#include <drm/drm_gem.h>
-#define to_exynos_gem_obj(x) container_of(x,\
- struct exynos_drm_gem_obj, base)
+#define to_exynos_gem(x) container_of(x, struct exynos_drm_gem, base)
#define IS_NONCONTIG_BUFFER(f) (f & EXYNOS_BO_NONCONTIG)
/*
- * exynos drm gem buffer structure.
- *
- * @cookie: cookie returned by dma_alloc_attrs
- * @kvaddr: kernel virtual address to allocated memory region.
- * *userptr: user space address.
- * @dma_addr: bus address(accessed by dma) to allocated memory region.
- * - this address could be physical address without IOMMU and
- * device address with IOMMU.
- * @write: whether pages will be written to by the caller.
- * @pages: Array of backing pages.
- * @sgt: sg table to transfer page data.
- * @size: size of allocated memory region.
- * @pfnmap: indicate whether memory region from userptr is mmaped with
- * VM_PFNMAP or not.
- */
-struct exynos_drm_gem_buf {
- void *cookie;
- void __iomem *kvaddr;
- unsigned long userptr;
- dma_addr_t dma_addr;
- struct dma_attrs dma_attrs;
- unsigned int write;
- struct page **pages;
- struct sg_table *sgt;
- unsigned long size;
- bool pfnmap;
-};
-
-/*
* exynos drm buffer structure.
*
* @base: a gem object.
@@ -59,35 +29,41 @@ struct exynos_drm_gem_buf {
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
* @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.
+ * @cookie: cookie returned by dma_alloc_attrs
+ * @kvaddr: kernel virtual address to allocated memory region.
+ * @dma_addr: bus address(accessed by dma) to allocated memory region.
+ * - this address could be physical address without IOMMU and
+ * device address with IOMMU.
+ * @pages: Array of backing pages.
+ * @sgt: Imported sg_table.
*
* P.S. this object would be transferred to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
*/
-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;
+struct exynos_drm_gem {
+ struct drm_gem_object base;
+ unsigned int flags;
+ unsigned long size;
+ void *cookie;
+ void __iomem *kvaddr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+ struct page **pages;
+ struct sg_table *sgt;
};
struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
/* destroy a buffer with gem object */
-void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
-
-/* create a private gem object and initialize it. */
-struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
- unsigned long size);
+void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem);
/* create a new buffer with gem object */
-struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned int flags,
- unsigned long size);
+struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev,
+ unsigned int flags,
+ unsigned long size);
/*
* request gem object creation and buffer allocation as the size
@@ -129,7 +105,7 @@ unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
struct drm_file *file_priv);
/* free gem object. */
-void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj);
+void exynos_drm_gem_free_object(struct drm_gem_object *obj);
/* create memory region for drm framebuffer. */
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
@@ -179,4 +155,13 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
enum dma_data_direction dir);
+/* low-level interface prime helpers */
+struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+exynos_drm_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
+void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj);
+void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+
#endif
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index 8040ed2a8..11b87d2a7 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -543,7 +543,7 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt)
GSC_IN_YUV420_2P);
break;
default:
- dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt);
+ dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
return -EINVAL;
}
@@ -582,19 +582,26 @@ static int gsc_src_set_transf(struct device *dev,
break;
case EXYNOS_DRM_DEGREE_180:
cfg |= GSC_IN_ROT_180;
+ if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+ cfg &= ~GSC_IN_ROT_XFLIP;
+ if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+ cfg &= ~GSC_IN_ROT_YFLIP;
break;
case EXYNOS_DRM_DEGREE_270:
cfg |= GSC_IN_ROT_270;
+ if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+ cfg &= ~GSC_IN_ROT_XFLIP;
+ if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+ cfg &= ~GSC_IN_ROT_YFLIP;
break;
default:
- dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree);
+ dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
return -EINVAL;
}
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;
@@ -714,7 +721,7 @@ static int gsc_src_set_addr(struct device *dev,
property->prop_id, buf_id, buf_type);
if (buf_id > GSC_MAX_SRC) {
- dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id);
+ dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id);
return -EINVAL;
}
@@ -807,7 +814,7 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt)
GSC_OUT_YUV420_2P);
break;
default:
- dev_err(ippdrv->dev, "inavlid target yuv order 0x%x.\n", fmt);
+ dev_err(ippdrv->dev, "invalid target yuv order 0x%x.\n", fmt);
return -EINVAL;
}
@@ -846,19 +853,26 @@ static int gsc_dst_set_transf(struct device *dev,
break;
case EXYNOS_DRM_DEGREE_180:
cfg |= GSC_IN_ROT_180;
+ if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+ cfg &= ~GSC_IN_ROT_XFLIP;
+ if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+ cfg &= ~GSC_IN_ROT_YFLIP;
break;
case EXYNOS_DRM_DEGREE_270:
cfg |= GSC_IN_ROT_270;
+ if (flip & EXYNOS_DRM_FLIP_VERTICAL)
+ cfg &= ~GSC_IN_ROT_XFLIP;
+ if (flip & EXYNOS_DRM_FLIP_HORIZONTAL)
+ cfg &= ~GSC_IN_ROT_YFLIP;
break;
default:
- dev_err(ippdrv->dev, "inavlid degree value %d.\n", degree);
+ dev_err(ippdrv->dev, "invalid degree value %d.\n", degree);
return -EINVAL;
}
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;
@@ -1162,7 +1176,7 @@ static int gsc_dst_set_addr(struct device *dev,
property->prop_id, buf_id, buf_type);
if (buf_id > GSC_MAX_DST) {
- dev_info(ippdrv->dev, "inavlid buf_id %d.\n", buf_id);
+ dev_info(ippdrv->dev, "invalid buf_id %d.\n", buf_id);
return -EINVAL;
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.c
index b32b291f8..d73b9ad35 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.c
@@ -87,10 +87,8 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *dev = drm_dev->dev;
int ret;
- if (!dev->archdata.mapping) {
- DRM_ERROR("iommu_mapping is null.\n");
- return -EFAULT;
- }
+ if (!dev->archdata.mapping)
+ return 0;
subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev,
sizeof(*subdrv_dev->dma_parms),
@@ -100,6 +98,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 +115,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;
}
@@ -138,6 +139,5 @@ void drm_iommu_detach_device(struct drm_device *drm_dev,
if (!mapping || !mapping->domain)
return;
- iommu_detach_device(mapping->domain, subdrv_dev);
- drm_release_iommu_mapping(drm_dev);
+ arm_iommu_detach_device(subdrv_dev);
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index 35d25889b..dc1b5441f 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -29,13 +29,9 @@ void drm_iommu_detach_device(struct drm_device *dev_dev,
static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
{
-#ifdef CONFIG_ARM_DMA_USE_IOMMU
struct device *dev = drm_dev->dev;
return dev->archdata.mapping ? true : false;
-#else
- return false;
-#endif
}
#else
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index b7f1cbc46..67d24236e 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/kernel/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;
}
@@ -1625,12 +1622,10 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
INIT_LIST_HEAD(&ippdrv->cmd_list);
mutex_init(&ippdrv->cmd_lock);
- if (is_drm_iommu_supported(drm_dev)) {
- ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
- if (ret) {
- DRM_ERROR("failed to activate iommu\n");
- goto err;
- }
+ ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
+ if (ret) {
+ DRM_ERROR("failed to activate iommu\n");
+ goto err;
}
}
@@ -1640,8 +1635,7 @@ err:
/* get ipp driver entry */
list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,
drv_list) {
- if (is_drm_iommu_supported(drm_dev))
- drm_iommu_detach_device(drm_dev, ippdrv->dev);
+ drm_iommu_detach_device(drm_dev, ippdrv->dev);
ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
ippdrv->prop_list.ipp_id);
@@ -1657,8 +1651,7 @@ static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
/* get ipp driver entry */
list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) {
- if (is_drm_iommu_supported(drm_dev))
- drm_iommu_detach_device(drm_dev, ippdrv->dev);
+ drm_iommu_detach_device(drm_dev, ippdrv->dev);
ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
ippdrv->prop_list.ipp_id);
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_mic.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_mic.c
new file mode 100644
index 000000000..8994eab56
--- /dev/null
+++ b/kernel/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/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.c
index b1180fbe7..179311760 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -13,18 +13,13 @@
#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"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
-static const uint32_t formats[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_NV12,
-};
-
/*
* This function is to get X or Y size shown via screen. This needs length and
* start position of CRTC.
@@ -61,42 +56,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)
@@ -117,75 +91,100 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
/* set drm framebuffer data. */
exynos_plane->src_x = src_x;
exynos_plane->src_y = src_y;
- exynos_plane->src_width = (actual_w * exynos_plane->h_ratio) >> 16;
- exynos_plane->src_height = (actual_h * exynos_plane->v_ratio) >> 16;
- exynos_plane->fb_width = fb->width;
- exynos_plane->fb_height = fb->height;
- exynos_plane->bpp = fb->bits_per_pixel;
- exynos_plane->pitch = fb->pitches[0];
- exynos_plane->pixel_format = fb->pixel_format;
+ exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16;
+ exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16;
/* set plane range to be displayed. */
exynos_plane->crtc_x = crtc_x;
exynos_plane->crtc_y = crtc_y;
- exynos_plane->crtc_width = actual_w;
- 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->crtc_w = actual_w;
+ exynos_plane->crtc_h = actual_h;
DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
exynos_plane->crtc_x, exynos_plane->crtc_y,
- exynos_plane->crtc_width, exynos_plane->crtc_height);
+ exynos_plane->crtc_w, exynos_plane->crtc_h);
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,
+};
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+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 ret;
+ int nr;
+ int i;
- ret = exynos_check_plane(plane, fb);
- if (ret < 0)
- return ret;
+ if (!state->fb)
+ return 0;
- 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);
+ nr = drm_format_num_planes(state->fb->pixel_format);
+ for (i = 0; i < nr; i++) {
+ struct exynos_drm_gem *exynos_gem =
+ exynos_drm_fb_gem(state->fb, i);
+ if (!exynos_gem) {
+ DRM_DEBUG_KMS("gem object is null\n");
+ return -EFAULT;
+ }
- if (exynos_crtc->ops->win_commit)
- exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
+ exynos_plane->dma_addr[i] = exynos_gem->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;
}
-static int exynos_disable_plane(struct drm_plane *plane)
+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);
- struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc);
- if (exynos_crtc && exynos_crtc->ops->win_disable)
- exynos_crtc->ops->win_disable(exynos_crtc,
- exynos_plane->zpos);
+ if (!state->crtc)
+ return;
- return 0;
+ 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);
+
+ exynos_plane->pending_fb = state->fb;
+
+ if (exynos_crtc->ops->update_plane)
+ exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
}
-static struct drm_plane_funcs exynos_plane_funcs = {
- .update_plane = exynos_update_plane,
- .disable_plane = exynos_disable_plane,
- .destroy = drm_plane_cleanup,
+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(old_state->crtc);
+
+ if (!old_state->crtc)
+ return;
+
+ if (exynos_crtc->ops->disable_plane)
+ exynos_crtc->ops->disable_plane(exynos_crtc,
+ exynos_plane);
+}
+
+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,
@@ -208,21 +207,35 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
drm_object_attach_property(&plane->base, prop, zpos);
}
+enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
+ unsigned int cursor_win)
+{
+ if (zpos == DEFAULT_WIN)
+ return DRM_PLANE_TYPE_PRIMARY;
+ else if (zpos == cursor_win)
+ return DRM_PLANE_TYPE_CURSOR;
+ else
+ return DRM_PLANE_TYPE_OVERLAY;
+}
+
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
+ const uint32_t *formats, unsigned int fcount,
unsigned int zpos)
{
int err;
err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
- &exynos_plane_funcs, formats,
- ARRAY_SIZE(formats), type);
+ &exynos_plane_funcs, formats, fcount,
+ type);
if (err) {
DRM_ERROR("failed to initialize plane\n");
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/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.h b/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.h
index f360590d1..abb641e64 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.h
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_plane.h
@@ -9,18 +9,10 @@
*
*/
-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);
+enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
+ unsigned int cursor_win);
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
+ const uint32_t *formats, unsigned int fcount,
unsigned int zpos);
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_rotator.c
index 425e70625..2f5c118f4 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_rotator.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -786,6 +786,7 @@ static int rotator_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
static int rotator_clk_crtl(struct rot_context *rot, bool enable)
{
if (enable) {
@@ -822,7 +823,6 @@ static int rotator_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM
static int rotator_runtime_suspend(struct device *dev)
{
struct rot_context *rot = dev_get_drvdata(dev);
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/kernel/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 1b3479a8d..669362c53 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -20,30 +20,29 @@
#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"
#include "exynos_drm_plane.h"
-#include "exynos_drm_encoder.h"
#include "exynos_drm_vidi.h"
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
+#define CURSOR_WIN 2
#define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector)
struct vidi_context {
- struct exynos_drm_display display;
+ struct drm_encoder encoder;
struct platform_device *pdev;
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
- struct drm_encoder *encoder;
struct drm_connector connector;
struct exynos_drm_plane planes[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
- unsigned int default_win;
unsigned long irq_flags;
unsigned int connected;
bool vblank_on;
@@ -54,9 +53,9 @@ struct vidi_context {
int pipe;
};
-static inline struct vidi_context *display_to_vidi(struct exynos_drm_display *d)
+static inline struct vidi_context *encoder_to_vidi(struct drm_encoder *e)
{
- return container_of(d, struct vidi_context, display);
+ return container_of(e, struct vidi_context, encoder);
}
static const char fake_edid_info[] = {
@@ -84,6 +83,12 @@ static const char fake_edid_info[] = {
0x00, 0x00, 0x00, 0x06
};
+static const uint32_t formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_NV12,
+};
+
static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
{
struct vidi_context *ctx = crtc->ctx;
@@ -99,7 +104,7 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
/*
* in case of page flip request, vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
- * that function will be called by crtc_ops->win_commit callback
+ * that function will be called by crtc_ops->update_plane callback
*/
schedule_work(&ctx->work);
@@ -117,91 +122,42 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
ctx->vblank_on = false;
}
-static void vidi_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+static void vidi_update_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct vidi_context *ctx = crtc->ctx;
- struct exynos_drm_plane *plane;
if (ctx->suspended)
return;
- if (win < 0 || win >= WINDOWS_NR)
- return;
-
- 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,17 +174,18 @@ 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,
+ .update_plane = vidi_update_plane,
};
static void vidi_fake_vblank_handler(struct work_struct *work)
{
struct vidi_context *ctx = container_of(work, struct vidi_context,
work);
+ int win;
if (ctx->pipe < 0)
return;
@@ -239,7 +196,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_lock(&ctx->lock);
if (ctx->direct_vblank) {
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ drm_crtc_handle_vblank(&ctx->crtc->base);
ctx->direct_vblank = false;
mutex_unlock(&ctx->lock);
return;
@@ -247,7 +204,14 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
mutex_unlock(&ctx->lock);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ for (win = 0 ; win < WINDOWS_NR ; win++) {
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+
+ if (!plane->pending_fb)
+ continue;
+
+ exynos_drm_crtc_finish_update(ctx->crtc, plane);
+ }
}
static int vidi_show_connection(struct device *dev,
@@ -302,9 +266,7 @@ static DEVICE_ATTR(connection, 0644, vidi_show_connection,
int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file_priv)
{
- struct vidi_context *ctx = NULL;
- struct drm_encoder *encoder;
- struct exynos_drm_display *display;
+ struct vidi_context *ctx = dev_get_drvdata(drm_dev->dev);
struct drm_exynos_vidi_connection *vidi = data;
if (!vidi) {
@@ -317,21 +279,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
return -EINVAL;
}
- list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
- head) {
- display = exynos_drm_get_display(encoder);
-
- if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
- ctx = display_to_vidi(display);
- break;
- }
- }
-
- if (!ctx) {
- DRM_DEBUG_KMS("not found virtual device type encoder.\n");
- return -EINVAL;
- }
-
if (ctx->connected == vidi->connection) {
DRM_DEBUG_KMS("same connection request.\n");
return -EINVAL;
@@ -384,10 +331,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)
@@ -421,7 +371,7 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
{
struct vidi_context *ctx = ctx_from_connector(connector);
- return ctx->encoder;
+ return &ctx->encoder;
}
static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
@@ -429,14 +379,12 @@ static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
.best_encoder = vidi_best_encoder,
};
-static int vidi_create_connector(struct exynos_drm_display *display,
- struct drm_encoder *encoder)
+static int vidi_create_connector(struct drm_encoder *encoder)
{
- struct vidi_context *ctx = display_to_vidi(display);
+ struct vidi_context *ctx = encoder_to_vidi(encoder);
struct drm_connector *connector = &ctx->connector;
int ret;
- ctx->encoder = encoder;
connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(ctx->drm_dev, connector,
@@ -453,32 +401,60 @@ static int vidi_create_connector(struct exynos_drm_display *display,
return 0;
}
+static bool exynos_vidi_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void exynos_vidi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void exynos_vidi_enable(struct drm_encoder *encoder)
+{
+}
+
+static void exynos_vidi_disable(struct drm_encoder *encoder)
+{
+}
+
+static struct drm_encoder_helper_funcs exynos_vidi_encoder_helper_funcs = {
+ .mode_fixup = exynos_vidi_mode_fixup,
+ .mode_set = exynos_vidi_mode_set,
+ .enable = exynos_vidi_enable,
+ .disable = exynos_vidi_disable,
+};
-static struct exynos_drm_display_ops vidi_display_ops = {
- .create_connector = vidi_create_connector,
+static struct drm_encoder_funcs exynos_vidi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static int vidi_bind(struct device *dev, struct device *master, void *data)
{
struct vidi_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
+ struct drm_encoder *encoder = &ctx->encoder;
struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos;
- int ret;
+ int pipe, ret;
vidi_ctx_initialize(ctx, drm_dev);
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
- type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
- DRM_PLANE_TYPE_OVERLAY;
+ type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
- 1 << ctx->pipe, type, zpos);
+ 1 << ctx->pipe, type, formats,
+ ARRAY_SIZE(formats), zpos);
if (ret)
return ret;
}
- exynos_plane = &ctx->planes[ctx->default_win];
+ exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_VIDI,
&vidi_crtc_ops, ctx);
@@ -487,9 +463,24 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(ctx->crtc);
}
- ret = exynos_drm_create_enc_conn(drm_dev, &ctx->display);
+ pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+ EXYNOS_DISPLAY_TYPE_VIDI);
+ if (pipe < 0)
+ return pipe;
+
+ encoder->possible_crtcs = 1 << pipe;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs);
+
+ ret = vidi_create_connector(encoder);
if (ret) {
- ctx->crtc->base.funcs->destroy(&ctx->crtc->base);
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
+ drm_encoder_cleanup(encoder);
return ret;
}
@@ -515,21 +506,8 @@ static int vidi_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
- ctx->display.type = EXYNOS_DISPLAY_TYPE_VIDI;
- ctx->display.ops = &vidi_display_ops;
- 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 +517,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 +528,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 +544,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 +556,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/kernel/drivers/gpu/drm/exynos/exynos_hdmi.c b/kernel/drivers/gpu/drm/exynos/exynos_hdmi.c
index 5eba971f3..57b675563 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -17,11 +17,11 @@
#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"
#include <linux/kernel.h>
-#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
@@ -30,11 +30,11 @@
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
-#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_gpio.h>
+#include <linux/of_device.h>
#include <linux/hdmi.h>
#include <linux/component.h>
#include <linux/mfd/syscon.h>
@@ -44,12 +44,6 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
-#include "exynos_mixer.h"
-
-#include <linux/gpio.h>
-#include <media/s5p_hdmi.h>
-
-#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
#define HOTPLUG_DEBOUNCE_MS 1100
@@ -67,155 +61,78 @@
enum hdmi_type {
HDMI_TYPE13,
HDMI_TYPE14,
+ HDMI_TYPE_COUNT
};
-struct hdmi_driver_data {
- unsigned int type;
- const struct hdmiphy_config *phy_confs;
- unsigned int phy_conf_count;
- unsigned int is_apb_phy:1;
-};
+#define HDMI_MAPPED_BASE 0xffff0000
-struct hdmi_resources {
- struct clk *hdmi;
- struct clk *sclk_hdmi;
- struct clk *sclk_pixel;
- struct clk *sclk_hdmiphy;
- struct clk *mout_hdmi;
- struct regulator_bulk_data *regul_bulk;
- struct regulator *reg_hdmi_en;
- int regul_count;
-};
-
-struct hdmi_tg_regs {
- u8 cmd[1];
- u8 h_fsz[2];
- u8 hact_st[2];
- u8 hact_sz[2];
- u8 v_fsz[2];
- u8 vsync[2];
- u8 vsync2[2];
- u8 vact_st[2];
- u8 vact_sz[2];
- u8 field_chg[2];
- u8 vact_st2[2];
- u8 vact_st3[2];
- u8 vact_st4[2];
- u8 vsync_top_hdmi[2];
- u8 vsync_bot_hdmi[2];
- u8 field_top_hdmi[2];
- u8 field_bot_hdmi[2];
- u8 tg_3d[1];
-};
-
-struct hdmi_v13_core_regs {
- u8 h_blank[2];
- u8 v_blank[3];
- u8 h_v_line[3];
- u8 vsync_pol[1];
- u8 int_pro_mode[1];
- u8 v_blank_f[3];
- u8 h_sync_gen[3];
- u8 v_sync_gen1[3];
- u8 v_sync_gen2[3];
- u8 v_sync_gen3[3];
+enum hdmi_mapped_regs {
+ HDMI_PHY_STATUS = HDMI_MAPPED_BASE,
+ HDMI_PHY_RSTOUT,
+ HDMI_ACR_CON,
+ HDMI_ACR_MCTS0,
+ HDMI_ACR_CTS0,
+ HDMI_ACR_N0
};
-struct hdmi_v14_core_regs {
- u8 h_blank[2];
- u8 v2_blank[2];
- u8 v1_blank[2];
- u8 v_line[2];
- u8 h_line[2];
- u8 hsync_pol[1];
- u8 vsync_pol[1];
- u8 int_pro_mode[1];
- u8 v_blank_f0[2];
- u8 v_blank_f1[2];
- u8 h_sync_start[2];
- u8 h_sync_end[2];
- u8 v_sync_line_bef_2[2];
- u8 v_sync_line_bef_1[2];
- u8 v_sync_line_aft_2[2];
- u8 v_sync_line_aft_1[2];
- u8 v_sync_line_aft_pxl_2[2];
- u8 v_sync_line_aft_pxl_1[2];
- u8 v_blank_f2[2]; /* for 3D mode */
- u8 v_blank_f3[2]; /* for 3D mode */
- u8 v_blank_f4[2]; /* for 3D mode */
- u8 v_blank_f5[2]; /* for 3D mode */
- u8 v_sync_line_aft_3[2];
- u8 v_sync_line_aft_4[2];
- u8 v_sync_line_aft_5[2];
- u8 v_sync_line_aft_6[2];
- u8 v_sync_line_aft_pxl_3[2];
- u8 v_sync_line_aft_pxl_4[2];
- u8 v_sync_line_aft_pxl_5[2];
- u8 v_sync_line_aft_pxl_6[2];
- u8 vact_space_1[2];
- u8 vact_space_2[2];
- u8 vact_space_3[2];
- u8 vact_space_4[2];
- u8 vact_space_5[2];
- u8 vact_space_6[2];
+static const u32 hdmi_reg_map[][HDMI_TYPE_COUNT] = {
+ { HDMI_V13_PHY_STATUS, HDMI_PHY_STATUS_0 },
+ { HDMI_V13_PHY_RSTOUT, HDMI_V14_PHY_RSTOUT },
+ { HDMI_V13_ACR_CON, HDMI_V14_ACR_CON },
+ { HDMI_V13_ACR_MCTS0, HDMI_V14_ACR_MCTS0 },
+ { HDMI_V13_ACR_CTS0, HDMI_V14_ACR_CTS0 },
+ { HDMI_V13_ACR_N0, HDMI_V14_ACR_N0 },
};
-struct hdmi_v13_conf {
- struct hdmi_v13_core_regs core;
- struct hdmi_tg_regs tg;
+static const char * const supply[] = {
+ "vdd",
+ "vdd_osc",
+ "vdd_pll",
};
-struct hdmi_v14_conf {
- struct hdmi_v14_core_regs core;
- struct hdmi_tg_regs tg;
-};
-
-struct hdmi_conf_regs {
- int pixel_clock;
- int cea_video_id;
- enum hdmi_picture_aspect aspect_ratio;
- union {
- struct hdmi_v13_conf v13_conf;
- struct hdmi_v14_conf v14_conf;
- } conf;
+struct hdmi_driver_data {
+ unsigned int type;
+ const struct hdmiphy_config *phy_confs;
+ unsigned int phy_conf_count;
+ unsigned int is_apb_phy:1;
};
struct hdmi_context {
- struct exynos_drm_display display;
+ struct drm_encoder encoder;
struct device *dev;
struct drm_device *drm_dev;
struct drm_connector connector;
- struct drm_encoder *encoder;
- bool hpd;
bool powered;
bool dvi_mode;
- struct mutex hdmi_mutex;
-
- void __iomem *regs;
- int irq;
struct delayed_work hotplug_work;
-
- struct i2c_adapter *ddc_adpt;
- struct i2c_client *hdmiphy_port;
-
- /* current hdmiphy conf regs */
struct drm_display_mode current_mode;
- struct hdmi_conf_regs mode_conf;
-
- struct hdmi_resources res;
+ u8 cea_video_id;
+ const struct hdmi_driver_data *drv_data;
- int hpd_gpio;
+ void __iomem *regs;
void __iomem *regs_hdmiphy;
- const struct hdmiphy_config *phy_confs;
- unsigned int phy_conf_count;
-
+ struct i2c_client *hdmiphy_port;
+ struct i2c_adapter *ddc_adpt;
+ struct gpio_desc *hpd_gpio;
+ int irq;
struct regmap *pmureg;
- enum hdmi_type type;
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *mout_hdmi;
+ struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)];
+ struct regulator *reg_hdmi_en;
};
-static inline struct hdmi_context *display_to_hdmi(struct exynos_drm_display *d)
+static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e)
+{
+ return container_of(e, struct hdmi_context, encoder);
+}
+
+static inline struct hdmi_context *connector_to_hdmi(struct drm_connector *c)
{
- return container_of(d, struct hdmi_context, display);
+ return container_of(c, struct hdmi_context, connector);
}
struct hdmiphy_config {
@@ -231,7 +148,7 @@ static const struct hdmiphy_config hdmiphy_v13_configs[] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
},
},
{
@@ -240,7 +157,7 @@ static const struct hdmiphy_config hdmiphy_v13_configs[] = {
0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x80,
},
},
{
@@ -249,7 +166,7 @@ static const struct hdmiphy_config hdmiphy_v13_configs[] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+ 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x80,
},
},
{
@@ -258,7 +175,7 @@ static const struct hdmiphy_config hdmiphy_v13_configs[] = {
0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
- 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+ 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x80,
},
},
{
@@ -267,7 +184,7 @@ static const struct hdmiphy_config hdmiphy_v13_configs[] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+ 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x80,
},
},
};
@@ -297,7 +214,7 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
},
},
{
@@ -360,7 +277,7 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+ 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
@@ -423,7 +340,7 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+ 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
};
@@ -605,50 +522,45 @@ static struct hdmi_driver_data exynos4210_hdmi_driver_data = {
.is_apb_phy = 0,
};
-static struct hdmi_driver_data exynos5_hdmi_driver_data = {
- .type = HDMI_TYPE14,
- .phy_confs = hdmiphy_v13_configs,
- .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
- .is_apb_phy = 0,
-};
+static inline u32 hdmi_map_reg(struct hdmi_context *hdata, u32 reg_id)
+{
+ if ((reg_id & 0xffff0000) == HDMI_MAPPED_BASE)
+ return hdmi_reg_map[reg_id & 0xffff][hdata->drv_data->type];
+ return reg_id;
+}
static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
{
- return readl(hdata->regs + reg_id);
+ return readl(hdata->regs + hdmi_map_reg(hdata, reg_id));
}
static inline void hdmi_reg_writeb(struct hdmi_context *hdata,
u32 reg_id, u8 value)
{
- writeb(value, hdata->regs + reg_id);
+ writel(value, hdata->regs + hdmi_map_reg(hdata, reg_id));
}
-static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
- u32 reg_id, u32 value, u32 mask)
+static inline void hdmi_reg_writev(struct hdmi_context *hdata, u32 reg_id,
+ int bytes, u32 val)
{
- u32 old = readl(hdata->regs + reg_id);
- value = (value & mask) | (old & ~mask);
- writel(value, hdata->regs + reg_id);
+ reg_id = hdmi_map_reg(hdata, reg_id);
+
+ while (--bytes >= 0) {
+ writel(val & 0xff, hdata->regs + reg_id);
+ val >>= 8;
+ reg_id += 4;
+ }
}
-static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
- u32 reg_offset, u8 value)
+static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
+ u32 reg_id, u32 value, u32 mask)
{
- if (hdata->hdmiphy_port) {
- u8 buffer[2];
- int ret;
-
- buffer[0] = reg_offset;
- buffer[1] = value;
+ u32 old;
- ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
- if (ret == 2)
- return 0;
- return ret;
- } else {
- writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
- return 0;
- }
+ reg_id = hdmi_map_reg(hdata, reg_id);
+ old = readl(hdata->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, hdata->regs + reg_id);
}
static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
@@ -667,7 +579,7 @@ static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
} else {
int i;
for (i = 0; i < len; i++)
- writeb(buf[i], hdata->regs_hdmiphy +
+ writel(buf[i], hdata->regs_hdmiphy +
((reg_offset + i)<<2));
return 0;
}
@@ -777,7 +689,7 @@ static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_PHY_STATUS_0);
DUMPREG(HDMI_PHY_STATUS_PLL);
DUMPREG(HDMI_PHY_CON_0);
- DUMPREG(HDMI_PHY_RSTOUT);
+ DUMPREG(HDMI_V14_PHY_RSTOUT);
DUMPREG(HDMI_PHY_VPLL);
DUMPREG(HDMI_PHY_CMU);
DUMPREG(HDMI_CORE_RSTOUT);
@@ -929,7 +841,7 @@ static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
{
- if (hdata->type == HDMI_TYPE13)
+ if (hdata->drv_data->type == HDMI_TYPE13)
hdmi_v13_regs_dump(hdata, prefix);
else
hdmi_v14_regs_dump(hdata, prefix);
@@ -956,7 +868,7 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
u32 hdr_sum;
u8 chksum;
u32 mod;
- u32 vic;
+ u8 ar;
mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
if (hdata->dvi_mode) {
@@ -987,27 +899,22 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
* Set the aspect ratio as per the mode, mentioned in
* Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard
*/
- switch (hdata->mode_conf.aspect_ratio) {
+ ar = hdata->current_mode.picture_aspect_ratio;
+ switch (ar) {
case HDMI_PICTURE_ASPECT_4_3:
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
- hdata->mode_conf.aspect_ratio |
- AVI_4_3_CENTER_RATIO);
+ ar |= AVI_4_3_CENTER_RATIO;
break;
case HDMI_PICTURE_ASPECT_16_9:
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
- hdata->mode_conf.aspect_ratio |
- AVI_16_9_CENTER_RATIO);
+ ar |= AVI_16_9_CENTER_RATIO;
break;
case HDMI_PICTURE_ASPECT_NONE:
default:
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2),
- hdata->mode_conf.aspect_ratio |
- AVI_SAME_AS_PIC_ASPECT_RATIO);
+ ar |= AVI_SAME_AS_PIC_ASPECT_RATIO;
break;
}
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), ar);
- vic = hdata->mode_conf.cea_video_id;
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic);
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), hdata->cea_video_id);
chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1),
infoframe->any.length, hdr_sum);
@@ -1035,12 +942,12 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
bool force)
{
- struct hdmi_context *hdata = ctx_from_connector(connector);
+ struct hdmi_context *hdata = connector_to_hdmi(connector);
- hdata->hpd = gpio_get_value(hdata->hpd_gpio);
+ if (gpiod_get_value(hdata->hpd_gpio))
+ return connector_status_connected;
- return hdata->hpd ? connector_status_connected :
- connector_status_disconnected;
+ return connector_status_disconnected;
}
static void hdmi_connector_destroy(struct drm_connector *connector)
@@ -1050,16 +957,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 hdmi_context *hdata = connector_to_hdmi(connector);
struct edid *edid;
+ int ret;
if (!hdata->ddc_adpt)
return -ENODEV;
@@ -1075,15 +986,19 @@ 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)
{
int i;
- for (i = 0; i < hdata->phy_conf_count; i++)
- if (hdata->phy_confs[i].pixel_clock == pixel_clock)
+ for (i = 0; i < hdata->drv_data->phy_conf_count; i++)
+ if (hdata->drv_data->phy_confs[i].pixel_clock == pixel_clock)
return i;
DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
@@ -1093,7 +1008,7 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
static int hdmi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct hdmi_context *hdata = ctx_from_connector(connector);
+ struct hdmi_context *hdata = connector_to_hdmi(connector);
int ret;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
@@ -1101,10 +1016,6 @@ static int hdmi_mode_valid(struct drm_connector *connector,
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
false, mode->clock * 1000);
- ret = mixer_check_mode(mode);
- if (ret)
- return MODE_BAD;
-
ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
if (ret < 0)
return MODE_BAD;
@@ -1114,9 +1025,9 @@ static int hdmi_mode_valid(struct drm_connector *connector,
static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
{
- struct hdmi_context *hdata = ctx_from_connector(connector);
+ struct hdmi_context *hdata = connector_to_hdmi(connector);
- return hdata->encoder;
+ return &hdata->encoder;
}
static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
@@ -1125,14 +1036,12 @@ static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
.best_encoder = hdmi_best_encoder,
};
-static int hdmi_create_connector(struct exynos_drm_display *display,
- struct drm_encoder *encoder)
+static int hdmi_create_connector(struct drm_encoder *encoder)
{
- struct hdmi_context *hdata = display_to_hdmi(display);
+ struct hdmi_context *hdata = encoder_to_hdmi(encoder);
struct drm_connector *connector = &hdata->connector;
int ret;
- hdata->encoder = encoder;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -1150,23 +1059,30 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
return 0;
}
-static void hdmi_mode_fixup(struct exynos_drm_display *display,
- struct drm_connector *connector,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static bool hdmi_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
struct drm_display_mode *m;
int mode_ok;
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
drm_mode_set_crtcinfo(adjusted_mode, 0);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder)
+ break;
+ }
+
+ if (connector->encoder != encoder)
+ return true;
+
mode_ok = hdmi_mode_valid(connector, adjusted_mode);
/* just return if user desired mode exists. */
if (mode_ok == MODE_OK)
- return;
+ return true;
/*
* otherwise, find the most suitable mode among modes and change it
@@ -1186,72 +1102,21 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display,
break;
}
}
+
+ return true;
}
-static void hdmi_set_acr(u32 freq, u8 *acr)
+static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq)
{
u32 n, cts;
- switch (freq) {
- case 32000:
- n = 4096;
- cts = 27000;
- break;
- case 44100:
- n = 6272;
- cts = 30000;
- break;
- case 88200:
- n = 12544;
- cts = 30000;
- break;
- case 176400:
- n = 25088;
- cts = 30000;
- break;
- case 48000:
- n = 6144;
- cts = 27000;
- break;
- case 96000:
- n = 12288;
- cts = 27000;
- break;
- case 192000:
- n = 24576;
- cts = 27000;
- break;
- default:
- n = 0;
- cts = 0;
- break;
- }
-
- acr[1] = cts >> 16;
- acr[2] = cts >> 8 & 0xff;
- acr[3] = cts & 0xff;
+ cts = (freq % 9) ? 27000 : 30000;
+ n = 128 * freq / (27000000 / cts);
- acr[4] = n >> 16;
- acr[5] = n >> 8 & 0xff;
- acr[6] = n & 0xff;
-}
-
-static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
-{
- hdmi_reg_writeb(hdata, HDMI_ACR_N0, acr[6]);
- hdmi_reg_writeb(hdata, HDMI_ACR_N1, acr[5]);
- hdmi_reg_writeb(hdata, HDMI_ACR_N2, acr[4]);
- hdmi_reg_writeb(hdata, HDMI_ACR_MCTS0, acr[3]);
- hdmi_reg_writeb(hdata, HDMI_ACR_MCTS1, acr[2]);
- hdmi_reg_writeb(hdata, HDMI_ACR_MCTS2, acr[1]);
- hdmi_reg_writeb(hdata, HDMI_ACR_CTS0, acr[3]);
- hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]);
- hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]);
-
- if (hdata->type == HDMI_TYPE13)
- hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4);
- else
- hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
+ hdmi_reg_writev(hdata, HDMI_ACR_N0, 3, n);
+ hdmi_reg_writev(hdata, HDMI_ACR_MCTS0, 3, cts);
+ hdmi_reg_writev(hdata, HDMI_ACR_CTS0, 3, cts);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
}
static void hdmi_audio_init(struct hdmi_context *hdata)
@@ -1259,7 +1124,6 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
u32 sample_rate, bits_per_sample;
u32 data_num, bit_ch, sample_frq;
u32 val;
- u8 acr[7];
sample_rate = 44100;
bits_per_sample = 16;
@@ -1279,8 +1143,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata)
break;
}
- hdmi_set_acr(sample_rate, acr);
- hdmi_reg_acr(hdata, acr);
+ hdmi_reg_acr(hdata, sample_rate);
hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
| HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
@@ -1382,7 +1245,7 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
}
- if (hdata->type == HDMI_TYPE13) {
+ if (hdata->drv_data->type == HDMI_TYPE13) {
/* choose bluescreen (fecal) color */
hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
@@ -1413,407 +1276,42 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
}
}
-static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
-{
- const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg;
- const struct hdmi_v13_core_regs *core =
- &hdata->mode_conf.conf.v13_conf.core;
- int tries;
-
- /* setting core registers */
- hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]);
- hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
- hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
- hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
- /* Timing generator registers */
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi[1]);
-
- /* waiting for HDMIPHY's PLL to get to steady state */
- for (tries = 100; tries; --tries) {
- u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS);
- if (val & HDMI_PHY_STATUS_READY)
- break;
- usleep_range(1000, 2000);
- }
- /* steady state not achieved */
- if (tries == 0) {
- DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
- hdmi_regs_dump(hdata, "timing apply");
- }
-
- clk_disable_unprepare(hdata->res.sclk_hdmi);
- clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
- clk_prepare_enable(hdata->res.sclk_hdmi);
-
- /* enable HDMI and timing generator */
- hdmi_start(hdata, true);
-}
-
-static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
+static void hdmiphy_wait_for_pll(struct hdmi_context *hdata)
{
- const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg;
- const struct hdmi_v14_core_regs *core =
- &hdata->mode_conf.conf.v14_conf.core;
int tries;
- /* setting core registers */
- hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]);
- hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]);
- hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]);
- hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]);
- hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]);
- hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
- hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0,
- core->v_sync_line_bef_2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1,
- core->v_sync_line_bef_2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0,
- core->v_sync_line_bef_1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1,
- core->v_sync_line_bef_1[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0,
- core->v_sync_line_aft_2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1,
- core->v_sync_line_aft_2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0,
- core->v_sync_line_aft_1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1,
- core->v_sync_line_aft_1[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0,
- core->v_sync_line_aft_pxl_2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1,
- core->v_sync_line_aft_pxl_2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0,
- core->v_sync_line_aft_pxl_1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1,
- core->v_sync_line_aft_pxl_1[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0,
- core->v_sync_line_aft_3[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1,
- core->v_sync_line_aft_3[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0,
- core->v_sync_line_aft_4[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1,
- core->v_sync_line_aft_4[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0,
- core->v_sync_line_aft_5[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1,
- core->v_sync_line_aft_5[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0,
- core->v_sync_line_aft_6[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1,
- core->v_sync_line_aft_6[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0,
- core->v_sync_line_aft_pxl_3[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1,
- core->v_sync_line_aft_pxl_3[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0,
- core->v_sync_line_aft_pxl_4[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1,
- core->v_sync_line_aft_pxl_4[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0,
- core->v_sync_line_aft_pxl_5[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1,
- core->v_sync_line_aft_pxl_5[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0,
- core->v_sync_line_aft_pxl_6[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1,
- core->v_sync_line_aft_pxl_6[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]);
- hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]);
-
- /* Timing generator registers */
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi[0]);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi[1]);
- hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d[0]);
-
- /* waiting for HDMIPHY's PLL to get to steady state */
- for (tries = 100; tries; --tries) {
- u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0);
- if (val & HDMI_PHY_STATUS_READY)
- break;
- usleep_range(1000, 2000);
- }
- /* steady state not achieved */
- if (tries == 0) {
- DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
- hdmi_regs_dump(hdata, "timing apply");
- }
-
- clk_disable_unprepare(hdata->res.sclk_hdmi);
- clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
- clk_prepare_enable(hdata->res.sclk_hdmi);
+ for (tries = 0; tries < 10; ++tries) {
+ u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
- /* enable HDMI and timing generator */
- hdmi_start(hdata, true);
-}
-
-static void hdmi_mode_apply(struct hdmi_context *hdata)
-{
- if (hdata->type == HDMI_TYPE13)
- hdmi_v13_mode_apply(hdata);
- else
- hdmi_v14_mode_apply(hdata);
-}
-
-static void hdmiphy_conf_reset(struct hdmi_context *hdata)
-{
- u32 reg;
-
- clk_disable_unprepare(hdata->res.sclk_hdmi);
- clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_pixel);
- clk_prepare_enable(hdata->res.sclk_hdmi);
-
- /* operation mode */
- hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_ENABLE_MODE_SET);
-
- if (hdata->type == HDMI_TYPE13)
- reg = HDMI_V13_PHY_RSTOUT;
- else
- reg = HDMI_PHY_RSTOUT;
-
- /* reset hdmiphy */
- hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT);
- usleep_range(10000, 12000);
- hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT);
- usleep_range(10000, 12000);
-}
-
-static void hdmiphy_poweron(struct hdmi_context *hdata)
-{
- if (hdata->type != HDMI_TYPE14)
- return;
-
- DRM_DEBUG_KMS("\n");
-
- /* For PHY Mode Setting */
- hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_ENABLE_MODE_SET);
- /* Phy Power On */
- hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
- HDMI_PHY_POWER_ON);
- /* For PHY Mode Setting */
- hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_DISABLE_MODE_SET);
- /* PHY SW Reset */
- hdmiphy_conf_reset(hdata);
-}
-
-static void hdmiphy_poweroff(struct hdmi_context *hdata)
-{
- if (hdata->type != HDMI_TYPE14)
- return;
-
- DRM_DEBUG_KMS("\n");
-
- /* PHY SW Reset */
- hdmiphy_conf_reset(hdata);
- /* For PHY Mode Setting */
- hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_ENABLE_MODE_SET);
-
- /* PHY Power Off */
- hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
- HDMI_PHY_POWER_OFF);
-
- /* For PHY Mode Setting */
- hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_DISABLE_MODE_SET);
-}
-
-static void hdmiphy_conf_apply(struct hdmi_context *hdata)
-{
- int ret;
- int i;
-
- /* pixel clock */
- i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
- if (i < 0) {
- DRM_ERROR("failed to find hdmiphy conf\n");
- return;
- }
-
- ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
- if (ret) {
- DRM_ERROR("failed to configure hdmiphy\n");
- return;
- }
-
- usleep_range(10000, 12000);
-
- ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
- HDMI_PHY_DISABLE_MODE_SET);
- if (ret) {
- DRM_ERROR("failed to enable hdmiphy\n");
- return;
+ if (val & HDMI_PHY_STATUS_READY) {
+ DRM_DEBUG_KMS("PLL stabilized after %d tries\n", tries);
+ return;
+ }
+ usleep_range(10, 20);
}
+ DRM_ERROR("PLL could not reach steady state\n");
}
-static void hdmi_conf_apply(struct hdmi_context *hdata)
-{
- hdmiphy_conf_reset(hdata);
- hdmiphy_conf_apply(hdata);
-
- mutex_lock(&hdata->hdmi_mutex);
- hdmi_start(hdata, false);
- hdmi_conf_init(hdata);
- mutex_unlock(&hdata->hdmi_mutex);
-
- hdmi_audio_init(hdata);
-
- /* setting core registers */
- hdmi_mode_apply(hdata);
- hdmi_audio_control(hdata, true);
-
- hdmi_regs_dump(hdata, "start");
-}
-
-static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
-{
- int i;
- BUG_ON(num_bytes > 4);
- for (i = 0; i < num_bytes; i++)
- reg_pair[i] = (value >> (8 * i)) & 0xff;
-}
-
-static void hdmi_v13_mode_set(struct hdmi_context *hdata,
- struct drm_display_mode *m)
+static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
{
- struct hdmi_v13_core_regs *core = &hdata->mode_conf.conf.v13_conf.core;
- struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg;
+ struct drm_display_mode *m = &hdata->current_mode;
unsigned int val;
- hdata->mode_conf.cea_video_id =
- drm_match_cea_mode((struct drm_display_mode *)m);
- hdata->mode_conf.pixel_clock = m->clock * 1000;
- hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
-
- hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_V13_H_V_LINE_0, 3,
+ (m->htotal << 12) | m->vtotal);
val = (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0;
- hdmi_set_reg(core->vsync_pol, 1, val);
+ hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1, val);
val = (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
- hdmi_set_reg(core->int_pro_mode, 1, val);
+ hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1, val);
val = (m->hsync_start - m->hdisplay - 2);
val |= ((m->hsync_end - m->hdisplay - 2) << 10);
val |= ((m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0)<<20;
- hdmi_set_reg(core->h_sync_gen, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_H_SYNC_GEN_0, 3, val);
/*
* Quirk requirement for exynos HDMI IP design,
@@ -1826,86 +1324,78 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,
/* Interlaced Mode */
val = ((m->vsync_end - m->vdisplay) / 2);
val |= ((m->vsync_start - m->vdisplay) / 2) << 12;
- hdmi_set_reg(core->v_sync_gen1, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
val = m->vtotal / 2;
val |= ((m->vtotal - m->vdisplay) / 2) << 11;
- hdmi_set_reg(core->v_blank, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
val = (m->vtotal +
((m->vsync_end - m->vsync_start) * 4) + 5) / 2;
val |= m->vtotal << 11;
- hdmi_set_reg(core->v_blank_f, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, val);
val = ((m->vtotal / 2) + 7);
val |= ((m->vtotal / 2) + 2) << 12;
- hdmi_set_reg(core->v_sync_gen2, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, val);
val = ((m->htotal / 2) + (m->hsync_start - m->hdisplay));
val |= ((m->htotal / 2) +
(m->hsync_start - m->hdisplay)) << 12;
- hdmi_set_reg(core->v_sync_gen3, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, val);
- hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
+ (m->vtotal - m->vdisplay) / 2);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
- hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x249);
} else {
/* Progressive Mode */
val = m->vtotal;
val |= (m->vtotal - m->vdisplay) << 11;
- hdmi_set_reg(core->v_blank, 3, val);
+ hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_0, 3, val);
- hdmi_set_reg(core->v_blank_f, 3, 0);
+ hdmi_reg_writev(hdata, HDMI_V13_V_BLANK_F_0, 3, 0);
val = (m->vsync_end - m->vdisplay);
val |= ((m->vsync_start - m->vdisplay) << 12);
- hdmi_set_reg(core->v_sync_gen1, 3, val);
-
- hdmi_set_reg(core->v_sync_gen2, 3, 0x1001);/* Reset value */
- hdmi_set_reg(core->v_sync_gen3, 3, 0x1001);/* Reset value */
- hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay);
- hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_1_0, 3, val);
+
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_2_0, 3, 0x1001);
+ hdmi_reg_writev(hdata, HDMI_V13_V_SYNC_GEN_3_0, 3, 0x1001);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
+ m->vtotal - m->vdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248);
}
/* Timing generator registers */
- hdmi_set_reg(tg->cmd, 1, 0x0);
- hdmi_set_reg(tg->h_fsz, 2, m->htotal);
- hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(tg->hact_sz, 2, m->hdisplay);
- hdmi_set_reg(tg->v_fsz, 2, m->vtotal);
- hdmi_set_reg(tg->vsync, 2, 0x1);
- hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */
- hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */
- hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->tg_3d, 1, 0x0); /* Not used */
+ hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
+ hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233);
}
-static void hdmi_v14_mode_set(struct hdmi_context *hdata,
- struct drm_display_mode *m)
+static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
{
- struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg;
- struct hdmi_v14_core_regs *core =
- &hdata->mode_conf.conf.v14_conf.core;
-
- hdata->mode_conf.cea_video_id =
- drm_match_cea_mode((struct drm_display_mode *)m);
- hdata->mode_conf.pixel_clock = m->clock * 1000;
- hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;
-
- hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(core->v_line, 2, m->vtotal);
- hdmi_set_reg(core->h_line, 2, m->htotal);
- hdmi_set_reg(core->hsync_pol, 1,
+ struct drm_display_mode *m = &hdata->current_mode;
+
+ hdmi_reg_writev(hdata, HDMI_H_BLANK_0, 2, m->htotal - m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_V_LINE_0, 2, m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_H_LINE_0, 2, m->htotal);
+ hdmi_reg_writev(hdata, HDMI_HSYNC_POL, 1,
(m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
- hdmi_set_reg(core->vsync_pol, 1,
+ hdmi_reg_writev(hdata, HDMI_VSYNC_POL, 1,
(m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
- hdmi_set_reg(core->int_pro_mode, 1,
+ hdmi_reg_writev(hdata, HDMI_INT_PRO_MODE, 1,
(m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
/*
@@ -1917,229 +1407,255 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
/* Following values & calculations differ for different type of modes */
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
/* Interlaced Mode */
- hdmi_set_reg(core->v_sync_line_bef_2, 2,
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
(m->vsync_end - m->vdisplay) / 2);
- hdmi_set_reg(core->v_sync_line_bef_1, 2,
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
(m->vsync_start - m->vdisplay) / 2);
- hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2);
- hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2);
- hdmi_set_reg(core->v_blank_f0, 2, m->vtotal - m->vdisplay / 2);
- hdmi_set_reg(core->v_blank_f1, 2, m->vtotal);
- hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7);
- hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2);
- hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2,
+ hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal / 2);
+ hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
+ (m->vtotal - m->vdisplay) / 2);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2,
+ m->vtotal - m->vdisplay / 2);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2,
+ (m->vtotal / 2) + 7);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2,
+ (m->vtotal / 2) + 2);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2,
(m->htotal / 2) + (m->hsync_start - m->hdisplay));
- hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2,
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2,
(m->htotal / 2) + (m->hsync_start - m->hdisplay));
- hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2);
- hdmi_set_reg(tg->vact_st2, 2, m->vtotal - m->vdisplay / 2);
- hdmi_set_reg(tg->vsync2, 2, (m->vtotal / 2) + 1);
- hdmi_set_reg(tg->vsync_bot_hdmi, 2, (m->vtotal / 2) + 1);
- hdmi_set_reg(tg->field_bot_hdmi, 2, (m->vtotal / 2) + 1);
- hdmi_set_reg(tg->vact_st3, 2, 0x0);
- hdmi_set_reg(tg->vact_st4, 2, 0x0);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
+ (m->vtotal - m->vdisplay) / 2);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay / 2);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2,
+ m->vtotal - m->vdisplay / 2);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2,
+ (m->vtotal / 2) + 1);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2,
+ (m->vtotal / 2) + 1);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2,
+ (m->vtotal / 2) + 1);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x0);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x0);
} else {
/* Progressive Mode */
- hdmi_set_reg(core->v_sync_line_bef_2, 2,
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_2_0, 2,
m->vsync_end - m->vdisplay);
- hdmi_set_reg(core->v_sync_line_bef_1, 2,
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_BEF_1_0, 2,
m->vsync_start - m->vdisplay);
- hdmi_set_reg(core->v2_blank, 2, m->vtotal);
- hdmi_set_reg(core->v1_blank, 2, m->vtotal - m->vdisplay);
- hdmi_set_reg(core->v_blank_f0, 2, 0xffff);
- hdmi_set_reg(core->v_blank_f1, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_2, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_1, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, 0xffff);
- hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay);
- hdmi_set_reg(tg->vact_sz, 2, m->vdisplay);
- hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
- hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */
- hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */
- hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
+ hdmi_reg_writev(hdata, HDMI_V2_BLANK_0, 2, m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_V1_BLANK_0, 2,
+ m->vtotal - m->vdisplay);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F0_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F1_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_2_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_1_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST_L, 2,
+ m->vtotal - m->vdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_SZ_L, 2, m->vdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST2_L, 2, 0x248);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST3_L, 2, 0x47b);
+ hdmi_reg_writev(hdata, HDMI_TG_VACT_ST4_L, 2, 0x6ae);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC2_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_BOT_HDMI_L, 2, 0x233);
}
/* Following values & calculations are same irrespective of mode type */
- hdmi_set_reg(core->h_sync_start, 2, m->hsync_start - m->hdisplay - 2);
- hdmi_set_reg(core->h_sync_end, 2, m->hsync_end - m->hdisplay - 2);
- hdmi_set_reg(core->vact_space_1, 2, 0xffff);
- hdmi_set_reg(core->vact_space_2, 2, 0xffff);
- hdmi_set_reg(core->vact_space_3, 2, 0xffff);
- hdmi_set_reg(core->vact_space_4, 2, 0xffff);
- hdmi_set_reg(core->vact_space_5, 2, 0xffff);
- hdmi_set_reg(core->vact_space_6, 2, 0xffff);
- hdmi_set_reg(core->v_blank_f2, 2, 0xffff);
- hdmi_set_reg(core->v_blank_f3, 2, 0xffff);
- hdmi_set_reg(core->v_blank_f4, 2, 0xffff);
- hdmi_set_reg(core->v_blank_f5, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_3, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_4, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_5, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_6, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_3, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_4, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_5, 2, 0xffff);
- hdmi_set_reg(core->v_sync_line_aft_pxl_6, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_H_SYNC_START_0, 2,
+ m->hsync_start - m->hdisplay - 2);
+ hdmi_reg_writev(hdata, HDMI_H_SYNC_END_0, 2,
+ m->hsync_end - m->hdisplay - 2);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_1_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_2_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_3_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_4_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_5_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_VACT_SPACE_6_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F2_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F3_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F4_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_BLANK_F5_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_3_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_4_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_5_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_6_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, 2, 0xffff);
+ hdmi_reg_writev(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, 2, 0xffff);
/* Timing generator registers */
- hdmi_set_reg(tg->cmd, 1, 0x0);
- hdmi_set_reg(tg->h_fsz, 2, m->htotal);
- hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay);
- hdmi_set_reg(tg->hact_sz, 2, m->hdisplay);
- hdmi_set_reg(tg->v_fsz, 2, m->vtotal);
- hdmi_set_reg(tg->vsync, 2, 0x1);
- hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
- hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */
- hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */
- hdmi_set_reg(tg->tg_3d, 1, 0x0);
+ hdmi_reg_writev(hdata, HDMI_TG_H_FSZ_L, 2, m->htotal);
+ hdmi_reg_writev(hdata, HDMI_TG_HACT_ST_L, 2, m->htotal - m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_HACT_SZ_L, 2, m->hdisplay);
+ hdmi_reg_writev(hdata, HDMI_TG_V_FSZ_L, 2, m->vtotal);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_CHG_L, 2, 0x233);
+ hdmi_reg_writev(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_FIELD_TOP_HDMI_L, 2, 0x1);
+ hdmi_reg_writev(hdata, HDMI_TG_3D, 1, 0x0);
}
-static void hdmi_mode_set(struct exynos_drm_display *display,
- struct drm_display_mode *mode)
+static void hdmi_mode_apply(struct hdmi_context *hdata)
{
- struct hdmi_context *hdata = display_to_hdmi(display);
- struct drm_display_mode *m = mode;
+ if (hdata->drv_data->type == HDMI_TYPE13)
+ hdmi_v13_mode_apply(hdata);
+ else
+ hdmi_v14_mode_apply(hdata);
- DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
- m->hdisplay, m->vdisplay,
- m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
- "INTERLACED" : "PROGRESSIVE");
+ hdmiphy_wait_for_pll(hdata);
- /* preserve mode information for later use. */
- drm_mode_copy(&hdata->current_mode, mode);
+ clk_set_parent(hdata->mout_hdmi, hdata->sclk_hdmiphy);
- if (hdata->type == HDMI_TYPE13)
- hdmi_v13_mode_set(hdata, mode);
- else
- hdmi_v14_mode_set(hdata, mode);
+ /* enable HDMI and timing generator */
+ hdmi_start(hdata, true);
+}
+
+static void hdmiphy_conf_reset(struct hdmi_context *hdata)
+{
+ clk_set_parent(hdata->mout_hdmi, hdata->sclk_pixel);
+
+ /* reset hdmiphy */
+ hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ usleep_range(10000, 12000);
+ hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ usleep_range(10000, 12000);
}
-static void hdmi_commit(struct exynos_drm_display *display)
+static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{
- struct hdmi_context *hdata = display_to_hdmi(display);
+ int ret;
+ int i;
- mutex_lock(&hdata->hdmi_mutex);
- if (!hdata->powered) {
- mutex_unlock(&hdata->hdmi_mutex);
+ /* pixel clock */
+ i = hdmi_find_phy_conf(hdata, hdata->current_mode.clock * 1000);
+ if (i < 0) {
+ DRM_ERROR("failed to find hdmiphy conf\n");
return;
}
- mutex_unlock(&hdata->hdmi_mutex);
- hdmi_conf_apply(hdata);
+ ret = hdmiphy_reg_write_buf(hdata, 0,
+ hdata->drv_data->phy_confs[i].conf, 32);
+ if (ret) {
+ DRM_ERROR("failed to configure hdmiphy\n");
+ return;
+ }
+
+ usleep_range(10000, 12000);
+}
+
+static void hdmi_conf_apply(struct hdmi_context *hdata)
+{
+ hdmiphy_conf_reset(hdata);
+ hdmiphy_conf_apply(hdata);
+
+ hdmi_start(hdata, false);
+ hdmi_conf_init(hdata);
+
+ hdmi_audio_init(hdata);
+
+ /* setting core registers */
+ hdmi_mode_apply(hdata);
+ hdmi_audio_control(hdata, true);
+
+ hdmi_regs_dump(hdata, "start");
}
-static void hdmi_poweron(struct hdmi_context *hdata)
+static void hdmi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct hdmi_resources *res = &hdata->res;
+ struct hdmi_context *hdata = encoder_to_hdmi(encoder);
+ struct drm_display_mode *m = adjusted_mode;
- mutex_lock(&hdata->hdmi_mutex);
- if (hdata->powered) {
- mutex_unlock(&hdata->hdmi_mutex);
+ DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
+ m->hdisplay, m->vdisplay,
+ m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
+ "INTERLACED" : "PROGRESSIVE");
+
+ drm_mode_copy(&hdata->current_mode, m);
+ hdata->cea_video_id = drm_match_cea_mode(mode);
+}
+
+static void hdmi_enable(struct drm_encoder *encoder)
+{
+ struct hdmi_context *hdata = encoder_to_hdmi(encoder);
+
+ if (hdata->powered)
return;
- }
hdata->powered = true;
- mutex_unlock(&hdata->hdmi_mutex);
-
pm_runtime_get_sync(hdata->dev);
- if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
+ if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
/* set pmu hdmiphy control bit to enable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 1);
- clk_prepare_enable(res->hdmi);
- clk_prepare_enable(res->sclk_hdmi);
+ clk_prepare_enable(hdata->hdmi);
+ clk_prepare_enable(hdata->sclk_hdmi);
- hdmiphy_poweron(hdata);
- hdmi_commit(&hdata->display);
+ hdmi_conf_apply(hdata);
}
-static void hdmi_poweroff(struct hdmi_context *hdata)
+static void hdmi_disable(struct drm_encoder *encoder)
{
- struct hdmi_resources *res = &hdata->res;
+ struct hdmi_context *hdata = encoder_to_hdmi(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
+ const struct drm_crtc_helper_funcs *funcs = NULL;
- mutex_lock(&hdata->hdmi_mutex);
if (!hdata->powered)
- goto out;
- mutex_unlock(&hdata->hdmi_mutex);
+ return;
+
+ /*
+ * The SFRs of VP and Mixer are updated by Vertical Sync of
+ * Timing generator which is a part of HDMI so the sequence
+ * to disable TV Subsystem should be as following,
+ * VP -> Mixer -> HDMI
+ *
+ * Below codes will try to disable Mixer and VP(if used)
+ * prior to disabling HDMI.
+ */
+ if (crtc)
+ funcs = crtc->helper_private;
+ if (funcs && funcs->disable)
+ (*funcs->disable)(crtc);
/* HDMI System Disable */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
- hdmiphy_poweroff(hdata);
-
cancel_delayed_work(&hdata->hotplug_work);
- clk_disable_unprepare(res->sclk_hdmi);
- clk_disable_unprepare(res->hdmi);
+ clk_disable_unprepare(hdata->sclk_hdmi);
+ clk_disable_unprepare(hdata->hdmi);
/* reset pmu hdmiphy control bit to disable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 0);
- regulator_bulk_disable(res->regul_count, res->regul_bulk);
+ regulator_bulk_disable(ARRAY_SIZE(supply), hdata->regul_bulk);
pm_runtime_put_sync(hdata->dev);
- mutex_lock(&hdata->hdmi_mutex);
hdata->powered = false;
-
-out:
- mutex_unlock(&hdata->hdmi_mutex);
}
-static void hdmi_dpms(struct exynos_drm_display *display, int mode)
-{
- struct hdmi_context *hdata = display_to_hdmi(display);
- struct drm_encoder *encoder = hdata->encoder;
- struct drm_crtc *crtc = encoder->crtc;
- const struct drm_crtc_helper_funcs *funcs = NULL;
-
- DRM_DEBUG_KMS("mode %d\n", mode);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- hdmi_poweron(hdata);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- /*
- * The SFRs of VP and Mixer are updated by Vertical Sync of
- * Timing generator which is a part of HDMI so the sequence
- * to disable TV Subsystem should be as following,
- * VP -> Mixer -> HDMI
- *
- * Below codes will try to disable Mixer and VP(if used)
- * prior to disabling HDMI.
- */
- if (crtc)
- funcs = crtc->helper_private;
- if (funcs && funcs->dpms)
- (*funcs->dpms)(crtc, mode);
-
- hdmi_poweroff(hdata);
- break;
- default:
- DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
- break;
- }
-}
-
-static struct exynos_drm_display_ops hdmi_display_ops = {
- .create_connector = hdmi_create_connector,
+static struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
- .dpms = hdmi_dpms,
- .commit = hdmi_commit,
+ .enable = hdmi_enable,
+ .disable = hdmi_disable,
+};
+
+static struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static void hdmi_hotplug_work_func(struct work_struct *work)
@@ -2148,10 +1664,6 @@ static void hdmi_hotplug_work_func(struct work_struct *work)
hdata = container_of(work, struct hdmi_context, hotplug_work.work);
- mutex_lock(&hdata->hdmi_mutex);
- hdata->hpd = gpio_get_value(hdata->hpd_gpio);
- mutex_unlock(&hdata->hdmi_mutex);
-
if (hdata->drm_dev)
drm_helper_hpd_irq_event(hdata->drm_dev);
}
@@ -2169,80 +1681,76 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
static int hdmi_resources_init(struct hdmi_context *hdata)
{
struct device *dev = hdata->dev;
- struct hdmi_resources *res = &hdata->res;
- static char *supply[] = {
- "vdd",
- "vdd_osc",
- "vdd_pll",
- };
int i, ret;
DRM_DEBUG_KMS("HDMI resource init\n");
+ hdata->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN);
+ if (IS_ERR(hdata->hpd_gpio)) {
+ DRM_ERROR("cannot get hpd gpio property\n");
+ return PTR_ERR(hdata->hpd_gpio);
+ }
+
+ hdata->irq = gpiod_to_irq(hdata->hpd_gpio);
+ if (hdata->irq < 0) {
+ DRM_ERROR("failed to get GPIO irq\n");
+ return hdata->irq;
+ }
/* get clocks, power */
- res->hdmi = devm_clk_get(dev, "hdmi");
- if (IS_ERR(res->hdmi)) {
+ hdata->hdmi = devm_clk_get(dev, "hdmi");
+ if (IS_ERR(hdata->hdmi)) {
DRM_ERROR("failed to get clock 'hdmi'\n");
- ret = PTR_ERR(res->hdmi);
+ ret = PTR_ERR(hdata->hdmi);
goto fail;
}
- res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
- if (IS_ERR(res->sclk_hdmi)) {
+ hdata->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
+ if (IS_ERR(hdata->sclk_hdmi)) {
DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
- ret = PTR_ERR(res->sclk_hdmi);
+ ret = PTR_ERR(hdata->sclk_hdmi);
goto fail;
}
- res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
- if (IS_ERR(res->sclk_pixel)) {
+ hdata->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
+ if (IS_ERR(hdata->sclk_pixel)) {
DRM_ERROR("failed to get clock 'sclk_pixel'\n");
- ret = PTR_ERR(res->sclk_pixel);
+ ret = PTR_ERR(hdata->sclk_pixel);
goto fail;
}
- res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
- if (IS_ERR(res->sclk_hdmiphy)) {
+ hdata->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
+ if (IS_ERR(hdata->sclk_hdmiphy)) {
DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
- ret = PTR_ERR(res->sclk_hdmiphy);
+ ret = PTR_ERR(hdata->sclk_hdmiphy);
goto fail;
}
- res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
- if (IS_ERR(res->mout_hdmi)) {
+ hdata->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
+ if (IS_ERR(hdata->mout_hdmi)) {
DRM_ERROR("failed to get clock 'mout_hdmi'\n");
- ret = PTR_ERR(res->mout_hdmi);
+ ret = PTR_ERR(hdata->mout_hdmi);
goto fail;
}
- clk_set_parent(res->mout_hdmi, res->sclk_pixel);
+ clk_set_parent(hdata->mout_hdmi, hdata->sclk_pixel);
- res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
- sizeof(res->regul_bulk[0]), GFP_KERNEL);
- if (!res->regul_bulk) {
- ret = -ENOMEM;
- goto fail;
- }
for (i = 0; i < ARRAY_SIZE(supply); ++i) {
- res->regul_bulk[i].supply = supply[i];
- res->regul_bulk[i].consumer = NULL;
+ hdata->regul_bulk[i].supply = supply[i];
+ hdata->regul_bulk[i].consumer = NULL;
}
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
if (ret) {
DRM_ERROR("failed to get regulators\n");
return ret;
}
- res->regul_count = ARRAY_SIZE(supply);
- res->reg_hdmi_en = devm_regulator_get(dev, "hdmi-en");
- if (IS_ERR(res->reg_hdmi_en) && PTR_ERR(res->reg_hdmi_en) != -ENOENT) {
- DRM_ERROR("failed to get hdmi-en regulator\n");
- return PTR_ERR(res->reg_hdmi_en);
- }
- if (!IS_ERR(res->reg_hdmi_en)) {
- ret = regulator_enable(res->reg_hdmi_en);
- if (ret) {
- DRM_ERROR("failed to enable hdmi-en regulator\n");
- return ret;
- }
- } else
- res->reg_hdmi_en = NULL;
+ hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
+
+ if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV)
+ return 0;
+
+ if (IS_ERR(hdata->reg_hdmi_en))
+ return PTR_ERR(hdata->reg_hdmi_en);
+
+ ret = regulator_enable(hdata->reg_hdmi_en);
+ if (ret)
+ DRM_ERROR("failed to enable hdmi-en regulator\n");
return ret;
fail:
@@ -2250,35 +1758,8 @@ fail:
return ret;
}
-static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
- (struct device *dev)
-{
- struct device_node *np = dev->of_node;
- struct s5p_hdmi_platform_data *pd;
- u32 value;
-
- pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
- if (!pd)
- goto err_data;
-
- if (!of_find_property(np, "hpd-gpio", &value)) {
- DRM_ERROR("no hpd gpio property found\n");
- goto err_data;
- }
-
- pd->hpd_gpio = of_get_named_gpio(np, "hpd-gpio", 0);
-
- return pd;
-
-err_data:
- return NULL;
-}
-
static struct of_device_id hdmi_match_types[] = {
{
- .compatible = "samsung,exynos5-hdmi",
- .data = &exynos5_hdmi_driver_data,
- }, {
.compatible = "samsung,exynos4210-hdmi",
.data = &exynos4210_hdmi_driver_data,
}, {
@@ -2297,10 +1778,33 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm_dev = data;
struct hdmi_context *hdata = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &hdata->encoder;
+ int ret, pipe;
hdata->drm_dev = drm_dev;
- return exynos_drm_create_enc_conn(drm_dev, &hdata->display);
+ pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+ EXYNOS_DISPLAY_TYPE_HDMI);
+ if (pipe < 0)
+ return pipe;
+
+ encoder->possible_crtcs = 1 << pipe;
+
+ DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+ drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
+
+ ret = hdmi_create_connector(encoder);
+ if (ret) {
+ DRM_ERROR("failed to create connector ret = %d\n", ret);
+ drm_encoder_cleanup(encoder);
+ return ret;
+ }
+
+ return 0;
}
static void hdmi_unbind(struct device *dev, struct device *master, void *data)
@@ -2334,49 +1838,24 @@ static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
static int hdmi_probe(struct platform_device *pdev)
{
struct device_node *ddc_node, *phy_node;
- struct s5p_hdmi_platform_data *pdata;
- struct hdmi_driver_data *drv_data;
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct hdmi_context *hdata;
struct resource *res;
int ret;
- if (!dev->of_node)
- return -ENODEV;
-
- pdata = drm_hdmi_dt_parse_pdata(dev);
- if (!pdata)
- return -EINVAL;
-
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata)
return -ENOMEM;
- 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;
+ match = of_match_device(hdmi_match_types, dev);
+ if (!match)
+ return -ENODEV;
- mutex_init(&hdata->hdmi_mutex);
+ hdata->drv_data = match->data;
platform_set_drvdata(pdev, hdata);
- match = of_match_node(hdmi_match_types, dev->of_node);
- if (!match) {
- ret = -ENODEV;
- goto err_del_component;
- }
-
- drv_data = (struct hdmi_driver_data *)match->data;
- hdata->type = drv_data->type;
- hdata->phy_confs = drv_data->phy_confs;
- hdata->phy_conf_count = drv_data->phy_conf_count;
-
- hdata->hpd_gpio = pdata->hpd_gpio;
hdata->dev = dev;
ret = hdmi_resources_init(hdata);
@@ -2389,13 +1868,7 @@ 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;
- }
-
- 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 +1879,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:
@@ -2430,7 +1902,7 @@ out_get_ddc_adpt:
}
out_get_phy_port:
- if (drv_data->is_apb_phy) {
+ if (hdata->drv_data->is_apb_phy) {
hdata->regs_hdmiphy = of_iomap(phy_node, 0);
if (!hdata->regs_hdmiphy) {
DRM_ERROR("failed to ioremap hdmi phy\n");
@@ -2446,15 +1918,6 @@ out_get_phy_port:
}
}
- hdata->irq = gpio_to_irq(hdata->hpd_gpio);
- if (hdata->irq < 0) {
- DRM_ERROR("failed to get GPIO irq\n");
- ret = hdata->irq;
- goto err_hdmiphy;
- }
-
- hdata->hpd = gpio_get_value(hdata->hpd_gpio);
-
INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
@@ -2491,9 +1954,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;
}
@@ -2503,17 +1963,18 @@ static int hdmi_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&hdata->hotplug_work);
- if (hdata->res.reg_hdmi_en)
- regulator_disable(hdata->res.reg_hdmi_en);
+ component_del(&pdev->dev, &hdmi_component_ops);
+
+ pm_runtime_disable(&pdev->dev);
+
+ if (!IS_ERR(hdata->reg_hdmi_en))
+ regulator_disable(hdata->reg_hdmi_en);
if (hdata->hdmiphy_port)
put_device(&hdata->hdmiphy_port->dev);
- put_device(&hdata->ddc_adpt->dev);
- pm_runtime_disable(&pdev->dev);
- component_del(&pdev->dev, &hdmi_component_ops);
+ put_device(&hdata->ddc_adpt->dev);
- exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_mixer.c b/kernel/drivers/gpu/drm/exynos/exynos_mixer.c
index 8874c1fcb..d09f8f9a8 100644
--- a/kernel/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/kernel/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -39,10 +39,10 @@
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h"
-#include "exynos_mixer.h"
#define MIXER_WIN_NR 3
-#define MIXER_DEFAULT_WIN 0
+#define VP_DEFAULT_WIN 2
+#define CURSOR_WIN 1
/* The pixelformats that are natively supported by the mixer. */
#define MXR_FORMAT_RGB565 4
@@ -69,6 +69,24 @@ enum mixer_version_id {
MXR_VER_128_0_0_184,
};
+enum mixer_flag_bits {
+ MXR_BIT_POWERED,
+ MXR_BIT_VSYNC,
+};
+
+static const uint32_t mixer_formats[] = {
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
+static const uint32_t vp_formats[] = {
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV21,
+};
+
struct mixer_context {
struct platform_device *pdev;
struct device *dev;
@@ -76,13 +94,11 @@ struct mixer_context {
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[MIXER_WIN_NR];
int pipe;
+ unsigned long flags;
bool interlace;
- bool powered;
bool vp_enabled;
bool has_sclk;
- u32 int_en;
- struct mutex mixer_mutex;
struct mixer_resources mixer_res;
enum mixer_version_id mxr_ver;
wait_queue_head_t wait_vsync_queue;
@@ -380,19 +396,20 @@ static void mixer_stop(struct mixer_context *ctx)
usleep_range(10000, 12000);
}
-static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)
+static void vp_video_buffer(struct mixer_context *ctx,
+ struct exynos_drm_plane *plane)
{
struct mixer_resources *res = &ctx->mixer_res;
+ struct drm_plane_state *state = plane->base.state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_display_mode *mode = &state->crtc->mode;
unsigned long flags;
- struct exynos_drm_plane *plane;
dma_addr_t luma_addr[2], chroma_addr[2];
bool tiled_mode = false;
bool crcb_mode = false;
u32 val;
- plane = &ctx->planes[win];
-
- switch (plane->pixel_format) {
+ switch (fb->pixel_format) {
case DRM_FORMAT_NV12:
crcb_mode = false;
break;
@@ -401,21 +418,21 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)
break;
default:
DRM_ERROR("pixel format for vp is wrong [%d].\n",
- plane->pixel_format);
+ fb->pixel_format);
return;
}
luma_addr[0] = plane->dma_addr[0];
chroma_addr[0] = plane->dma_addr[1];
- if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) {
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
ctx->interlace = true;
if (tiled_mode) {
luma_addr[1] = luma_addr[0] + 0x40;
chroma_addr[1] = chroma_addr[0] + 0x40;
} else {
- luma_addr[1] = luma_addr[0] + plane->pitch;
- chroma_addr[1] = chroma_addr[0] + plane->pitch;
+ luma_addr[1] = luma_addr[0] + fb->pitches[0];
+ chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
}
} else {
ctx->interlace = false;
@@ -436,25 +453,25 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)
vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
/* setting size of input image */
- vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(plane->pitch) |
- VP_IMG_VSIZE(plane->fb_height));
+ vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
+ VP_IMG_VSIZE(fb->height));
/* chroma height has to reduced by 2 to avoid chroma distorions */
- vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(plane->pitch) |
- VP_IMG_VSIZE(plane->fb_height / 2));
+ vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
+ VP_IMG_VSIZE(fb->height / 2));
- vp_reg_write(res, VP_SRC_WIDTH, plane->src_width);
- vp_reg_write(res, VP_SRC_HEIGHT, plane->src_height);
+ vp_reg_write(res, VP_SRC_WIDTH, plane->src_w);
+ vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h);
vp_reg_write(res, VP_SRC_H_POSITION,
VP_SRC_H_POSITION_VAL(plane->src_x));
vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y);
- vp_reg_write(res, VP_DST_WIDTH, plane->crtc_width);
+ vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w);
vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x);
if (ctx->interlace) {
- vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height / 2);
+ vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2);
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2);
} else {
- vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_height);
+ vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h);
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y);
}
@@ -469,9 +486,9 @@ static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)
vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
- mixer_cfg_scan(ctx, plane->mode_height);
- mixer_cfg_rgb_fmt(ctx, plane->mode_height);
- mixer_cfg_layer(ctx, win, true);
+ mixer_cfg_scan(ctx, mode->vdisplay);
+ mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
+ mixer_cfg_layer(ctx, plane->zpos, true);
mixer_run(ctx);
mixer_vsync_set_update(ctx, true);
@@ -491,15 +508,15 @@ static void mixer_layer_update(struct mixer_context *ctx)
static int mixer_setup_scale(const struct exynos_drm_plane *plane,
unsigned int *x_ratio, unsigned int *y_ratio)
{
- if (plane->crtc_width != plane->src_width) {
- if (plane->crtc_width == 2 * plane->src_width)
+ if (plane->crtc_w != plane->src_w) {
+ if (plane->crtc_w == 2 * plane->src_w)
*x_ratio = 1;
else
goto fail;
}
- if (plane->crtc_height != plane->src_height) {
- if (plane->crtc_height == 2 * plane->src_height)
+ if (plane->crtc_h != plane->src_h) {
+ if (plane->crtc_h == 2 * plane->src_h)
*y_ratio = 1;
else
goto fail;
@@ -512,20 +529,22 @@ fail:
return -ENOTSUPP;
}
-static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)
+static void mixer_graph_buffer(struct mixer_context *ctx,
+ struct exynos_drm_plane *plane)
{
struct mixer_resources *res = &ctx->mixer_res;
+ struct drm_plane_state *state = plane->base.state;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_display_mode *mode = &state->crtc->mode;
unsigned long flags;
- struct exynos_drm_plane *plane;
+ unsigned int win = plane->zpos;
unsigned int x_ratio = 0, y_ratio = 0;
unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
dma_addr_t dma_addr;
unsigned int fmt;
u32 val;
- plane = &ctx->planes[win];
-
- switch (plane->pixel_format) {
+ switch (fb->pixel_format) {
case DRM_FORMAT_XRGB4444:
fmt = MXR_FORMAT_ARGB4444;
break;
@@ -557,12 +576,12 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)
/* converting dma address base and source offset */
dma_addr = plane->dma_addr[0]
- + (plane->src_x * plane->bpp >> 3)
- + (plane->src_y * plane->pitch);
+ + (plane->src_x * fb->bits_per_pixel >> 3)
+ + (plane->src_y * fb->pitches[0]);
src_x_offset = 0;
src_y_offset = 0;
- if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE)
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
ctx->interlace = true;
else
ctx->interlace = false;
@@ -576,18 +595,18 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)
/* setup geometry */
mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
- plane->pitch / (plane->bpp >> 3));
+ fb->pitches[0] / (fb->bits_per_pixel >> 3));
/* setup display size */
if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
- win == MIXER_DEFAULT_WIN) {
- val = MXR_MXR_RES_HEIGHT(plane->mode_height);
- val |= MXR_MXR_RES_WIDTH(plane->mode_width);
+ win == DEFAULT_WIN) {
+ val = MXR_MXR_RES_HEIGHT(mode->vdisplay);
+ val |= MXR_MXR_RES_WIDTH(mode->hdisplay);
mixer_reg_write(res, MXR_RESOLUTION, val);
}
- val = MXR_GRP_WH_WIDTH(plane->src_width);
- val |= MXR_GRP_WH_HEIGHT(plane->src_height);
+ val = MXR_GRP_WH_WIDTH(plane->src_w);
+ val |= MXR_GRP_WH_HEIGHT(plane->src_h);
val |= MXR_GRP_WH_H_SCALE(x_ratio);
val |= MXR_GRP_WH_V_SCALE(y_ratio);
mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
@@ -605,8 +624,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)
/* set buffer address to mixer */
mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
- mixer_cfg_scan(ctx, plane->mode_height);
- mixer_cfg_rgb_fmt(ctx, plane->mode_height);
+ mixer_cfg_scan(ctx, mode->vdisplay);
+ mixer_cfg_rgb_fmt(ctx, mode->vdisplay);
mixer_cfg_layer(ctx, win, true);
/* layer update mandatory for mixer 16.0.33.0 */
@@ -632,7 +651,7 @@ static void vp_win_reset(struct mixer_context *ctx)
/* waiting until VP_SRESET_PROCESSING is 0 */
if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
break;
- usleep_range(10000, 12000);
+ mdelay(10);
}
WARN(tries == 0, "failed to reset Video Processor\n");
}
@@ -710,6 +729,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
struct mixer_context *ctx = arg;
struct mixer_resources *res = &ctx->mixer_res;
u32 val, base, shadow;
+ int win;
spin_lock(&res->reg_slock);
@@ -718,6 +738,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));
@@ -731,8 +755,15 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
goto out;
}
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ drm_crtc_handle_vblank(&ctx->crtc->base);
+ for (win = 0 ; win < MIXER_WIN_NR ; win++) {
+ struct exynos_drm_plane *plane = &ctx->planes[win];
+
+ if (!plane->pending_fb)
+ continue;
+
+ exynos_drm_crtc_finish_update(ctx->crtc, plane);
+ }
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
@@ -743,11 +774,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,16 +908,16 @@ 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(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)
{
- if (is_drm_iommu_supported(mixer_ctx->drm_dev))
- drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
+ drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
}
static int mixer_enable_vblank(struct exynos_drm_crtc *crtc)
@@ -899,14 +925,13 @@ static int mixer_enable_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;
+ __set_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
return 0;
- }
/* 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,56 +941,51 @@ 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;
+ __clear_bit(MXR_BIT_VSYNC, &mixer_ctx->flags);
+
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
+ 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);
}
-static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
+static void mixer_update_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct mixer_context *mixer_ctx = crtc->ctx;
- DRM_DEBUG_KMS("win: %d\n", win);
+ DRM_DEBUG_KMS("win: %d\n", plane->zpos);
- mutex_lock(&mixer_ctx->mixer_mutex);
- if (!mixer_ctx->powered) {
- mutex_unlock(&mixer_ctx->mixer_mutex);
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
return;
- }
- mutex_unlock(&mixer_ctx->mixer_mutex);
- if (win > 1 && mixer_ctx->vp_enabled)
- vp_video_buffer(mixer_ctx, win);
+ if (plane->zpos > 1 && mixer_ctx->vp_enabled)
+ vp_video_buffer(mixer_ctx, plane);
else
- mixer_graph_buffer(mixer_ctx, win);
-
- mixer_ctx->planes[win].enabled = true;
+ mixer_graph_buffer(mixer_ctx, plane);
}
-static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
+static void mixer_disable_plane(struct exynos_drm_crtc *crtc,
+ struct exynos_drm_plane *plane)
{
struct mixer_context *mixer_ctx = crtc->ctx;
struct mixer_resources *res = &mixer_ctx->mixer_res;
unsigned long flags;
- DRM_DEBUG_KMS("win: %d\n", win);
+ DRM_DEBUG_KMS("win: %d\n", plane->zpos);
- mutex_lock(&mixer_ctx->mixer_mutex);
- if (!mixer_ctx->powered) {
- mutex_unlock(&mixer_ctx->mixer_mutex);
- mixer_ctx->planes[win].resume = false;
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
return;
- }
- mutex_unlock(&mixer_ctx->mixer_mutex);
spin_lock_irqsave(&res->reg_slock, flags);
mixer_vsync_set_update(mixer_ctx, false);
- mixer_cfg_layer(mixer_ctx, win, false);
+ mixer_cfg_layer(mixer_ctx, plane->zpos, false);
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)
@@ -973,12 +993,8 @@ static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
struct mixer_context *mixer_ctx = crtc->ctx;
int err;
- mutex_lock(&mixer_ctx->mixer_mutex);
- if (!mixer_ctx->powered) {
- mutex_unlock(&mixer_ctx->mixer_mutex);
+ if (!test_bit(MXR_BIT_POWERED, &mixer_ctx->flags))
return;
- }
- mutex_unlock(&mixer_ctx->mixer_mutex);
err = drm_vblank_get(mixer_ctx->drm_dev, mixer_ctx->pipe);
if (err < 0) {
@@ -1000,87 +1016,72 @@ 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) {
- mutex_unlock(&ctx->mixer_mutex);
+ if (test_bit(MXR_BIT_POWERED, &ctx->flags))
return;
- }
-
- mutex_unlock(&ctx->mixer_mutex);
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);
- ctx->powered = true;
- mutex_unlock(&ctx->mixer_mutex);
+ set_bit(MXR_BIT_POWERED, &ctx->flags);
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
- mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
+ if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
+ mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC);
+ mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
+ }
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) {
- mutex_unlock(&ctx->mixer_mutex);
+ if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
return;
- }
- mutex_unlock(&ctx->mixer_mutex);
mixer_stop(ctx);
mixer_regs_dump(ctx);
- mixer_window_suspend(ctx);
- ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
+ for (i = 0; i < MIXER_WIN_NR; i++)
+ mixer_disable_plane(crtc, &ctx->planes[i]);
- mutex_lock(&ctx->mixer_mutex);
- ctx->powered = false;
- mutex_unlock(&ctx->mixer_mutex);
+ clear_bit(MXR_BIT_POWERED, &ctx->flags);
clk_disable_unprepare(res->hdmi);
clk_disable_unprepare(res->mixer);
@@ -1093,26 +1094,11 @@ 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)
+static int mixer_atomic_check(struct exynos_drm_crtc *crtc,
+ struct drm_crtc_state *state)
{
+ struct drm_display_mode *mode = &state->adjusted_mode;
u32 w, h;
w = mode->hdisplay;
@@ -1131,12 +1117,14 @@ 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,
- .win_commit = mixer_win_commit,
- .win_disable = mixer_win_disable,
+ .update_plane = mixer_update_plane,
+ .disable_plane = mixer_disable_plane,
+ .atomic_check = mixer_atomic_check,
};
static struct mixer_drv_data exynos5420_mxr_drv_data = {
@@ -1199,7 +1187,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
struct mixer_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct exynos_drm_plane *exynos_plane;
- enum drm_plane_type type;
unsigned int zpos;
int ret;
@@ -1208,15 +1195,27 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
return ret;
for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) {
- type = (zpos == MIXER_DEFAULT_WIN) ? DRM_PLANE_TYPE_PRIMARY :
- DRM_PLANE_TYPE_OVERLAY;
+ enum drm_plane_type type;
+ const uint32_t *formats;
+ unsigned int fcount;
+
+ if (zpos < VP_DEFAULT_WIN) {
+ formats = mixer_formats;
+ fcount = ARRAY_SIZE(mixer_formats);
+ } else {
+ formats = vp_formats;
+ fcount = ARRAY_SIZE(vp_formats);
+ }
+
+ type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
- 1 << ctx->pipe, type, zpos);
+ 1 << ctx->pipe, type, formats, fcount,
+ zpos);
if (ret)
return ret;
}
- exynos_plane = &ctx->planes[MIXER_DEFAULT_WIN];
+ exynos_plane = &ctx->planes[DEFAULT_WIN];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_HDMI,
&mixer_crtc_ops, ctx);
@@ -1258,8 +1257,6 @@ static int mixer_probe(struct platform_device *pdev)
return -ENOMEM;
}
- mutex_init(&ctx->mixer_mutex);
-
if (dev->of_node) {
const struct of_device_id *match;
@@ -1280,18 +1277,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 +1289,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;
}
diff --git a/kernel/drivers/gpu/drm/exynos/exynos_mixer.h b/kernel/drivers/gpu/drm/exynos/exynos_mixer.h
deleted file mode 100644
index 3811e417f..000000000
--- a/kernel/drivers/gpu/drm/exynos/exynos_mixer.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _EXYNOS_MIXER_H_
-#define _EXYNOS_MIXER_H_
-
-/* This function returns 0 if the given timing is valid for the mixer */
-int mixer_check_mode(struct drm_display_mode *mode);
-
-#endif
diff --git a/kernel/drivers/gpu/drm/exynos/regs-hdmi.h b/kernel/drivers/gpu/drm/exynos/regs-hdmi.h
index 3f35ac6d8..8c891e59b 100644
--- a/kernel/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/kernel/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -72,7 +72,6 @@
#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
-#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180)
#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300)
#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0)
@@ -171,7 +170,7 @@
#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044)
#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050)
#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070)
-#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074)
+#define HDMI_V14_PHY_RSTOUT HDMI_CTRL_BASE(0x0074)
#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078)
#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C)
#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080)
@@ -277,16 +276,26 @@
#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318)
#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C)
-#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400)
-#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410)
-#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414)
-#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418)
-#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0420)
-#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0424)
-#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0428)
-#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430)
-#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434)
-#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438)
+#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180)
+#define HDMI_V13_ACR_MCTS0 HDMI_CORE_BASE(0x0184)
+#define HDMI_V13_ACR_MCTS1 HDMI_CORE_BASE(0x0188)
+#define HDMI_V13_ACR_MCTS2 HDMI_CORE_BASE(0x018C)
+#define HDMI_V13_ACR_CTS0 HDMI_CORE_BASE(0x0190)
+#define HDMI_V13_ACR_CTS1 HDMI_CORE_BASE(0x0194)
+#define HDMI_V13_ACR_CTS2 HDMI_CORE_BASE(0x0198)
+#define HDMI_V13_ACR_N0 HDMI_CORE_BASE(0x01A0)
+#define HDMI_V13_ACR_N1 HDMI_CORE_BASE(0x01A4)
+#define HDMI_V13_ACR_N2 HDMI_CORE_BASE(0x01A8)
+#define HDMI_V14_ACR_CON HDMI_CORE_BASE(0x0400)
+#define HDMI_V14_ACR_MCTS0 HDMI_CORE_BASE(0x0410)
+#define HDMI_V14_ACR_MCTS1 HDMI_CORE_BASE(0x0414)
+#define HDMI_V14_ACR_MCTS2 HDMI_CORE_BASE(0x0418)
+#define HDMI_V14_ACR_CTS0 HDMI_CORE_BASE(0x0420)
+#define HDMI_V14_ACR_CTS1 HDMI_CORE_BASE(0x0424)
+#define HDMI_V14_ACR_CTS2 HDMI_CORE_BASE(0x0428)
+#define HDMI_V14_ACR_N0 HDMI_CORE_BASE(0x0430)
+#define HDMI_V14_ACR_N1 HDMI_CORE_BASE(0x0434)
+#define HDMI_V14_ACR_N2 HDMI_CORE_BASE(0x0438)
/* Packet related registers */
#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500)