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 type of panel connected to it.
......@@ -66,8 +83,15 @@ Optional properties for dp-controller:
Hotplug detect GPIO.
Indicates which GPIO should be used for hotplug
detection
-video interfaces: Device node can contain video interface port
nodes according to [1].
Video interfaces:
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
......@@ -111,9 +135,18 @@ Board Specific portion:
};
ports {
port@0 {
port {
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:
- reg: should contain G-Scaler physical address location and length.
- 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:
gsc_0: gsc@0x13e00000 {
......
......@@ -122,6 +122,12 @@ panel: panel {
compatible = "auo,b133htn01";
power-supply = <&tps65090_fet6>;
backlight = <&backlight>;
port {
panel_in: endpoint {
remote-endpoint = <&dp_out>;
};
};
};
mmc1_pwrseq: mmc1_pwrseq {
......@@ -148,7 +154,14 @@ &dp {
samsung,link-rate = <0x0a>;
samsung,lane-count = <2>;
samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
panel = <&panel>;
ports {
port {
dp_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};
&fimd {
......
......@@ -118,7 +118,7 @@ config DRM_EXYNOS_ROTATOR
config DRM_EXYNOS_GSC
bool "GScaler"
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !ARCH_MULTIPLATFORM
depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && !VIDEO_SAMSUNG_EXYNOS_GSC
help
Choose this option if you want to use Exynos GSC for DRM.
......
......@@ -21,11 +21,11 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h"
#define WINDOWS_NR 3
#define CURSOR_WIN 2
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
static const char * const decon_clks_name[] = {
......@@ -56,6 +56,7 @@ struct decon_context {
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
void __iomem *addr;
struct clk *clks[ARRAY_SIZE(decon_clks_name)];
int pipe;
......@@ -71,6 +72,12 @@ static const uint32_t decon_formats[] = {
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,
u32 val)
{
......@@ -259,21 +266,24 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
static void decon_update_plane(struct exynos_drm_crtc *crtc,
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 drm_plane_state *state = plane->base.state;
struct drm_framebuffer *fb = state->base.fb;
unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0];
unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = fb->pitches[0];
dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
u32 val;
if (test_bit(BIT_SUSPENDED, &ctx->flags))
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));
val = COORDINATE_X(plane->crtc_x + plane->crtc_w - 1) |
COORDINATE_Y(plane->crtc_y + plane->crtc_h - 1);
val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) |
COORDINATE_Y(state->crtc.y + state->crtc.h - 1);
writel(val, ctx->addr + DECON_VIDOSDxB(win));
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,
VIDOSD_Wx_ALPHA_B_F(0x0);
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));
if (ctx->out_type != IFTYPE_HDMI)
val = BIT_VAL(pitch - plane->crtc_w * bpp, 27, 14)
| BIT_VAL(plane->crtc_w * bpp, 13, 0);
val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14)
| BIT_VAL(state->crtc.w * bpp, 13, 0);
else
val = BIT_VAL(pitch - plane->crtc_w * bpp, 29, 15)
| BIT_VAL(plane->crtc_w * bpp, 14, 0);
val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15)
| BIT_VAL(state->crtc.w * bpp, 14, 0);
writel(val, ctx->addr + DECON_VIDW0xADD2(win));
decon_win_set_pixfmt(ctx, win, state->fb);
decon_win_set_pixfmt(ctx, win, fb);
/* window enable */
decon_set_bits(ctx, DECON_WINCONx(win), WINCONx_ENWIN_F, ~0);
......@@ -377,20 +387,12 @@ static void decon_swreset(struct decon_context *ctx)
static void decon_enable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int ret;
int i;
if (!test_and_clear_bit(BIT_SUSPENDED, &ctx->flags))
return;
pm_runtime_get_sync(ctx->dev);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
ret = clk_prepare_enable(ctx->clks[i]);
if (ret < 0)
goto err;
}
set_bit(BIT_CLKS_ENABLED, &ctx->flags);
/* if vblank was enabled status, enable it again. */
......@@ -399,11 +401,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
decon_commit(ctx->crtc);
return;
err:
while (--i >= 0)
clk_disable_unprepare(ctx->clks[i]);
set_bit(BIT_SUSPENDED, &ctx->flags);
}
......@@ -425,9 +422,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
decon_swreset(ctx);
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++)
clk_disable_unprepare(ctx->clks[i]);
clear_bit(BIT_CLKS_ENABLED, &ctx->flags);
pm_runtime_put_sync(ctx->dev);
......@@ -478,7 +472,6 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
static struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable = decon_enable,
.disable = decon_disable,
.commit = decon_commit,
.enable_vblank = decon_enable_vblank,
.disable_vblank = decon_disable_vblank,
.atomic_begin = decon_atomic_begin,
......@@ -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_plane *exynos_plane;
enum exynos_drm_output_type out_type;
enum drm_plane_type type;
unsigned int win;
int ret;
......@@ -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++) {
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],
1 << ctx->pipe, type, decon_formats,
ARRAY_SIZE(decon_formats), win);
1 << ctx->pipe, &ctx->configs[win]);
if (ret)
return ret;
}
......@@ -581,6 +576,44 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
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[] = {
{
.compatible = "samsung,exynos5433-decon",
......@@ -684,6 +717,7 @@ struct platform_driver exynos5433_decon_driver = {
.remove = exynos5433_decon_remove,
.driver = {
.name = "exynos5433-decon",
.pm = &exynos5433_decon_pm_ops,
.of_match_table = exynos5433_decon_driver_dt_match,
},
};
......@@ -30,6 +30,7 @@
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_iommu.h"
......@@ -40,13 +41,13 @@
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
#define WINDOWS_NR 2
#define CURSOR_WIN 1
struct decon_context {
struct device *dev;
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
struct clk *pclk;
struct clk *aclk;
struct clk *eclk;
......@@ -81,6 +82,11 @@ static const uint32_t decon_formats[] = {
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)
{
struct decon_context *ctx = crtc->ctx;
......@@ -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 */
if (ch_enabled) {
unsigned int state = ctx->suspended;
ctx->suspended = 0;
if (ch_enabled)
decon_wait_for_vblank(ctx->crtc);
ctx->suspended = state;
}
}
static int decon_ctx_initialize(struct decon_context *ctx,
......@@ -398,16 +399,17 @@ static void decon_atomic_begin(struct exynos_drm_crtc *crtc,
static void decon_update_plane(struct exynos_drm_crtc *crtc,
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 drm_display_mode *mode = &crtc->base.state->adjusted_mode;
struct drm_plane_state *state = plane->base.state;
struct drm_framebuffer *fb = state->base.fb;
int padding;
unsigned long val, alpha;
unsigned int last_x;
unsigned int last_y;
unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0];
unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = fb->pitches[0];
if (ctx->suspended)
return;
......@@ -423,41 +425,32 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
*/
/* 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));
padding = (pitch / bpp) - state->fb->width;
padding = (pitch / bpp) - fb->width;
/* buffer size */
writel(state->fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
writel(state->fb->height, ctx->regs + VIDW_WHOLE_Y(win));
writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
writel(fb->height, ctx->regs + VIDW_WHOLE_Y(win));
/* offset from the start of the buffer to read */
writel(plane->src_x, ctx->regs + VIDW_OFFSET_X(win));
writel(plane->src_y, ctx->regs + VIDW_OFFSET_Y(win));
writel(state->src.x, ctx->regs + VIDW_OFFSET_X(win));
writel(state->src.y, ctx->regs + VIDW_OFFSET_Y(win));
DRM_DEBUG_KMS("start addr = 0x%lx\n",
(unsigned long)val);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
plane->crtc_w, plane->crtc_h);
state->crtc.w, state->crtc.h);
/*
* OSD position.
* 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);
val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
VIDOSDxA_TOPLEFT_Y(state->crtc.y);
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)
last_x--;
last_y = plane->crtc_y + plane->crtc_h;
last_y = state->crtc.y + state->crtc.h;
if (last_y)
last_y--;
......@@ -466,7 +459,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDOSD_B(win));
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 */
alpha = VIDOSDxC_ALPHA0_R_F(0x0) |
......@@ -481,7 +474,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
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. */
if (win != 0)
......@@ -555,39 +548,12 @@ static void decon_init(struct decon_context *ctx)
static void decon_enable(struct exynos_drm_crtc *crtc)
{
struct decon_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
return;
ctx->suspended = false;
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);
/* if vblank was enabled status, enable it again. */
......@@ -595,6 +561,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc)
decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc);
ctx->suspended = false;
}
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++)
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);
ctx->suspended = true;
......@@ -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 drm_device *drm_dev = data;
struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos;
unsigned int i;
int ret;
ret = decon_ctx_initialize(ctx, drm_dev);
......@@ -689,11 +651,14 @@ static int decon_bind(struct device *dev, struct device *master, void *data)
return ret;
}
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, decon_formats,
ARRAY_SIZE(decon_formats), zpos);
for (i = 0; i < WINDOWS_NR; i++) {
ctx->configs[i].pixel_formats = decon_formats;
ctx->configs[i].num_pixel_formats = ARRAY_SIZE(decon_formats);
ctx->configs[i].zpos = i;
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)
return ret;
}
......@@ -843,11 +808,63 @@ static int decon_remove(struct platform_device *pdev)
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 = {
.probe = decon_probe,
.remove = decon_remove,
.driver = {
.name = "exynos-decon",
.pm = &exynos7_decon_pm_ops,
.of_match_table = decon_driver_dt_match,
},
};
......@@ -1009,9 +1009,9 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
{
int ret;
encoder->bridge = dp->bridge;
dp->bridge->encoder = encoder;
ret = drm_bridge_attach(encoder->dev, dp->bridge);
encoder->bridge->next = dp->ptn_bridge;
dp->ptn_bridge->encoder = encoder;
ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
if (ret) {
DRM_ERROR("Failed to attach bridge to drm\n");
return ret;
......@@ -1020,14 +1020,15 @@ static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
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;
int ret;
/* 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);
if (!ret)
return 0;
......@@ -1052,27 +1053,16 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
return ret;
}
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_bridge_enable(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);
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return;
pm_runtime_get_sync(dp->dev);
if (dp->panel) {
if (drm_panel_prepare(dp->panel)) {
DRM_ERROR("failed to setup the panel\n");
......@@ -1083,7 +1073,6 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
if (crtc->ops->clock_enable)
crtc->ops->clock_enable(dp_to_crtc(dp), true);
clk_prepare_enable(dp->clock);
phy_power_on(dp->phy);
exynos_dp_init_dp(dp);
enable_irq(dp->irq);
......@@ -1092,9 +1081,9 @@ static void exynos_dp_enable(struct drm_encoder *encoder)
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);
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
......@@ -1110,7 +1099,6 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
phy_power_off(dp->phy);
clk_disable_unprepare(dp->clock);
if (crtc->ops->clock_enable)
crtc->ops->clock_enable(dp_to_crtc(dp), false);
......@@ -1120,9 +1108,74 @@ static void exynos_dp_disable(struct drm_encoder *encoder)
DRM_ERROR("failed to turnoff the panel\n");
}
pm_runtime_put_sync(dp->dev);
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 = {
.mode_fixup = exynos_dp_mode_fixup,
.mode_set = exynos_dp_mode_set,
......@@ -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);
if (ret)
return ret;
......@@ -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);
phy_power_on(dp->phy);
exynos_dp_init_dp(dp);
ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
irq_flags, "exynos-dp", dp);
if (ret) {
......@@ -1343,8 +1392,9 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev)
{
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;
int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
......@@ -1353,36 +1403,96 @@ static int exynos_dp_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dp);
/* This is for the backward compatibility. */
panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) {
dp->panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
if (!dp->panel)
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);
if (endpoint) {
bridge_node = of_graph_get_remote_port_parent(endpoint);
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);
if (!dp->bridge)
if (!dp->ptn_bridge)
return -EPROBE_DEFER;
} else
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)
{
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &exynos_dp_ops);
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[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
......@@ -1395,6 +1505,7 @@ struct platform_driver dp_driver = {
.driver = {
.name = "exynos-dp",
.owner = THIS_MODULE,
.pm = &exynos_dp_pm_ops,
.of_match_table = exynos_dp_match,
},
};
......
......@@ -153,6 +153,7 @@ struct exynos_dp_device {
struct drm_connector connector;
struct drm_panel *panel;
struct drm_bridge *bridge;
struct drm_bridge *ptn_bridge;
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
......
......@@ -304,45 +304,6 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
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)
{
struct drm_exynos_file_private *file_priv;
......@@ -476,31 +437,54 @@ static struct drm_driver exynos_drm_driver = {
};
#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);
pm_message_t message;
struct drm_connector *connector;
if (pm_runtime_suspended(dev) || !drm_dev)
return 0;
message.event = PM_EVENT_SUSPEND;
return exynos_drm_suspend(drm_dev, message);
drm_modeset_lock_all(drm_dev);
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_connector *connector;
if (pm_runtime_suspended(dev) || !drm_dev)
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
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 */
......
......@@ -38,24 +38,44 @@ enum exynos_drm_output_type {
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
* @src_x: offset x on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @src_y: offset y on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @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).
* @base: plane_state object (contains drm_framebuffer pointer)
* @src: rectangle of the source image data to be displayed (clipped to
* visible part).
* @crtc: rectangle of the target image position on hardware screen
* (clipped to visible part).
* @h_ratio: horizontal 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).
*
* this structure is common to exynos SoC and its contents would be copied
......@@ -64,21 +84,32 @@ enum exynos_drm_output_type {
struct exynos_drm_plane {
struct drm_plane base;
unsigned int src_x;
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];
const struct exynos_drm_plane_config *config;
unsigned int zpos;
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
*
......
......@@ -1458,66 +1458,6 @@ static const struct mipi_dsi_host_ops exynos_dsi_ops = {
.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)
{
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
......@@ -1526,16 +1466,14 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
if (dsi->state & DSIM_STATE_ENABLED)
return;
ret = exynos_dsi_poweron(dsi);
if (ret < 0)
return;
pm_runtime_get_sync(dsi->dev);
dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi);
pm_runtime_put_sync(dsi->dev);
return;
}
......@@ -1547,7 +1485,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi);
pm_runtime_put_sync(dsi->dev);
return;
}
......@@ -1569,7 +1507,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_poweroff(dsi);
pm_runtime_put_sync(dsi->dev);
}
static enum drm_connector_status
......@@ -1797,13 +1735,13 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
ep = of_graph_get_next_endpoint(node, NULL);
if (!ep) {
ret = -ENXIO;
ret = -EINVAL;
goto end;
}
dsi->bridge_node = of_graph_get_remote_port_parent(ep);
if (!dsi->bridge_node) {
ret = -ENXIO;
ret = -EINVAL;
goto end;
}
end:
......@@ -1954,22 +1892,99 @@ static int exynos_dsi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &dsi->encoder);
pm_runtime_enable(dev);
return component_add(dev, &exynos_dsi_component_ops);
}
static int exynos_dsi_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &exynos_dsi_component_ops);
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 = {
.probe = exynos_dsi_probe,
.remove = exynos_dsi_remove,
.driver = {
.name = "exynos-dsi",
.owner = THIS_MODULE,
.pm = &exynos_dsi_pm_ops,
.of_match_table = exynos_dsi_of_match,
},
};
......
......@@ -37,6 +37,7 @@
struct exynos_drm_fb {
struct drm_framebuffer fb;
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,
......@@ -135,6 +136,8 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
goto err;
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);
......@@ -189,21 +192,14 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
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_gem *exynos_gem;
if (index >= MAX_FB_BUFFER)
return NULL;
return DMA_ERROR_CODE;
exynos_gem = exynos_fb->exynos_gem[index];
if (!exynos_gem)
return NULL;
DRM_DEBUG_KMS("dma_addr: 0x%lx\n", (unsigned long)exynos_gem->dma_addr);
return exynos_gem;
return exynos_fb->dma_addr[index];
}
static void exynos_drm_output_poll_changed(struct drm_device *dev)
......
......@@ -22,8 +22,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
struct exynos_drm_gem **exynos_gem,
int count);
/* get gem object of a drm framebuffer */
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);
void exynos_drm_mode_config_init(struct drm_device *dev);
......
......@@ -29,6 +29,7 @@
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
......@@ -87,7 +88,6 @@
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
#define CURSOR_WIN 4
struct fimd_driver_data {
unsigned int timing_base;
......@@ -150,6 +150,7 @@ struct fimd_context {
struct drm_device *drm_dev;
struct exynos_drm_crtc *crtc;
struct exynos_drm_plane planes[WINDOWS_NR];
struct exynos_drm_plane_config configs[WINDOWS_NR];
struct clk *bus_clk;
struct clk *lcd_clk;
void __iomem *regs;
......@@ -187,6 +188,14 @@ static const struct of_device_id 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[] = {
DRM_FORMAT_C8,
DRM_FORMAT_XRGB1555,
......@@ -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,
struct drm_framebuffer *fb)
uint32_t pixel_format, int width)
{
unsigned long val;
......@@ -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.
*/
if (ctx->driver_data->has_limited_fmt && !win) {
if (fb->pixel_format == DRM_FORMAT_ARGB8888)
fb->pixel_format = DRM_FORMAT_XRGB8888;
if (pixel_format == DRM_FORMAT_ARGB8888)
pixel_format = DRM_FORMAT_XRGB8888;
}
switch (fb->pixel_format) {
switch (pixel_format) {
case DRM_FORMAT_C8:
val |= WINCON0_BPPMODE_8BPP_PALETTE;
val |= WINCONx_BURSTLEN_8WORD;
......@@ -529,17 +538,15 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
break;
}
DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel);
/*
* In case of exynos, setting dma-burst to 16Word causes permanent
* tearing for very small buffers, e.g. cursor buffer. Burst Mode
* switching which is based on plane size is not recommended as
* plane size varies alot towards the end of the screen and rapid
* movement causes unstable DMA which results into iommu crash/tear.
* Setting dma-burst to 16Word causes permanent tearing for very small
* buffers, e.g. cursor buffer. Burst Mode switching which based on
* plane size is not recommended as plane size varies alot towards the
* end of the screen and rapid movement causes unstable DMA, but it is
* 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_4WORD;
}
......@@ -640,39 +647,41 @@ static void fimd_atomic_flush(struct exynos_drm_crtc *crtc,
static void fimd_update_plane(struct exynos_drm_crtc *crtc,
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 drm_plane_state *state = plane->base.state;
struct drm_framebuffer *fb = state->base.fb;
dma_addr_t dma_addr;
unsigned long val, size, offset;
unsigned int last_x, last_y, buf_offsize, line_size;
unsigned int win = plane->zpos;
unsigned int bpp = state->fb->bits_per_pixel >> 3;
unsigned int pitch = state->fb->pitches[0];
unsigned int bpp = fb->bits_per_pixel >> 3;
unsigned int pitch = fb->pitches[0];
if (ctx->suspended)
return;
offset = plane->src_x * bpp;
offset += plane->src_y * pitch;
offset = state->src.x * bpp;
offset += state->src.y * pitch;
/* 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;
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
size = pitch * plane->crtc_h;
size = pitch * state->crtc.h;
val = (unsigned long)(dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)dma_addr, val, size);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
plane->crtc_w, plane->crtc_h);
state->crtc.w, state->crtc.h);
/* buffer size */
buf_offsize = pitch - (plane->crtc_w * bpp);
line_size = plane->crtc_w * bpp;
buf_offsize = pitch - (state->crtc.w * bpp);
line_size = state->crtc.w * bpp;
val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
......@@ -680,16 +689,16 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
/* OSD position */
val = VIDOSDxA_TOPLEFT_X(plane->crtc_x) |
VIDOSDxA_TOPLEFT_Y(plane->crtc_y) |
VIDOSDxA_TOPLEFT_X_E(plane->crtc_x) |
VIDOSDxA_TOPLEFT_Y_E(plane->crtc_y);
val = VIDOSDxA_TOPLEFT_X(state->crtc.x) |
VIDOSDxA_TOPLEFT_Y(state->crtc.y) |
VIDOSDxA_TOPLEFT_X_E(state->crtc.x) |
VIDOSDxA_TOPLEFT_Y_E(state->crtc.y);
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)
last_x--;
last_y = plane->crtc_y + plane->crtc_h;
last_y = state->crtc.y + state->crtc.h;
if (last_y)
last_y--;
......@@ -699,20 +708,20 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
writel(val, ctx->regs + VIDOSD_B(win));
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 */
if (win != 3 && win != 4) {
u32 offset = VIDOSD_D(win);
if (win == 0)
offset = VIDOSD_C(win);
val = plane->crtc_w * plane->crtc_h;
val = state->crtc.w * state->crtc.h;
writel(val, ctx->regs + offset);
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. */
if (win != 0)
......@@ -745,7 +754,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc,
static void fimd_enable(struct exynos_drm_crtc *crtc)
{
struct fimd_context *ctx = crtc->ctx;
int ret;
if (!ctx->suspended)
return;
......@@ -754,18 +762,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc)
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 (test_and_clear_bit(0, &ctx->irq_flags))
fimd_enable_vblank(ctx->crtc);
......@@ -795,11 +791,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)
writel(0, ctx->regs + VIDCON0);
clk_disable_unprepare(ctx->lcd_clk);
clk_disable_unprepare(ctx->bus_clk);
pm_runtime_put_sync(ctx->dev);
ctx->suspended = true;
}
......@@ -941,18 +933,19 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm_dev = data;
struct exynos_drm_private *priv = drm_dev->dev_private;
struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos;
unsigned int i;
int ret;
ctx->drm_dev = drm_dev;
ctx->pipe = priv->pipe++;
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, fimd_formats,
ARRAY_SIZE(fimd_formats), zpos);
for (i = 0; i < WINDOWS_NR; i++) {
ctx->configs[i].pixel_formats = fimd_formats;
ctx->configs[i].num_pixel_formats = ARRAY_SIZE(fimd_formats);
ctx->configs[i].zpos = i;
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)
return ret;
}
......@@ -1121,12 +1114,49 @@ static int fimd_remove(struct platform_device *pdev)
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 = {
.probe = fimd_probe,
.remove = fimd_remove,
.driver = {
.name = "exynos4-fb",
.owner = THIS_MODULE,
.pm = &exynos_fimd_pm_ops,
.of_match_table = fimd_driver_dt_match,
},
};
......@@ -55,8 +55,6 @@ struct exynos_drm_gem {
struct sg_table *sgt;
};
struct page **exynos_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
/* destroy a buffer with gem object */
void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem);
......@@ -91,10 +89,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
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. */
int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
......@@ -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. */
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. */
int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
struct sg_table *sgt,
......
......@@ -15,7 +15,8 @@
#include <linux/platform_device.h>
#include <linux/clk.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/exynos_drm.h>
......@@ -126,6 +127,7 @@ struct gsc_capability {
* @ippdrv: prepare initialization using ippdrv.
* @regs_res: register resources.
* @regs: memory mapped io registers.
* @sysreg: handle to SYSREG block regmap.
* @lock: locking of operations.
* @gsc_clk: gsc gate clock.
* @sc: scaler infomations.
......@@ -138,6 +140,7 @@ struct gsc_context {
struct exynos_drm_ippdrv ippdrv;
struct resource *regs_res;
void __iomem *regs;
struct regmap *sysreg;
struct mutex lock;
struct clk *gsc_clk;
struct gsc_scaler sc;
......@@ -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)
{
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)
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)
else
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,
......@@ -1215,10 +1221,10 @@ static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable);
if (enable) {
clk_enable(ctx->gsc_clk);
clk_prepare_enable(ctx->gsc_clk);
ctx->suspended = false;
} else {
clk_disable(ctx->gsc_clk);
clk_disable_unprepare(ctx->gsc_clk);
ctx->suspended = true;
}
......@@ -1663,6 +1669,15 @@ static int gsc_probe(struct platform_device *pdev)
if (!ctx)
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 */
ctx->gsc_clk = devm_clk_get(dev, "gscl");
if (IS_ERR(ctx->gsc_clk)) {
......@@ -1713,7 +1728,6 @@ static int gsc_probe(struct platform_device *pdev)
mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = exynos_drm_ippdrv_register(ippdrv);
......@@ -1797,6 +1811,12 @@ static const struct dev_pm_ops gsc_pm_ops = {
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 = {
.probe = gsc_probe,
.remove = gsc_remove,
......@@ -1804,6 +1824,7 @@ struct platform_driver gsc_driver = {
.name = "exynos-drm-gsc",
.owner = THIS_MODULE,
.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)
return size;
}
static void exynos_plane_mode_set(struct drm_plane *plane,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
{
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;
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_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_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
if (crtc_x < 0) {
if (actual_w)
src_x -= crtc_x;
src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16;
crtc_x = 0;
}
if (crtc_y < 0) {
if (actual_h)
src_y -= crtc_y;
src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16;
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. */
exynos_plane->src_x = src_x;
exynos_plane->src_y = src_y;
exynos_plane->src_w = (actual_w * exynos_plane->h_ratio) >> 16;
exynos_plane->src_h = (actual_h * exynos_plane->v_ratio) >> 16;
exynos_state->src.x = src_x;
exynos_state->src.y = src_y;
exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16;
exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16;
/* set plane range to be displayed. */
exynos_plane->crtc_x = crtc_x;
exynos_plane->crtc_y = crtc_y;
exynos_plane->crtc_w = actual_w;
exynos_plane->crtc_h = actual_h;
exynos_state->crtc.x = crtc_x;
exynos_state->crtc.y = crtc_y;
exynos_state->crtc.w = actual_w;
exynos_state->crtc.h = actual_h;
DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
exynos_plane->crtc_x, exynos_plane->crtc_y,
exynos_plane->crtc_w, exynos_plane->crtc_h);
exynos_state->crtc.x, exynos_state->crtc.y,
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 = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.reset = exynos_drm_plane_reset,
.atomic_duplicate_state = exynos_drm_plane_duplicate_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,
struct drm_plane_state *state)
{
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
int nr;
int i;
struct exynos_drm_plane_state *exynos_state =
to_exynos_plane_state(state);
int ret = 0;
if (!state->fb)
if (!state->crtc || !state->fb)
return 0;
nr = drm_format_num_planes(state->fb->pixel_format);
for (i = 0; i < nr; i++) {
struct exynos_drm_gem *exynos_gem =
exynos_drm_fb_gem(state->fb, i);
if (!exynos_gem) {
DRM_DEBUG_KMS("gem object is null\n");
return -EFAULT;
}
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]);
}
/* translate state into exynos_state */
exynos_plane_mode_set(exynos_state);
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,
......@@ -155,12 +232,7 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
if (!state->crtc)
return;
exynos_plane_mode_set(plane, state->crtc, state->fb,
state->crtc_x, state->crtc_y,
state->crtc_w, state->crtc_h,
state->src_x >> 16, state->src_y >> 16,
state->src_w >> 16, state->src_h >> 16);
plane->crtc = state->crtc;
exynos_plane->pending_fb = state->fb;
if (exynos_crtc->ops->update_plane)
......@@ -177,8 +249,7 @@ static void exynos_plane_atomic_disable(struct drm_plane *plane,
return;
if (exynos_crtc->ops->disable_plane)
exynos_crtc->ops->disable_plane(exynos_crtc,
exynos_plane);
exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
}
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,
drm_object_attach_property(&plane->base, prop, zpos);
}
enum drm_plane_type exynos_plane_get_type(unsigned int zpos,
unsigned int cursor_win)
{
if (zpos == DEFAULT_WIN)
return DRM_PLANE_TYPE_PRIMARY;
else if (zpos == cursor_win)
return DRM_PLANE_TYPE_CURSOR;
else
return DRM_PLANE_TYPE_OVERLAY;
}
int exynos_plane_init(struct drm_device *dev,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
const uint32_t *formats, unsigned int fcount,
unsigned int zpos)
unsigned long possible_crtcs,
const struct exynos_drm_plane_config *config)
{
int err;
err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
&exynos_plane_funcs, formats, fcount,
type, NULL);
err = drm_universal_plane_init(dev, &exynos_plane->base,
possible_crtcs,
&exynos_plane_funcs,
config->pixel_formats,
config->num_pixel_formats,
config->type, NULL);
if (err) {
DRM_ERROR("failed to initialize plane\n");
return err;
......@@ -236,10 +298,12 @@ int exynos_plane_init(struct drm_device *dev,
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)
exynos_plane_attach_zpos_property(&exynos_plane->base, zpos);
if (config->type == DRM_PLANE_TYPE_OVERLAY)
exynos_plane_attach_zpos_property(&exynos_plane->base,
config->zpos);
return 0;
}
......@@ -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,
struct exynos_drm_plane *exynos_plane,
unsigned long possible_crtcs, enum drm_plane_type type,
const uint32_t *formats, unsigned int fcount,
unsigned int zpos);
unsigned long possible_crtcs,
const struct exynos_drm_plane_config *config);
......@@ -790,10 +790,10 @@ static int rotator_remove(struct platform_device *pdev)
static int rotator_clk_crtl(struct rot_context *rot, bool enable)
{
if (enable) {
clk_enable(rot->clock);
clk_prepare_enable(rot->clock);
rot->suspended = false;
} else {
clk_disable(rot->clock);
clk_disable_unprepare(rot->clock);
rot->suspended = true;
}
......
......@@ -24,12 +24,12 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h"
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
#define CURSOR_WIN 2
#define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector)
......@@ -89,6 +89,12 @@ static const uint32_t formats[] = {
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)
{
struct vidi_context *ctx = crtc->ctx;
......@@ -125,12 +131,15 @@ static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
static void vidi_update_plane(struct exynos_drm_crtc *crtc,
struct exynos_drm_plane *plane)
{
struct drm_plane_state *state = plane->base.state;
struct vidi_context *ctx = crtc->ctx;
dma_addr_t addr;
if (ctx->suspended)
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)
schedule_work(&ctx->work);
......@@ -439,17 +448,21 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm_dev = data;
struct drm_encoder *encoder = &ctx->encoder;
struct exynos_drm_plane *exynos_plane;
enum drm_plane_type type;
unsigned int zpos;
struct exynos_drm_plane_config plane_config = { 0 };
unsigned int i;
int pipe, ret;
vidi_ctx_initialize(ctx, drm_dev);
for (zpos = 0; zpos < WINDOWS_NR; zpos++) {
type = exynos_plane_get_type(zpos, CURSOR_WIN);
ret = exynos_plane_init(drm_dev, &ctx->planes[zpos],
1 << ctx->pipe, type, formats,
ARRAY_SIZE(formats), zpos);
plane_config.pixel_formats = formats;
plane_config.num_pixel_formats = ARRAY_SIZE(formats);
for (i = 0; i < WINDOWS_NR; i++) {
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)
return ret;
}
......
......@@ -113,7 +113,7 @@ struct hdmi_context {
void __iomem *regs_hdmiphy;
struct i2c_client *hdmiphy_port;
struct i2c_adapter *ddc_adpt;
struct gpio_desc *hpd_gpio;
struct gpio_desc *hpd_gpio;
int irq;
struct regmap *pmureg;
struct clk *hdmi;
......@@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder)
if (hdata->powered)
return;
hdata->powered = true;
pm_runtime_get_sync(hdata->dev);
if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk))
......@@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder)
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 1);
clk_prepare_enable(hdata->hdmi);
clk_prepare_enable(hdata->sclk_hdmi);
hdmi_conf_apply(hdata);
hdata->powered = true;
}
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);
clk_disable_unprepare(hdata->sclk_hdmi);
clk_disable_unprepare(hdata->hdmi);
/* reset pmu hdmiphy control bit to disable hdmiphy */
regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
PMU_HDMI_PHY_ENABLE_BIT, 0);
......@@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev)
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 = {
.probe = hdmi_probe,
.remove = hdmi_remove,
.driver = {
.name = "exynos-hdmi",
.owner = THIS_MODULE,
.pm = &exynos_hdmi_pm_ops,
.of_match_table = hdmi_match_types,
},
};
This diff is collapsed.
......@@ -273,12 +273,12 @@
#define GSC_CLK_GATE_MODE_SNOOP_CNT(x) ((x) << 0)
/* 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_SW_RESET_WB_DEST(x) (1 << (18 + 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 SYSREG_GSCBLK_CFG2 (S3C_VA_SYS + 0x2000)
#define SYSREG_GSCBLK_CFG2 0x2000
#define PXLASYNC_LO_MASK_CAMIF_GSCL(x) (1 << (x))
#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