Commit 75c73861 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Summary:
. Add atomic feature support
  - Exynos also now supports atomic feature. However, it doesn't
    guarantee atomic operation yet, and is required for more cleanups.
    This time we just modified for Exynos drm driver to use atomic
    interfaces instead of legacy ones. Next time, we will enhance
    Exynos drm driver to support the atomic operation.
. Add iommu support
  - This is a patch series according to below Exynos iommu integration
    work with DT and dma-mapping subsystem,
    http://lwn.net/Articles/607626/
. Consolidate Exynos drm driver initialization.
  - This patch sereis resolves the issue that only the first compoments
    was bound when happened deferred probing for other pipelines and
    also makes the driver to be more cleanned up by moving the dispered
    codes for registering kms drivers to one place.
. Add new MIC, DECON drivers, and MIPI-DSI support for Exynos5433.
  - Add MIC(Mobile image compressor) driver. MIC is a new IP for Exynos5433
    and later, which is used to transfer frame data to MIPI-DSI controller
    compressing the data to reduce memory bandwidth.
  - Add DECON driver for Exynos5433 SoC. This IP is a dislay controller
    similar to Exynos7's one but this controller has much different registers
    from Exynos7's ones so this driver has been implemented separately.
    We will implement a helper modules for FIMD and two DECON controllers
    to remove duplicated codes later.
  - Add Exynos5433 SoC support to MIPI-DSI driver, and device tree
    relevant patches.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (50 commits)
  ARM: dts: rename the clock of MIPI DSI 'pll_clk' to 'sclk_mipi'
  drm/exynos: dsi: do not set TE GPIO direction by input
  drm/exynos: dsi: add support for MIC driver as a bridge
  drm/exynos: dsi: add support for Exynos5433
  drm/exynos: dsi: make use of array for clock access
  drm/exynos: dsi: make use of driver data for static values
  drm/exynos: dsi: add macros for register access
  drm/exynos: dsi: rename pll_clk to sclk_clk
  drm/exynos: mic: add MIC driver
  of: add helper for getting endpoint node of specific identifiers
  drm/exynos: add Exynos5433 decon driver
  drm/exynos: fix the input prompt of Exynos7 DECON
  drm/exynos: add drm_iommu_attach_device_if_possible()
  drm/exynos: Add the dependency for DRM_EXYNOS to DPI/DSI/DP
  drm/exynos: remove the dependency of DP driver for ARCH_EXYNOS
  drm/exynos: do not wait for vblank at atomic operation
  drm/exynos: Remove unused vma field of exynos_drm_gem_obj
  drm/exynos: fimd: fix page fault issue with iommu
  drm/exynos: iommu: improve a check for non-iommu dma_ops
  drm/exynos: iommu: detach from default dma-mapping domain on init
  ...
parents b7ddeee5 4f01e650
Device-Tree bindings for Samsung Exynos SoC mobile image compressor (MIC)
MIC (mobile image compressor) resides between decon and mipi dsi. Mipi dsi is
not capable to transfer high resoltuion frame data as decon can send. MIC
solves this problem by compressing the frame data by 1/2 before it is
transferred through mipi dsi. The compressed frame data must be uncompressed in
the panel PCB.
Required properties:
- compatible: value should be "samsung,exynos5433-mic".
- reg: physical base address and length of the MIC registers set and system
register of mic.
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
- clock-names: list of clock names sorted in the same order as the clocks
property. Must contain "pclk_mic0", "sclk_rgb_vclk_to_mic0".
- samsung,disp-syscon: the reference node for syscon for DISP block.
- ports: contains a port which is connected to decon node and dsi node.
address-cells and size-cells must 1 and 0, respectively.
- port: contains an endpoint node which is connected to the endpoint in the
decon node or dsi node. The reg value must be 0 and 1 respectively.
Example:
SoC specific DT entry:
mic: mic@13930000 {
compatible = "samsung,exynos5433-mic";
reg = <0x13930000 0x48>;
clocks = <&cmu_disp CLK_PCLK_MIC0>,
<&cmu_disp CLK_SCLK_RGB_VCLK_TO_MIC0>;
clock-names = "pclk_mic0", "sclk_rgb_vclk_to_mic0";
samsung,disp-syscon = <&syscon_disp>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mic_to_decon: endpoint {
remote-endpoint = <&decon_to_mic>;
};
};
port@1 {
reg = <1>;
mic_to_dsi: endpoint {
remote-endpoint = <&dsi_to_mic>;
};
};
};
};
Device-Tree bindings for Samsung Exynos SoC display controller (DECON)
DECON (Display and Enhancement Controller) is the Display Controller for the
Exynos series of SoCs which transfers the image data from a video memory
buffer to an external LCD interface.
Required properties:
- compatible: value should be "samsung,exynos5433-decon";
- reg: physical base address and length of the DECON registers set.
- interrupts: should contain a list of all DECON IP block interrupts in the
order: VSYNC, LCD_SYSTEM. The interrupt specifier format
depends on the interrupt controller used.
- interrupt-names: should contain the interrupt names: "vsync", "lcd_sys"
in the same order as they were listed in the interrupts
property.
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
- clock-names: list of clock names sorted in the same order as the clocks
property. Must contain "aclk_decon", "aclk_smmu_decon0x",
"aclk_xiu_decon0x", "pclk_smmu_decon0x", clk_decon_vclk",
"sclk_decon_eclk"
- ports: contains a port which is connected to mic node. address-cells and
size-cells must 1 and 0, respectively.
- port: contains an endpoint node which is connected to the endpoint in the mic
node. The reg value muset be 0.
- i80-if-timings: specify whether the panel which is connected to decon uses
i80 lcd interface or mipi video interface. This node contains
no timing information as that of fimd does. Because there is
no register in decon to specify i80 interface timing value,
it is not needed, but make it remain to use same kind of node
in fimd and exynos7 decon.
Example:
SoC specific DT entry:
decon: decon@13800000 {
compatible = "samsung,exynos5433-decon";
reg = <0x13800000 0x2104>;
clocks = <&cmu_disp CLK_ACLK_DECON>, <&cmu_disp CLK_ACLK_SMMU_DECON0X>,
<&cmu_disp CLK_ACLK_XIU_DECON0X>,
<&cmu_disp CLK_PCLK_SMMU_DECON0X>,
<&cmu_disp CLK_SCLK_DECON_VCLK>,
<&cmu_disp CLK_SCLK_DECON_ECLK>;
clock-names = "aclk_decon", "aclk_smmu_decon0x", "aclk_xiu_decon0x",
"pclk_smmu_decon0x", "sclk_decon_vclk", "sclk_decon_eclk";
interrupt-names = "vsync", "lcd_sys";
interrupts = <0 202 0>, <0 203 0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
decon_to_mic: endpoint {
remote-endpoint = <&mic_to_decon>;
};
};
};
};
Board specific DT entry:
&decon {
i80-if-timings {
};
};
......@@ -6,17 +6,19 @@ Required properties:
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
"samsung,exynos4415-mipi-dsi" /* for Exynos4415 SoC */
"samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */
"samsung,exynos5433-mipi-dsi" /* for Exynos5433 SoCs */
- reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt
- clocks: list of clock specifiers, must contain an entry for each required
entry in clock-names
- clock-names: should include "bus_clk"and "pll_clk" entries
- clock-names: should include "bus_clk"and "sclk_mipi" entries
the use of "pll_clk" is deprecated
- phys: list of phy specifiers, must contain an entry for each required
entry in phy-names
- phy-names: should include "dsim" entry
- vddcore-supply: MIPI DSIM Core voltage supply (e.g. 1.1V)
- vddio-supply: MIPI DSIM I/O and PLL voltage supply (e.g. 1.8V)
- samsung,pll-clock-frequency: specifies frequency of the "pll_clk" clock
- samsung,pll-clock-frequency: specifies frequency of the oscillator clock
- #address-cells, #size-cells: should be set respectively to <1> and <0>
according to DSI host bindings (see MIPI DSI bindings [1])
......@@ -30,10 +32,19 @@ Video interfaces:
Device node can contain video interface port nodes according to [2].
The following are properties specific to those nodes:
port node:
- reg: (required) can be 0 for input RGB/I80 port or 1 for DSI port;
port node inbound:
- reg: (required) must be 0.
port node outbound:
- reg: (required) must be 1.
endpoint node of DSI port (reg = 1):
endpoint node connected from mic node (reg = 0):
- remote-endpoint: specifies the endpoint in mic node. This node is required
for Exynos5433 mipi dsi. So mic can access to panel node
thoughout this dsi node.
endpoint node connected to panel node (reg = 1):
- remote-endpoint: specifies the endpoint in panel node. This node is
required in all kinds of exynos mipi dsi to represent
the connection between mipi dsi and panel.
- samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst
mode
- samsung,esc-clock-frequency: specifies DSI frequency in escape mode
......@@ -48,7 +59,7 @@ Example:
reg = <0x11C80000 0x10000>;
interrupts = <0 79 0>;
clocks = <&clock 286>, <&clock 143>;
clock-names = "bus_clk", "pll_clk";
clock-names = "bus_clk", "sclk_mipi";
phys = <&mipi_phy 1>;
phy-names = "dsim";
vddcore-supply = <&vusb_reg>;
......@@ -72,7 +83,15 @@ Example:
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
decon_to_mic: endpoint {
remote-endpoint = <&mic_to_decon>;
};
};
port@1 {
reg = <1>;
dsi_ep: endpoint {
reg = <0>;
samsung,burst-clock-frequency = <500000000>;
......
......@@ -167,7 +167,7 @@ dsi_0: dsi@11C80000 {
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock CLK_DSIM0>, <&clock CLK_SCLK_MIPI0>;
clock-names = "bus_clk", "pll_clk";
clock-names = "bus_clk", "sclk_mipi";
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
......
......@@ -32,6 +32,7 @@
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_atomic_helper.h"
/* Brightness scale on the Parade chip */
#define PS8622_MAX_BRIGHTNESS 0xff
......@@ -499,10 +500,13 @@ static void ps8622_connector_destroy(struct drm_connector *connector)
}
static const struct drm_connector_funcs ps8622_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ps8622_detect,
.destroy = ps8622_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 ps8622_attach(struct drm_bridge *bridge)
......
......@@ -26,6 +26,7 @@
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_atomic_helper.h"
#include "drm_edid.h"
#include "drmP.h"
......@@ -258,10 +259,13 @@ static void ptn3460_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs ptn3460_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = ptn3460_detect,
.destroy = ptn3460_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 ptn3460_bridge_attach(struct drm_bridge *bridge)
......
......@@ -24,16 +24,22 @@ config DRM_EXYNOS_FIMD
help
Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS7_DECON
bool "Exynos DRM DECON"
config DRM_EXYNOS5433_DECON
bool "Exynos5433 DRM DECON"
depends on DRM_EXYNOS
help
Choose this option if you want to use Exynos5433 DECON for DRM.
config DRM_EXYNOS7_DECON
bool "Exynos7 DRM DECON"
depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
help
Choose this option if you want to use Exynos DECON for DRM.
config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support"
depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
select DRM_PANEL
default n
help
......@@ -41,7 +47,7 @@ config DRM_EXYNOS_DPI
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON)
depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON)
select DRM_MIPI_DSI
select DRM_PANEL
default n
......@@ -50,7 +56,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
depends on DRM_EXYNOS && (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
select DRM_PANEL
help
......@@ -97,3 +103,9 @@ config DRM_EXYNOS_GSC
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM
help
Choose this option if you want to use Exynos GSC for DRM.
config DRM_EXYNOS_MIC
bool "Exynos DRM MIC"
depends on (DRM_EXYNOS && DRM_EXYNOS5433_DECON)
help
Choose this option if you want to use Exynos MIC for DRM.
......@@ -10,6 +10,7 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o \
exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON) += exynos5433_drm_decon.o
exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON) += exynos7_drm_decon.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o
......@@ -21,5 +22,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o
exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o
exynosdrm-$(CONFIG_DRM_EXYNOS_MIC) += exynos_drm_mic.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
/* drivers/gpu/drm/exynos5433_drm_decon.c
*
* Copyright (C) 2015 Samsung Electronics Co.Ltd
* Authors:
* Joonyoung Shim <jy0922.shim@samsung.com>
* Hyungwon Hwang <human.hwang@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundationr
*/
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <video/exynos5433_decon.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h"
#define WINDOWS_NR 3
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
struct decon_context {
struct device *dev;
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR];
void __iomem *addr;
struct clk *clks[6];
unsigned int default_win;
unsigned long irq_flags;
int pipe;
bool suspended;
#define BIT_CLKS_ENABLED 0
#define BIT_IRQS_ENABLED 1
unsigned long enabled;
bool i80_if;
atomic_t win_updated;
};
static const char * const decon_clks_name[] = {
"aclk_decon",
"aclk_smmu_decon0x",
"aclk_xiu_decon0x",
"pclk_smmu_decon0x",
"sclk_decon_vclk",
"sclk_decon_eclk",
};
static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
u32 val;
if (ctx->suspended)
return -EPERM;
if (test_and_set_bit(0, &ctx->irq_flags)) {
val = VIDINTCON0_INTEN;
if (ctx->i80_if)
val |= VIDINTCON0_FRAMEDONE;
else
val |= VIDINTCON0_INTFRMEN;
writel(val, ctx->addr + DECON_VIDINTCON0);
}
return 0;
}
static void decon_disable_vblank(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags))
writel(0, ctx->addr + DECON_VIDINTCON0);
}
static void decon_setup_trigger(struct decon_context *ctx)
{
u32 val = TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F |
TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN;
writel(val, ctx->addr + DECON_TRIGCON);
}
static void decon_commit(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.mode;
u32 val;
if (ctx->suspended)
return;
/* enable clock gate */
val = CMU_CLKGAGE_MODE_SFR_F | CMU_CLKGAGE_MODE_MEM_F;
writel(val, ctx->addr + DECON_CMU);
/* lcd on and use command if */
val = VIDOUT_LCD_ON;
if (ctx->i80_if)
val |= VIDOUT_COMMAND_IF;
else
val |= VIDOUT_RGB_IF;
writel(val, ctx->addr + DECON_VIDOUTCON0);
val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
VIDTCON2_HOZVAL(mode->hdisplay - 1);
writel(val, ctx->addr + DECON_VIDTCON2);
if (!ctx->i80_if) {
val = VIDTCON00_VBPD_F(
mode->crtc_vtotal - mode->crtc_vsync_end) |
VIDTCON00_VFPD_F(
mode->crtc_vsync_start - mode->crtc_vdisplay);
writel(val, ctx->addr + DECON_VIDTCON00);
val = VIDTCON01_VSPW_F(
mode->crtc_vsync_end - mode->crtc_vsync_start);
writel(val, ctx->addr + DECON_VIDTCON01);
val = VIDTCON10_HBPD_F(
mode->crtc_htotal - mode->crtc_hsync_end) |
VIDTCON10_HFPD_F(
mode->crtc_hsync_start - mode->crtc_hdisplay);
writel(val, ctx->addr + DECON_VIDTCON10);
val = VIDTCON11_HSPW_F(
mode->crtc_hsync_end - mode->crtc_hsync_start);
writel(val, ctx->addr + DECON_VIDTCON11);
}
decon_setup_trigger(ctx);
/* enable output and display signal */
val = VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(val, ctx->addr + DECON_VIDCON0);
}
#define COORDINATE_X(x) (((x) & 0xfff) << 12)
#define COORDINATE_Y(x) ((x) & 0xfff)
#define OFFSIZE(x) (((x) & 0x3fff) << 14)
#define PAGEWIDTH(x) ((x) & 0x3fff)
static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win)
{
struct exynos_drm_plane *plane = &ctx->planes[win];
unsigned long val;
val = readl(ctx->addr + DECON_WINCONx(win));
val &= ~WINCONx_BPPMODE_MASK;
switch (plane->pixel_format) {
case DRM_FORMAT_XRGB1555:
val |= WINCONx_BPPMODE_16BPP_I1555;
val |= WINCONx_HAWSWP_F;
val |= WINCONx_BURSTLEN_16WORD;
break;
case DRM_FORMAT_RGB565:
val |= WINCONx_BPPMODE_16BPP_565;
val |= WINCONx_HAWSWP_F;
val |= WINCONx_BURSTLEN_16WORD;
break;
case DRM_FORMAT_XRGB8888:
val |= WINCONx_BPPMODE_24BPP_888;
val |= WINCONx_WSWP_F;
val |= WINCONx_BURSTLEN_16WORD;
break;
case DRM_FORMAT_ARGB8888:
val |= WINCONx_BPPMODE_32BPP_A8888;
val |= WINCONx_WSWP_F | WINCONx_BLD_PIX_F | WINCONx_ALPHA_SEL_F;
val |= WINCONx_BURSTLEN_16WORD;
break;
default:
DRM_ERROR("Proper pixel format is not set\n");
return;
}
DRM_DEBUG_KMS("bpp = %u\n", plane->bpp);
/*
* In case of exynos, setting dma-burst to 16Word causes permanent
* tearing for very small buffers, e.g. cursor buffer. Burst Mode
* switching which is based on plane size is not recommended as
* plane size varies a lot towards the end of the screen and rapid
* movement causes unstable DMA which results into iommu crash/tear.
*/
if (plane->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
val &= ~WINCONx_BURSTLEN_MASK;
val |= WINCONx_BURSTLEN_8WORD;
}
writel(val, ctx->addr + DECON_WINCONx(win));
}
static void decon_shadow_protect_win(struct decon_context *ctx, int win,
bool protect)
{
u32 val;
val = readl(ctx->addr + DECON_SHADOWCON);
if (protect)
val |= SHADOWCON_Wx_PROTECT(win);
else
val &= ~SHADOWCON_Wx_PROTECT(win);
writel(val, ctx->addr + DECON_SHADOWCON);
}
static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
{
struct decon_context *ctx = crtc->ctx;
struct exynos_drm_plane *plane;
u32 val;
if (win < 0 || win >= WINDOWS_NR)
return;
plane = &ctx->planes[win];
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, win, true);
val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y);
writel(val, ctx->addr + DECON_VIDOSDxA(win));
val = COORDINATE_X(plane->crtc_x + plane->crtc_width - 1) |
COORDINATE_Y(plane->crtc_y + plane->crtc_height - 1);
writel(val, ctx->addr + DECON_VIDOSDxB(win));
val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
VIDOSD_Wx_ALPHA_B_F(0x0);
writel(val, ctx->addr + DECON_VIDOSDxC(win));
val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
VIDOSD_Wx_ALPHA_B_F(0x0);
writel(val, ctx->addr + DECON_VIDOSDxD(win));
writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win));
val = plane->dma_addr[0] + plane->pitch * plane->crtc_height;
writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
val = OFFSIZE(plane->pitch - plane->crtc_width * (plane->bpp >> 3))
| PAGEWIDTH(plane->crtc_width * (plane->bpp >> 3));
writel(val, ctx->addr + DECON_VIDW0xADD2(win));
decon_win_set_pixfmt(ctx, win);
/* window enable */
val = readl(ctx->addr + DECON_WINCONx(win));
val |= WINCONx_ENWIN_F;
writel(val, ctx->addr + DECON_WINCONx(win));
decon_shadow_protect_win(ctx, win, false);
/* standalone update */
val = readl(ctx->addr + DECON_UPDATE);
val |= STANDALONE_UPDATE_F;
writel(val, ctx->addr + DECON_UPDATE);
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
}
static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
{
struct decon_context *ctx = crtc->ctx;
struct exynos_drm_plane *plane;
u32 val;
if (win < 0 || win >= WINDOWS_NR)
return;
plane = &ctx->planes[win];
if (ctx->suspended)
return;
decon_shadow_protect_win(ctx, win, true);
/* window disable */
val = readl(ctx->addr + DECON_WINCONx(win));
val &= ~WINCONx_ENWIN_F;
writel(val, ctx->addr + DECON_WINCONx(win));
decon_shadow_protect_win(ctx, win, false);
/* standalone update */
val = readl(ctx->addr + DECON_UPDATE);
val |= STANDALONE_UPDATE_F;
writel(val, ctx->addr + DECON_UPDATE);
}
static void decon_swreset(struct decon_context *ctx)
{
unsigned int tries;
writel(0, ctx->addr + DECON_VIDCON0);
for (tries = 2000; tries; --tries) {
if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_STOP_STATUS)
break;
udelay(10);
}
WARN(tries == 0, "failed to disable DECON\n");
writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0);
for (tries = 2000; tries; --tries) {
if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_SWRESET)
break;
udelay(10);
}
WARN(tries == 0, "failed to software reset DECON\n");
}
static void decon_enable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int ret;
int i;
if (!ctx->suspended)
return;
ctx->suspended = false;
pm_runtime_get_sync(ctx->dev);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
ret = clk_prepare_enable(ctx->clks[i]);
if (ret < 0)
goto err;
}
set_bit(BIT_CLKS_ENABLED, &ctx->enabled);
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc);
return;
err:
while (--i >= 0)
clk_disable_unprepare(ctx->clks[i]);
ctx->suspended = true;
}
static void decon_disable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int i;
if (ctx->suspended)
return;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
for (i = 0; i < WINDOWS_NR; i++)
decon_win_disable(crtc, i);
decon_swreset(ctx);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
clk_disable_unprepare(ctx->clks[i]);
clear_bit(BIT_CLKS_ENABLED, &ctx->enabled);
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
}
void decon_te_irq_handler(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
u32 val;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
return;
if (atomic_add_unless(&ctx->win_updated, -1, 0)) {
/* trigger */
val = readl(ctx->addr + DECON_TRIGCON);
val |= TRIGCON_SWTRIGCMD;
writel(val, ctx->addr + DECON_TRIGCON);
}
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
}
static void decon_clear_channels(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int win, i, ret;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
ret = clk_prepare_enable(ctx->clks[i]);
if (ret < 0)
goto err;
}
for (win = 0; win < WINDOWS_NR; win++) {
/* shadow update disable */
val = readl(ctx->addr + DECON_SHADOWCON);
val |= SHADOWCON_Wx_PROTECT(win);
writel(val, ctx->addr + DECON_SHADOWCON);
/* window disable */
val = readl(ctx->addr + DECON_WINCONx(win));
val &= ~WINCONx_ENWIN_F;
writel(val, ctx->addr + DECON_WINCONx(win));
/* shadow update enable */
val = readl(ctx->addr + DECON_SHADOWCON);
val &= ~SHADOWCON_Wx_PROTECT(win);
writel(val, ctx->addr + DECON_SHADOWCON);
/* standalone update */
val = readl(ctx->addr + DECON_UPDATE);
val |= STANDALONE_UPDATE_F;
writel(val, ctx->addr + DECON_UPDATE);
}
/* TODO: wait for possible vsync */
msleep(50);
err:
while (--i >= 0)
clk_disable_unprepare(ctx->clks[i]);
}
static struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable = decon_enable,
.disable = decon_disable,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
.commit = decon_commit,
.win_commit = decon_win_commit,
.win_disable = decon_win_disable,
.te_handler = decon_te_irq_handler,
.clear_channels = decon_clear_channels,
};
static int decon_bind(struct device *dev, struct device *master, void *data)
{
struct decon_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
type = (zpos == ctx->default_win) ? DRM_PLANE_TYPE_PRIMARY :
DRM_PLANE_TYPE_OVERLAY;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, zpos);
if (ret)
return ret;
}
exynos_plane = &ctx->planes[ctx->default_win];
ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD,
&decon_crtc_ops, ctx);
if (IS_ERR(ctx->crtc)) {
ret = PTR_ERR(ctx->crtc);
goto err;
}
ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev);
if (ret)
goto err;
return ret;
err:
priv->pipe--;
return ret;
}
static void decon_unbind(struct device *dev, struct device *master, void *data)
{
struct decon_context *ctx = dev_get_drvdata(dev);
decon_disable(ctx->crtc);
/* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
}
static const struct component_ops decon_component_ops = {
.bind = decon_bind,
.unbind = decon_unbind,
};
static irqreturn_t decon_vsync_irq_handler(int irq, void *dev_id)
{
struct decon_context *ctx = dev_id;
u32 val;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
goto out;
val = readl(ctx->addr + DECON_VIDINTCON1);
if (val & VIDINTCON1_INTFRMPEND) {
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
/* clear */
writel(VIDINTCON1_INTFRMPEND, ctx->addr + DECON_VIDINTCON1);
}
out:
return IRQ_HANDLED;
}
static irqreturn_t decon_lcd_sys_irq_handler(int irq, void *dev_id)
{
struct decon_context *ctx = dev_id;
u32 val;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->enabled))
goto out;
val = readl(ctx->addr + DECON_VIDINTCON1);
if (val & VIDINTCON1_INTFRMDONEPEND) {
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
/* clear */
writel(VIDINTCON1_INTFRMDONEPEND,
ctx->addr + DECON_VIDINTCON1);
}
out:
return IRQ_HANDLED;
}
static int exynos5433_decon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct decon_context *ctx;
struct resource *res;
int ret;
int i;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->default_win = 0;
ctx->suspended = true;
ctx->dev = dev;
if (of_get_child_by_name(dev->of_node, "i80-if-timings"))
ctx->i80_if = true;
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
struct clk *clk;
clk = devm_clk_get(ctx->dev, decon_clks_name[i]);
if (IS_ERR(clk))
return PTR_ERR(clk);
ctx->clks[i] = clk;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "cannot find IO resource\n");
return -ENXIO;
}
ctx->addr = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->addr)) {
dev_err(dev, "ioremap failed\n");
return PTR_ERR(ctx->addr);
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
ctx->i80_if ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "cannot find IRQ resource\n");
return -ENXIO;
}
ret = devm_request_irq(dev, res->start, ctx->i80_if ?
decon_lcd_sys_irq_handler : decon_vsync_irq_handler, 0,
"drm_decon", ctx);
if (ret < 0) {
dev_err(dev, "lcd_sys irq request failed\n");
return ret;
}
platform_set_drvdata(pdev, ctx);
pm_runtime_enable(dev);
ret = component_add(dev, &decon_component_ops);
if (ret)
goto err_disable_pm_runtime;
return 0;
err_disable_pm_runtime:
pm_runtime_disable(dev);
return ret;
}
static int exynos5433_decon_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &decon_component_ops);
return 0;
}
static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
{ .compatible = "samsung,exynos5433-decon" },
{},
};
MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
struct platform_driver exynos5433_decon_driver = {
.probe = exynos5433_decon_probe,
.remove = exynos5433_decon_remove,
.driver = {
.name = "exynos5433-decon",
.of_match_table = exynos5433_decon_driver_dt_match,
},
};
......@@ -89,8 +89,9 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
static void decon_clear_channel(struct decon_context *ctx)
static void decon_clear_channels(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
......@@ -120,27 +121,16 @@ static int decon_ctx_initialize(struct decon_context *ctx,
struct drm_device *drm_dev)
{
struct exynos_drm_private *priv = drm_dev->dev_private;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev)) {
int ret;
ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, ctx->dev);
if (ret)
priv->pipe--;
/*
* 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;
}
}
return 0;
}
static void decon_ctx_remove(struct decon_context *ctx)
......@@ -175,7 +165,7 @@ static bool decon_mode_fixup(struct exynos_drm_crtc *crtc,
static void decon_commit(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.mode;
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
u32 val, clkdiv;
if (ctx->suspended)
......@@ -395,7 +385,7 @@ static void decon_shadow_protect_win(struct decon_context *ctx,
static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
{
struct decon_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.mode;
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
struct exynos_drm_plane *plane;
int padding;
unsigned long val, alpha;
......@@ -410,11 +400,8 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
/* If suspended, enable this on resume */
if (ctx->suspended) {
plane->resume = true;
if (ctx->suspended)
return;
}
/*
* SHADOWCON/PRTCON register is used for enabling timing.
......@@ -506,8 +493,6 @@ static void decon_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
val = readl(ctx->regs + DECON_UPDATE);
val |= DECON_UPDATE_STANDALONE_F;
writel(val, ctx->regs + DECON_UPDATE);
plane->enabled = true;
}
static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
......@@ -521,11 +506,8 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
if (ctx->suspended) {
/* do not resume this window*/
plane->resume = false;
if (ctx->suspended)
return;
}
/* protect windows */
decon_shadow_protect_win(ctx, win, true);
......@@ -541,49 +523,6 @@ static void decon_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
val = readl(ctx->regs + DECON_UPDATE);
val |= DECON_UPDATE_STANDALONE_F;
writel(val, ctx->regs + DECON_UPDATE);
plane->enabled = false;
}
static void decon_window_suspend(struct decon_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
plane->resume = plane->enabled;
if (plane->enabled)
decon_win_disable(ctx->crtc, i);
}
}
static void decon_window_resume(struct decon_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
plane->enabled = plane->resume;
plane->resume = false;
}
}
static void decon_apply(struct decon_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
if (plane->enabled)
decon_win_commit(ctx->crtc, i);
else
decon_win_disable(ctx->crtc, i);
}
decon_commit(ctx->crtc);
}
static void decon_init(struct decon_context *ctx)
......@@ -603,12 +542,13 @@ static void decon_init(struct decon_context *ctx)
writel(VIDCON1_VCLK_HOLD, ctx->regs + VIDCON1(0));
}
static int decon_poweron(struct decon_context *ctx)
static void decon_enable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
return 0;
return;
ctx->suspended = false;
......@@ -617,68 +557,51 @@ static int decon_poweron(struct decon_context *ctx)
ret = clk_prepare_enable(ctx->pclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
goto pclk_err;
return;
}
ret = clk_prepare_enable(ctx->aclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
goto aclk_err;
return;
}
ret = clk_prepare_enable(ctx->eclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
goto eclk_err;
return;
}
ret = clk_prepare_enable(ctx->vclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
goto vclk_err;
return;
}
decon_init(ctx);
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags)) {
ret = decon_enable_vblank(ctx->crtc);
if (ret) {
DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
goto err;
}
}
decon_window_resume(ctx);
if (test_and_clear_bit(0, &ctx->irq_flags))
decon_enable_vblank(ctx->crtc);
decon_apply(ctx);
return 0;
err:
clk_disable_unprepare(ctx->vclk);
vclk_err:
clk_disable_unprepare(ctx->eclk);
eclk_err:
clk_disable_unprepare(ctx->aclk);
aclk_err:
clk_disable_unprepare(ctx->pclk);
pclk_err:
ctx->suspended = true;
return ret;
decon_commit(ctx->crtc);
}
static int decon_poweroff(struct decon_context *ctx)
static void decon_disable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int i;
if (ctx->suspended)
return 0;
return;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
decon_window_suspend(ctx);
for (i = 0; i < WINDOWS_NR; i++)
decon_win_disable(crtc, i);
clk_disable_unprepare(ctx->vclk);
clk_disable_unprepare(ctx->eclk);
......@@ -688,30 +611,11 @@ static int decon_poweroff(struct decon_context *ctx)
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
return 0;
}
static void decon_dpms(struct exynos_drm_crtc *crtc, int mode)
{
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
decon_poweron(crtc->ctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
decon_poweroff(crtc->ctx);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
}
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.dpms = decon_dpms,
.enable = decon_enable,
.disable = decon_disable,
.mode_fixup = decon_mode_fixup,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
......@@ -719,6 +623,7 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.wait_for_vblank = decon_wait_for_vblank,
.win_commit = decon_win_commit,
.win_disable = decon_win_disable,
.clear_channels = decon_clear_channels,
};
......@@ -796,7 +701,7 @@ static void decon_unbind(struct device *dev, struct device *master,
{
struct decon_context *ctx = dev_get_drvdata(dev);
decon_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
decon_disable(ctx->crtc);
if (ctx->display)
exynos_dpi_remove(ctx->display);
......@@ -824,11 +729,6 @@ static int decon_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
EXYNOS_DISPLAY_TYPE_LCD);
if (ret)
return ret;
ctx->dev = dev;
ctx->suspended = true;
......@@ -838,10 +738,8 @@ static int decon_probe(struct platform_device *pdev)
of_node_put(i80_if_timings);
ctx->regs = of_iomap(dev->of_node, 0);
if (!ctx->regs) {
ret = -ENOMEM;
goto err_del_component;
}
if (!ctx->regs)
return -ENOMEM;
ctx->pclk = devm_clk_get(dev, "pclk_decon0");
if (IS_ERR(ctx->pclk)) {
......@@ -911,8 +809,6 @@ static int decon_probe(struct platform_device *pdev)
err_iounmap:
iounmap(ctx->regs);
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
......@@ -925,7 +821,6 @@ static int decon_remove(struct platform_device *pdev)
iounmap(ctx->regs);
component_del(&pdev->dev, &decon_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
......
......@@ -28,6 +28,7 @@
#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 "exynos_dp_core.h"
......@@ -952,10 +953,13 @@ static void exynos_dp_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = exynos_dp_detect,
.destroy = exynos_dp_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dp_get_modes(struct drm_connector *connector)
......@@ -1328,7 +1332,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *panel_node, *bridge_node, *endpoint;
struct exynos_dp_device *dp;
int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
......@@ -1339,11 +1342,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
dp->display.ops = &exynos_dp_display_ops;
platform_set_drvdata(pdev, dp);
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
dp->display.type);
if (ret)
return ret;
panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) {
dp->panel = of_drm_find_panel(panel_node);
......@@ -1364,18 +1362,12 @@ static int exynos_dp_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
exynos_drm_component_del(&pdev->dev,
EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
return component_add(&pdev->dev, &exynos_dp_ops);
}
static int exynos_dp_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_dp_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
......
......@@ -14,57 +14,47 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_plane.h"
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
if (exynos_crtc->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
if (exynos_crtc->enabled)
return;
}
if (mode > DRM_MODE_DPMS_ON) {
/* wait for the completion of page flip. */
if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
(exynos_crtc->event == NULL), HZ/20))
exynos_crtc->event = NULL;
drm_crtc_vblank_off(crtc);
}
if (exynos_crtc->ops->dpms)
exynos_crtc->ops->dpms(exynos_crtc, mode);
if (exynos_crtc->ops->enable)
exynos_crtc->ops->enable(exynos_crtc);
exynos_crtc->dpms = mode;
exynos_crtc->enabled = true;
if (mode == DRM_MODE_DPMS_ON)
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);
if (!exynos_crtc->enabled)
return;
/* 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->win_commit)
exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
if (exynos_crtc->ops->disable)
exynos_crtc->ops->disable(exynos_crtc);
if (exynos_crtc->ops->commit)
exynos_crtc->ops->commit(exynos_crtc);
exynos_crtc->enabled = false;
}
static bool
......@@ -81,145 +71,38 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
static int
exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
static void
exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct drm_framebuffer *fb = crtc->primary->fb;
unsigned int crtc_w;
unsigned int crtc_h;
int ret;
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
ret = exynos_check_plane(crtc->primary, fb);
if (ret < 0)
return ret;
crtc_w = fb->width - x;
crtc_h = fb->height - y;
exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
crtc_w, crtc_h, x, y, crtc_w, crtc_h);
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
return 0;
if (exynos_crtc->ops->commit)
exynos_crtc->ops->commit(exynos_crtc);
}
static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
static void exynos_crtc_atomic_begin(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_framebuffer *fb = crtc->primary->fb;
unsigned int crtc_w;
unsigned int crtc_h;
/* when framebuffer changing is requested, crtc's dpms should be on */
if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed framebuffer changing request.\n");
return -EPERM;
if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
exynos_crtc->event = crtc->state->event;
}
crtc_w = fb->width - x;
crtc_h = fb->height - y;
return exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
crtc_w, crtc_h, x, y, crtc_w, crtc_h);
}
static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
static void exynos_crtc_atomic_flush(struct drm_crtc *crtc)
{
struct drm_plane *plane;
int ret;
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
if (plane->crtc != crtc)
continue;
ret = plane->funcs->disable_plane(plane);
if (ret)
DRM_ERROR("Failed to disable plane %d\n", ret);
}
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.dpms = exynos_drm_crtc_dpms,
.prepare = exynos_drm_crtc_prepare,
.commit = exynos_drm_crtc_commit,
.mode_fixup = exynos_drm_crtc_mode_fixup,
.mode_set = exynos_drm_crtc_mode_set,
.mode_set_base = exynos_drm_crtc_mode_set_base,
.enable = exynos_drm_crtc_enable,
.disable = exynos_drm_crtc_disable,
.mode_fixup = exynos_drm_crtc_mode_fixup,
.mode_set_nofb = exynos_drm_crtc_mode_set_nofb,
.atomic_begin = exynos_crtc_atomic_begin,
.atomic_flush = exynos_crtc_atomic_flush,
};
static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t page_flip_flags)
{
struct drm_device *dev = crtc->dev;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_framebuffer *old_fb = crtc->primary->fb;
unsigned int crtc_w, crtc_h;
int ret;
/* when the page flip is requested, crtc's dpms should be on */
if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
DRM_ERROR("failed page flip request.\n");
return -EINVAL;
}
if (!event)
return -EINVAL;
spin_lock_irq(&dev->event_lock);
if (exynos_crtc->event) {
ret = -EBUSY;
goto out;
}
ret = drm_vblank_get(dev, exynos_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
goto out;
}
exynos_crtc->event = event;
spin_unlock_irq(&dev->event_lock);
/*
* the pipe from user always is 0 so we can set pipe number
* of current owner to event.
*/
event->pipe = exynos_crtc->pipe;
crtc->primary->fb = fb;
crtc_w = fb->width - crtc->x;
crtc_h = fb->height - crtc->y;
ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
crtc_w, crtc_h, crtc->x, crtc->y,
crtc_w, crtc_h);
if (ret) {
crtc->primary->fb = old_fb;
spin_lock_irq(&dev->event_lock);
exynos_crtc->event = NULL;
drm_vblank_put(dev, exynos_crtc->pipe);
spin_unlock_irq(&dev->event_lock);
return ret;
}
return 0;
out:
spin_unlock_irq(&dev->event_lock);
return ret;
}
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
......@@ -232,9 +115,12 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.page_flip = exynos_drm_crtc_page_flip,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = exynos_drm_crtc_destroy,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
......@@ -255,7 +141,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
init_waitqueue_head(&exynos_crtc->pending_flip_queue);
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->pipe = pipe;
exynos_crtc->type = type;
exynos_crtc->ops = ops;
......@@ -286,7 +171,7 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
if (!exynos_crtc->enabled)
return -EPERM;
if (exynos_crtc->ops->enable_vblank)
......@@ -301,7 +186,7 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[pipe]);
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
if (!exynos_crtc->enabled)
return;
if (exynos_crtc->ops->disable_vblank)
......
......@@ -13,6 +13,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
#include <drm/drm_atomic_helper.h>
#include <linux/regulator/consumer.h>
......@@ -59,10 +60,13 @@ static void exynos_dpi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dpi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.detect = exynos_dpi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = exynos_dpi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dpi_get_modes(struct drm_connector *connector)
......@@ -309,33 +313,19 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
ctx->dev = dev;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_drm_component_add(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR,
ctx->display.type);
if (ret)
return ERR_PTR(ret);
ret = exynos_dpi_parse_dt(ctx);
if (ret < 0) {
devm_kfree(dev, ctx);
goto err_del_component;
return NULL;
}
if (ctx->panel_node) {
ctx->panel = of_drm_find_panel(ctx->panel_node);
if (!ctx->panel) {
exynos_drm_component_del(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR);
if (!ctx->panel)
return ERR_PTR(-EPROBE_DEFER);
}
}
return &ctx->display;
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return NULL;
}
int exynos_dpi_remove(struct exynos_drm_display *display)
......@@ -347,7 +337,5 @@ int exynos_dpi_remove(struct exynos_drm_display *display)
if (ctx->panel)
drm_panel_detach(ctx->panel);
exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
......@@ -38,19 +38,6 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
static struct platform_device *exynos_drm_pdev;
static DEFINE_MUTEX(drm_component_lock);
static LIST_HEAD(drm_component_list);
struct component_dev {
struct list_head list;
struct device *crtc_dev;
struct device *conn_dev;
enum exynos_drm_output_type out_type;
unsigned int dev_type_flag;
};
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
......@@ -98,6 +85,8 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_cleanup_vblank;
drm_mode_config_reset(dev);
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
......@@ -348,190 +337,29 @@ static const struct dev_pm_ops exynos_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
};
int exynos_drm_component_add(struct device *dev,
enum exynos_drm_device_type dev_type,
enum exynos_drm_output_type out_type)
{
struct component_dev *cdev;
if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
DRM_ERROR("invalid device type.\n");
return -EINVAL;
}
mutex_lock(&drm_component_lock);
/*
* Make sure to check if there is a component which has two device
* objects, for connector and for encoder/connector.
* It should make sure that crtc and encoder/connector drivers are
* ready before exynos drm core binds them.
*/
list_for_each_entry(cdev, &drm_component_list, list) {
if (cdev->out_type == out_type) {
/*
* If crtc and encoder/connector device objects are
* added already just return.
*/
if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
EXYNOS_DEVICE_TYPE_CONNECTOR)) {
mutex_unlock(&drm_component_lock);
return 0;
}
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
cdev->crtc_dev = dev;
cdev->dev_type_flag |= dev_type;
}
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
cdev->conn_dev = dev;
cdev->dev_type_flag |= dev_type;
}
mutex_unlock(&drm_component_lock);
return 0;
}
}
mutex_unlock(&drm_component_lock);
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENOMEM;
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
cdev->crtc_dev = dev;
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
cdev->conn_dev = dev;
cdev->out_type = out_type;
cdev->dev_type_flag = dev_type;
mutex_lock(&drm_component_lock);
list_add_tail(&cdev->list, &drm_component_list);
mutex_unlock(&drm_component_lock);
return 0;
}
void exynos_drm_component_del(struct device *dev,
enum exynos_drm_device_type dev_type)
{
struct component_dev *cdev, *next;
mutex_lock(&drm_component_lock);
list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
if (cdev->crtc_dev == dev) {
cdev->crtc_dev = NULL;
cdev->dev_type_flag &= ~dev_type;
}
}
if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
if (cdev->conn_dev == dev) {
cdev->conn_dev = NULL;
cdev->dev_type_flag &= ~dev_type;
}
}
/*
* Release cdev object only in case that both of crtc and
* encoder/connector device objects are NULL.
*/
if (!cdev->crtc_dev && !cdev->conn_dev) {
list_del(&cdev->list);
kfree(cdev);
}
}
mutex_unlock(&drm_component_lock);
}
static int compare_dev(struct device *dev, void *data)
{
return dev == (struct device *)data;
}
/* forward declaration */
static struct platform_driver exynos_drm_platform_driver;
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.
/*
* Connector drivers should not be placed before associated crtc drivers,
* because connector requires pipe number of its crtc during initialization.
*/
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,
};
static struct platform_driver *const exynos_drm_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_VIDI
&vidi_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
&fimd_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS5433_DECON
&exynos5433_decon_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS7_DECON
&decon_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS_MIC
&mic_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
&dp_driver,
#endif
......@@ -560,6 +388,59 @@ static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_IPP
&ipp_driver,
#endif
&exynos_drm_platform_driver,
};
static struct platform_driver *const exynos_drm_drv_with_simple_dev[] = {
#ifdef CONFIG_DRM_EXYNOS_VIDI
&vidi_driver,
#endif
#ifdef CONFIG_DRM_EXYNOS_IPP
&ipp_driver,
#endif
&exynos_drm_platform_driver,
};
#define PDEV_COUNT ARRAY_SIZE(exynos_drm_drv_with_simple_dev)
static int compare_dev(struct device *dev, void *data)
{
return dev == (struct device *)data;
}
static struct component_match *exynos_drm_match_add(struct device *dev)
{
struct component_match *match = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
struct device_driver *drv = &exynos_drm_kms_drivers[i]->driver;
struct device *p = NULL, *d;
while ((d = bus_find_device(&platform_bus_type, p, drv,
(void *)platform_bus_type.match))) {
put_device(p);
component_match_add(dev, &match, compare_dev, d);
p = d;
}
put_device(p);
}
return match ?: ERR_PTR(-ENODEV);
}
static int exynos_drm_bind(struct device *dev)
{
return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
}
static void exynos_drm_unbind(struct device *dev)
{
drm_put_dev(dev_get_drvdata(dev));
}
static const struct component_master_ops exynos_drm_ops = {
.bind = exynos_drm_bind,
.unbind = exynos_drm_unbind,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
......@@ -570,9 +451,8 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
match = exynos_drm_match_add(&pdev->dev);
if (IS_ERR(match)) {
if (IS_ERR(match))
return PTR_ERR(match);
}
return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
match);
......@@ -584,13 +464,6 @@ static int exynos_drm_platform_remove(struct platform_device *pdev)
return 0;
}
static const char * const strings[] = {
"samsung,exynos3",
"samsung,exynos4",
"samsung,exynos5",
"samsung,exynos7",
};
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
......@@ -600,101 +473,125 @@ static struct platform_driver exynos_drm_platform_driver = {
},
};
static int exynos_drm_init(void)
static struct platform_device *exynos_drm_pdevs[PDEV_COUNT];
static void exynos_drm_unregister_devices(void)
{
bool is_exynos = false;
int ret, i, j;
int i = PDEV_COUNT;
/*
* Register device object only in case of Exynos SoC.
*
* Below codes resolves temporarily infinite loop issue incurred
* by Exynos drm driver when using multi-platform kernel.
* So these codes will be replaced with more generic way later.
*/
for (i = 0; i < ARRAY_SIZE(strings); i++) {
if (of_machine_is_compatible(strings[i])) {
is_exynos = true;
break;
}
while (--i >= 0) {
platform_device_unregister(exynos_drm_pdevs[i]);
exynos_drm_pdevs[i] = NULL;
}
}
if (!is_exynos)
return -ENODEV;
static int exynos_drm_register_devices(void)
{
int i;
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
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);
if (IS_ERR(exynos_drm_pdev))
return PTR_ERR(exynos_drm_pdev);
ret = exynos_drm_probe_vidi();
if (ret < 0)
goto err_unregister_pd;
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;
if (!IS_ERR(pdev)) {
exynos_drm_pdevs[i] = pdev;
continue;
}
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;
while (--i >= 0) {
platform_device_unregister(exynos_drm_pdevs[i]);
exynos_drm_pdevs[i] = NULL;
}
#ifdef CONFIG_DRM_EXYNOS_IPP
ret = exynos_platform_device_ipp_register();
if (ret < 0)
goto err_unregister_non_kms_drivers;
#endif
ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret)
goto err_unregister_resources;
return PTR_ERR(pdev);
}
return 0;
}
err_unregister_resources:
#ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
#endif
static void exynos_drm_unregister_drivers(struct platform_driver * const *drv,
int count)
{
while (--count >= 0)
platform_driver_unregister(drv[count]);
}
err_unregister_non_kms_drivers:
while (--j >= 0)
platform_driver_unregister(exynos_drm_non_kms_drivers[j]);
static int exynos_drm_register_drivers(struct platform_driver * const *drv,
int count)
{
int i, ret;
for (i = 0; i < count; ++i) {
ret = platform_driver_register(drv[i]);
if (!ret)
continue;
err_unregister_kms_drivers:
while (--i >= 0)
platform_driver_unregister(exynos_drm_kms_drivers[i]);
platform_driver_unregister(drv[i]);
exynos_drm_remove_vidi();
return ret;
}
err_unregister_pd:
platform_device_unregister(exynos_drm_pdev);
return 0;
}
return ret;
static inline int exynos_drm_register_kms_drivers(void)
{
return exynos_drm_register_drivers(exynos_drm_kms_drivers,
ARRAY_SIZE(exynos_drm_kms_drivers));
}
static void exynos_drm_exit(void)
static inline int exynos_drm_register_non_kms_drivers(void)
{
int i;
return exynos_drm_register_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 inline void exynos_drm_unregister_kms_drivers(void)
{
exynos_drm_unregister_drivers(exynos_drm_kms_drivers,
ARRAY_SIZE(exynos_drm_kms_drivers));
}
static inline void exynos_drm_unregister_non_kms_drivers(void)
{
exynos_drm_unregister_drivers(exynos_drm_non_kms_drivers,
ARRAY_SIZE(exynos_drm_non_kms_drivers));
}
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);
......
......@@ -25,13 +25,6 @@
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc, base)
#define to_exynos_plane(x) container_of(x, struct exynos_drm_plane, base)
/* This enumerates device type. */
enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE,
EXYNOS_DEVICE_TYPE_CRTC,
EXYNOS_DEVICE_TYPE_CONNECTOR,
};
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
......@@ -71,8 +64,6 @@ enum exynos_drm_output_type {
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @zpos: order of overlay layer(z position).
* @enabled: enabled or not.
* @resume: to resume or not.
*
* this structure is common to exynos SoC and its contents would be copied
* to hardware specific overlay info.
......@@ -101,9 +92,6 @@ struct exynos_drm_plane {
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos;
bool enabled:1;
bool resume:1;
};
/*
......@@ -157,7 +145,8 @@ struct exynos_drm_display {
/*
* Exynos drm crtc ops
*
* @dpms: control device power.
* @enable: enable the device
* @disable: disable the device
* @mode_fixup: fix mode data before applying it
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
......@@ -175,7 +164,8 @@ struct exynos_drm_display {
*/
struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
void (*dpms)(struct exynos_drm_crtc *crtc, int mode);
void (*enable)(struct exynos_drm_crtc *crtc);
void (*disable)(struct exynos_drm_crtc *crtc);
bool (*mode_fixup)(struct exynos_drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
......@@ -187,6 +177,7 @@ struct exynos_drm_crtc_ops {
void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos);
void (*te_handler)(struct exynos_drm_crtc *crtc);
void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
void (*clear_channels)(struct exynos_drm_crtc *crtc);
};
/*
......@@ -201,7 +192,7 @@ struct exynos_drm_crtc_ops {
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occurred through
* this pipe value.
* @dpms: store the crtc dpms value
* @enabled: if the crtc is enabled or not
* @event: vblank event that is currently queued for flip
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the crtc's implementation specific context
......@@ -210,7 +201,7 @@ struct exynos_drm_crtc {
struct drm_crtc base;
enum exynos_drm_output_type type;
unsigned int pipe;
unsigned int dpms;
bool enabled;
wait_queue_head_t pending_flip_queue;
struct drm_pending_vblank_event *event;
const struct exynos_drm_crtc_ops *ops;
......@@ -293,15 +284,6 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
#ifdef CONFIG_DRM_EXYNOS_IPP
int exynos_platform_device_ipp_register(void);
void exynos_platform_device_ipp_unregister(void);
#else
static inline int exynos_platform_device_ipp_register(void) { return 0; }
static inline void exynos_platform_device_ipp_unregister(void) {}
#endif
#ifdef CONFIG_DRM_EXYNOS_DPI
struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct exynos_drm_display *display);
......@@ -314,26 +296,12 @@ static inline int exynos_dpi_remove(struct exynos_drm_display *display)
}
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
int exynos_drm_probe_vidi(void);
void exynos_drm_remove_vidi(void);
#else
static inline int exynos_drm_probe_vidi(void) { return 0; }
static inline void exynos_drm_remove_vidi(void) {}
#endif
/* This function creates a encoder and a connector, and initializes them. */
int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_display *display);
int exynos_drm_component_add(struct device *dev,
enum exynos_drm_device_type dev_type,
enum exynos_drm_output_type out_type);
void exynos_drm_component_del(struct device *dev,
enum exynos_drm_device_type dev_type);
extern struct platform_driver fimd_driver;
extern struct platform_driver exynos5433_decon_driver;
extern struct platform_driver decon_driver;
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
......@@ -346,4 +314,5 @@ extern struct platform_driver fimc_driver;
extern struct platform_driver rotator_driver;
extern struct platform_driver gsc_driver;
extern struct platform_driver ipp_driver;
extern struct platform_driver mic_driver;
#endif
......@@ -14,12 +14,14 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_atomic_helper.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/irq.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
......@@ -33,38 +35,6 @@
/* returns true iff both arguments logically differs */
#define NEQV(a, b) (!(a) ^ !(b))
#define DSIM_STATUS_REG 0x0 /* Status register */
#define DSIM_SWRST_REG 0x4 /* Software reset register */
#define DSIM_CLKCTRL_REG 0x8 /* Clock control register */
#define DSIM_TIMEOUT_REG 0xc /* Time out register */
#define DSIM_CONFIG_REG 0x10 /* Configuration register */
#define DSIM_ESCMODE_REG 0x14 /* Escape mode register */
/* Main display image resolution register */
#define DSIM_MDRESOL_REG 0x18
#define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */
#define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */
#define DSIM_MSYNC_REG 0x24 /* Main display sync area register */
/* Sub display image resolution register */
#define DSIM_SDRESOL_REG 0x28
#define DSIM_INTSRC_REG 0x2c /* Interrupt source register */
#define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */
#define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */
#define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */
#define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */
#define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */
#define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */
/* FIFO memory AC characteristic register */
#define DSIM_PLLCTRL_REG 0x4c /* PLL control register */
#define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */
#define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */
#define DSIM_PHYCTRL_REG 0x5c
#define DSIM_PHYTIMING_REG 0x64
#define DSIM_PHYTIMING1_REG 0x68
#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */
#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
#define DSIM_STOP_STATE_CLK (1 << 8)
......@@ -128,8 +98,8 @@
/* DSIM_MDRESOL */
#define DSIM_MAIN_STAND_BY (1 << 31)
#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16)
#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0)
#define DSIM_MAIN_VRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 16)
#define DSIM_MAIN_HRESOL(x, num_bits) (((x) & ((1 << (num_bits)) - 1)) << 0)
/* DSIM_MVPORCH */
#define DSIM_CMD_ALLOW(x) ((x) << 28)
......@@ -163,6 +133,7 @@
#define DSIM_INT_PLL_STABLE (1 << 31)
#define DSIM_INT_SW_RST_RELEASE (1 << 30)
#define DSIM_INT_SFR_FIFO_EMPTY (1 << 29)
#define DSIM_INT_SFR_HDR_FIFO_EMPTY (1 << 28)
#define DSIM_INT_BTA (1 << 25)
#define DSIM_INT_FRAME_DONE (1 << 24)
#define DSIM_INT_RX_TIMEOUT (1 << 21)
......@@ -211,6 +182,8 @@
/* DSIM_PHYCTRL */
#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0)
#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP (1 << 30)
#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP (1 << 14)
/* DSIM_PHYTIMING */
#define DSIM_PHYTIMING_LPX(x) ((x) << 8)
......@@ -234,6 +207,18 @@
#define DSI_XFER_TIMEOUT_MS 100
#define DSI_RX_FIFO_EMPTY 0x30800002
#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
#define REG_ADDR(dsi, reg_idx) ((dsi)->reg_base + \
dsi->driver_data->reg_ofs[(reg_idx)])
#define DSI_WRITE(dsi, reg_idx, val) writel((val), \
REG_ADDR((dsi), (reg_idx)))
#define DSI_READ(dsi, reg_idx) readl(REG_ADDR((dsi), (reg_idx)))
static char *clk_names[5] = { "bus_clk", "sclk_mipi",
"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
"sclk_rgb_vclk_to_dsim0" };
enum exynos_dsi_transfer_type {
EXYNOS_DSI_TX,
EXYNOS_DSI_RX,
......@@ -259,12 +244,18 @@ struct exynos_dsi_transfer {
#define DSIM_STATE_ENABLED BIT(0)
#define DSIM_STATE_INITIALIZED BIT(1)
#define DSIM_STATE_CMD_LPM BIT(2)
#define DSIM_STATE_VIDOUT_AVAILABLE BIT(3)
struct exynos_dsi_driver_data {
unsigned int *reg_ofs;
unsigned int plltmr_reg;
unsigned int has_freqband:1;
unsigned int has_clklane_stop:1;
unsigned int num_clks;
unsigned int max_freq;
unsigned int wait_for_reset;
unsigned int num_bits_resol;
unsigned int *reg_values;
};
struct exynos_dsi {
......@@ -277,8 +268,7 @@ struct exynos_dsi {
void __iomem *reg_base;
struct phy *phy;
struct clk *pll_clk;
struct clk *bus_clk;
struct clk **clks;
struct regulator_bulk_data supplies[2];
int irq;
int te_gpio;
......@@ -299,6 +289,7 @@ struct exynos_dsi {
struct list_head transfer_list;
struct exynos_dsi_driver_data *driver_data;
struct device_node *bridge_node;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
......@@ -309,25 +300,186 @@ static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d)
return container_of(d, struct exynos_dsi, display);
}
enum reg_idx {
DSIM_STATUS_REG, /* Status register */
DSIM_SWRST_REG, /* Software reset register */
DSIM_CLKCTRL_REG, /* Clock control register */
DSIM_TIMEOUT_REG, /* Time out register */
DSIM_CONFIG_REG, /* Configuration register */
DSIM_ESCMODE_REG, /* Escape mode register */
DSIM_MDRESOL_REG,
DSIM_MVPORCH_REG, /* Main display Vporch register */
DSIM_MHPORCH_REG, /* Main display Hporch register */
DSIM_MSYNC_REG, /* Main display sync area register */
DSIM_INTSRC_REG, /* Interrupt source register */
DSIM_INTMSK_REG, /* Interrupt mask register */
DSIM_PKTHDR_REG, /* Packet Header FIFO register */
DSIM_PAYLOAD_REG, /* Payload FIFO register */
DSIM_RXFIFO_REG, /* Read FIFO register */
DSIM_FIFOCTRL_REG, /* FIFO status and control register */
DSIM_PLLCTRL_REG, /* PLL control register */
DSIM_PHYCTRL_REG,
DSIM_PHYTIMING_REG,
DSIM_PHYTIMING1_REG,
DSIM_PHYTIMING2_REG,
NUM_REGS
};
static unsigned int exynos_reg_ofs[] = {
[DSIM_STATUS_REG] = 0x00,
[DSIM_SWRST_REG] = 0x04,
[DSIM_CLKCTRL_REG] = 0x08,
[DSIM_TIMEOUT_REG] = 0x0c,
[DSIM_CONFIG_REG] = 0x10,
[DSIM_ESCMODE_REG] = 0x14,
[DSIM_MDRESOL_REG] = 0x18,
[DSIM_MVPORCH_REG] = 0x1c,
[DSIM_MHPORCH_REG] = 0x20,
[DSIM_MSYNC_REG] = 0x24,
[DSIM_INTSRC_REG] = 0x2c,
[DSIM_INTMSK_REG] = 0x30,
[DSIM_PKTHDR_REG] = 0x34,
[DSIM_PAYLOAD_REG] = 0x38,
[DSIM_RXFIFO_REG] = 0x3c,
[DSIM_FIFOCTRL_REG] = 0x44,
[DSIM_PLLCTRL_REG] = 0x4c,
[DSIM_PHYCTRL_REG] = 0x5c,
[DSIM_PHYTIMING_REG] = 0x64,
[DSIM_PHYTIMING1_REG] = 0x68,
[DSIM_PHYTIMING2_REG] = 0x6c,
};
static unsigned int exynos5433_reg_ofs[] = {
[DSIM_STATUS_REG] = 0x04,
[DSIM_SWRST_REG] = 0x0C,
[DSIM_CLKCTRL_REG] = 0x10,
[DSIM_TIMEOUT_REG] = 0x14,
[DSIM_CONFIG_REG] = 0x18,
[DSIM_ESCMODE_REG] = 0x1C,
[DSIM_MDRESOL_REG] = 0x20,
[DSIM_MVPORCH_REG] = 0x24,
[DSIM_MHPORCH_REG] = 0x28,
[DSIM_MSYNC_REG] = 0x2C,
[DSIM_INTSRC_REG] = 0x34,
[DSIM_INTMSK_REG] = 0x38,
[DSIM_PKTHDR_REG] = 0x3C,
[DSIM_PAYLOAD_REG] = 0x40,
[DSIM_RXFIFO_REG] = 0x44,
[DSIM_FIFOCTRL_REG] = 0x4C,
[DSIM_PLLCTRL_REG] = 0x94,
[DSIM_PHYCTRL_REG] = 0xA4,
[DSIM_PHYTIMING_REG] = 0xB4,
[DSIM_PHYTIMING1_REG] = 0xB8,
[DSIM_PHYTIMING2_REG] = 0xBC,
};
enum reg_value_idx {
RESET_TYPE,
PLL_TIMER,
STOP_STATE_CNT,
PHYCTRL_ULPS_EXIT,
PHYCTRL_VREG_LP,
PHYCTRL_SLEW_UP,
PHYTIMING_LPX,
PHYTIMING_HS_EXIT,
PHYTIMING_CLK_PREPARE,
PHYTIMING_CLK_ZERO,
PHYTIMING_CLK_POST,
PHYTIMING_CLK_TRAIL,
PHYTIMING_HS_PREPARE,
PHYTIMING_HS_ZERO,
PHYTIMING_HS_TRAIL
};
static unsigned int reg_values[] = {
[RESET_TYPE] = DSIM_SWRST,
[PLL_TIMER] = 500,
[STOP_STATE_CNT] = 0xf,
[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af),
[PHYCTRL_VREG_LP] = 0,
[PHYCTRL_SLEW_UP] = 0,
[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06),
[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b),
[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07),
[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27),
[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d),
[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08),
[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09),
[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d),
[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b),
};
static unsigned int exynos5433_reg_values[] = {
[RESET_TYPE] = DSIM_FUNCRST,
[PLL_TIMER] = 22200,
[STOP_STATE_CNT] = 0xa,
[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190),
[PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP,
[PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP,
[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07),
[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c),
[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d),
[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09),
[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b),
[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10),
[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c),
};
static struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
.reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x50,
.has_freqband = 1,
.has_clklane_stop = 1,
.num_clks = 2,
.max_freq = 1000,
.wait_for_reset = 1,
.num_bits_resol = 11,
.reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
.reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x50,
.has_freqband = 1,
.has_clklane_stop = 1,
.num_clks = 2,
.max_freq = 1000,
.wait_for_reset = 1,
.num_bits_resol = 11,
.reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = {
.reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x58,
.has_clklane_stop = 1,
.num_clks = 2,
.max_freq = 1000,
.wait_for_reset = 1,
.num_bits_resol = 11,
.reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
.reg_ofs = exynos_reg_ofs,
.plltmr_reg = 0x58,
.num_clks = 2,
.max_freq = 1000,
.wait_for_reset = 1,
.num_bits_resol = 11,
.reg_values = reg_values,
};
static struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
.reg_ofs = exynos5433_reg_ofs,
.plltmr_reg = 0xa0,
.has_clklane_stop = 1,
.num_clks = 5,
.max_freq = 1500,
.wait_for_reset = 0,
.num_bits_resol = 12,
.reg_values = exynos5433_reg_values,
};
static struct of_device_id exynos_dsi_of_match[] = {
......@@ -339,6 +491,8 @@ static struct of_device_id exynos_dsi_of_match[] = {
.data = &exynos4415_dsi_driver_data },
{ .compatible = "samsung,exynos5410-mipi-dsi",
.data = &exynos5_dsi_driver_data },
{ .compatible = "samsung,exynos5433-mipi-dsi",
.data = &exynos5433_dsi_driver_data },
{ }
};
......@@ -361,8 +515,10 @@ static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
static void exynos_dsi_reset(struct exynos_dsi *dsi)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
reinit_completion(&dsi->completed);
writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
DSI_WRITE(dsi, DSIM_SWRST_REG, driver_data->reg_values[RESET_TYPE]);
}
#ifndef MHZ
......@@ -372,6 +528,7 @@ static void exynos_dsi_reset(struct exynos_dsi *dsi)
static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned long best_freq = 0;
u32 min_delta = 0xffffffff;
u8 p_min, p_max;
......@@ -395,7 +552,8 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
tmp = (u64)_m * fin;
do_div(tmp, _p);
if (tmp < 500 * MHZ || tmp > 1000 * MHZ)
if (tmp < 500 * MHZ ||
tmp > driver_data->max_freq * MHZ)
continue;
tmp = (u64)_m * fin;
......@@ -431,16 +589,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
u16 m;
u32 reg;
clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate);
fin = clk_get_rate(dsi->pll_clk);
if (!fin) {
dev_err(dsi->dev, "failed to get PLL clock frequency\n");
return 0;
}
dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin);
fin = dsi->pll_clk_rate;
fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
if (!fout) {
dev_err(dsi->dev,
......@@ -449,7 +598,8 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
}
dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
writel(500, dsi->reg_base + driver_data->plltmr_reg);
writel(driver_data->reg_values[PLL_TIMER],
dsi->reg_base + driver_data->plltmr_reg);
reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
......@@ -471,7 +621,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
reg |= DSIM_FREQ_BAND(band);
}
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg);
timeout = 1000;
do {
......@@ -479,7 +629,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
dev_err(dsi->dev, "PLL failed to stabilize\n");
return 0;
}
reg = readl(dsi->reg_base + DSIM_STATUS_REG);
reg = DSI_READ(dsi, DSIM_STATUS_REG);
} while ((reg & DSIM_PLL_STABLE) == 0);
return fout;
......@@ -509,7 +659,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
hs_clk, byte_clk, esc_clk);
reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
reg = DSI_READ(dsi, DSIM_CLKCTRL_REG);
reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
| DSIM_BYTE_CLK_SRC_MASK);
......@@ -519,7 +669,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
| DSIM_BYTE_CLK_SRC(0)
| DSIM_TX_REQUEST_HSCLK;
writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg);
return 0;
}
......@@ -527,22 +677,24 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned int *reg_values = driver_data->reg_values;
u32 reg;
if (driver_data->has_freqband)
return;
/* B D-PHY: D-PHY Master & Slave Analog Block control */
reg = DSIM_PHYCTRL_ULPS_EXIT(0x0af);
writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] |
reg_values[PHYCTRL_SLEW_UP];
DSI_WRITE(dsi, DSIM_PHYCTRL_REG, reg);
/*
* T LPX: Transmitted length of any Low-Power state period
* T HS-EXIT: Time that the transmitter drives LP-11 following a HS
* burst
*/
reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT];
DSI_WRITE(dsi, DSIM_PHYTIMING_REG, reg);
/*
* T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
......@@ -557,11 +709,12 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
* T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
* the last payload clock bit of a HS transmission burst
*/
reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
DSIM_PHYTIMING1_CLK_ZERO(0x27) |
DSIM_PHYTIMING1_CLK_POST(0x0d) |
DSIM_PHYTIMING1_CLK_TRAIL(0x08);
writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
reg = reg_values[PHYTIMING_CLK_PREPARE] |
reg_values[PHYTIMING_CLK_ZERO] |
reg_values[PHYTIMING_CLK_POST] |
reg_values[PHYTIMING_CLK_TRAIL];
DSI_WRITE(dsi, DSIM_PHYTIMING1_REG, reg);
/*
* T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
......@@ -572,23 +725,31 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
* T HS-TRAIL: Time that the transmitter drives the flipped differential
* state after last payload data bit of a HS transmission burst
*/
reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
DSIM_PHYTIMING2_HS_TRAIL(0x0b);
writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] |
reg_values[PHYTIMING_HS_TRAIL];
DSI_WRITE(dsi, DSIM_PHYTIMING2_REG, reg);
}
static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
{
u32 reg;
reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
reg = DSI_READ(dsi, DSIM_CLKCTRL_REG);
reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg);
reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG);
reg = DSI_READ(dsi, DSIM_PLLCTRL_REG);
reg &= ~DSIM_PLL_EN;
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg);
}
static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
{
u32 reg = DSI_READ(dsi, DSIM_CONFIG_REG);
reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
DSIM_LANE_EN(lane));
DSI_WRITE(dsi, DSIM_CONFIG_REG, reg);
}
static int exynos_dsi_init_link(struct exynos_dsi *dsi)
......@@ -599,15 +760,14 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
u32 lanes_mask;
/* Initialize FIFO pointers */
reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG);
reg &= ~0x1f;
writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg);
usleep_range(9000, 11000);
reg |= 0x1f;
writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg);
usleep_range(9000, 11000);
/* DSI configuration */
......@@ -664,17 +824,6 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
return -EINVAL;
}
reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1);
writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
reg |= DSIM_LANE_EN_CLK;
writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
lanes_mask = BIT(dsi->lanes) - 1;
reg |= DSIM_LANE_EN(lanes_mask);
writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
/*
* Use non-continuous clock mode if the periparal wants and
* host controller supports
......@@ -686,8 +835,11 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
if (driver_data->has_clklane_stop &&
dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
reg |= DSIM_CLKLANE_STOP;
writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
}
DSI_WRITE(dsi, DSIM_CONFIG_REG, reg);
lanes_mask = BIT(dsi->lanes) - 1;
exynos_dsi_enable_lane(dsi, lanes_mask);
/* Check clock and data lane state are stop state */
timeout = 100;
......@@ -697,19 +849,19 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
return -EFAULT;
}
reg = readl(dsi->reg_base + DSIM_STATUS_REG);
reg = DSI_READ(dsi, DSIM_STATUS_REG);
if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
!= DSIM_STOP_STATE_DAT(lanes_mask))
continue;
} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
reg = readl(dsi->reg_base + DSIM_ESCMODE_REG);
reg = DSI_READ(dsi, DSIM_ESCMODE_REG);
reg &= ~DSIM_STOP_STATE_CNT_MASK;
reg |= DSIM_STOP_STATE_CNT(0xf);
writel(reg, dsi->reg_base + DSIM_ESCMODE_REG);
reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
DSI_WRITE(dsi, DSIM_ESCMODE_REG, reg);
reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG);
DSI_WRITE(dsi, DSIM_TIMEOUT_REG, reg);
return 0;
}
......@@ -717,25 +869,27 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
{
struct videomode *vm = &dsi->vm;
unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
u32 reg;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
reg = DSIM_CMD_ALLOW(0xf)
| DSIM_STABLE_VFP(vm->vfront_porch)
| DSIM_MAIN_VBP(vm->vback_porch);
writel(reg, dsi->reg_base + DSIM_MVPORCH_REG);
DSI_WRITE(dsi, DSIM_MVPORCH_REG, reg);
reg = DSIM_MAIN_HFP(vm->hfront_porch)
| DSIM_MAIN_HBP(vm->hback_porch);
writel(reg, dsi->reg_base + DSIM_MHPORCH_REG);
DSI_WRITE(dsi, DSIM_MHPORCH_REG, reg);
reg = DSIM_MAIN_VSA(vm->vsync_len)
| DSIM_MAIN_HSA(vm->hsync_len);
writel(reg, dsi->reg_base + DSIM_MSYNC_REG);
DSI_WRITE(dsi, DSIM_MSYNC_REG, reg);
}
reg = DSIM_MAIN_HRESOL(vm->hactive, num_bits_resol) |
DSIM_MAIN_VRESOL(vm->vactive, num_bits_resol);
reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive);
writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg);
dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive);
}
......@@ -744,12 +898,12 @@ static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
{
u32 reg;
reg = readl(dsi->reg_base + DSIM_MDRESOL_REG);
reg = DSI_READ(dsi, DSIM_MDRESOL_REG);
if (enable)
reg |= DSIM_MAIN_STAND_BY;
else
reg &= ~DSIM_MAIN_STAND_BY;
writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg);
}
static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
......@@ -757,7 +911,7 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
int timeout = 2000;
do {
u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
u32 reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG);
if (!(reg & DSIM_SFR_HEADER_FULL))
return 0;
......@@ -771,22 +925,21 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
{
u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG);
if (lpm)
v |= DSIM_CMD_LPDT_LP;
else
v &= ~DSIM_CMD_LPDT_LP;
writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
DSI_WRITE(dsi, DSIM_ESCMODE_REG, v);
}
static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
{
u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG);
u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG);
v |= DSIM_FORCE_BTA;
writel(v, dsi->reg_base + DSIM_ESCMODE_REG);
DSI_WRITE(dsi, DSIM_ESCMODE_REG, v);
}
static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
......@@ -810,7 +963,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
while (length >= 4) {
reg = (payload[3] << 24) | (payload[2] << 16)
| (payload[1] << 8) | payload[0];
writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg);
payload += 4;
length -= 4;
}
......@@ -825,7 +978,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
/* Fall through */
case 1:
reg |= payload[0];
writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg);
break;
case 0:
/* Do nothing */
......@@ -848,7 +1001,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
dsi->state ^= DSIM_STATE_CMD_LPM;
}
writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
DSI_WRITE(dsi, DSIM_PKTHDR_REG, reg);
if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
exynos_dsi_force_bta(dsi);
......@@ -864,7 +1017,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
u32 reg;
if (first) {
reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
switch (reg & 0x3f) {
case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
......@@ -903,7 +1056,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
/* Receive payload */
while (length >= 4) {
reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
payload[0] = (reg >> 0) & 0xff;
payload[1] = (reg >> 8) & 0xff;
payload[2] = (reg >> 16) & 0xff;
......@@ -913,7 +1066,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
}
if (length) {
reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
switch (length) {
case 3:
payload[2] = (reg >> 16) & 0xff;
......@@ -932,7 +1085,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
clear_fifo:
length = DSI_RX_FIFO_SIZE / 4;
do {
reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
reg = DSI_READ(dsi, DSIM_RXFIFO_REG);
if (reg == DSI_RX_FIFO_EMPTY)
break;
} while (--length);
......@@ -1088,23 +1241,26 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
struct exynos_dsi *dsi = dev_id;
u32 status;
status = readl(dsi->reg_base + DSIM_INTSRC_REG);
status = DSI_READ(dsi, DSIM_INTSRC_REG);
if (!status) {
static unsigned long int j;
if (printk_timed_ratelimit(&j, 500))
dev_warn(dsi->dev, "spurious interrupt\n");
return IRQ_HANDLED;
}
writel(status, dsi->reg_base + DSIM_INTSRC_REG);
DSI_WRITE(dsi, DSIM_INTSRC_REG, status);
if (status & DSIM_INT_SW_RST_RELEASE) {
u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY);
writel(mask, dsi->reg_base + DSIM_INTMSK_REG);
u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_FRAME_DONE |
DSIM_INT_RX_ECC_ERR | DSIM_INT_SW_RST_RELEASE);
DSI_WRITE(dsi, DSIM_INTMSK_REG, mask);
complete(&dsi->completed);
return IRQ_HANDLED;
}
if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY)))
if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
DSIM_INT_FRAME_DONE | DSIM_INT_PLL_STABLE)))
return IRQ_HANDLED;
if (exynos_dsi_transfer_finish(dsi))
......@@ -1118,7 +1274,7 @@ static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
struct drm_encoder *encoder = dsi->display.encoder;
if (dsi->state & DSIM_STATE_ENABLED)
if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)
exynos_drm_crtc_te_handler(encoder->crtc);
return IRQ_HANDLED;
......@@ -1142,9 +1298,16 @@ 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);
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 @@ static int exynos_dsi_poweron(struct exynos_dsi *dsi)
static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
{
int ret;
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int ret, i;
usleep_range(10000, 20000);
......@@ -1350,8 +1510,8 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
phy_power_off(dsi->phy);
clk_disable_unprepare(dsi->pll_clk);
clk_disable_unprepare(dsi->bus_clk);
for (i = driver_data->num_clks - 1; i > -1; i--)
clk_disable_unprepare(dsi->clks[i]);
ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
if (ret < 0)
......@@ -1369,8 +1529,11 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
if (ret < 0)
return ret;
dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi);
return ret;
}
......@@ -1378,8 +1541,6 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_enable(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
......@@ -1389,6 +1550,8 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
return ret;
}
dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
return 0;
}
......@@ -1397,12 +1560,15 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
if (!(dsi->state & DSIM_STATE_ENABLED))
return;
dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
drm_panel_disable(dsi->panel);
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi);
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi);
}
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
......@@ -1457,10 +1623,13 @@ static void exynos_dsi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs exynos_dsi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.detect = exynos_dsi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = exynos_dsi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int exynos_dsi_get_modes(struct drm_connector *connector)
......@@ -1627,7 +1796,22 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency",
&dsi->esc_clk_rate);
if (ret < 0)
goto end;
of_node_put(ep);
ep = of_graph_get_next_endpoint(node, NULL);
if (!ep) {
ret = -ENXIO;
goto end;
}
dsi->bridge_node = of_graph_get_remote_port_parent(ep);
if (!dsi->bridge_node) {
ret = -ENXIO;
goto end;
}
end:
of_node_put(ep);
......@@ -1640,6 +1824,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
struct exynos_drm_display *display = dev_get_drvdata(dev);
struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_device *drm_dev = data;
struct drm_bridge *bridge;
int ret;
ret = exynos_drm_create_enc_conn(drm_dev, display);
......@@ -1649,6 +1834,12 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
return ret;
}
bridge = of_drm_find_bridge(dsi->bridge_node);
if (bridge) {
display->encoder->bridge = bridge;
drm_bridge_attach(drm_dev, bridge);
}
return mipi_dsi_host_register(&dsi->dsi_host);
}
......@@ -1673,7 +1864,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct resource *res;
struct exynos_dsi *dsi;
int ret;
int ret, i;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
......@@ -1682,11 +1873,6 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD;
dsi->display.ops = &exynos_dsi_display_ops;
ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
dsi->display.type);
if (ret)
return ret;
/* To be checked as invalid one */
dsi->te_gpio = -ENOENT;
......@@ -1702,7 +1888,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
ret = exynos_dsi_parse_dt(dsi);
if (ret)
goto err_del_component;
return ret;
dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio";
......@@ -1713,40 +1899,44 @@ static int exynos_dsi_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
dsi->pll_clk = devm_clk_get(dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) {
dev_info(dev, "failed to get dsi pll input clock\n");
ret = PTR_ERR(dsi->pll_clk);
goto err_del_component;
dsi->clks = devm_kzalloc(dev,
sizeof(*dsi->clks) * dsi->driver_data->num_clks,
GFP_KERNEL);
if (!dsi->clks)
return -ENOMEM;
for (i = 0; i < dsi->driver_data->num_clks; i++) {
dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
if (IS_ERR(dsi->clks[i])) {
if (strcmp(clk_names[i], "sclk_mipi") == 0) {
strcpy(clk_names[i], OLD_SCLK_MIPI_CLK_NAME);
i--;
continue;
}
dsi->bus_clk = devm_clk_get(dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) {
dev_info(dev, "failed to get dsi bus clock\n");
ret = PTR_ERR(dsi->bus_clk);
goto err_del_component;
dev_info(dev, "failed to get the clock: %s\n",
clk_names[i]);
return PTR_ERR(dsi->clks[i]);
}
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dsi->reg_base)) {
dev_err(dev, "failed to remap io region\n");
ret = PTR_ERR(dsi->reg_base);
goto err_del_component;
return PTR_ERR(dsi->reg_base);
}
dsi->phy = devm_phy_get(dev, "dsim");
if (IS_ERR(dsi->phy)) {
dev_info(dev, "failed to get dsim phy\n");
ret = PTR_ERR(dsi->phy);
goto err_del_component;
return PTR_ERR(dsi->phy);
}
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) {
dev_err(dev, "failed to request dsi irq resource\n");
ret = dsi->irq;
goto err_del_component;
return dsi->irq;
}
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
......@@ -1755,26 +1945,17 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dev_name(dev), dsi);
if (ret) {
dev_err(dev, "failed to request dsi irq\n");
goto err_del_component;
return ret;
}
platform_set_drvdata(pdev, &dsi->display);
ret = component_add(dev, &exynos_dsi_component_ops);
if (ret)
goto err_del_component;
return ret;
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
return component_add(dev, &exynos_dsi_component_ops);
}
static int exynos_dsi_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_dsi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
......
......@@ -32,17 +32,6 @@ struct exynos_drm_encoder {
struct exynos_drm_display *display;
};
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
if (display->ops->dpms)
display->ops->dpms(display, mode);
}
static bool
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
......@@ -76,12 +65,7 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
display->ops->mode_set(display, adjusted_mode);
}
static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
{
/* drm framework doesn't check NULL. */
}
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
static void exynos_drm_encoder_enable(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
......@@ -95,24 +79,17 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_plane *plane;
struct drm_device *dev = encoder->dev;
exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display *display = exynos_encoder->display;
/* all planes connected to this encoder should be also disabled. */
drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
if (plane->crtc && (plane->crtc == encoder->crtc))
plane->funcs->disable_plane(plane);
}
if (display->ops->dpms)
display->ops->dpms(display, DRM_MODE_DPMS_OFF);
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
.dpms = exynos_drm_encoder_dpms,
.mode_fixup = exynos_drm_encoder_mode_fixup,
.mode_set = exynos_drm_encoder_mode_set,
.prepare = exynos_drm_encoder_prepare,
.commit = exynos_drm_encoder_commit,
.enable = exynos_drm_encoder_enable,
.disable = exynos_drm_encoder_disable,
};
......
......@@ -16,6 +16,8 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <uapi/drm/exynos_drm.h>
#include "exynos_drm_drv.h"
......@@ -265,9 +267,46 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
exynos_drm_fbdev_init(dev);
}
static int exynos_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool async)
{
int ret;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/* This is the point of no return */
drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
/*
* Exynos can't update planes with CRTCs and encoders disabled,
* its updates routines, specially for FIMD, requires the clocks
* to be enabled. So it is necessary to handle the modeset operations
* *before* the commit_planes() step, this way it will always
* have the relevant clocks enabled to perform the update.
*/
drm_atomic_helper_commit_planes(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
drm_atomic_state_free(state);
return 0;
}
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = exynos_atomic_commit,
};
void exynos_drm_mode_config_init(struct drm_device *dev)
......
......@@ -275,9 +275,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
}
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
......
......@@ -196,6 +196,62 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
u32 val;
if (ctx->suspended)
return -EPERM;
if (!test_and_set_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE;
if (ctx->i80_if) {
val |= VIDINTCON0_INT_I80IFDONE;
val |= VIDINTCON0_INT_SYSMAINCON;
val &= ~VIDINTCON0_INT_SYSSUBCON;
} else {
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL0_MASK;
val |= VIDINTCON0_FRAMESEL0_VSYNC;
val &= ~VIDINTCON0_FRAMESEL1_MASK;
val |= VIDINTCON0_FRAMESEL1_NONE;
}
writel(val, ctx->regs + VIDINTCON0);
}
return 0;
}
static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
u32 val;
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val &= ~VIDINTCON0_INT_ENABLE;
if (ctx->i80_if) {
val &= ~VIDINTCON0_INT_I80IFDONE;
val &= ~VIDINTCON0_INT_SYSMAINCON;
val &= ~VIDINTCON0_INT_SYSSUBCON;
} else
val &= ~VIDINTCON0_INT_FRAME;
writel(val, ctx->regs + VIDINTCON0);
}
}
static void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
......@@ -242,12 +298,19 @@ static void fimd_enable_shadow_channel_path(struct fimd_context *ctx,
writel(val, ctx->regs + SHADOWCON);
}
static void fimd_clear_channel(struct fimd_context *ctx)
static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* Hardware is in unknown state, so ensure it gets enabled properly */
pm_runtime_get_sync(ctx->dev);
clk_prepare_enable(ctx->bus_clk);
clk_prepare_enable(ctx->lcd_clk);
/* Check if any channel is enabled. */
for (win = 0; win < WINDOWS_NR; win++) {
u32 val = readl(ctx->regs + WINCON(win));
......@@ -265,36 +328,24 @@ static void fimd_clear_channel(struct fimd_context *ctx)
/* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled) {
unsigned int state = ctx->suspended;
ctx->suspended = 0;
fimd_wait_for_vblank(ctx->crtc);
ctx->suspended = state;
}
}
int pipe = ctx->pipe;
static int fimd_iommu_attach_devices(struct fimd_context *ctx,
struct drm_device *drm_dev)
{
/* ensure that vblank interrupt won't be reported to core */
ctx->suspended = false;
ctx->pipe = -1;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev)) {
int ret;
fimd_enable_vblank(ctx->crtc);
fimd_wait_for_vblank(ctx->crtc);
fimd_disable_vblank(ctx->crtc);
/*
* 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;
ctx->suspended = true;
ctx->pipe = pipe;
}
}
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
return 0;
pm_runtime_put(ctx->dev);
}
static void fimd_iommu_detach_devices(struct fimd_context *ctx)
......@@ -337,7 +388,7 @@ static bool fimd_mode_fixup(struct exynos_drm_crtc *crtc,
static void fimd_commit(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.mode;
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 val, clkdiv;
......@@ -434,61 +485,6 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
writel(val, ctx->regs + VIDCON0);
}
static int fimd_enable_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
u32 val;
if (ctx->suspended)
return -EPERM;
if (!test_and_set_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE;
if (ctx->i80_if) {
val |= VIDINTCON0_INT_I80IFDONE;
val |= VIDINTCON0_INT_SYSMAINCON;
val &= ~VIDINTCON0_INT_SYSSUBCON;
} else {
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL0_MASK;
val |= VIDINTCON0_FRAMESEL0_VSYNC;
val &= ~VIDINTCON0_FRAMESEL1_MASK;
val |= VIDINTCON0_FRAMESEL1_NONE;
}
writel(val, ctx->regs + VIDINTCON0);
}
return 0;
}
static void fimd_disable_vblank(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
u32 val;
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val &= ~VIDINTCON0_INT_ENABLE;
if (ctx->i80_if) {
val &= ~VIDINTCON0_INT_I80IFDONE;
val &= ~VIDINTCON0_INT_SYSMAINCON;
val &= ~VIDINTCON0_INT_SYSSUBCON;
} else
val &= ~VIDINTCON0_INT_FRAME;
writel(val, ctx->regs + VIDINTCON0);
}
}
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
{
......@@ -634,11 +630,8 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
/* If suspended, enable this on resume */
if (ctx->suspended) {
plane->resume = true;
if (ctx->suspended)
return;
}
/*
* SHADOWCON/PRTCON register is used for enabling timing.
......@@ -728,8 +721,6 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
/* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
plane->enabled = true;
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
}
......@@ -744,11 +735,8 @@ static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
if (ctx->suspended) {
/* do not resume this window*/
plane->resume = false;
if (ctx->suspended)
return;
}
/* protect windows */
fimd_shadow_protect_win(ctx, win, true);
......@@ -760,57 +748,15 @@ static void fimd_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
/* unprotect windows */
fimd_shadow_protect_win(ctx, win, false);
plane->enabled = false;
}
static void fimd_window_suspend(struct fimd_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
plane->resume = plane->enabled;
if (plane->enabled)
fimd_win_disable(ctx->crtc, i);
}
}
static void fimd_window_resume(struct fimd_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
plane->enabled = plane->resume;
plane->resume = false;
}
}
static void fimd_apply(struct fimd_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
if (plane->enabled)
fimd_win_commit(ctx->crtc, i);
else
fimd_win_disable(ctx->crtc, i);
}
fimd_commit(ctx->crtc);
}
static int fimd_poweron(struct fimd_context *ctx)
static void fimd_enable(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
return 0;
return;
ctx->suspended = false;
......@@ -819,50 +765,43 @@ static int fimd_poweron(struct fimd_context *ctx)
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
goto bus_clk_err;
return;
}
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
goto lcd_clk_err;
return;
}
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags)) {
ret = fimd_enable_vblank(ctx->crtc);
if (ret) {
DRM_ERROR("Failed to re-enable vblank [%d]\n", ret);
goto enable_vblank_err;
}
}
if (test_and_clear_bit(0, &ctx->irq_flags))
fimd_enable_vblank(ctx->crtc);
fimd_window_resume(ctx);
fimd_apply(ctx);
return 0;
enable_vblank_err:
clk_disable_unprepare(ctx->lcd_clk);
lcd_clk_err:
clk_disable_unprepare(ctx->bus_clk);
bus_clk_err:
ctx->suspended = true;
return ret;
fimd_commit(ctx->crtc);
}
static int fimd_poweroff(struct fimd_context *ctx)
static void fimd_disable(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
int i;
if (ctx->suspended)
return 0;
return;
/*
* We need to make sure that all windows are disabled before we
* suspend that connector. Otherwise we might try to scan from
* a destroyed buffer later.
*/
fimd_window_suspend(ctx);
for (i = 0; i < WINDOWS_NR; i++)
fimd_win_disable(crtc, i);
fimd_enable_vblank(crtc);
fimd_wait_for_vblank(crtc);
fimd_disable_vblank(crtc);
writel(0, ctx->regs + VIDCON0);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
......@@ -870,26 +809,6 @@ static int fimd_poweroff(struct fimd_context *ctx)
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
return 0;
}
static void fimd_dpms(struct exynos_drm_crtc *crtc, int mode)
{
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
fimd_poweron(crtc->ctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
fimd_poweroff(crtc->ctx);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
}
static void fimd_trigger(struct device *dev)
......@@ -964,7 +883,8 @@ static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
}
static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.dpms = fimd_dpms,
.enable = fimd_enable,
.disable = fimd_disable,
.mode_fixup = fimd_mode_fixup,
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
......@@ -974,6 +894,7 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.win_disable = fimd_win_disable,
.te_handler = fimd_te_handler,
.clock_enable = fimd_dp_clock_enable,
.clear_channels = fimd_clear_channels,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
......@@ -1043,7 +964,11 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
return fimd_iommu_attach_devices(ctx, drm_dev);
ret = drm_iommu_attach_device_if_possible(ctx->crtc, drm_dev, dev);
if (ret)
priv->pipe--;
return ret;
}
static void fimd_unbind(struct device *dev, struct device *master,
......@@ -1051,7 +976,7 @@ static void fimd_unbind(struct device *dev, struct device *master,
{
struct fimd_context *ctx = dev_get_drvdata(dev);
fimd_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
fimd_disable(ctx->crtc);
fimd_iommu_detach_devices(ctx);
......@@ -1079,11 +1004,6 @@ static int fimd_probe(struct platform_device *pdev)
if (!ctx)
return -ENOMEM;
ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
EXYNOS_DISPLAY_TYPE_LCD);
if (ret)
return ret;
ctx->dev = dev;
ctx->suspended = true;
ctx->driver_data = drm_fimd_get_driver_data(pdev);
......@@ -1134,38 +1054,33 @@ static int fimd_probe(struct platform_device *pdev)
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
ret = PTR_ERR(ctx->bus_clk);
goto err_del_component;
return PTR_ERR(ctx->bus_clk);
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
ret = PTR_ERR(ctx->lcd_clk);
goto err_del_component;
return PTR_ERR(ctx->lcd_clk);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->regs)) {
ret = PTR_ERR(ctx->regs);
goto err_del_component;
}
if (IS_ERR(ctx->regs))
return PTR_ERR(ctx->regs);
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
ctx->i80_if ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "irq request failed.\n");
ret = -ENXIO;
goto err_del_component;
return -ENXIO;
}
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
goto err_del_component;
return ret;
}
init_waitqueue_head(&ctx->wait_vsync_queue);
......@@ -1175,8 +1090,7 @@ static int fimd_probe(struct platform_device *pdev)
ctx->display = exynos_dpi_probe(dev);
if (IS_ERR(ctx->display)) {
ret = PTR_ERR(ctx->display);
goto err_del_component;
return PTR_ERR(ctx->display);
}
pm_runtime_enable(dev);
......@@ -1190,8 +1104,6 @@ static int fimd_probe(struct platform_device *pdev)
err_disable_pm_runtime:
pm_runtime_disable(dev);
err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
......@@ -1200,7 +1112,6 @@ static int fimd_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &fimd_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
......
......@@ -61,7 +61,6 @@ struct exynos_drm_gem_buf {
* or at framebuffer creation.
* @size: size requested from user, in bytes and this size is aligned
* in page unit.
* @vma: a pointer to vm_area.
* @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transferred to user as kms_bo.handle so
......@@ -71,7 +70,6 @@ struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_gem_buf *buffer;
unsigned long size;
struct vm_area_struct *vma;
unsigned int flags;
};
......
......@@ -100,6 +100,9 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
if (subdrv_dev->archdata.mapping)
arm_iommu_detach_device(subdrv_dev);
ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping);
if (ret < 0) {
DRM_DEBUG_KMS("failed iommu attach.\n");
......@@ -114,8 +117,8 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
* If iommu attach succeeded, the sub driver would have dma_ops
* for iommu and also all sub drivers have same dma_ops.
*/
if (!dev->archdata.dma_ops)
dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops;
if (get_dma_ops(dev) == get_dma_ops(NULL))
set_dma_ops(dev, get_dma_ops(subdrv_dev));
return 0;
}
......@@ -141,3 +144,17 @@ void drm_iommu_detach_device(struct drm_device *drm_dev,
iommu_detach_device(mapping->domain, subdrv_dev);
drm_release_iommu_mapping(drm_dev);
}
int drm_iommu_attach_device_if_possible(struct exynos_drm_crtc *exynos_crtc,
struct drm_device *drm_dev, struct device *subdrv_dev)
{
int ret = 0;
if (is_drm_iommu_supported(drm_dev)) {
if (exynos_crtc->ops->clear_channels)
exynos_crtc->ops->clear_channels(exynos_crtc);
return drm_iommu_attach_device(drm_dev, subdrv_dev);
}
return ret;
}
......@@ -38,6 +38,10 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
#endif
}
int drm_iommu_attach_device_if_possible(
struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev,
struct device *subdrv_dev);
#else
static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
......@@ -65,5 +69,12 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
return false;
}
static inline int drm_iommu_attach_device_if_possible(
struct exynos_drm_crtc *exynos_crtc, struct drm_device *drm_dev,
struct device *subdrv_dev)
{
return 0;
}
#endif
#endif
......@@ -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;
/*
* 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 (min_size > size) {
DRM_ERROR("invalid size for plane %d\n", i);
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;
}
......
/*
* 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,
},
};
......@@ -13,6 +13,7 @@
#include <drm/exynos_drm.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_atomic_helper.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
......@@ -61,42 +62,21 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last)
return size;
}
int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
int nr;
int i;
nr = exynos_drm_fb_get_buf_cnt(fb);
for (i = 0; i < nr; i++) {
struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
if (!buffer) {
DRM_DEBUG_KMS("buffer is null\n");
return -EFAULT;
}
exynos_plane->dma_addr[i] = buffer->dma_addr + fb->offsets[i];
DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
i, (unsigned long)exynos_plane->dma_addr[i]);
}
return 0;
}
void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
static void exynos_plane_mode_set(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
unsigned int actual_w;
unsigned int actual_h;
actual_w = exynos_plane_get_size(crtc_x, crtc_w, crtc->mode.hdisplay);
actual_h = exynos_plane_get_size(crtc_y, crtc_h, crtc->mode.vdisplay);
actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
if (crtc_x < 0) {
if (actual_w)
......@@ -132,10 +112,10 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
exynos_plane->crtc_height = actual_h;
/* set drm mode data. */
exynos_plane->mode_width = crtc->mode.hdisplay;
exynos_plane->mode_height = crtc->mode.vdisplay;
exynos_plane->refresh = crtc->mode.vrefresh;
exynos_plane->scan_flag = crtc->mode.flags;
exynos_plane->mode_width = mode->hdisplay;
exynos_plane->mode_height = mode->vdisplay;
exynos_plane->refresh = mode->vrefresh;
exynos_plane->scan_flag = mode->flags;
DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
exynos_plane->crtc_x, exynos_plane->crtc_y,
......@@ -144,48 +124,83 @@ void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
plane->crtc = crtc;
}
int
exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
static struct drm_plane_funcs exynos_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static int exynos_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
int nr;
int i;
if (!state->fb)
return 0;
nr = exynos_drm_fb_get_buf_cnt(state->fb);
for (i = 0; i < nr; i++) {
struct exynos_drm_gem_buf *buffer =
exynos_drm_fb_buffer(state->fb, i);
if (!buffer) {
DRM_DEBUG_KMS("buffer is null\n");
return -EFAULT;
}
exynos_plane->dma_addr[i] = buffer->dma_addr +
state->fb->offsets[i];
DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
i, (unsigned long)exynos_plane->dma_addr[i]);
}
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
return 0;
}
static void exynos_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = plane->state;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc);
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
int ret;
ret = exynos_check_plane(plane, fb);
if (ret < 0)
return ret;
if (!state->crtc)
return;
exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
crtc_w, crtc_h, src_x >> 16, src_y >> 16,
src_w >> 16, src_h >> 16);
exynos_plane_mode_set(plane, state->crtc, state->fb,
state->crtc_x, state->crtc_y,
state->crtc_w, state->crtc_h,
state->src_x >> 16, state->src_y >> 16,
state->src_w >> 16, state->src_h >> 16);
if (exynos_crtc->ops->win_commit)
exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
return 0;
}
static int exynos_disable_plane(struct drm_plane *plane)
static void exynos_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc);
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc);
if (exynos_crtc && exynos_crtc->ops->win_disable)
if (!old_state->crtc)
return;
if (exynos_crtc->ops->win_disable)
exynos_crtc->ops->win_disable(exynos_crtc,
exynos_plane->zpos);
return 0;
}
static struct drm_plane_funcs exynos_plane_funcs = {
.update_plane = exynos_update_plane,
.disable_plane = exynos_disable_plane,
.destroy = drm_plane_cleanup,
static const struct drm_plane_helper_funcs plane_helper_funcs = {
.atomic_check = exynos_plane_atomic_check,
.atomic_update = exynos_plane_atomic_update,
.atomic_disable = exynos_plane_atomic_disable,
};
static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
......@@ -223,6 +238,8 @@ int exynos_plane_init(struct drm_device *dev,
return err;
}
drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
exynos_plane->zpos = zpos;
if (type == DRM_PLANE_TYPE_OVERLAY)
......
......@@ -9,17 +9,6 @@
*
*/
int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb);
void exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
......
......@@ -20,6 +20,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic_helper.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
......@@ -130,78 +131,34 @@ static void vidi_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
plane = &ctx->planes[win];
plane->enabled = true;
DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr);
if (ctx->vblank_on)
schedule_work(&ctx->work);
}
static void vidi_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
static void vidi_enable(struct exynos_drm_crtc *crtc)
{
struct vidi_context *ctx = crtc->ctx;
struct exynos_drm_plane *plane;
if (win < 0 || win >= WINDOWS_NR)
return;
plane = &ctx->planes[win];
plane->enabled = false;
/* TODO. */
}
static int vidi_power_on(struct vidi_context *ctx, bool enable)
{
struct exynos_drm_plane *plane;
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable != false && enable != true)
return -EINVAL;
mutex_lock(&ctx->lock);
if (enable) {
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
vidi_enable_vblank(ctx->crtc);
for (i = 0; i < WINDOWS_NR; i++) {
plane = &ctx->planes[i];
if (plane->enabled)
vidi_win_commit(ctx->crtc, i);
}
} else {
ctx->suspended = true;
}
return 0;
mutex_unlock(&ctx->lock);
}
static void vidi_dpms(struct exynos_drm_crtc *crtc, int mode)
static void vidi_disable(struct exynos_drm_crtc *crtc)
{
struct vidi_context *ctx = crtc->ctx;
DRM_DEBUG_KMS("%d\n", mode);
mutex_lock(&ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
vidi_power_on(ctx, true);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
vidi_power_on(ctx, false);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
ctx->suspended = true;
mutex_unlock(&ctx->lock);
}
......@@ -218,11 +175,11 @@ static int vidi_ctx_initialize(struct vidi_context *ctx,
}
static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
.dpms = vidi_dpms,
.enable = vidi_enable,
.disable = vidi_disable,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
.win_commit = vidi_win_commit,
.win_disable = vidi_win_disable,
};
static void vidi_fake_vblank_handler(struct work_struct *work)
......@@ -384,10 +341,13 @@ static void vidi_connector_destroy(struct drm_connector *connector)
}
static struct drm_connector_funcs vidi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = vidi_detect,
.destroy = vidi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int vidi_get_modes(struct drm_connector *connector)
......@@ -520,16 +480,6 @@ static int vidi_probe(struct platform_device *pdev)
ctx->default_win = 0;
ctx->pdev = pdev;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
EXYNOS_DISPLAY_TYPE_VIDI);
if (ret)
return ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
ctx->display.type);
if (ret)
goto err_del_crtc_component;
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
mutex_init(&ctx->lock);
......@@ -539,7 +489,7 @@ static int vidi_probe(struct platform_device *pdev)
ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0) {
DRM_ERROR("failed to create connection sysfs.\n");
goto err_del_conn_component;
return ret;
}
ret = component_add(&pdev->dev, &vidi_component_ops);
......@@ -550,10 +500,6 @@ static int vidi_probe(struct platform_device *pdev)
err_remove_file:
device_remove_file(&pdev->dev, &dev_attr_connection);
err_del_conn_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
err_del_crtc_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
......@@ -570,8 +516,6 @@ static int vidi_remove(struct platform_device *pdev)
}
component_del(&pdev->dev, &vidi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
......@@ -584,38 +528,3 @@ struct platform_driver vidi_driver = {
.owner = THIS_MODULE,
},
};
int exynos_drm_probe_vidi(void)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ret = platform_driver_register(&vidi_driver);
if (ret) {
platform_device_unregister(pdev);
return ret;
}
return ret;
}
static int exynos_drm_remove_vidi_device(struct device *dev, void *data)
{
platform_device_unregister(to_platform_device(dev));
return 0;
}
void exynos_drm_remove_vidi(void)
{
int ret = driver_for_each_device(&vidi_driver.driver, NULL, NULL,
exynos_drm_remove_vidi_device);
/* silence compiler warning */
(void)ret;
platform_driver_unregister(&vidi_driver);
}
......@@ -17,6 +17,7 @@
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic_helper.h>
#include "regs-hdmi.h"
......@@ -1050,10 +1051,13 @@ 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)
......@@ -2123,8 +2127,8 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
*/
if (crtc)
funcs = crtc->helper_private;
if (funcs && funcs->dpms)
(*funcs->dpms)(crtc, mode);
if (funcs && funcs->disable)
(*funcs->disable)(crtc);
hdmi_poweroff(hdata);
break;
......@@ -2356,20 +2360,13 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->display.type = EXYNOS_DISPLAY_TYPE_HDMI;
hdata->display.ops = &hdmi_display_ops;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
hdata->display.type);
if (ret)
return ret;
mutex_init(&hdata->hdmi_mutex);
platform_set_drvdata(pdev, hdata);
match = of_match_node(hdmi_match_types, dev->of_node);
if (!match) {
ret = -ENODEV;
goto err_del_component;
}
if (!match)
return -ENODEV;
drv_data = (struct hdmi_driver_data *)match->data;
hdata->type = drv_data->type;
......@@ -2389,13 +2386,13 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hdata->regs)) {
ret = PTR_ERR(hdata->regs);
goto err_del_component;
return ret;
}
ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
if (ret) {
DRM_ERROR("failed to request HPD gpio\n");
goto err_del_component;
return ret;
}
ddc_node = hdmi_legacy_ddc_dt_binding(dev);
......@@ -2406,8 +2403,7 @@ static int hdmi_probe(struct platform_device *pdev)
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
ret = -ENODEV;
goto err_del_component;
return -ENODEV;
}
out_get_ddc_adpt:
......@@ -2491,9 +2487,6 @@ static int hdmi_probe(struct platform_device *pdev)
err_ddc:
put_device(&hdata->ddc_adpt->dev);
err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret;
}
......@@ -2513,7 +2506,6 @@ static int hdmi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &hdmi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
......
......@@ -882,10 +882,12 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
}
}
if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
return 0;
ret = drm_iommu_attach_device_if_possible(mixer_ctx->crtc, drm_dev,
mixer_ctx->dev);
if (ret)
priv->pipe--;
return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
return ret;
}
static void mixer_ctx_remove(struct mixer_context *mixer_ctx)
......@@ -937,8 +939,6 @@ static void mixer_win_commit(struct exynos_drm_crtc *crtc, unsigned int win)
vp_video_buffer(mixer_ctx, win);
else
mixer_graph_buffer(mixer_ctx, win);
mixer_ctx->planes[win].enabled = true;
}
static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
......@@ -952,7 +952,6 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) {
mutex_unlock(&mixer_ctx->mixer_mutex);
mixer_ctx->planes[win].resume = false;
return;
}
mutex_unlock(&mixer_ctx->mixer_mutex);
......@@ -964,8 +963,6 @@ static void mixer_win_disable(struct exynos_drm_crtc *crtc, unsigned int win)
mixer_vsync_set_update(mixer_ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
mixer_ctx->planes[win].enabled = false;
}
static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
......@@ -1000,36 +997,11 @@ static void mixer_wait_for_vblank(struct exynos_drm_crtc *crtc)
drm_vblank_put(mixer_ctx->drm_dev, mixer_ctx->pipe);
}
static void mixer_window_suspend(struct mixer_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < MIXER_WIN_NR; i++) {
plane = &ctx->planes[i];
plane->resume = plane->enabled;
mixer_win_disable(ctx->crtc, i);
}
mixer_wait_for_vblank(ctx->crtc);
}
static void mixer_window_resume(struct mixer_context *ctx)
{
struct exynos_drm_plane *plane;
int i;
for (i = 0; i < MIXER_WIN_NR; i++) {
plane = &ctx->planes[i];
plane->enabled = plane->resume;
plane->resume = false;
if (plane->enabled)
mixer_win_commit(ctx->crtc, i);
}
}
static void mixer_poweron(struct mixer_context *ctx)
static void mixer_enable(struct exynos_drm_crtc *crtc)
{
struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res;
int ret;
mutex_lock(&ctx->mixer_mutex);
if (ctx->powered) {
......@@ -1041,12 +1013,32 @@ static void mixer_poweron(struct mixer_context *ctx)
pm_runtime_get_sync(ctx->dev);
clk_prepare_enable(res->mixer);
clk_prepare_enable(res->hdmi);
ret = clk_prepare_enable(res->mixer);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
return;
}
ret = clk_prepare_enable(res->hdmi);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
return;
}
if (ctx->vp_enabled) {
clk_prepare_enable(res->vp);
if (ctx->has_sclk)
clk_prepare_enable(res->sclk_mixer);
ret = clk_prepare_enable(res->vp);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
ret);
return;
}
if (ctx->has_sclk) {
ret = clk_prepare_enable(res->sclk_mixer);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the " \
"sclk_mixer clk [%d]\n",
ret);
return;
}
}
}
mutex_lock(&ctx->mixer_mutex);
......@@ -1057,13 +1049,13 @@ static void mixer_poweron(struct mixer_context *ctx)
mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
mixer_win_reset(ctx);
mixer_window_resume(ctx);
}
static void mixer_poweroff(struct mixer_context *ctx)
static void mixer_disable(struct exynos_drm_crtc *crtc)
{
struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res;
int i;
mutex_lock(&ctx->mixer_mutex);
if (!ctx->powered) {
......@@ -1074,7 +1066,9 @@ static void mixer_poweroff(struct mixer_context *ctx)
mixer_stop(ctx);
mixer_regs_dump(ctx);
mixer_window_suspend(ctx);
for (i = 0; i < MIXER_WIN_NR; i++)
mixer_win_disable(crtc, i);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
......@@ -1093,23 +1087,6 @@ static void mixer_poweroff(struct mixer_context *ctx)
pm_runtime_put_sync(ctx->dev);
}
static void mixer_dpms(struct exynos_drm_crtc *crtc, int mode)
{
switch (mode) {
case DRM_MODE_DPMS_ON:
mixer_poweron(crtc->ctx);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
mixer_poweroff(crtc->ctx);
break;
default:
DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
break;
}
}
/* Only valid for Mixer version 16.0.33.0 */
int mixer_check_mode(struct drm_display_mode *mode)
{
......@@ -1131,7 +1108,8 @@ int mixer_check_mode(struct drm_display_mode *mode)
}
static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
.dpms = mixer_dpms,
.enable = mixer_enable,
.disable = mixer_disable,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
.wait_for_vblank = mixer_wait_for_vblank,
......@@ -1280,17 +1258,8 @@ 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;
}
if (!ret)
pm_runtime_enable(dev);
return ret;
......@@ -1301,7 +1270,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;
}
......
......@@ -2232,6 +2232,39 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);
/**
* of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
* @parent: pointer to the parent device node
* @port_reg: identifier (value of reg property) of the parent port node
* @reg: identifier (value of reg property) of the endpoint node
*
* Return: An 'endpoint' node pointer which is identified by reg and at the same
* is the child of a port node identified by port_reg. reg and port_reg are
* ignored when they are -1.
*/
struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg)
{
struct of_endpoint endpoint;
struct device_node *node, *prev_node = NULL;
while (1) {
node = of_graph_get_next_endpoint(parent, prev_node);
of_node_put(prev_node);
if (!node)
break;
of_graph_parse_endpoint(node, &endpoint);
if (((port_reg == -1) || (endpoint.port == port_reg)) &&
((reg == -1) || (endpoint.id == reg)))
return node;
prev_node = node;
}
return NULL;
}
/**
* of_graph_get_remote_port_parent() - get remote port's parent node
* @node: pointer to a local endpoint device_node
......
......@@ -45,6 +45,8 @@ int of_graph_parse_endpoint(const struct device_node *node,
struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *previous);
struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg);
struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node);
struct device_node *of_graph_get_remote_port(const struct device_node *node);
......@@ -69,6 +71,12 @@ static inline struct device_node *of_graph_get_next_endpoint(
return NULL;
}
struct device_node *of_graph_get_endpoint_by_regs(
const struct device_node *parent, int port_reg, int reg)
{
return NULL;
}
static inline struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node)
{
......
/*
* Copyright (C) 2014 Samsung Electronics Co.Ltd
*
* 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
*/
#ifndef EXYNOS_REGS_DECON_H
#define EXYNOS_REGS_DECON_H
/* Exynos543X DECON */
#define DECON_VIDCON0 0x0000
#define DECON_VIDOUTCON0 0x0010
#define DECON_WINCONx(n) (0x0020 + ((n) * 4))
#define DECON_VIDOSDxH(n) (0x0080 + ((n) * 4))
#define DECON_SHADOWCON 0x00A0
#define DECON_VIDOSDxA(n) (0x00B0 + ((n) * 0x20))
#define DECON_VIDOSDxB(n) (0x00B4 + ((n) * 0x20))
#define DECON_VIDOSDxC(n) (0x00B8 + ((n) * 0x20))
#define DECON_VIDOSDxD(n) (0x00BC + ((n) * 0x20))
#define DECON_VIDOSDxE(n) (0x00C0 + ((n) * 0x20))
#define DECON_VIDW0xADD0B0(n) (0x0150 + ((n) * 0x10))
#define DECON_VIDW0xADD0B1(n) (0x0154 + ((n) * 0x10))
#define DECON_VIDW0xADD0B2(n) (0x0158 + ((n) * 0x10))
#define DECON_VIDW0xADD1B0(n) (0x01A0 + ((n) * 0x10))
#define DECON_VIDW0xADD1B1(n) (0x01A4 + ((n) * 0x10))
#define DECON_VIDW0xADD1B2(n) (0x01A8 + ((n) * 0x10))
#define DECON_VIDW0xADD2(n) (0x0200 + ((n) * 4))
#define DECON_LOCALxSIZE(n) (0x0214 + ((n) * 4))
#define DECON_VIDINTCON0 0x0220
#define DECON_VIDINTCON1 0x0224
#define DECON_WxKEYCON0(n) (0x0230 + ((n - 1) * 8))
#define DECON_WxKEYCON1(n) (0x0234 + ((n - 1) * 8))
#define DECON_WxKEYALPHA(n) (0x0250 + ((n - 1) * 4))
#define DECON_WINxMAP(n) (0x0270 + ((n) * 4))
#define DECON_QOSLUT07_00 0x02C0
#define DECON_QOSLUT15_08 0x02C4
#define DECON_QOSCTRL 0x02C8
#define DECON_BLENDERQx(n) (0x0300 + ((n - 1) * 4))
#define DECON_BLENDCON 0x0310
#define DECON_OPE_VIDW0xADD0(n) (0x0400 + ((n) * 4))
#define DECON_OPE_VIDW0xADD1(n) (0x0414 + ((n) * 4))
#define DECON_FRAMEFIFO_REG7 0x051C
#define DECON_FRAMEFIFO_REG8 0x0520
#define DECON_FRAMEFIFO_STATUS 0x0524
#define DECON_CMU 0x1404
#define DECON_UPDATE 0x1410
#define DECON_UPDATE_SCHEME 0x1438
#define DECON_VIDCON1 0x2000
#define DECON_VIDCON2 0x2004
#define DECON_VIDCON3 0x2008
#define DECON_VIDCON4 0x200C
#define DECON_VIDTCON2 0x2028
#define DECON_FRAME_SIZE 0x2038
#define DECON_LINECNT_OP_THRESHOLD 0x203C
#define DECON_TRIGCON 0x2040
#define DECON_TRIGSKIP 0x2050
#define DECON_CRCRDATA 0x20B0
#define DECON_CRCCTRL 0x20B4
/* Exynos5430 DECON */
#define DECON_VIDTCON0 0x2020
#define DECON_VIDTCON1 0x2024
/* Exynos5433 DECON */
#define DECON_VIDTCON00 0x2010
#define DECON_VIDTCON01 0x2014
#define DECON_VIDTCON10 0x2018
#define DECON_VIDTCON11 0x201C
/* Exynos543X DECON Internal */
#define DECON_W013DSTREOCON 0x0320
#define DECON_W233DSTREOCON 0x0324
#define DECON_FRAMEFIFO_REG0 0x0500
#define DECON_ENHANCER_CTRL 0x2100
/* Exynos543X DECON TV */
#define DECON_VCLKCON0 0x0014
#define DECON_VIDINTCON2 0x0228
#define DECON_VIDINTCON3 0x022C
/* VIDCON0 */
#define VIDCON0_SWRESET (1 << 28)
#define VIDCON0_STOP_STATUS (1 << 2)
#define VIDCON0_ENVID (1 << 1)
#define VIDCON0_ENVID_F (1 << 0)
/* VIDOUTCON0 */
#define VIDOUT_LCD_ON (1 << 24)
#define VIDOUT_IF_F_MASK (0x3 << 20)
#define VIDOUT_RGB_IF (0x0 << 20)
#define VIDOUT_COMMAND_IF (0x2 << 20)
/* WINCONx */
#define WINCONx_HAWSWP_F (1 << 16)
#define WINCONx_WSWP_F (1 << 15)
#define WINCONx_BURSTLEN_MASK (0x3 << 10)
#define WINCONx_BURSTLEN_16WORD (0x0 << 10)
#define WINCONx_BURSTLEN_8WORD (0x1 << 10)
#define WINCONx_BURSTLEN_4WORD (0x2 << 10)
#define WINCONx_BLD_PIX_F (1 << 6)
#define WINCONx_BPPMODE_MASK (0xf << 2)
#define WINCONx_BPPMODE_16BPP_565 (0x5 << 2)
#define WINCONx_BPPMODE_16BPP_A1555 (0x6 << 2)
#define WINCONx_BPPMODE_16BPP_I1555 (0x7 << 2)
#define WINCONx_BPPMODE_24BPP_888 (0xb << 2)
#define WINCONx_BPPMODE_24BPP_A1887 (0xc << 2)
#define WINCONx_BPPMODE_25BPP_A1888 (0xd << 2)
#define WINCONx_BPPMODE_32BPP_A8888 (0xd << 2)
#define WINCONx_BPPMODE_16BPP_A4444 (0xe << 2)
#define WINCONx_ALPHA_SEL_F (1 << 1)
#define WINCONx_ENWIN_F (1 << 0)
/* SHADOWCON */
#define SHADOWCON_Wx_PROTECT(n) (1 << (10 + (n)))
/* VIDOSDxD */
#define VIDOSD_Wx_ALPHA_R_F(n) (((n) & 0xff) << 16)
#define VIDOSD_Wx_ALPHA_G_F(n) (((n) & 0xff) << 8)
#define VIDOSD_Wx_ALPHA_B_F(n) (((n) & 0xff) << 0)
/* VIDINTCON0 */
#define VIDINTCON0_FRAMEDONE (1 << 17)
#define VIDINTCON0_INTFRMEN (1 << 12)
#define VIDINTCON0_INTEN (1 << 0)
/* VIDINTCON1 */
#define VIDINTCON1_INTFRMDONEPEND (1 << 2)
#define VIDINTCON1_INTFRMPEND (1 << 1)
#define VIDINTCON1_INTFIFOPEND (1 << 0)
/* DECON_CMU */
#define CMU_CLKGAGE_MODE_SFR_F (1 << 1)
#define CMU_CLKGAGE_MODE_MEM_F (1 << 0)
/* DECON_UPDATE */
#define STANDALONE_UPDATE_F (1 << 0)
/* DECON_VIDTCON00 */
#define VIDTCON00_VBPD_F(x) (((x) & 0xfff) << 16)
#define VIDTCON00_VFPD_F(x) ((x) & 0xfff)
/* DECON_VIDTCON01 */
#define VIDTCON01_VSPW_F(x) (((x) & 0xfff) << 16)
/* DECON_VIDTCON10 */
#define VIDTCON10_HBPD_F(x) (((x) & 0xfff) << 16)
#define VIDTCON10_HFPD_F(x) ((x) & 0xfff)
/* DECON_VIDTCON11 */
#define VIDTCON11_HSPW_F(x) (((x) & 0xfff) << 16)
/* DECON_VIDTCON2 */
#define VIDTCON2_LINEVAL(x) (((x) & 0xfff) << 16)
#define VIDTCON2_HOZVAL(x) ((x) & 0xfff)
/* TRIGCON */
#define TRIGCON_TRIGEN_PER_F (1 << 31)
#define TRIGCON_TRIGEN_F (1 << 30)
#define TRIGCON_TE_AUTO_MASK (1 << 29)
#define TRIGCON_SWTRIGCMD (1 << 1)
#define TRIGCON_SWTRIGEN (1 << 0)
#endif /* EXYNOS_REGS_DECON_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment