Commit 870a1718 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

- Support runtime pm
  . In case of most ARM SoC, each IP has each power domain which should be
    controlled by each IP driver using runtime pm interface. So this patch
    series makes each IP driver to control its own power domain when
    drm dpms is requested.
- Support of_graph based dt binding for DP panel.
  . This patch series adds of_graph based dt binding for DP panel.
    And also it keeps backward compatibility. This includes dt binding
    patch so I got Acked-by from Krzysztof Kozlowski who is a Exynos
    SoC maintainer and from Rob Herring who is a device tree maintainer.

- Cleanup for Exynos DRM IPP enhancement.
  . This patch series is a first step for enhancing existing IPP framework
    which will integrate existing IPP functions with DRM KMS part so that
    these can be transparent to userspace. For other portion of the patch
    series, we will have more times for the review.]

* 'exynos-drm-next' of git://git.kernel.org:/pub/scm/linux/kernel/git/daeinki/drm-exynos: (29 commits)
  drm/exynos: gem: remove old unused prototypes
  drm/exynos: fimd: fix dma burst size setting for small plane size
  drm/exynos: fix clipping when scaling is enabled
  drm/exynos: mixer: use ratio precalculated in exynos_state
  drm/exynos: add generic check for plane state
  drm/exynos: introduce exynos_drm_plane_config structure
  drm/exynos: mixer: enable video overlay plane only when VP is available
  drm/exynos: mixer: use crtc->state->adjusted_mode instead of crtc->mode
  drm/exynos: introduce exynos_drm_plane_state structure
  drm/exynos: move dma_addr attribute from exynos plane to exynos fb
  drm/exynos: exynos7-decon: remove excessive check
  drm/exynos: rotator: convert to common clock framework
  drm/exynos: gsc: add device tree support and remove usage of static mappings
  drm/exynos: gsc: fix wrong pm_runtime state
  drm/exynos: gsc: prepare and unprepare gsc clock
  ARM: dts: Use OF graph for DP to panel connection in exynos5800-peach-pi
  dt-bindings: exynos-dp: update ports node binding for panel
  drm/exynos: dp: add of_graph dt binding support for panel
  drm/exynos: decon: remove unused variables
  drm/exynos: dsi: modify a error type when getting a node failed
  ...
parents b15c50be 9bac40cf
Device-Tree bindings for Samsung Exynos Embedded DisplayPort Transmitter(eDP)
DisplayPort is industry standard to accommodate the growing board adoption
of digital display technology within the PC and CE industries.
It consolidates the internal and external connection methods to reduce device
complexity and cost. It also supports necessary features for important cross
industry applications and provides performance scalability to enable the next
generation of displays that feature higher color depths, refresh rates, and
display resolutions.
eDP (embedded display port) device is compliant with Embedded DisplayPort
standard as follows,
- DisplayPort standard 1.1a for Exynos5250 and Exynos5260.
- DisplayPort standard 1.3 for Exynos5422s and Exynos5800.
eDP resides between FIMD and panel or FIMD and bridge such as LVDS.
The Exynos display port interface should be configured based on The Exynos display port interface should be configured based on
the type of panel connected to it. the type of panel connected to it.
...@@ -66,8 +83,15 @@ Optional properties for dp-controller: ...@@ -66,8 +83,15 @@ Optional properties for dp-controller:
Hotplug detect GPIO. Hotplug detect GPIO.
Indicates which GPIO should be used for hotplug Indicates which GPIO should be used for hotplug
detection detection
-video interfaces: Device node can contain video interface port Video interfaces:
nodes according to [1]. Device node can contain video interface port nodes according to [1].
The following are properties specific to those nodes:
endpoint node connected to bridge or panel node:
- remote-endpoint: specifies the endpoint in panel or bridge node.
This node is required in all kinds of exynos dp
to represent the connection between dp and bridge
or dp and panel.
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt [1]: Documentation/devicetree/bindings/media/video-interfaces.txt
...@@ -111,9 +135,18 @@ Board Specific portion: ...@@ -111,9 +135,18 @@ Board Specific portion:
}; };
ports { ports {
port@0 { port {
dp_out: endpoint { dp_out: endpoint {
remote-endpoint = <&bridge_in>; remote-endpoint = <&dp_in>;
};
};
};
panel {
...
port {
dp_in: endpoint {
remote-endpoint = <&dp_out>;
}; };
}; };
}; };
......
...@@ -7,6 +7,10 @@ Required properties: ...@@ -7,6 +7,10 @@ Required properties:
- reg: should contain G-Scaler physical address location and length. - reg: should contain G-Scaler physical address location and length.
- interrupts: should contain G-Scaler interrupt number - interrupts: should contain G-Scaler interrupt number
Optional properties:
- samsung,sysreg: handle to syscon used to control the system registers to
set writeback input and destination
Example: Example:
gsc_0: gsc@0x13e00000 { gsc_0: gsc@0x13e00000 {
......
...@@ -122,6 +122,12 @@ panel: panel { ...@@ -122,6 +122,12 @@ panel: panel {
compatible = "auo,b133htn01"; compatible = "auo,b133htn01";
power-supply = <&tps65090_fet6>; power-supply = <&tps65090_fet6>;
backlight = <&backlight>; backlight = <&backlight>;
port {
panel_in: endpoint {
remote-endpoint = <&dp_out>;
};
};
}; };
mmc1_pwrseq: mmc1_pwrseq { mmc1_pwrseq: mmc1_pwrseq {
...@@ -148,7 +154,14 @@ &dp { ...@@ -148,7 +154,14 @@ &dp {
samsung,link-rate = <0x0a>; samsung,link-rate = <0x0a>;
samsung,lane-count = <2>; samsung,lane-count = <2>;
samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>; samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
panel = <&panel>;
ports {
port {
dp_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
}; };
&fimd { &fimd {
......
...@@ -118,7 +118,7 @@ config DRM_EXYNOS_ROTATOR ...@@ -118,7 +118,7 @@ config DRM_EXYNOS_ROTATOR
config DRM_EXYNOS_GSC config DRM_EXYNOS_GSC
bool "GScaler" bool "GScaler"
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !VIDEO_SAMSUNG_EXYNOS_GSC
help help
Choose this option if you want to use Exynos GSC for DRM. Choose this option if you want to use Exynos GSC for DRM.
......
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h" #include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h" #include "exynos_drm_iommu.h"
#define WINDOWS_NR 3 #define WINDOWS_NR 3
#define CURSOR_WIN 2
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
static const char * const decon_clks_name[] = { static const char * const decon_clks_name[] = {
...@@ -56,6 +56,7 @@ struct decon_context { ...@@ -56,6 +56,7 @@ struct decon_context {
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc; struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR]; struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
void __iomem *addr; void __iomem *addr;
struct clk *clks[ARRAY_SIZE(decon_clks_name)]; struct clk *clks[ARRAY_SIZE(decon_clks_name)];
int pipe; int pipe;
...@@ -71,6 +72,12 @@ static const uint32_t decon_formats[] = { ...@@ -71,6 +72,12 @@ static const uint32_t decon_formats[] = {
DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB8888,
}; };
static const enum drm_plane_type decon_win_types[WINDOWS_NR] = {
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_CURSOR,
};
static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask, static inline void decon_set_bits(struct decon_context *ctx, u32 reg, u32 mask,
u32 val) u32 val)
{ {
...@@ -259,21 +266,24 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc, ...@@ -259,21 +266,24 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
static void decon_update_plane(struct exynos_drm_crtc *crtc, static void decon_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct exynos_drm_plane_state *state =
to_exynos_plane_state(plane->base.state);
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
struct drm_plane_state *state = plane->base.state; struct drm_framebuffer *fb = state->base.fb;
unsigned int win = plane->zpos; unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3; unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0]; unsigned int pitch = fb->pitches[0];
dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
u32 val; u32 val;
if (test_bit(BIT_SUSPENDED, &ctx->flags)) if (test_bit(BIT_SUSPENDED, &ctx->flags))
return; return;
val = COORDINATE_X(plane->crtc_x) | COORDINATE_Y(plane->crtc_y); val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y);
writel(val, ctx->addr + DECON_VIDOSDxA(win)); writel(val, ctx->addr + DECON_VIDOSDxA(win));
val = COORDINATE_X(plane->crtc_x + plane->crtc_w - 1) | val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) |
COORDINATE_Y(plane->crtc_y + plane->crtc_h - 1); COORDINATE_Y(state->crtc.y + state->crtc.h - 1);
writel(val, ctx->addr + DECON_VIDOSDxB(win)); writel(val, ctx->addr + DECON_VIDOSDxB(win));
val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) |
...@@ -284,20 +294,20 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -284,20 +294,20 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
VIDOSD_Wx_ALPHA_B_F(0x0); VIDOSD_Wx_ALPHA_B_F(0x0);
writel(val, ctx->addr + DECON_VIDOSDxD(win)); writel(val, ctx->addr + DECON_VIDOSDxD(win));
writel(plane->dma_addr[0], ctx->addr + DECON_VIDW0xADD0B0(win)); writel(dma_addr, ctx->addr + DECON_VIDW0xADD0B0(win));
val = plane->dma_addr[0] + pitch * plane->crtc_h; val = dma_addr + pitch * state->src.h;
writel(val, ctx->addr + DECON_VIDW0xADD1B0(win)); writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
if (ctx->out_type != IFTYPE_HDMI) if (ctx->out_type != IFTYPE_HDMI)
val = BIT_VAL(pitch - plane->crtc_w * bpp, 27, 14) val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14)
| BIT_VAL(plane->crtc_w * bpp, 13, 0); | BIT_VAL(state->crtc.w * bpp, 13, 0);
else else
val = BIT_VAL(pitch - plane->crtc_w * bpp, 29, 15) val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15)
| BIT_VAL(plane->crtc_w * bpp, 14, 0); | BIT_VAL(state->crtc.w * bpp, 14, 0);
writel(val, ctx->addr + DECON_VIDW0xADD2(win)); writel(val, ctx->addr + DECON_VIDW0xADD2(win));
decon_win_set_pixfmt(ctx, win, state->fb); decon_win_set_pixfmt(ctx, win, fb);
/* window enable */ /* window enable */
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0); decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
...@@ -377,20 +387,12 @@ static void decon_swreset(struct decon_context *ctx) ...@@ -377,20 +387,12 @@ static void decon_swreset(struct decon_context *ctx)
static void decon_enable(struct exynos_drm_crtc *crtc) static void decon_enable(struct exynos_drm_crtc *crtc)
{ {
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
int ret;
int i;
if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags)) if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags))
return; return;
pm_runtime_get_sync(ctx->dev); pm_runtime_get_sync(ctx->dev);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
ret = clk_prepare_enable(ctx->clks[i]);
if (ret < 0)
goto err;
}
set_bit(BIT_CLKS_ENABLED, &ctx->flags); set_bit(BIT_CLKS_ENABLED, &ctx->flags);
/* if vblank was enabled status, enable it again. */ /* if vblank was enabled status, enable it again. */
...@@ -399,11 +401,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc) ...@@ -399,11 +401,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
decon_commit(ctx->crtc); decon_commit(ctx->crtc);
return;
err:
while (--i >= 0)
clk_disable_unprepare(ctx->clks[i]);
set_bit(BIT_SUSPENDED, &ctx->flags); set_bit(BIT_SUSPENDED, &ctx->flags);
} }
...@@ -425,9 +422,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc) ...@@ -425,9 +422,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
decon_swreset(ctx); decon_swreset(ctx);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
clk_disable_unprepare(ctx->clks[i]);
clear_bit(BIT_CLKS_ENABLED, &ctx->flags); clear_bit(BIT_CLKS_ENABLED, &ctx->flags);
pm_runtime_put_sync(ctx->dev); pm_runtime_put_sync(ctx->dev);
...@@ -478,7 +472,6 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc) ...@@ -478,7 +472,6 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
static struct exynos_drm_crtc_ops decon_crtc_ops = { static struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable = decon_enable, .enable = decon_enable,
.disable = decon_disable, .disable = decon_disable,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank, .enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank, .disable_vblank = decon_disable_vblank,
.atomic_begin = decon_atomic_begin, .atomic_begin = decon_atomic_begin,
...@@ -495,7 +488,6 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -495,7 +488,6 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
struct exynos_drm_private *priv = drm_dev->dev_private; struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
enum exynos_drm_output_type out_type; enum exynos_drm_output_type out_type;
enum drm_plane_type type;
unsigned int win; unsigned int win;
int ret; int ret;
...@@ -505,10 +497,13 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -505,10 +497,13 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
for (win = ctx->first_win; win < WINDOWS_NR; win++) { for (win = ctx->first_win; win < WINDOWS_NR; win++) {
int tmp = (win == ctx->first_win) ? 0 : win; int tmp = (win == ctx->first_win) ? 0 : win;
type = exynos_plane_get_type(tmp, CURSOR_WIN); ctx->configs[win].pixel_formats = decon_formats;
ctx->configs[win].num_pixel_formats = ARRAY_SIZE(decon_formats);
ctx->configs[win].zpos = win;
ctx->configs[win].type = decon_win_types[tmp];
ret = exynos_plane_init(drm_dev, &ctx->planes[win], ret = exynos_plane_init(drm_dev, &ctx->planes[win],
1 << ctx->pipe, type, decon_formats, 1 << ctx->pipe, &ctx->configs[win]);
ARRAY_SIZE(decon_formats), win);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -581,6 +576,44 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -581,6 +576,44 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
#ifdef CONFIG_PM
static int exynos5433_decon_suspend(struct device *dev)
{
struct decon_context *ctx = dev_get_drvdata(dev);
int i;
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
clk_disable_unprepare(ctx->clks[i]);
return 0;
}
static int exynos5433_decon_resume(struct device *dev)
{
struct decon_context *ctx = dev_get_drvdata(dev);
int i, ret;
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
ret = clk_prepare_enable(ctx->clks[i]);
if (ret < 0)
goto err;
}
return 0;
err:
while (--i >= 0)
clk_disable_unprepare(ctx->clks[i]);
return ret;
}
#endif
static const struct dev_pm_ops exynos5433_decon_pm_ops = {
SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume,
NULL)
};
static const struct of_device_id exynos5433_decon_driver_dt_match[] = { static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
{ {
.compatible = "samsung,exynos5433-decon", .compatible = "samsung,exynos5433-decon",
...@@ -684,6 +717,7 @@ struct platform_driver exynos5433_decon_driver = { ...@@ -684,6 +717,7 @@ struct platform_driver exynos5433_decon_driver = {
.remove = exynos5433_decon_remove, .remove = exynos5433_decon_remove,
.driver = { .driver = {
.name = "exynos5433-decon", .name = "exynos5433-decon",
.pm = &exynos5433_decon_pm_ops,
.of_match_table = exynos5433_decon_driver_dt_match, .of_match_table = exynos5433_decon_driver_dt_match,
}, },
}; };
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "exynos_drm_crtc.h" #include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h" #include "exynos_drm_fbdev.h"
#include "exynos_drm_iommu.h" #include "exynos_drm_iommu.h"
...@@ -40,13 +41,13 @@ ...@@ -40,13 +41,13 @@
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128
#define WINDOWS_NR 2 #define WINDOWS_NR 2
#define CURSOR_WIN 1
struct decon_context { struct decon_context {
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc; struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR]; struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
struct clk *pclk; struct clk *pclk;
struct clk *aclk; struct clk *aclk;
struct clk *eclk; struct clk *eclk;
...@@ -81,6 +82,11 @@ static const uint32_t decon_formats[] = { ...@@ -81,6 +82,11 @@ static const uint32_t decon_formats[] = {
DRM_FORMAT_BGRA8888, DRM_FORMAT_BGRA8888,
}; };
static const enum drm_plane_type decon_win_types[WINDOWS_NR] = {
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_CURSOR,
};
static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc) static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
{ {
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
...@@ -119,13 +125,8 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc) ...@@ -119,13 +125,8 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
} }
/* Wait for vsync, as disable channel takes effect at next vsync */ /* Wait for vsync, as disable channel takes effect at next vsync */
if (ch_enabled) { if (ch_enabled)
unsigned int state = ctx->suspended;
ctx->suspended = 0;
decon_wait_for_vblank(ctx->crtc); decon_wait_for_vblank(ctx->crtc);
ctx->suspended = state;
}
} }
static int decon_ctx_initialize(struct decon_context *ctx, static int decon_ctx_initialize(struct decon_context *ctx,
...@@ -398,16 +399,17 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc, ...@@ -398,16 +399,17 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
static void decon_update_plane(struct exynos_drm_crtc *crtc, static void decon_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct exynos_drm_plane_state *state =
to_exynos_plane_state(plane->base.state);
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; struct drm_framebuffer *fb = state->base.fb;
struct drm_plane_state *state = plane->base.state;
int padding; int padding;
unsigned long val, alpha; unsigned long val, alpha;
unsigned int last_x; unsigned int last_x;
unsigned int last_y; unsigned int last_y;
unsigned int win = plane->zpos; unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3; unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0]; unsigned int pitch = fb->pitches[0];
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -423,41 +425,32 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -423,41 +425,32 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
*/ */
/* buffer start address */ /* buffer start address */
val = (unsigned long)plane->dma_addr[0]; val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0);
writel(val, ctx->regs + VIDW_BUF_START(win)); writel(val, ctx->regs + VIDW_BUF_START(win));
padding = (pitch / bpp) - state->fb->width; padding = (pitch / bpp) - fb->width;
/* buffer size */ /* buffer size */
writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win)); writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win)); writel(fb->height, ctx->regs + VIDW_WHOLE_Y(win));
/* offset from the start of the buffer to read */ /* offset from the start of the buffer to read */
writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win)); writel(state->src.x, ctx->regs + VIDW_OFFSET_X(win));
writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win)); writel(state->src.y, ctx->regs + VIDW_OFFSET_Y(win));
DRM_DEBUG_KMS("start addr = 0x%lx\n", DRM_DEBUG_KMS("start addr = 0x%lx\n",
(unsigned long)val); (unsigned long)val);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
plane->crtc_w, plane->crtc_h); state->crtc.w, state->crtc.h);
/* val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
* OSD position. VIDOSDxA_TOPLEFT_Y(state->crtc.y);
* In case the window layout goes of LCD layout, DECON fails.
*/
if ((plane->crtc_x + plane->crtc_w) > mode->hdisplay)
plane->crtc_x = mode->hdisplay - plane->crtc_w;
if ((plane->crtc_y + plane->crtc_h) > mode->vdisplay)
plane->crtc_y = mode->vdisplay - plane->crtc_h;
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
VIDOSDxA_TOPLEFT_Y(plane->crtc_y);
writel(val, ctx->regs + VIDOSD_A(win)); writel(val, ctx->regs + VIDOSD_A(win));
last_x = plane->crtc_x + plane->crtc_w; last_x = state->crtc.x + state->crtc.w;
if (last_x) if (last_x)
last_x--; last_x--;
last_y = plane->crtc_y + plane->crtc_h; last_y = state->crtc.y + state->crtc.h;
if (last_y) if (last_y)
last_y--; last_y--;
...@@ -466,7 +459,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -466,7 +459,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDOSD_B(win)); writel(val, ctx->regs + VIDOSD_B(win));
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
plane->crtc_x, plane->crtc_y, last_x, last_y); state->crtc.x, state->crtc.y, last_x, last_y);
/* OSD alpha */ /* OSD alpha */
alpha = VIDOSDxC_ALPHA0_R_F(0x0) | alpha = VIDOSDxC_ALPHA0_R_F(0x0) |
...@@ -481,7 +474,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, ...@@ -481,7 +474,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
writel(alpha, ctx->regs + VIDOSD_D(win)); writel(alpha, ctx->regs + VIDOSD_D(win));
decon_win_set_pixfmt(ctx, win, state->fb); decon_win_set_pixfmt(ctx, win, fb);
/* hardware window 0 doesn't support color key. */ /* hardware window 0 doesn't support color key. */
if (win != 0) if (win != 0)
...@@ -555,39 +548,12 @@ static void decon_init(struct decon_context *ctx) ...@@ -555,39 +548,12 @@ static void decon_init(struct decon_context *ctx)
static void decon_enable(struct exynos_drm_crtc *crtc) static void decon_enable(struct exynos_drm_crtc *crtc)
{ {
struct decon_context *ctx = crtc->ctx; struct decon_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended) if (!ctx->suspended)
return; return;
ctx->suspended = false;
pm_runtime_get_sync(ctx->dev); pm_runtime_get_sync(ctx->dev);
ret = clk_prepare_enable(ctx->pclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
return;
}
ret = clk_prepare_enable(ctx->aclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
return;
}
ret = clk_prepare_enable(ctx->eclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
return;
}
ret = clk_prepare_enable(ctx->vclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
return;
}
decon_init(ctx); decon_init(ctx);
/* if vblank was enabled status, enable it again. */ /* if vblank was enabled status, enable it again. */
...@@ -595,6 +561,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc) ...@@ -595,6 +561,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
decon_enable_vblank(ctx->crtc); decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc); decon_commit(ctx->crtc);
ctx->suspended = false;
} }
static void decon_disable(struct exynos_drm_crtc *crtc) static void decon_disable(struct exynos_drm_crtc *crtc)
...@@ -613,11 +581,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc) ...@@ -613,11 +581,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
for (i = 0; i < WINDOWS_NR; i++) for (i = 0; i < WINDOWS_NR; i++)
decon_disable_plane(crtc, &ctx->planes[i]); decon_disable_plane(crtc, &ctx->planes[i]);
clk_disable_unprepare(ctx->vclk);
clk_disable_unprepare(ctx->eclk);
clk_disable_unprepare(ctx->aclk);
clk_disable_unprepare(ctx->pclk);
pm_runtime_put_sync(ctx->dev); pm_runtime_put_sync(ctx->dev);
ctx->suspended = true; ctx->suspended = true;
...@@ -679,8 +642,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -679,8 +642,7 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
struct decon_context *ctx = dev_get_drvdata(dev); struct decon_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type; unsigned int i;
unsigned int zpos;
int ret; int ret;
ret = decon_ctx_initialize(ctx, drm_dev); ret = decon_ctx_initialize(ctx, drm_dev);
...@@ -689,11 +651,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data) ...@@ -689,11 +651,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
return ret; return ret;
} }
for (zpos = 0; zpos < WINDOWS_NR; zpos++) { for (i = 0; i < WINDOWS_NR; i++) {
type = exynos_plane_get_type(zpos, CURSOR_WIN); ctx->configs[i].pixel_formats = decon_formats;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ctx->configs[i].num_pixel_formats = ARRAY_SIZE(decon_formats);
1 << ctx->pipe, type, decon_formats, ctx->configs[i].zpos = i;
ARRAY_SIZE(decon_formats), zpos); ctx->configs[i].type = decon_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
1 << ctx->pipe, &ctx->configs[i]);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -843,11 +808,63 @@ static int decon_remove(struct platform_device *pdev) ...@@ -843,11 +808,63 @@ static int decon_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos7_decon_suspend(struct device *dev)
{
struct decon_context *ctx = dev_get_drvdata(dev);
clk_disable_unprepare(ctx->vclk);
clk_disable_unprepare(ctx->eclk);
clk_disable_unprepare(ctx->aclk);
clk_disable_unprepare(ctx->pclk);
return 0;
}
static int exynos7_decon_resume(struct device *dev)
{
struct decon_context *ctx = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(ctx->pclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(ctx->aclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(ctx->eclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(ctx->vclk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret);
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops exynos7_decon_pm_ops = {
SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume,
NULL)
};
struct platform_driver decon_driver = { struct platform_driver decon_driver = {
.probe = decon_probe, .probe = decon_probe,
.remove = decon_remove, .remove = decon_remove,
.driver = { .driver = {
.name = "exynos-decon", .name = "exynos-decon",
.pm = &exynos7_decon_pm_ops,
.of_match_table = decon_driver_dt_match, .of_match_table = decon_driver_dt_match,
}, },
}; };
...@@ -1009,9 +1009,9 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, ...@@ -1009,9 +1009,9 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
{ {
int ret; int ret;
encoder->bridge = dp->bridge; encoder->bridge->next = dp->ptn_bridge;
dp->bridge->encoder = encoder; dp->ptn_bridge->encoder = encoder;
ret = drm_bridge_attach(encoder->dev, dp->bridge); ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
if (ret) { if (ret) {
DRM_ERROR("Failed to attach bridge to drm\n"); DRM_ERROR("Failed to attach bridge to drm\n");
return ret; return ret;
...@@ -1020,14 +1020,15 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, ...@@ -1020,14 +1020,15 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
return 0; return 0;
} }
static int exynos_dp_create_connector(struct drm_encoder *encoder) static int exynos_dp_bridge_attach(struct drm_bridge *bridge)
{ {
struct exynos_dp_device *dp = encoder_to_dp(encoder); struct exynos_dp_device *dp = bridge->driver_private;
struct drm_encoder *encoder = &dp->encoder;
struct drm_connector *connector = &dp->connector; struct drm_connector *connector = &dp->connector;
int ret; int ret;
/* Pre-empt DP connector creation if there's a bridge */ /* Pre-empt DP connector creation if there's a bridge */
if (dp->bridge) { if (dp->ptn_bridge) {
ret = exynos_drm_attach_lcd_bridge(dp, encoder); ret = exynos_drm_attach_lcd_bridge(dp, encoder);
if (!ret) if (!ret)
return 0; return 0;
...@@ -1052,27 +1053,16 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder) ...@@ -1052,27 +1053,16 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
return ret; return ret;
} }
static bool exynos_dp_mode_fixup(struct drm_encoder *encoder, static void exynos_dp_bridge_enable(struct drm_bridge *bridge)
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void exynos_dp_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
}
static void exynos_dp_enable(struct drm_encoder *encoder)
{ {
struct exynos_dp_device *dp = encoder_to_dp(encoder); struct exynos_dp_device *dp = bridge->driver_private;
struct exynos_drm_crtc *crtc = dp_to_crtc(dp); struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
if (dp->dpms_mode == DRM_MODE_DPMS_ON) if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return; return;
pm_runtime_get_sync(dp->dev);
if (dp->panel) { if (dp->panel) {
if (drm_panel_prepare(dp->panel)) { if (drm_panel_prepare(dp->panel)) {
DRM_ERROR("failed to setup the panel\n"); DRM_ERROR("failed to setup the panel\n");
...@@ -1083,7 +1073,6 @@ static void exynos_dp_enable(struct drm_encoder *encoder) ...@@ -1083,7 +1073,6 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
if (crtc->ops->clock_enable) if (crtc->ops->clock_enable)
crtc->ops->clock_enable(dp_to_crtc(dp), true); crtc->ops->clock_enable(dp_to_crtc(dp), true);
clk_prepare_enable(dp->clock);
phy_power_on(dp->phy); phy_power_on(dp->phy);
exynos_dp_init_dp(dp); exynos_dp_init_dp(dp);
enable_irq(dp->irq); enable_irq(dp->irq);
...@@ -1092,9 +1081,9 @@ static void exynos_dp_enable(struct drm_encoder *encoder) ...@@ -1092,9 +1081,9 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
dp->dpms_mode = DRM_MODE_DPMS_ON; dp->dpms_mode = DRM_MODE_DPMS_ON;
} }
static void exynos_dp_disable(struct drm_encoder *encoder) static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
{ {
struct exynos_dp_device *dp = encoder_to_dp(encoder); struct exynos_dp_device *dp = bridge->driver_private;
struct exynos_drm_crtc *crtc = dp_to_crtc(dp); struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
if (dp->dpms_mode != DRM_MODE_DPMS_ON) if (dp->dpms_mode != DRM_MODE_DPMS_ON)
...@@ -1110,7 +1099,6 @@ static void exynos_dp_disable(struct drm_encoder *encoder) ...@@ -1110,7 +1099,6 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
disable_irq(dp->irq); disable_irq(dp->irq);
flush_work(&dp->hotplug_work); flush_work(&dp->hotplug_work);
phy_power_off(dp->phy); phy_power_off(dp->phy);
clk_disable_unprepare(dp->clock);
if (crtc->ops->clock_enable) if (crtc->ops->clock_enable)
crtc->ops->clock_enable(dp_to_crtc(dp), false); crtc->ops->clock_enable(dp_to_crtc(dp), false);
...@@ -1120,9 +1108,74 @@ static void exynos_dp_disable(struct drm_encoder *encoder) ...@@ -1120,9 +1108,74 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
DRM_ERROR("failed to turnoff the panel\n"); DRM_ERROR("failed to turnoff the panel\n");
} }
pm_runtime_put_sync(dp->dev);
dp->dpms_mode = DRM_MODE_DPMS_OFF; dp->dpms_mode = DRM_MODE_DPMS_OFF;
} }
static void exynos_dp_bridge_nop(struct drm_bridge *bridge)
{
/* do nothing */
}
static const struct drm_bridge_funcs exynos_dp_bridge_funcs = {
.enable = exynos_dp_bridge_enable,
.disable = exynos_dp_bridge_disable,
.pre_enable = exynos_dp_bridge_nop,
.post_disable = exynos_dp_bridge_nop,
.attach = exynos_dp_bridge_attach,
};
static int exynos_dp_create_connector(struct drm_encoder *encoder)
{
struct exynos_dp_device *dp = encoder_to_dp(encoder);
struct drm_device *drm_dev = dp->drm_dev;
struct drm_bridge *bridge;
int ret;
bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
DRM_ERROR("failed to allocate for drm bridge\n");
return -ENOMEM;
}
dp->bridge = bridge;
encoder->bridge = bridge;
bridge->driver_private = dp;
bridge->encoder = encoder;
bridge->funcs = &exynos_dp_bridge_funcs;
ret = drm_bridge_attach(drm_dev, bridge);
if (ret) {
DRM_ERROR("failed to attach drm bridge\n");
return -EINVAL;
}
return 0;
}
static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void exynos_dp_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
}
static void exynos_dp_enable(struct drm_encoder *encoder)
{
}
static void exynos_dp_disable(struct drm_encoder *encoder)
{
}
static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = { static struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
.mode_fixup = exynos_dp_mode_fixup, .mode_fixup = exynos_dp_mode_fixup,
.mode_set = exynos_dp_mode_set, .mode_set = exynos_dp_mode_set,
...@@ -1238,7 +1291,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) ...@@ -1238,7 +1291,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
} }
} }
if (!dp->panel && !dp->bridge) { if (!dp->panel && !dp->ptn_bridge) {
ret = exynos_dp_dt_parse_panel(dp); ret = exynos_dp_dt_parse_panel(dp);
if (ret) if (ret)
return ret; return ret;
...@@ -1289,10 +1342,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) ...@@ -1289,10 +1342,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug); INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
phy_power_on(dp->phy);
exynos_dp_init_dp(dp);
ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
irq_flags, "exynos-dp", dp); irq_flags, "exynos-dp", dp);
if (ret) { if (ret) {
...@@ -1343,8 +1392,9 @@ static const struct component_ops exynos_dp_ops = { ...@@ -1343,8 +1392,9 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev) static int exynos_dp_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *panel_node, *bridge_node, *endpoint; struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
struct exynos_dp_device *dp; struct exynos_dp_device *dp;
int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL); GFP_KERNEL);
...@@ -1353,36 +1403,96 @@ static int exynos_dp_probe(struct platform_device *pdev) ...@@ -1353,36 +1403,96 @@ static int exynos_dp_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dp); platform_set_drvdata(pdev, dp);
/* This is for the backward compatibility. */
panel_node = of_parse_phandle(dev->of_node, "panel", 0); panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) { if (panel_node) {
dp->panel = of_drm_find_panel(panel_node); dp->panel = of_drm_find_panel(panel_node);
of_node_put(panel_node); of_node_put(panel_node);
if (!dp->panel) if (!dp->panel)
return -EPROBE_DEFER; return -EPROBE_DEFER;
} else {
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (endpoint) {
panel_node = of_graph_get_remote_port_parent(endpoint);
if (panel_node) {
dp->panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (!dp->panel)
return -EPROBE_DEFER;
} else {
DRM_ERROR("no port node for panel device.\n");
return -EINVAL;
}
}
} }
if (endpoint)
goto out;
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (endpoint) { if (endpoint) {
bridge_node = of_graph_get_remote_port_parent(endpoint); bridge_node = of_graph_get_remote_port_parent(endpoint);
if (bridge_node) { if (bridge_node) {
dp->bridge = of_drm_find_bridge(bridge_node); dp->ptn_bridge = of_drm_find_bridge(bridge_node);
of_node_put(bridge_node); of_node_put(bridge_node);
if (!dp->bridge) if (!dp->ptn_bridge)
return -EPROBE_DEFER; return -EPROBE_DEFER;
} else } else
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
return component_add(&pdev->dev, &exynos_dp_ops); out:
pm_runtime_enable(dev);
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
goto err_disable_pm_runtime;
return ret;
err_disable_pm_runtime:
pm_runtime_disable(dev);
return ret;
} }
static int exynos_dp_remove(struct platform_device *pdev) static int exynos_dp_remove(struct platform_device *pdev)
{ {
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &exynos_dp_ops); component_del(&pdev->dev, &exynos_dp_ops);
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_dp_suspend(struct device *dev)
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
clk_disable_unprepare(dp->clock);
return 0;
}
static int exynos_dp_resume(struct device *dev)
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(dp->clock);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops exynos_dp_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
};
static const struct of_device_id exynos_dp_match[] = { static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" }, { .compatible = "samsung,exynos5-dp" },
{}, {},
...@@ -1395,6 +1505,7 @@ struct platform_driver dp_driver = { ...@@ -1395,6 +1505,7 @@ struct platform_driver dp_driver = {
.driver = { .driver = {
.name = "exynos-dp", .name = "exynos-dp",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_dp_pm_ops,
.of_match_table = exynos_dp_match, .of_match_table = exynos_dp_match,
}, },
}; };
......
...@@ -153,6 +153,7 @@ struct exynos_dp_device { ...@@ -153,6 +153,7 @@ struct exynos_dp_device {
struct drm_connector connector; struct drm_connector connector;
struct drm_panel *panel; struct drm_panel *panel;
struct drm_bridge *bridge; struct drm_bridge *bridge;
struct drm_bridge *ptn_bridge;
struct clk *clock; struct clk *clock;
unsigned int irq; unsigned int irq;
void __iomem *reg_base; void __iomem *reg_base;
......
...@@ -304,45 +304,6 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, ...@@ -304,45 +304,6 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_connector *connector;
drm_modeset_lock_all(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int old_dpms = connector->dpms;
if (connector->funcs->dpms)
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
/* Set the old mode back to the connector for resume */
connector->dpms = old_dpms;
}
drm_modeset_unlock_all(dev);
return 0;
}
static int exynos_drm_resume(struct drm_device *dev)
{
struct drm_connector *connector;
drm_modeset_lock_all(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->funcs->dpms) {
int dpms = connector->dpms;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->funcs->dpms(connector, dpms);
}
}
drm_modeset_unlock_all(dev);
return 0;
}
#endif
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv; struct drm_exynos_file_private *file_priv;
...@@ -476,31 +437,54 @@ static struct drm_driver exynos_drm_driver = { ...@@ -476,31 +437,54 @@ static struct drm_driver exynos_drm_driver = {
}; };
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int exynos_drm_sys_suspend(struct device *dev) static int exynos_drm_suspend(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message; struct drm_connector *connector;
if (pm_runtime_suspended(dev) || !drm_dev) if (pm_runtime_suspended(dev) || !drm_dev)
return 0; return 0;
message.event = PM_EVENT_SUSPEND; drm_modeset_lock_all(drm_dev);
return exynos_drm_suspend(drm_dev, message); drm_for_each_connector(connector, drm_dev) {
int old_dpms = connector->dpms;
if (connector->funcs->dpms)
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
/* Set the old mode back to the connector for resume */
connector->dpms = old_dpms;
}
drm_modeset_unlock_all(drm_dev);
return 0;
} }
static int exynos_drm_sys_resume(struct device *dev) static int exynos_drm_resume(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
struct drm_connector *connector;
if (pm_runtime_suspended(dev) || !drm_dev) if (pm_runtime_suspended(dev) || !drm_dev)
return 0; return 0;
return exynos_drm_resume(drm_dev); drm_modeset_lock_all(drm_dev);
drm_for_each_connector(connector, drm_dev) {
if (connector->funcs->dpms) {
int dpms = connector->dpms;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->funcs->dpms(connector, dpms);
}
}
drm_modeset_unlock_all(drm_dev);
return 0;
} }
#endif #endif
static const struct dev_pm_ops exynos_drm_pm_ops = { static const struct dev_pm_ops exynos_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume) SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_suspend, exynos_drm_resume)
}; };
/* forward declaration */ /* forward declaration */
......
...@@ -38,24 +38,44 @@ enum exynos_drm_output_type { ...@@ -38,24 +38,44 @@ enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_VIDI, EXYNOS_DISPLAY_TYPE_VIDI,
}; };
struct exynos_drm_rect {
unsigned int x, y;
unsigned int w, h;
};
/* /*
* Exynos drm common overlay structure. * Exynos drm plane state structure.
* *
* @base: plane object * @base: plane_state object (contains drm_framebuffer pointer)
* @src_x: offset x on a framebuffer to be displayed. * @src: rectangle of the source image data to be displayed (clipped to
* - the unit is screen coordinates. * visible part).
* @src_y: offset y on a framebuffer to be displayed. * @crtc: rectangle of the target image position on hardware screen
* - the unit is screen coordinates. * (clipped to visible part).
* @src_w: width of a partial image to be displayed from framebuffer.
* @src_h: height of a partial image to be displayed from framebuffer.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_w: window width to be displayed (hardware screen).
* @crtc_h: window height to be displayed (hardware screen).
* @h_ratio: horizontal scaling ratio, 16.16 fixed point * @h_ratio: horizontal scaling ratio, 16.16 fixed point
* @v_ratio: vertical scaling ratio, 16.16 fixed point * @v_ratio: vertical scaling ratio, 16.16 fixed point
* @dma_addr: array of bus(accessed by dma) address to the memory region *
* allocated for a overlay. * this structure consists plane state data that will be applied to hardware
* specific overlay info.
*/
struct exynos_drm_plane_state {
struct drm_plane_state base;
struct exynos_drm_rect crtc;
struct exynos_drm_rect src;
unsigned int h_ratio;
unsigned int v_ratio;
};
static inline struct exynos_drm_plane_state *
to_exynos_plane_state(struct drm_plane_state *state)
{
return container_of(state, struct exynos_drm_plane_state, base);
}
/*
* Exynos drm common overlay structure.
*
* @base: plane object
* @zpos: order of overlay layer(z position). * @zpos: order of overlay layer(z position).
* *
* this structure is common to exynos SoC and its contents would be copied * this structure is common to exynos SoC and its contents would be copied
...@@ -64,21 +84,32 @@ enum exynos_drm_output_type { ...@@ -64,21 +84,32 @@ enum exynos_drm_output_type {
struct exynos_drm_plane { struct exynos_drm_plane {
struct drm_plane base; struct drm_plane base;
unsigned int src_x; const struct exynos_drm_plane_config *config;
unsigned int src_y;
unsigned int src_w;
unsigned int src_h;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_w;
unsigned int crtc_h;
unsigned int h_ratio;
unsigned int v_ratio;
dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos; unsigned int zpos;
struct drm_framebuffer *pending_fb; struct drm_framebuffer *pending_fb;
}; };
#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0)
#define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1)
/*
* Exynos DRM plane configuration structure.
*
* @zpos: z-position of the plane.
* @type: type of the plane (primary, cursor or overlay).
* @pixel_formats: supported pixel formats.
* @num_pixel_formats: number of elements in 'pixel_formats'.
* @capabilities: supported features (see EXYNOS_DRM_PLANE_CAP_*)
*/
struct exynos_drm_plane_config {
unsigned int zpos;
enum drm_plane_type type;
const uint32_t *pixel_formats;
unsigned int num_pixel_formats;
unsigned int capabilities;
};
/* /*
* Exynos drm crtc ops * Exynos drm crtc ops
* *
......
...@@ -1458,66 +1458,6 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = { ...@@ -1458,66 +1458,6 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = {
.transfer = exynos_dsi_host_transfer, .transfer = exynos_dsi_host_transfer,
}; };
static int exynos_dsi_poweron(struct exynos_dsi *dsi)
{
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) {
dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
return ret;
}
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_clk;
}
return 0;
err_clk:
while (--i > -1)
clk_disable_unprepare(dsi->clks[i]);
regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
return ret;
}
static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int ret, i;
usleep_range(10000, 20000);
if (dsi->state & DSIM_STATE_INITIALIZED) {
dsi->state &= ~DSIM_STATE_INITIALIZED;
exynos_dsi_disable_clock(dsi);
exynos_dsi_disable_irq(dsi);
}
dsi->state &= ~DSIM_STATE_CMD_LPM;
phy_power_off(dsi->phy);
for (i = driver_data->num_clks - 1; i > -1; i--)
clk_disable_unprepare(dsi->clks[i]);
ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
if (ret < 0)
dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
}
static void exynos_dsi_enable(struct drm_encoder *encoder) static void exynos_dsi_enable(struct drm_encoder *encoder)
{ {
struct exynos_dsi *dsi = encoder_to_dsi(encoder); struct exynos_dsi *dsi = encoder_to_dsi(encoder);
...@@ -1526,16 +1466,14 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ...@@ -1526,16 +1466,14 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
if (dsi->state & DSIM_STATE_ENABLED) if (dsi->state & DSIM_STATE_ENABLED)
return; return;
ret = exynos_dsi_poweron(dsi); pm_runtime_get_sync(dsi->dev);
if (ret < 0)
return;
dsi->state |= DSIM_STATE_ENABLED; dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_prepare(dsi->panel); ret = drm_panel_prepare(dsi->panel);
if (ret < 0) { if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED; dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi); pm_runtime_put_sync(dsi->dev);
return; return;
} }
...@@ -1547,7 +1485,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder) ...@@ -1547,7 +1485,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
dsi->state &= ~DSIM_STATE_ENABLED; dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_set_display_enable(dsi, false); exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel); drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi); pm_runtime_put_sync(dsi->dev);
return; return;
} }
...@@ -1569,7 +1507,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder) ...@@ -1569,7 +1507,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
dsi->state &= ~DSIM_STATE_ENABLED; dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi); pm_runtime_put_sync(dsi->dev);
} }
static enum drm_connector_status static enum drm_connector_status
...@@ -1797,13 +1735,13 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) ...@@ -1797,13 +1735,13 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
ep = of_graph_get_next_endpoint(node, NULL); ep = of_graph_get_next_endpoint(node, NULL);
if (!ep) { if (!ep) {
ret = -ENXIO; ret = -EINVAL;
goto end; goto end;
} }
dsi->bridge_node = of_graph_get_remote_port_parent(ep); dsi->bridge_node = of_graph_get_remote_port_parent(ep);
if (!dsi->bridge_node) { if (!dsi->bridge_node) {
ret = -ENXIO; ret = -EINVAL;
goto end; goto end;
} }
end: end:
...@@ -1954,22 +1892,99 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1954,22 +1892,99 @@ static int exynos_dsi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &dsi->encoder); platform_set_drvdata(pdev, &dsi->encoder);
pm_runtime_enable(dev);
return component_add(dev, &exynos_dsi_component_ops); return component_add(dev, &exynos_dsi_component_ops);
} }
static int exynos_dsi_remove(struct platform_device *pdev) static int exynos_dsi_remove(struct platform_device *pdev)
{ {
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &exynos_dsi_component_ops); component_del(&pdev->dev, &exynos_dsi_component_ops);
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_dsi_suspend(struct device *dev)
{
struct drm_encoder *encoder = dev_get_drvdata(dev);
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int ret, i;
usleep_range(10000, 20000);
if (dsi->state & DSIM_STATE_INITIALIZED) {
dsi->state &= ~DSIM_STATE_INITIALIZED;
exynos_dsi_disable_clock(dsi);
exynos_dsi_disable_irq(dsi);
}
dsi->state &= ~DSIM_STATE_CMD_LPM;
phy_power_off(dsi->phy);
for (i = driver_data->num_clks - 1; i > -1; i--)
clk_disable_unprepare(dsi->clks[i]);
ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
if (ret < 0)
dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
return 0;
}
static int exynos_dsi_resume(struct device *dev)
{
struct drm_encoder *encoder = dev_get_drvdata(dev);
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
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) {
dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
return ret;
}
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_clk;
}
return 0;
err_clk:
while (--i > -1)
clk_disable_unprepare(dsi->clks[i]);
regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
return ret;
}
#endif
static const struct dev_pm_ops exynos_dsi_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
};
struct platform_driver dsi_driver = { struct platform_driver dsi_driver = {
.probe = exynos_dsi_probe, .probe = exynos_dsi_probe,
.remove = exynos_dsi_remove, .remove = exynos_dsi_remove,
.driver = { .driver = {
.name = "exynos-dsi", .name = "exynos-dsi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_dsi_pm_ops,
.of_match_table = exynos_dsi_of_match, .of_match_table = exynos_dsi_of_match,
}, },
}; };
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
struct exynos_drm_fb { struct exynos_drm_fb {
struct drm_framebuffer fb; struct drm_framebuffer fb;
struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER]; struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
dma_addr_t dma_addr[MAX_FB_BUFFER];
}; };
static int check_fb_gem_memory_type(struct drm_device *drm_dev, static int check_fb_gem_memory_type(struct drm_device *drm_dev,
...@@ -135,6 +136,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev, ...@@ -135,6 +136,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
goto err; goto err;
exynos_fb->exynos_gem[i] = exynos_gem[i]; exynos_fb->exynos_gem[i] = exynos_gem[i];
exynos_fb->dma_addr[i] = exynos_gem[i]->dma_addr
+ mode_cmd->offsets[i];
} }
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
...@@ -189,21 +192,14 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, ...@@ -189,21 +192,14 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index) dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index)
{ {
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
struct exynos_drm_gem *exynos_gem;
if (index >= MAX_FB_BUFFER) if (index >= MAX_FB_BUFFER)
return NULL; return DMA_ERROR_CODE;
exynos_gem = exynos_fb->exynos_gem[index]; return exynos_fb->dma_addr[index];
if (!exynos_gem)
return NULL;
DRM_DEBUG_KMS("dma_addr: 0x%lx\n", (unsigned long)exynos_gem->dma_addr);
return exynos_gem;
} }
static void exynos_drm_output_poll_changed(struct drm_device *dev) static void exynos_drm_output_poll_changed(struct drm_device *dev)
......
...@@ -22,8 +22,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, ...@@ -22,8 +22,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
struct exynos_drm_gem **exynos_gem, struct exynos_drm_gem **exynos_gem,
int count); int count);
/* get gem object of a drm framebuffer */ dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index);
struct exynos_drm_gem *exynos_drm_fb_gem(struct drm_framebuffer *fb, int index);
void exynos_drm_mode_config_init(struct drm_device *dev); void exynos_drm_mode_config_init(struct drm_device *dev);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h" #include "exynos_drm_fbdev.h"
#include "exynos_drm_crtc.h" #include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
...@@ -87,7 +88,6 @@ ...@@ -87,7 +88,6 @@
/* FIMD has totally five hardware windows. */ /* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5 #define WINDOWS_NR 5
#define CURSOR_WIN 4
struct fimd_driver_data { struct fimd_driver_data {
unsigned int timing_base; unsigned int timing_base;
...@@ -150,6 +150,7 @@ struct fimd_context { ...@@ -150,6 +150,7 @@ struct fimd_context {
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc; struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR]; struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
struct clk *bus_clk; struct clk *bus_clk;
struct clk *lcd_clk; struct clk *lcd_clk;
void __iomem *regs; void __iomem *regs;
...@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = { ...@@ -187,6 +188,14 @@ static const struct of_device_id fimd_driver_dt_match[] = {
}; };
MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
static const enum drm_plane_type fimd_win_types[WINDOWS_NR] = {
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_CURSOR,
};
static const uint32_t fimd_formats[] = { static const uint32_t fimd_formats[] = {
DRM_FORMAT_C8, DRM_FORMAT_C8,
DRM_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555,
...@@ -478,7 +487,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) ...@@ -478,7 +487,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
struct drm_framebuffer *fb) uint32_t pixel_format, int width)
{ {
unsigned long val; unsigned long val;
...@@ -489,11 +498,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, ...@@ -489,11 +498,11 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
* So the request format is ARGB8888 then change it to XRGB8888. * So the request format is ARGB8888 then change it to XRGB8888.
*/ */
if (ctx->driver_data->has_limited_fmt && !win) { if (ctx->driver_data->has_limited_fmt && !win) {
if (fb->pixel_format == DRM_FORMAT_ARGB8888) if (pixel_format == DRM_FORMAT_ARGB8888)
fb->pixel_format = DRM_FORMAT_XRGB8888; pixel_format = DRM_FORMAT_XRGB8888;
} }
switch (fb->pixel_format) { switch (pixel_format) {
case DRM_FORMAT_C8: case DRM_FORMAT_C8:
val |= WINCON0_BPPMODE_8BPP_PALETTE; val |= WINCON0_BPPMODE_8BPP_PALETTE;
val |= WINCONx_BURSTLEN_8WORD; val |= WINCONx_BURSTLEN_8WORD;
...@@ -529,17 +538,15 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, ...@@ -529,17 +538,15 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
break; break;
} }
DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
/* /*
* In case of exynos, setting dma-burst to 16Word causes permanent * Setting dma-burst to 16Word causes permanent tearing for very small
* tearing for very small buffers, e.g. cursor buffer. Burst Mode * buffers, e.g. cursor buffer. Burst Mode switching which based on
* switching which is based on plane size is not recommended as * plane size is not recommended as plane size varies alot towards the
* plane size varies alot towards the end of the screen and rapid * end of the screen and rapid movement causes unstable DMA, but it is
* movement causes unstable DMA which results into iommu crash/tear. * still better to change dma-burst than displaying garbage.
*/ */
if (fb->width < MIN_FB_WIDTH_FOR_16WORD_BURST) { if (width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
val &= ~WINCONx_BURSTLEN_MASK; val &= ~WINCONx_BURSTLEN_MASK;
val |= WINCONx_BURSTLEN_4WORD; val |= WINCONx_BURSTLEN_4WORD;
} }
...@@ -640,39 +647,41 @@ static void fimd_atomic_flush(struct exynos_drm_crtc *crtc, ...@@ -640,39 +647,41 @@ static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
static void fimd_update_plane(struct exynos_drm_crtc *crtc, static void fimd_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct exynos_drm_plane_state *state =
to_exynos_plane_state(plane->base.state);
struct fimd_context *ctx = crtc->ctx; struct fimd_context *ctx = crtc->ctx;
struct drm_plane_state *state = plane->base.state; struct drm_framebuffer *fb = state->base.fb;
dma_addr_t dma_addr; dma_addr_t dma_addr;
unsigned long val, size, offset; unsigned long val, size, offset;
unsigned int last_x, last_y, buf_offsize, line_size; unsigned int last_x, last_y, buf_offsize, line_size;
unsigned int win = plane->zpos; unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3; unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0]; unsigned int pitch = fb->pitches[0];
if (ctx->suspended) if (ctx->suspended)
return; return;
offset = plane->src_x * bpp; offset = state->src.x * bpp;
offset += plane->src_y * pitch; offset += state->src.y * pitch;
/* buffer start address */ /* buffer start address */
dma_addr = plane->dma_addr[0] + offset; dma_addr = exynos_drm_fb_dma_addr(fb, 0) + offset;
val = (unsigned long)dma_addr; val = (unsigned long)dma_addr;
writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */ /* buffer end address */
size = pitch * plane->crtc_h; size = pitch * state->crtc.h;
val = (unsigned long)(dma_addr + size); val = (unsigned long)(dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)dma_addr, val, size); (unsigned long)dma_addr, val, size);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
plane->crtc_w, plane->crtc_h); state->crtc.w, state->crtc.h);
/* buffer size */ /* buffer size */
buf_offsize = pitch - (plane->crtc_w * bpp); buf_offsize = pitch - (state->crtc.w * bpp);
line_size = plane->crtc_w * bpp; line_size = state->crtc.w * bpp;
val = VIDW_BUF_SIZE_OFFSET(buf_offsize) | val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
VIDW_BUF_SIZE_PAGEWIDTH(line_size) | VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
VIDW_BUF_SIZE_OFFSET_E(buf_offsize) | VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
...@@ -680,16 +689,16 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, ...@@ -680,16 +689,16 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
/* OSD position */ /* OSD position */
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) | val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
VIDOSDxA_TOPLEFT_Y(plane->crtc_y) | VIDOSDxA_TOPLEFT_Y(state->crtc.y) |
VIDOSDxA_TOPLEFT_X_E(plane->crtc_x) | VIDOSDxA_TOPLEFT_X_E(state->crtc.x) |
VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y); VIDOSDxA_TOPLEFT_Y_E(state->crtc.y);
writel(val, ctx->regs + VIDOSD_A(win)); writel(val, ctx->regs + VIDOSD_A(win));
last_x = plane->crtc_x + plane->crtc_w; last_x = state->crtc.x + state->crtc.w;
if (last_x) if (last_x)
last_x--; last_x--;
last_y = plane->crtc_y + plane->crtc_h; last_y = state->crtc.y + state->crtc.h;
if (last_y) if (last_y)
last_y--; last_y--;
...@@ -699,20 +708,20 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, ...@@ -699,20 +708,20 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDOSD_B(win)); writel(val, ctx->regs + VIDOSD_B(win));
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n", DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
plane->crtc_x, plane->crtc_y, last_x, last_y); state->crtc.x, state->crtc.y, last_x, last_y);
/* OSD size */ /* OSD size */
if (win != 3 && win != 4) { if (win != 3 && win != 4) {
u32 offset = VIDOSD_D(win); u32 offset = VIDOSD_D(win);
if (win == 0) if (win == 0)
offset = VIDOSD_C(win); offset = VIDOSD_C(win);
val = plane->crtc_w * plane->crtc_h; val = state->crtc.w * state->crtc.h;
writel(val, ctx->regs + offset); writel(val, ctx->regs + offset);
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
} }
fimd_win_set_pixfmt(ctx, win, state->fb); fimd_win_set_pixfmt(ctx, win, fb->pixel_format, state->src.w);
/* hardware window 0 doesn't support color key. */ /* hardware window 0 doesn't support color key. */
if (win != 0) if (win != 0)
...@@ -745,7 +754,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, ...@@ -745,7 +754,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
static void fimd_enable(struct exynos_drm_crtc *crtc) static void fimd_enable(struct exynos_drm_crtc *crtc)
{ {
struct fimd_context *ctx = crtc->ctx; struct fimd_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended) if (!ctx->suspended)
return; return;
...@@ -754,18 +762,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc) ...@@ -754,18 +762,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc)
pm_runtime_get_sync(ctx->dev); pm_runtime_get_sync(ctx->dev);
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
return;
}
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
return;
}
/* if vblank was enabled status, enable it again. */ /* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags)) if (test_and_clear_bit(0, &ctx->irq_flags))
fimd_enable_vblank(ctx->crtc); fimd_enable_vblank(ctx->crtc);
...@@ -795,11 +791,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc) ...@@ -795,11 +791,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)
writel(0, ctx->regs + VIDCON0); writel(0, ctx->regs + VIDCON0);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
pm_runtime_put_sync(ctx->dev); pm_runtime_put_sync(ctx->dev);
ctx->suspended = true; ctx->suspended = true;
} }
...@@ -941,18 +933,19 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) ...@@ -941,18 +933,19 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct exynos_drm_private *priv = drm_dev->dev_private; struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type; unsigned int i;
unsigned int zpos;
int ret; int ret;
ctx->drm_dev = drm_dev; ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++; ctx->pipe = priv->pipe++;
for (zpos = 0; zpos < WINDOWS_NR; zpos++) { for (i = 0; i < WINDOWS_NR; i++) {
type = exynos_plane_get_type(zpos, CURSOR_WIN); ctx->configs[i].pixel_formats = fimd_formats;
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
1 << ctx->pipe, type, fimd_formats, ctx->configs[i].zpos = i;
ARRAY_SIZE(fimd_formats), zpos); ctx->configs[i].type = fimd_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
1 << ctx->pipe, &ctx->configs[i]);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1121,12 +1114,49 @@ static int fimd_remove(struct platform_device *pdev) ...@@ -1121,12 +1114,49 @@ static int fimd_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_fimd_suspend(struct device *dev)
{
struct fimd_context *ctx = dev_get_drvdata(dev);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
return 0;
}
static int exynos_fimd_resume(struct device *dev)
{
struct fimd_context *ctx = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(ctx->bus_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(ctx->lcd_clk);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret);
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops exynos_fimd_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL)
};
struct platform_driver fimd_driver = { struct platform_driver fimd_driver = {
.probe = fimd_probe, .probe = fimd_probe,
.remove = fimd_remove, .remove = fimd_remove,
.driver = { .driver = {
.name = "exynos4-fb", .name = "exynos4-fb",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_fimd_pm_ops,
.of_match_table = fimd_driver_dt_match, .of_match_table = fimd_driver_dt_match,
}, },
}; };
...@@ -55,8 +55,6 @@ struct exynos_drm_gem { ...@@ -55,8 +55,6 @@ struct exynos_drm_gem {
struct sg_table *sgt; struct sg_table *sgt;
}; };
struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
/* destroy a buffer with gem object */ /* destroy a buffer with gem object */
void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem); void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem);
...@@ -91,10 +89,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, ...@@ -91,10 +89,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *filp); struct drm_file *filp);
/* map user space allocated by malloc to pages. */
int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* get buffer information to memory region allocated by gem. */ /* get buffer information to memory region allocated by gem. */
int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
...@@ -123,28 +117,6 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); ...@@ -123,28 +117,6 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
/* set vm_flags and we can change the vm attribute to other one at here. */ /* set vm_flags and we can change the vm attribute to other one at here. */
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
static inline int vma_is_io(struct vm_area_struct *vma)
{
return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
}
/* get a copy of a virtual memory region. */
struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma);
/* release a userspace virtual memory area. */
void exynos_gem_put_vma(struct vm_area_struct *vma);
/* get pages from user space. */
int exynos_gem_get_pages_from_userptr(unsigned long start,
unsigned int npages,
struct page **pages,
struct vm_area_struct *vma);
/* drop the reference to pages. */
void exynos_gem_put_pages_to_userptr(struct page **pages,
unsigned int npages,
struct vm_area_struct *vma);
/* map sgt with dma region. */ /* map sgt with dma region. */
int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
struct sg_table *sgt, struct sg_table *sgt,
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <plat/map-base.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -126,6 +127,7 @@ struct gsc_capability { ...@@ -126,6 +127,7 @@ struct gsc_capability {
* @ippdrv: prepare initialization using ippdrv. * @ippdrv: prepare initialization using ippdrv.
* @regs_res: register resources. * @regs_res: register resources.
* @regs: memory mapped io registers. * @regs: memory mapped io registers.
* @sysreg: handle to SYSREG block regmap.
* @lock: locking of operations. * @lock: locking of operations.
* @gsc_clk: gsc gate clock. * @gsc_clk: gsc gate clock.
* @sc: scaler infomations. * @sc: scaler infomations.
...@@ -138,6 +140,7 @@ struct gsc_context { ...@@ -138,6 +140,7 @@ struct gsc_context {
struct exynos_drm_ippdrv ippdrv; struct exynos_drm_ippdrv ippdrv;
struct resource *regs_res; struct resource *regs_res;
void __iomem *regs; void __iomem *regs;
struct regmap *sysreg;
struct mutex lock; struct mutex lock;
struct clk *gsc_clk; struct clk *gsc_clk;
struct gsc_scaler sc; struct gsc_scaler sc;
...@@ -437,9 +440,12 @@ static int gsc_sw_reset(struct gsc_context *ctx) ...@@ -437,9 +440,12 @@ static int gsc_sw_reset(struct gsc_context *ctx)
static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
{ {
u32 gscblk_cfg; unsigned int gscblk_cfg;
gscblk_cfg = readl(SYSREG_GSCBLK_CFG1); if (!ctx->sysreg)
return;
regmap_read(ctx->sysreg, SYSREG_GSCBLK_CFG1, &gscblk_cfg);
if (enable) if (enable)
gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) | gscblk_cfg |= GSC_BLK_DISP1WB_DEST(ctx->id) |
...@@ -448,7 +454,7 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) ...@@ -448,7 +454,7 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
else else
gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id); gscblk_cfg |= GSC_BLK_PXLASYNC_LO_MASK_WB(ctx->id);
writel(gscblk_cfg, SYSREG_GSCBLK_CFG1); regmap_write(ctx->sysreg, SYSREG_GSCBLK_CFG1, gscblk_cfg);
} }
static void gsc_handle_irq(struct gsc_context *ctx, bool enable, static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
...@@ -1215,10 +1221,10 @@ static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) ...@@ -1215,10 +1221,10 @@ static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable); DRM_DEBUG_KMS("enable[%d]\n", enable);
if (enable) { if (enable) {
clk_enable(ctx->gsc_clk); clk_prepare_enable(ctx->gsc_clk);
ctx->suspended = false; ctx->suspended = false;
} else { } else {
clk_disable(ctx->gsc_clk); clk_disable_unprepare(ctx->gsc_clk);
ctx->suspended = true; ctx->suspended = true;
} }
...@@ -1663,6 +1669,15 @@ static int gsc_probe(struct platform_device *pdev) ...@@ -1663,6 +1669,15 @@ static int gsc_probe(struct platform_device *pdev)
if (!ctx) if (!ctx)
return -ENOMEM; return -ENOMEM;
if (dev->of_node) {
ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,sysreg");
if (IS_ERR(ctx->sysreg)) {
dev_warn(dev, "failed to get system register.\n");
ctx->sysreg = NULL;
}
}
/* clock control */ /* clock control */
ctx->gsc_clk = devm_clk_get(dev, "gscl"); ctx->gsc_clk = devm_clk_get(dev, "gscl");
if (IS_ERR(ctx->gsc_clk)) { if (IS_ERR(ctx->gsc_clk)) {
...@@ -1713,7 +1728,6 @@ static int gsc_probe(struct platform_device *pdev) ...@@ -1713,7 +1728,6 @@ static int gsc_probe(struct platform_device *pdev)
mutex_init(&ctx->lock); mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx);
pm_runtime_set_active(dev);
pm_runtime_enable(dev); pm_runtime_enable(dev);
ret = exynos_drm_ippdrv_register(ippdrv); ret = exynos_drm_ippdrv_register(ippdrv);
...@@ -1797,6 +1811,12 @@ static const struct dev_pm_ops gsc_pm_ops = { ...@@ -1797,6 +1811,12 @@ static const struct dev_pm_ops gsc_pm_ops = {
SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
}; };
static const struct of_device_id exynos_drm_gsc_of_match[] = {
{ .compatible = "samsung,exynos5-gsc" },
{ },
};
MODULE_DEVICE_TABLE(of, exynos_drm_gsc_of_match);
struct platform_driver gsc_driver = { struct platform_driver gsc_driver = {
.probe = gsc_probe, .probe = gsc_probe,
.remove = gsc_remove, .remove = gsc_remove,
...@@ -1804,6 +1824,7 @@ struct platform_driver gsc_driver = { ...@@ -1804,6 +1824,7 @@ struct platform_driver gsc_driver = {
.name = "exynos-drm-gsc", .name = "exynos-drm-gsc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &gsc_pm_ops, .pm = &gsc_pm_ops,
.of_match_table = of_match_ptr(exynos_drm_gsc_of_match),
}, },
}; };
...@@ -56,93 +56,170 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last) ...@@ -56,93 +56,170 @@ static int exynos_plane_get_size(int start, unsigned length, unsigned last)
return size; return size;
} }
static void exynos_plane_mode_set(struct drm_plane *plane, static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
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_plane_state *state = &exynos_state->base;
struct drm_crtc *crtc = exynos_state->base.crtc;
struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct drm_display_mode *mode = &crtc->state->adjusted_mode;
int crtc_x, crtc_y;
unsigned int crtc_w, crtc_h;
unsigned int src_x, src_y;
unsigned int src_w, src_h;
unsigned int actual_w; unsigned int actual_w;
unsigned int actual_h; unsigned int actual_h;
/*
* The original src/dest coordinates are stored in exynos_state->base,
* but we want to keep another copy internal to our driver that we can
* clip/modify ourselves.
*/
crtc_x = state->crtc_x;
crtc_y = state->crtc_y;
crtc_w = state->crtc_w;
crtc_h = state->crtc_h;
src_x = state->src_x >> 16;
src_y = state->src_y >> 16;
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
/* set ratio */
exynos_state->h_ratio = (src_w << 16) / crtc_w;
exynos_state->v_ratio = (src_h << 16) / crtc_h;
/* clip to visible area */
actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay); actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay); actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
if (crtc_x < 0) { if (crtc_x < 0) {
if (actual_w) if (actual_w)
src_x -= crtc_x; src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16;
crtc_x = 0; crtc_x = 0;
} }
if (crtc_y < 0) { if (crtc_y < 0) {
if (actual_h) if (actual_h)
src_y -= crtc_y; src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16;
crtc_y = 0; crtc_y = 0;
} }
/* set ratio */
exynos_plane->h_ratio = (src_w << 16) / crtc_w;
exynos_plane->v_ratio = (src_h << 16) / crtc_h;
/* set drm framebuffer data. */ /* set drm framebuffer data. */
exynos_plane->src_x = src_x; exynos_state->src.x = src_x;
exynos_plane->src_y = src_y; exynos_state->src.y = src_y;
exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16; exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16;
exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16; exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16;
/* set plane range to be displayed. */ /* set plane range to be displayed. */
exynos_plane->crtc_x = crtc_x; exynos_state->crtc.x = crtc_x;
exynos_plane->crtc_y = crtc_y; exynos_state->crtc.y = crtc_y;
exynos_plane->crtc_w = actual_w; exynos_state->crtc.w = actual_w;
exynos_plane->crtc_h = actual_h; exynos_state->crtc.h = actual_h;
DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)", DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
exynos_plane->crtc_x, exynos_plane->crtc_y, exynos_state->crtc.x, exynos_state->crtc.y,
exynos_plane->crtc_w, exynos_plane->crtc_h); exynos_state->crtc.w, exynos_state->crtc.h);
}
static void exynos_drm_plane_reset(struct drm_plane *plane)
{
struct exynos_drm_plane_state *exynos_state;
if (plane->state) {
exynos_state = to_exynos_plane_state(plane->state);
if (exynos_state->base.fb)
drm_framebuffer_unreference(exynos_state->base.fb);
kfree(exynos_state);
plane->state = NULL;
}
exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
if (exynos_state) {
plane->state = &exynos_state->base;
plane->state->plane = plane;
}
}
static struct drm_plane_state *
exynos_drm_plane_duplicate_state(struct drm_plane *plane)
{
struct exynos_drm_plane_state *exynos_state;
struct exynos_drm_plane_state *copy;
exynos_state = to_exynos_plane_state(plane->state);
copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
if (!copy)
return NULL;
__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
return &copy->base;
}
plane->crtc = crtc; static void exynos_drm_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct exynos_drm_plane_state *old_exynos_state =
to_exynos_plane_state(old_state);
__drm_atomic_helper_plane_destroy_state(plane, old_state);
kfree(old_exynos_state);
} }
static struct drm_plane_funcs exynos_plane_funcs = { static struct drm_plane_funcs exynos_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane, .update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane, .disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup, .destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset, .reset = exynos_drm_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_duplicate_state = exynos_drm_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_destroy_state = exynos_drm_plane_destroy_state,
}; };
static int
exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config,
struct exynos_drm_plane_state *state)
{
bool width_ok = false, height_ok = false;
if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE)
return 0;
if (state->src.w == state->crtc.w)
width_ok = true;
if (state->src.h == state->crtc.h)
height_ok = true;
if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
state->h_ratio == (1 << 15))
width_ok = true;
if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
state->v_ratio == (1 << 15))
height_ok = true;
if (width_ok & height_ok)
return 0;
DRM_DEBUG_KMS("scaling mode is not supported");
return -ENOTSUPP;
}
static int exynos_plane_atomic_check(struct drm_plane *plane, static int exynos_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state) struct drm_plane_state *state)
{ {
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
int nr; struct exynos_drm_plane_state *exynos_state =
int i; to_exynos_plane_state(state);
int ret = 0;
if (!state->fb) if (!state->crtc || !state->fb)
return 0; return 0;
nr = drm_format_num_planes(state->fb->pixel_format); /* translate state into exynos_state */
for (i = 0; i < nr; i++) { exynos_plane_mode_set(exynos_state);
struct exynos_drm_gem *exynos_gem =
exynos_drm_fb_gem(state->fb, i);
if (!exynos_gem) {
DRM_DEBUG_KMS("gem object is null\n");
return -EFAULT;
}
exynos_plane->dma_addr[i] = exynos_gem->dma_addr +
state->fb->offsets[i];
DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
i, (unsigned long)exynos_plane->dma_addr[i]);
}
return 0; ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
return ret;
} }
static void exynos_plane_atomic_update(struct drm_plane *plane, static void exynos_plane_atomic_update(struct drm_plane *plane,
...@@ -155,12 +232,7 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, ...@@ -155,12 +232,7 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
if (!state->crtc) if (!state->crtc)
return; return;
exynos_plane_mode_set(plane, state->crtc, state->fb, plane->crtc = state->crtc;
state->crtc_x, state->crtc_y,
state->crtc_w, state->crtc_h,
state->src_x >> 16, state->src_y >> 16,
state->src_w >> 16, state->src_h >> 16);
exynos_plane->pending_fb = state->fb; exynos_plane->pending_fb = state->fb;
if (exynos_crtc->ops->update_plane) if (exynos_crtc->ops->update_plane)
...@@ -177,8 +249,7 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane, ...@@ -177,8 +249,7 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane,
return; return;
if (exynos_crtc->ops->disable_plane) if (exynos_crtc->ops->disable_plane)
exynos_crtc->ops->disable_plane(exynos_crtc, exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
exynos_plane);
} }
static const struct drm_plane_helper_funcs plane_helper_funcs = { static const struct drm_plane_helper_funcs plane_helper_funcs = {
...@@ -207,28 +278,19 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane, ...@@ -207,28 +278,19 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
drm_object_attach_property(&plane->base, prop, zpos); drm_object_attach_property(&plane->base, prop, zpos);
} }
enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
unsigned int cursor_win)
{
if (zpos == DEFAULT_WIN)
return DRM_PLANE_TYPE_PRIMARY;
else if (zpos == cursor_win)
return DRM_PLANE_TYPE_CURSOR;
else
return DRM_PLANE_TYPE_OVERLAY;
}
int exynos_plane_init(struct drm_device *dev, int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane, struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type, unsigned long possible_crtcs,
const uint32_t *formats, unsigned int fcount, const struct exynos_drm_plane_config *config)
unsigned int zpos)
{ {
int err; int err;
err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, err = drm_universal_plane_init(dev, &exynos_plane->base,
&exynos_plane_funcs, formats, fcount, possible_crtcs,
type, NULL); &exynos_plane_funcs,
config->pixel_formats,
config->num_pixel_formats,
config->type, NULL);
if (err) { if (err) {
DRM_ERROR("failed to initialize plane\n"); DRM_ERROR("failed to initialize plane\n");
return err; return err;
...@@ -236,10 +298,12 @@ int exynos_plane_init(struct drm_device *dev, ...@@ -236,10 +298,12 @@ int exynos_plane_init(struct drm_device *dev,
drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs); drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
exynos_plane->zpos = zpos; exynos_plane->zpos = config->zpos;
exynos_plane->config = config;
if (type == DRM_PLANE_TYPE_OVERLAY) if (config->type == DRM_PLANE_TYPE_OVERLAY)
exynos_plane_attach_zpos_property(&exynos_plane->base, zpos); exynos_plane_attach_zpos_property(&exynos_plane->base,
config->zpos);
return 0; return 0;
} }
...@@ -9,10 +9,7 @@ ...@@ -9,10 +9,7 @@
* *
*/ */
enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
unsigned int cursor_win);
int exynos_plane_init(struct drm_device *dev, int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane, struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type, unsigned long possible_crtcs,
const uint32_t *formats, unsigned int fcount, const struct exynos_drm_plane_config *config);
unsigned int zpos);
...@@ -790,10 +790,10 @@ static int rotator_remove(struct platform_device *pdev) ...@@ -790,10 +790,10 @@ static int rotator_remove(struct platform_device *pdev)
static int rotator_clk_crtl(struct rot_context *rot, bool enable) static int rotator_clk_crtl(struct rot_context *rot, bool enable)
{ {
if (enable) { if (enable) {
clk_enable(rot->clock); clk_prepare_enable(rot->clock);
rot->suspended = false; rot->suspended = false;
} else { } else {
clk_disable(rot->clock); clk_disable_unprepare(rot->clock);
rot->suspended = true; rot->suspended = true;
} }
......
...@@ -24,12 +24,12 @@ ...@@ -24,12 +24,12 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h" #include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h" #include "exynos_drm_vidi.h"
/* vidi has totally three virtual windows. */ /* vidi has totally three virtual windows. */
#define WINDOWS_NR 3 #define WINDOWS_NR 3
#define CURSOR_WIN 2
#define ctx_from_connector(c) container_of(c, struct vidi_context, \ #define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector) connector)
...@@ -89,6 +89,12 @@ static const uint32_t formats[] = { ...@@ -89,6 +89,12 @@ static const uint32_t formats[] = {
DRM_FORMAT_NV12, DRM_FORMAT_NV12,
}; };
static const enum drm_plane_type vidi_win_types[WINDOWS_NR] = {
DRM_PLANE_TYPE_PRIMARY,
DRM_PLANE_TYPE_OVERLAY,
DRM_PLANE_TYPE_CURSOR,
};
static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
{ {
struct vidi_context *ctx = crtc->ctx; struct vidi_context *ctx = crtc->ctx;
...@@ -125,12 +131,15 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc) ...@@ -125,12 +131,15 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
static void vidi_update_plane(struct exynos_drm_crtc *crtc, static void vidi_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct drm_plane_state *state = plane->base.state;
struct vidi_context *ctx = crtc->ctx; struct vidi_context *ctx = crtc->ctx;
dma_addr_t addr;
if (ctx->suspended) if (ctx->suspended)
return; return;
DRM_DEBUG_KMS("dma_addr = %pad\n", plane->dma_addr); addr = exynos_drm_fb_dma_addr(state->fb, 0);
DRM_DEBUG_KMS("dma_addr = %pad\n", &addr);
if (ctx->vblank_on) if (ctx->vblank_on)
schedule_work(&ctx->work); schedule_work(&ctx->work);
...@@ -439,17 +448,21 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) ...@@ -439,17 +448,21 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct drm_encoder *encoder = &ctx->encoder; struct drm_encoder *encoder = &ctx->encoder;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type; struct exynos_drm_plane_config plane_config = { 0 };
unsigned int zpos; unsigned int i;
int pipe, ret; int pipe, ret;
vidi_ctx_initialize(ctx, drm_dev); vidi_ctx_initialize(ctx, drm_dev);
for (zpos = 0; zpos < WINDOWS_NR; zpos++) { plane_config.pixel_formats = formats;
type = exynos_plane_get_type(zpos, CURSOR_WIN); plane_config.num_pixel_formats = ARRAY_SIZE(formats);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, formats, for (i = 0; i < WINDOWS_NR; i++) {
ARRAY_SIZE(formats), zpos); plane_config.zpos = i;
plane_config.type = vidi_win_types[i];
ret = exynos_plane_init(drm_dev, &ctx->planes[i],
1 << ctx->pipe, &plane_config);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -113,7 +113,7 @@ struct hdmi_context { ...@@ -113,7 +113,7 @@ struct hdmi_context {
void __iomem *regs_hdmiphy; void __iomem *regs_hdmiphy;
struct i2c_client *hdmiphy_port; struct i2c_client *hdmiphy_port;
struct i2c_adapter *ddc_adpt; struct i2c_adapter *ddc_adpt;
struct gpio_desc *hpd_gpio; struct gpio_desc *hpd_gpio;
int irq; int irq;
struct regmap *pmureg; struct regmap *pmureg;
struct clk *hdmi; struct clk *hdmi;
...@@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder) ...@@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder)
if (hdata->powered) if (hdata->powered)
return; return;
hdata->powered = true;
pm_runtime_get_sync(hdata->dev); pm_runtime_get_sync(hdata->dev);
if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk)) if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
...@@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder) ...@@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder)
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 1); PMU_HDMI_PHY_ENABLE_BIT, 1);
clk_prepare_enable(hdata->hdmi);
clk_prepare_enable(hdata->sclk_hdmi);
hdmi_conf_apply(hdata); hdmi_conf_apply(hdata);
hdata->powered = true;
} }
static void hdmi_disable(struct drm_encoder *encoder) static void hdmi_disable(struct drm_encoder *encoder)
...@@ -1633,9 +1630,6 @@ static void hdmi_disable(struct drm_encoder *encoder) ...@@ -1633,9 +1630,6 @@ static void hdmi_disable(struct drm_encoder *encoder)
cancel_delayed_work(&hdata->hotplug_work); cancel_delayed_work(&hdata->hotplug_work);
clk_disable_unprepare(hdata->sclk_hdmi);
clk_disable_unprepare(hdata->hdmi);
/* reset pmu hdmiphy control bit to disable hdmiphy */ /* reset pmu hdmiphy control bit to disable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 0); PMU_HDMI_PHY_ENABLE_BIT, 0);
...@@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev) ...@@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int exynos_hdmi_suspend(struct device *dev)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
clk_disable_unprepare(hdata->sclk_hdmi);
clk_disable_unprepare(hdata->hdmi);
return 0;
}
static int exynos_hdmi_resume(struct device *dev)
{
struct hdmi_context *hdata = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(hdata->hdmi);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(hdata->sclk_hdmi);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the sclk_mixer clk [%d]\n",
ret);
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops exynos_hdmi_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL)
};
struct platform_driver hdmi_driver = { struct platform_driver hdmi_driver = {
.probe = hdmi_probe, .probe = hdmi_probe,
.remove = hdmi_remove, .remove = hdmi_remove,
.driver = { .driver = {
.name = "exynos-hdmi", .name = "exynos-hdmi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_hdmi_pm_ops,
.of_match_table = hdmi_match_types, .of_match_table = hdmi_match_types,
}, },
}; };
...@@ -37,12 +37,12 @@ ...@@ -37,12 +37,12 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h" #include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h" #include "exynos_drm_iommu.h"
#define MIXER_WIN_NR 3 #define MIXER_WIN_NR 3
#define VP_DEFAULT_WIN 2 #define VP_DEFAULT_WIN 2
#define CURSOR_WIN 1
/* The pixelformats that are natively supported by the mixer. */ /* The pixelformats that are natively supported by the mixer. */
#define MXR_FORMAT_RGB565 4 #define MXR_FORMAT_RGB565 4
...@@ -111,6 +111,28 @@ struct mixer_drv_data { ...@@ -111,6 +111,28 @@ struct mixer_drv_data {
bool has_sclk; bool has_sclk;
}; };
static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
{
.zpos = 0,
.type = DRM_PLANE_TYPE_PRIMARY,
.pixel_formats = mixer_formats,
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
}, {
.zpos = 1,
.type = DRM_PLANE_TYPE_CURSOR,
.pixel_formats = mixer_formats,
.num_pixel_formats = ARRAY_SIZE(mixer_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_DOUBLE,
}, {
.zpos = 2,
.type = DRM_PLANE_TYPE_OVERLAY,
.pixel_formats = vp_formats,
.num_pixel_formats = ARRAY_SIZE(vp_formats),
.capabilities = EXYNOS_DRM_PLANE_CAP_SCALE,
},
};
static const u8 filter_y_horiz_tap8[] = { static const u8 filter_y_horiz_tap8[] = {
0, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0,
...@@ -399,10 +421,11 @@ static void mixer_stop(struct mixer_context *ctx) ...@@ -399,10 +421,11 @@ static void mixer_stop(struct mixer_context *ctx)
static void vp_video_buffer(struct mixer_context *ctx, static void vp_video_buffer(struct mixer_context *ctx,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct exynos_drm_plane_state *state =
to_exynos_plane_state(plane->base.state);
struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
struct drm_plane_state *state = plane->base.state; struct drm_framebuffer *fb = state->base.fb;
struct drm_framebuffer *fb = state->fb;
struct drm_display_mode *mode = &state->crtc->mode;
unsigned long flags; unsigned long flags;
dma_addr_t luma_addr[2], chroma_addr[2]; dma_addr_t luma_addr[2], chroma_addr[2];
bool tiled_mode = false; bool tiled_mode = false;
...@@ -422,8 +445,8 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -422,8 +445,8 @@ static void vp_video_buffer(struct mixer_context *ctx,
return; return;
} }
luma_addr[0] = plane->dma_addr[0]; luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
chroma_addr[0] = plane->dma_addr[1]; chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
if (mode->flags & DRM_MODE_FLAG_INTERLACE) { if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
ctx->interlace = true; ctx->interlace = true;
...@@ -459,24 +482,24 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -459,24 +482,24 @@ static void vp_video_buffer(struct mixer_context *ctx,
vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) | vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
VP_IMG_VSIZE(fb->height / 2)); VP_IMG_VSIZE(fb->height / 2));
vp_reg_write(res, VP_SRC_WIDTH, plane->src_w); vp_reg_write(res, VP_SRC_WIDTH, state->src.w);
vp_reg_write(res, VP_SRC_HEIGHT, plane->src_h); vp_reg_write(res, VP_SRC_HEIGHT, state->src.h);
vp_reg_write(res, VP_SRC_H_POSITION, vp_reg_write(res, VP_SRC_H_POSITION,
VP_SRC_H_POSITION_VAL(plane->src_x)); VP_SRC_H_POSITION_VAL(state->src.x));
vp_reg_write(res, VP_SRC_V_POSITION, plane->src_y); vp_reg_write(res, VP_SRC_V_POSITION, state->src.y);
vp_reg_write(res, VP_DST_WIDTH, plane->crtc_w); vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
vp_reg_write(res, VP_DST_H_POSITION, plane->crtc_x); vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
if (ctx->interlace) { if (ctx->interlace) {
vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h / 2); vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y / 2); vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
} else { } else {
vp_reg_write(res, VP_DST_HEIGHT, plane->crtc_h); vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h);
vp_reg_write(res, VP_DST_V_POSITION, plane->crtc_y); vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y);
} }
vp_reg_write(res, VP_H_RATIO, plane->h_ratio); vp_reg_write(res, VP_H_RATIO, state->h_ratio);
vp_reg_write(res, VP_V_RATIO, plane->v_ratio); vp_reg_write(res, VP_V_RATIO, state->v_ratio);
vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
...@@ -505,37 +528,14 @@ static void mixer_layer_update(struct mixer_context *ctx) ...@@ -505,37 +528,14 @@ static void mixer_layer_update(struct mixer_context *ctx)
mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE); mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
} }
static int mixer_setup_scale(const struct exynos_drm_plane *plane,
unsigned int *x_ratio, unsigned int *y_ratio)
{
if (plane->crtc_w != plane->src_w) {
if (plane->crtc_w == 2 * plane->src_w)
*x_ratio = 1;
else
goto fail;
}
if (plane->crtc_h != plane->src_h) {
if (plane->crtc_h == 2 * plane->src_h)
*y_ratio = 1;
else
goto fail;
}
return 0;
fail:
DRM_DEBUG_KMS("only 2x width/height scaling of plane supported\n");
return -ENOTSUPP;
}
static void mixer_graph_buffer(struct mixer_context *ctx, static void mixer_graph_buffer(struct mixer_context *ctx,
struct exynos_drm_plane *plane) struct exynos_drm_plane *plane)
{ {
struct exynos_drm_plane_state *state =
to_exynos_plane_state(plane->base.state);
struct drm_display_mode *mode = &state->base.crtc->state->adjusted_mode;
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
struct drm_plane_state *state = plane->base.state; struct drm_framebuffer *fb = state->base.fb;
struct drm_framebuffer *fb = state->fb;
struct drm_display_mode *mode = &state->crtc->mode;
unsigned long flags; unsigned long flags;
unsigned int win = plane->zpos; unsigned int win = plane->zpos;
unsigned int x_ratio = 0, y_ratio = 0; unsigned int x_ratio = 0, y_ratio = 0;
...@@ -567,17 +567,17 @@ static void mixer_graph_buffer(struct mixer_context *ctx, ...@@ -567,17 +567,17 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
return; return;
} }
/* check if mixer supports requested scaling setup */ /* ratio is already checked by common plane code */
if (mixer_setup_scale(plane, &x_ratio, &y_ratio)) x_ratio = state->h_ratio == (1 << 15);
return; y_ratio = state->v_ratio == (1 << 15);
dst_x_offset = plane->crtc_x; dst_x_offset = state->crtc.x;
dst_y_offset = plane->crtc_y; dst_y_offset = state->crtc.y;
/* converting dma address base and source offset */ /* converting dma address base and source offset */
dma_addr = plane->dma_addr[0] dma_addr = exynos_drm_fb_dma_addr(fb, 0)
+ (plane->src_x * fb->bits_per_pixel >> 3) + (state->src.x * fb->bits_per_pixel >> 3)
+ (plane->src_y * fb->pitches[0]); + (state->src.y * fb->pitches[0]);
src_x_offset = 0; src_x_offset = 0;
src_y_offset = 0; src_y_offset = 0;
...@@ -605,8 +605,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, ...@@ -605,8 +605,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
mixer_reg_write(res, MXR_RESOLUTION, val); mixer_reg_write(res, MXR_RESOLUTION, val);
} }
val = MXR_GRP_WH_WIDTH(plane->src_w); val = MXR_GRP_WH_WIDTH(state->src.w);
val |= MXR_GRP_WH_HEIGHT(plane->src_h); val |= MXR_GRP_WH_HEIGHT(state->src.h);
val |= MXR_GRP_WH_H_SCALE(x_ratio); val |= MXR_GRP_WH_H_SCALE(x_ratio);
val |= MXR_GRP_WH_V_SCALE(y_ratio); val |= MXR_GRP_WH_V_SCALE(y_ratio);
mixer_reg_write(res, MXR_GRAPHIC_WH(win), val); mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
...@@ -1020,43 +1020,12 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) ...@@ -1020,43 +1020,12 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
{ {
struct mixer_context *ctx = crtc->ctx; struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
int ret;
if (test_bit(MXR_BIT_POWERED, &ctx->flags)) if (test_bit(MXR_BIT_POWERED, &ctx->flags))
return; return;
pm_runtime_get_sync(ctx->dev); pm_runtime_get_sync(ctx->dev);
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) {
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;
}
}
}
set_bit(MXR_BIT_POWERED, &ctx->flags);
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) { if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) {
...@@ -1064,12 +1033,13 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) ...@@ -1064,12 +1033,13 @@ static void mixer_enable(struct exynos_drm_crtc *crtc)
mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC);
} }
mixer_win_reset(ctx); mixer_win_reset(ctx);
set_bit(MXR_BIT_POWERED, &ctx->flags);
} }
static void mixer_disable(struct exynos_drm_crtc *crtc) static void mixer_disable(struct exynos_drm_crtc *crtc)
{ {
struct mixer_context *ctx = crtc->ctx; struct mixer_context *ctx = crtc->ctx;
struct mixer_resources *res = &ctx->mixer_res;
int i; int i;
if (!test_bit(MXR_BIT_POWERED, &ctx->flags)) if (!test_bit(MXR_BIT_POWERED, &ctx->flags))
...@@ -1081,17 +1051,9 @@ static void mixer_disable(struct exynos_drm_crtc *crtc) ...@@ -1081,17 +1051,9 @@ static void mixer_disable(struct exynos_drm_crtc *crtc)
for (i = 0; i < MIXER_WIN_NR; i++) for (i = 0; i < MIXER_WIN_NR; i++)
mixer_disable_plane(crtc, &ctx->planes[i]); mixer_disable_plane(crtc, &ctx->planes[i]);
clear_bit(MXR_BIT_POWERED, &ctx->flags); pm_runtime_put(ctx->dev);
clk_disable_unprepare(res->hdmi); clear_bit(MXR_BIT_POWERED, &ctx->flags);
clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) {
clk_disable_unprepare(res->vp);
if (ctx->has_sclk)
clk_disable_unprepare(res->sclk_mixer);
}
pm_runtime_put_sync(ctx->dev);
} }
/* Only valid for Mixer version 16.0.33.0 */ /* Only valid for Mixer version 16.0.33.0 */
...@@ -1187,30 +1149,19 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1187,30 +1149,19 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
struct mixer_context *ctx = dev_get_drvdata(dev); struct mixer_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct exynos_drm_plane *exynos_plane; struct exynos_drm_plane *exynos_plane;
unsigned int zpos; unsigned int i;
int ret; int ret;
ret = mixer_initialize(ctx, drm_dev); ret = mixer_initialize(ctx, drm_dev);
if (ret) if (ret)
return ret; return ret;
for (zpos = 0; zpos < MIXER_WIN_NR; zpos++) { for (i = 0; i < MIXER_WIN_NR; i++) {
enum drm_plane_type type; if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
const uint32_t *formats; continue;
unsigned int fcount;
if (zpos < VP_DEFAULT_WIN) {
formats = mixer_formats;
fcount = ARRAY_SIZE(mixer_formats);
} else {
formats = vp_formats;
fcount = ARRAY_SIZE(vp_formats);
}
type = exynos_plane_get_type(zpos, CURSOR_WIN); ret = exynos_plane_init(drm_dev, &ctx->planes[i],
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos], 1 << ctx->pipe, &plane_configs[i]);
1 << ctx->pipe, type, formats, fcount,
zpos);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1293,10 +1244,70 @@ static int mixer_remove(struct platform_device *pdev) ...@@ -1293,10 +1244,70 @@ static int mixer_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int exynos_mixer_suspend(struct device *dev)
{
struct mixer_context *ctx = dev_get_drvdata(dev);
struct mixer_resources *res = &ctx->mixer_res;
clk_disable_unprepare(res->hdmi);
clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) {
clk_disable_unprepare(res->vp);
if (ctx->has_sclk)
clk_disable_unprepare(res->sclk_mixer);
}
return 0;
}
static int exynos_mixer_resume(struct device *dev)
{
struct mixer_context *ctx = dev_get_drvdata(dev);
struct mixer_resources *res = &ctx->mixer_res;
int ret;
ret = clk_prepare_enable(res->mixer);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret);
return ret;
}
ret = clk_prepare_enable(res->hdmi);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
return ret;
}
if (ctx->vp_enabled) {
ret = clk_prepare_enable(res->vp);
if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
ret);
return ret;
}
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 ret;
}
}
}
return 0;
}
#endif
static const struct dev_pm_ops exynos_mixer_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL)
};
struct platform_driver mixer_driver = { struct platform_driver mixer_driver = {
.driver = { .driver = {
.name = "exynos-mixer", .name = "exynos-mixer",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &exynos_mixer_pm_ops,
.of_match_table = mixer_match_types, .of_match_table = mixer_match_types,
}, },
.probe = mixer_probe, .probe = mixer_probe,
......
...@@ -273,12 +273,12 @@ ...@@ -273,12 +273,12 @@
#define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0) #define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0)
/* SYSCON. GSCBLK_CFG */ /* SYSCON. GSCBLK_CFG */
#define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224) #define SYSREG_GSCBLK_CFG1 0x0224
#define GSC_BLK_DISP1WB_DEST(x) (x << 10) #define GSC_BLK_DISP1WB_DEST(x) (x << 10)
#define GSC_BLK_SW_RESET_WB_DEST(x) (1 << (18 + x)) #define GSC_BLK_SW_RESET_WB_DEST(x) (1 << (18 + x))
#define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x)) #define GSC_BLK_PXLASYNC_LO_MASK_WB(x) (0 << (14 + x))
#define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1 << (2 * x)) #define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1 << (2 * x))
#define SYSREG_GSCBLK_CFG2 (S3C_VA_SYS + 0x2000) #define SYSREG_GSCBLK_CFG2 0x2000
#define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x)) #define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x))
#endif /* EXYNOS_REGS_GSC_H_ */ #endif /* EXYNOS_REGS_GSC_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