forked from Minki/linux
ummary:
- Provide NV12MT pixel format support of Mixer driver in generic way. - Refactor Exynos KMS drivers . Refactoring to panel detection way . Refactoring to setting up possible_crtcs . Refactoring to video and command mode support - Some cleanups -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZn77sAAoJEFc4NIkMQxK4TaAP/1jb9CO2+gMnTTNdjJU+tCEx D/bztIDG0bxltpGjm7cDTe0S71GEfdoQ2rN75SCWTAkofVfe9bUUCecpCusWTchF lgkF3eTEVCSWw+7qko7sDvxmdC+8p0yZ4LHziozaB2Kd2yvIYLlkfiJeAHF30MpG tA2AErKJVOQxOS+z2/BHI7q4T9q5cdON5CW4j2OYQjzuOP2F/62RQlde48BG/WgA m9qK4zg4wVGkzadKTtBrK134girceAlC27gLabrLpsz6sv/EwYMtGFkAs4C4P/N5 fDJKNjaiSphMwLJI9m4y9Q8mSvJWydDvr8JqO0Y3u2MPF6k2e7xOGTEsnqkBGTip vNoX1j6qHSC7DnXUCrvSqVJ+GDZZQWGnX1ggOtatNc38+oVnd8k3WIEJkFrKA5ap M5/0l2n01AnBbT1U+/N0a3dkHUd3Ecg+s+cSaOIe7aEMuUrM1hTAkQFHEUcPV54S 5bqj9HquQcXeZdtbhB4X9b7/i+Aexj6YPm/Tv9aTn7cz4MJrB2N5hhdp5tt2Mqpj 8+kZwGNi54AXB5Q+L6RFlefelWVxjGtmsoEp4M+wxZqP31+CeektoaxO0Cgfn0iJ JJOfpPIhHEUGE8pHH6TZWd8yFhB8oH2OAg7uZwHWgneJHZs3lQFmebwOfKl5p9cz tPyND6oasX8KouRIM/T5 =JZ8z -----END PGP SIGNATURE----- Merge tag 'exynos-drm-next-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next Summary: - Provide NV12MT pixel format support of Mixer driver in generic way. - Refactor Exynos KMS drivers . Refactoring to panel detection way . Refactoring to setting up possible_crtcs . Refactoring to video and command mode support - Some cleanups * tag 'exynos-drm-next-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: drm/exynos: simplify set_pixfmt() in DECON and FIMD drivers drm/exynos: consistent use of cpp drm/exynos: mixer: remove src offset from mixer_graph_buffer() drm/exynos: mixer: simplify mixer_graph_buffer() drm/exynos: mixer: simplify vp_video_buffer() drm/exynos: mixer: enable NV12MT support for the video plane drm/exynos: mixer: fix chroma comment in vp_video_buffer() arm64: dts: exynos: remove i80-if-timings nodes dt-bindings: exynos5433-decon: remove i80-if-timings property drm/exynos/decon5433: use mode info stored in CRTC to detect i80 mode drm/exynos: add mode_valid callback to exynos_drm drm/exynos/decon5433: refactor irq requesting code drm/exynos/mic: use mode info stored in CRTC to detect i80 mode drm/exynos/dsi: propagate info about command mode from panel drm/exynos/dsi: refactor panel detection logic drm/exynos: use helper to set possible crtcs drm/exynos/decon5433: use readl_poll_timeout helpers
This commit is contained in:
commit
7ebdb0dd52
@ -25,12 +25,6 @@ Required properties:
|
||||
size-cells must 1 and 0, respectively.
|
||||
- port: contains an endpoint node which is connected to the endpoint in the mic
|
||||
node. The reg value muset be 0.
|
||||
- i80-if-timings: specify whether the panel which is connected to decon uses
|
||||
i80 lcd interface or mipi video interface. This node contains
|
||||
no timing information as that of fimd does. Because there is
|
||||
no register in decon to specify i80 interface timing value,
|
||||
it is not needed, but make it remain to use same kind of node
|
||||
in fimd and exynos7 decon.
|
||||
|
||||
Example:
|
||||
SoC specific DT entry:
|
||||
@ -59,9 +53,3 @@ decon: decon@13800000 {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Board specific DT entry:
|
||||
&decon {
|
||||
i80-if-timings {
|
||||
};
|
||||
};
|
||||
|
@ -280,9 +280,6 @@
|
||||
|
||||
&decon {
|
||||
status = "okay";
|
||||
|
||||
i80-if-timings {
|
||||
};
|
||||
};
|
||||
|
||||
&decon_tv {
|
||||
@ -1116,9 +1113,6 @@
|
||||
|
||||
&mic {
|
||||
status = "okay";
|
||||
|
||||
i80-if-timings {
|
||||
};
|
||||
};
|
||||
|
||||
&pmu_system_controller {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
@ -33,9 +34,8 @@
|
||||
#define WINDOWS_NR 3
|
||||
#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
|
||||
|
||||
#define IFTYPE_I80 (1 << 0)
|
||||
#define I80_HW_TRG (1 << 1)
|
||||
#define IFTYPE_HDMI (1 << 2)
|
||||
#define I80_HW_TRG (1 << 0)
|
||||
#define IFTYPE_HDMI (1 << 1)
|
||||
|
||||
static const char * const decon_clks_name[] = {
|
||||
"pclk",
|
||||
@ -57,6 +57,8 @@ struct decon_context {
|
||||
struct regmap *sysreg;
|
||||
struct clk *clks[ARRAY_SIZE(decon_clks_name)];
|
||||
unsigned int irq;
|
||||
unsigned int irq_vsync;
|
||||
unsigned int irq_lcd_sys;
|
||||
unsigned int te_irq;
|
||||
unsigned long out_type;
|
||||
int first_win;
|
||||
@ -90,7 +92,7 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc)
|
||||
u32 val;
|
||||
|
||||
val = VIDINTCON0_INTEN;
|
||||
if (ctx->out_type & IFTYPE_I80)
|
||||
if (crtc->i80_mode)
|
||||
val |= VIDINTCON0_FRAMEDONE;
|
||||
else
|
||||
val |= VIDINTCON0_INTFRMEN | VIDINTCON0_FRAMESEL_FP;
|
||||
@ -139,7 +141,7 @@ static u32 decon_get_frame_count(struct decon_context *ctx, bool end)
|
||||
|
||||
switch (status & (VIDCON1_VSTATUS_MASK | VIDCON1_I80_ACTIVE)) {
|
||||
case VIDCON1_VSTATUS_VS:
|
||||
if (!(ctx->out_type & IFTYPE_I80))
|
||||
if (!(ctx->crtc->i80_mode))
|
||||
--frm;
|
||||
break;
|
||||
case VIDCON1_VSTATUS_BP:
|
||||
@ -166,7 +168,7 @@ static u32 decon_get_vblank_counter(struct exynos_drm_crtc *crtc)
|
||||
|
||||
static void decon_setup_trigger(struct decon_context *ctx)
|
||||
{
|
||||
if (!(ctx->out_type & (IFTYPE_I80 | I80_HW_TRG)))
|
||||
if (!ctx->crtc->i80_mode && !(ctx->out_type & I80_HW_TRG))
|
||||
return;
|
||||
|
||||
if (!(ctx->out_type & I80_HW_TRG)) {
|
||||
@ -206,7 +208,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
|
||||
val = VIDOUT_LCD_ON;
|
||||
if (interlaced)
|
||||
val |= VIDOUT_INTERLACE_EN_F;
|
||||
if (ctx->out_type & IFTYPE_I80) {
|
||||
if (crtc->i80_mode) {
|
||||
val |= VIDOUT_COMMAND_IF;
|
||||
} else {
|
||||
val |= VIDOUT_RGB_IF;
|
||||
@ -222,7 +224,7 @@ static void decon_commit(struct exynos_drm_crtc *crtc)
|
||||
VIDTCON2_HOZVAL(m->hdisplay - 1);
|
||||
writel(val, ctx->addr + DECON_VIDTCON2);
|
||||
|
||||
if (!(ctx->out_type & IFTYPE_I80)) {
|
||||
if (!crtc->i80_mode) {
|
||||
int vbp = m->crtc_vtotal - m->crtc_vsync_end;
|
||||
int vfp = m->crtc_vsync_start - m->crtc_vdisplay;
|
||||
|
||||
@ -277,16 +279,14 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
default:
|
||||
val |= WINCONx_BPPMODE_32BPP_A8888;
|
||||
val |= WINCONx_WSWP_F | WINCONx_BLD_PIX_F | WINCONx_ALPHA_SEL_F;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("Proper pixel format is not set\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("bpp = %u\n", fb->format->cpp[0] * 8);
|
||||
DRM_DEBUG_KMS("cpp = %u\n", fb->format->cpp[0]);
|
||||
|
||||
/*
|
||||
* In case of exynos, setting dma-burst to 16Word causes permanent
|
||||
@ -329,7 +329,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->format->cpp[0];
|
||||
unsigned int cpp = fb->format->cpp[0];
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0);
|
||||
u32 val;
|
||||
@ -365,11 +365,11 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
writel(val, ctx->addr + DECON_VIDW0xADD1B0(win));
|
||||
|
||||
if (!(ctx->out_type & IFTYPE_HDMI))
|
||||
val = BIT_VAL(pitch - state->crtc.w * bpp, 27, 14)
|
||||
| BIT_VAL(state->crtc.w * bpp, 13, 0);
|
||||
val = BIT_VAL(pitch - state->crtc.w * cpp, 27, 14)
|
||||
| BIT_VAL(state->crtc.w * cpp, 13, 0);
|
||||
else
|
||||
val = BIT_VAL(pitch - state->crtc.w * bpp, 29, 15)
|
||||
| BIT_VAL(state->crtc.w * bpp, 14, 0);
|
||||
val = BIT_VAL(pitch - state->crtc.w * cpp, 29, 15)
|
||||
| BIT_VAL(state->crtc.w * cpp, 14, 0);
|
||||
writel(val, ctx->addr + DECON_VIDW0xADD2(win));
|
||||
|
||||
decon_win_set_pixfmt(ctx, win, fb);
|
||||
@ -407,24 +407,19 @@ static void decon_atomic_flush(struct exynos_drm_crtc *crtc)
|
||||
|
||||
static void decon_swreset(struct decon_context *ctx)
|
||||
{
|
||||
unsigned int tries;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
writel(0, ctx->addr + DECON_VIDCON0);
|
||||
for (tries = 2000; tries; --tries) {
|
||||
if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_STOP_STATUS)
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
readl_poll_timeout(ctx->addr + DECON_VIDCON0, val,
|
||||
~val & VIDCON0_STOP_STATUS, 12, 20000);
|
||||
|
||||
writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0);
|
||||
for (tries = 2000; tries; --tries) {
|
||||
if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_SWRESET)
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
ret = readl_poll_timeout(ctx->addr + DECON_VIDCON0, val,
|
||||
~val & VIDCON0_SWRESET, 12, 20000);
|
||||
|
||||
WARN(tries == 0, "failed to software reset DECON\n");
|
||||
WARN(ret < 0, "failed to software reset DECON\n");
|
||||
|
||||
spin_lock_irqsave(&ctx->vblank_lock, flags);
|
||||
ctx->frame_id = 0;
|
||||
@ -515,6 +510,22 @@ err:
|
||||
clk_disable_unprepare(ctx->clks[i]);
|
||||
}
|
||||
|
||||
static enum drm_mode_status decon_mode_valid(struct exynos_drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct decon_context *ctx = crtc->ctx;
|
||||
|
||||
ctx->irq = crtc->i80_mode ? ctx->irq_lcd_sys : ctx->irq_vsync;
|
||||
|
||||
if (ctx->irq)
|
||||
return MODE_OK;
|
||||
|
||||
dev_info(ctx->dev, "Sink requires %s mode, but appropriate interrupt is not provided.\n",
|
||||
crtc->i80_mode ? "command" : "video");
|
||||
|
||||
return MODE_BAD;
|
||||
}
|
||||
|
||||
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
|
||||
.enable = decon_enable,
|
||||
.disable = decon_disable,
|
||||
@ -524,6 +535,7 @@ static const struct exynos_drm_crtc_ops decon_crtc_ops = {
|
||||
.atomic_begin = decon_atomic_begin,
|
||||
.update_plane = decon_update_plane,
|
||||
.disable_plane = decon_disable_plane,
|
||||
.mode_valid = decon_mode_valid,
|
||||
.atomic_flush = decon_atomic_flush,
|
||||
};
|
||||
|
||||
@ -674,19 +686,22 @@ static const struct of_device_id exynos5433_decon_driver_dt_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, exynos5433_decon_driver_dt_match);
|
||||
|
||||
static int decon_conf_irq(struct decon_context *ctx, const char *name,
|
||||
irq_handler_t handler, unsigned long int flags, bool required)
|
||||
irq_handler_t handler, unsigned long int flags)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(ctx->dev);
|
||||
int ret, irq = platform_get_irq_byname(pdev, name);
|
||||
|
||||
if (irq < 0) {
|
||||
if (irq == -EPROBE_DEFER)
|
||||
switch (irq) {
|
||||
case -EPROBE_DEFER:
|
||||
return irq;
|
||||
if (required)
|
||||
dev_err(ctx->dev, "cannot get %s IRQ\n", name);
|
||||
else
|
||||
irq = 0;
|
||||
return irq;
|
||||
case -ENODATA:
|
||||
case -ENXIO:
|
||||
return 0;
|
||||
default:
|
||||
dev_err(ctx->dev, "IRQ %s get failed, %d\n", name, irq);
|
||||
return irq;
|
||||
}
|
||||
}
|
||||
irq_set_status_flags(irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_irq(ctx->dev, irq, handler, flags, "drm_decon", ctx);
|
||||
@ -714,11 +729,8 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
|
||||
ctx->out_type = (unsigned long)of_device_get_match_data(dev);
|
||||
spin_lock_init(&ctx->vblank_lock);
|
||||
|
||||
if (ctx->out_type & IFTYPE_HDMI) {
|
||||
if (ctx->out_type & IFTYPE_HDMI)
|
||||
ctx->first_win = 1;
|
||||
} else if (of_get_child_by_name(dev->of_node, "i80-if-timings")) {
|
||||
ctx->out_type |= IFTYPE_I80;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) {
|
||||
struct clk *clk;
|
||||
@ -742,25 +754,23 @@ static int exynos5433_decon_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(ctx->addr);
|
||||
}
|
||||
|
||||
if (ctx->out_type & IFTYPE_I80) {
|
||||
ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ctx->irq = ret;
|
||||
ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ctx->irq_vsync = ret;
|
||||
|
||||
ret = decon_conf_irq(ctx, "te", decon_te_irq_handler,
|
||||
IRQF_TRIGGER_RISING, false);
|
||||
if (ret < 0)
|
||||
ret = decon_conf_irq(ctx, "lcd_sys", decon_irq_handler, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ctx->irq_lcd_sys = ret;
|
||||
|
||||
ret = decon_conf_irq(ctx, "te", decon_te_irq_handler,
|
||||
IRQF_TRIGGER_RISING);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret) {
|
||||
ctx->te_irq = ret;
|
||||
ctx->out_type &= ~I80_HW_TRG;
|
||||
}
|
||||
} else {
|
||||
ret = decon_conf_irq(ctx, "vsync", decon_irq_handler, 0, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ctx->irq = ret;
|
||||
if (ret) {
|
||||
ctx->te_irq = ret;
|
||||
ctx->out_type &= ~I80_HW_TRG;
|
||||
}
|
||||
|
||||
if (ctx->out_type & I80_HW_TRG) {
|
||||
|
@ -309,19 +309,14 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win,
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
default:
|
||||
val |= WINCONx_BPPMODE_32BPP_BGRA | WINCONx_BLD_PIX |
|
||||
WINCONx_ALPHA_SEL;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
|
||||
|
||||
val |= WINCONx_BPPMODE_24BPP_xRGB;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("bpp = %d\n", fb->format->cpp[0] * 8);
|
||||
DRM_DEBUG_KMS("cpp = %d\n", fb->format->cpp[0]);
|
||||
|
||||
/*
|
||||
* In case of exynos, setting dma-burst to 16Word causes permanent
|
||||
@ -398,7 +393,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
unsigned int last_x;
|
||||
unsigned int last_y;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->format->cpp[0];
|
||||
unsigned int cpp = fb->format->cpp[0];
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
|
||||
if (ctx->suspended)
|
||||
@ -418,7 +413,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc,
|
||||
val = (unsigned long)exynos_drm_fb_dma_addr(fb, 0);
|
||||
writel(val, ctx->regs + VIDW_BUF_START(win));
|
||||
|
||||
padding = (pitch / bpp) - fb->width;
|
||||
padding = (pitch / cpp) - fb->width;
|
||||
|
||||
/* buffer size */
|
||||
writel(fb->width + padding, ctx->regs + VIDW_WHOLE_X(win));
|
||||
|
@ -155,7 +155,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
|
||||
struct exynos_dp_device *dp = dev_get_drvdata(dev);
|
||||
struct drm_encoder *encoder = &dp->encoder;
|
||||
struct drm_device *drm_dev = data;
|
||||
int pipe, ret;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Just like the probe function said, we don't need the
|
||||
@ -179,20 +179,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
|
||||
EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (pipe < 0)
|
||||
return pipe;
|
||||
|
||||
encoder->possible_crtcs = 1 << pipe;
|
||||
|
||||
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
|
||||
|
||||
drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
|
||||
drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
|
||||
|
||||
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dp->plat_data.encoder = encoder;
|
||||
|
||||
return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
|
||||
|
@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_drv.h"
|
||||
@ -83,7 +84,19 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
exynos_crtc->ops->atomic_flush(exynos_crtc);
|
||||
}
|
||||
|
||||
static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
|
||||
if (exynos_crtc->ops->mode_valid)
|
||||
return exynos_crtc->ops->mode_valid(exynos_crtc, mode);
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
|
||||
.mode_valid = exynos_crtc_mode_valid,
|
||||
.atomic_check = exynos_crtc_atomic_check,
|
||||
.atomic_begin = exynos_crtc_atomic_begin,
|
||||
.atomic_flush = exynos_crtc_atomic_flush,
|
||||
@ -191,16 +204,30 @@ err_crtc:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
|
||||
enum exynos_drm_output_type out_type)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
drm_for_each_crtc(crtc, drm_dev)
|
||||
if (to_exynos_crtc(crtc)->type == out_type)
|
||||
return drm_crtc_index(crtc);
|
||||
return to_exynos_crtc(crtc);
|
||||
|
||||
return -EPERM;
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
||||
int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
|
||||
enum exynos_drm_output_type out_type)
|
||||
{
|
||||
struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev,
|
||||
out_type);
|
||||
|
||||
if (IS_ERR(crtc))
|
||||
return PTR_ERR(crtc);
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
|
||||
|
@ -15,21 +15,25 @@
|
||||
#ifndef _EXYNOS_DRM_CRTC_H_
|
||||
#define _EXYNOS_DRM_CRTC_H_
|
||||
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
|
||||
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
||||
struct drm_plane *plane,
|
||||
enum exynos_drm_output_type type,
|
||||
enum exynos_drm_output_type out_type,
|
||||
const struct exynos_drm_crtc_ops *ops,
|
||||
void *context);
|
||||
void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc);
|
||||
void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
|
||||
struct exynos_drm_plane *exynos_plane);
|
||||
|
||||
/* This function gets pipe value to crtc device matched with out_type. */
|
||||
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
|
||||
/* This function gets crtc device matched with out_type. */
|
||||
struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
|
||||
enum exynos_drm_output_type out_type);
|
||||
|
||||
int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
|
||||
enum exynos_drm_output_type out_type);
|
||||
|
||||
/*
|
||||
* This function calls the crtc device(manager)'s te_handler() callback
|
||||
* to trigger to transfer video image at the tearing effect synchronization
|
||||
|
@ -202,19 +202,15 @@ int exynos_dpi_bind(struct drm_device *dev, struct drm_encoder *encoder)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_crtc_get_pipe_from_type(dev, EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
encoder->possible_crtcs = 1 << ret;
|
||||
|
||||
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
|
||||
|
||||
drm_encoder_init(dev, encoder, &exynos_dpi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
|
||||
drm_encoder_helper_add(encoder, &exynos_dpi_encoder_helper_funcs);
|
||||
|
||||
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = exynos_dpi_create_connector(encoder);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to create connector ret = %d\n", ret);
|
||||
|
@ -91,6 +91,7 @@ struct exynos_drm_plane {
|
||||
#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0)
|
||||
#define EXYNOS_DRM_PLANE_CAP_SCALE (1 << 1)
|
||||
#define EXYNOS_DRM_PLANE_CAP_ZPOS (1 << 2)
|
||||
#define EXYNOS_DRM_PLANE_CAP_TILE (1 << 3)
|
||||
|
||||
/*
|
||||
* Exynos DRM plane configuration structure.
|
||||
@ -117,6 +118,7 @@ struct exynos_drm_plane_config {
|
||||
* @disable: disable the device
|
||||
* @enable_vblank: specific driver callback for enabling vblank interrupt.
|
||||
* @disable_vblank: specific driver callback for disabling vblank interrupt.
|
||||
* @mode_valid: specific driver callback for mode validation
|
||||
* @atomic_check: validate state
|
||||
* @atomic_begin: prepare device to receive an update
|
||||
* @atomic_flush: mark the end of device update
|
||||
@ -132,6 +134,8 @@ struct exynos_drm_crtc_ops {
|
||||
int (*enable_vblank)(struct exynos_drm_crtc *crtc);
|
||||
void (*disable_vblank)(struct exynos_drm_crtc *crtc);
|
||||
u32 (*get_vblank_counter)(struct exynos_drm_crtc *crtc);
|
||||
enum drm_mode_status (*mode_valid)(struct exynos_drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode);
|
||||
int (*atomic_check)(struct exynos_drm_crtc *crtc,
|
||||
struct drm_crtc_state *state);
|
||||
void (*atomic_begin)(struct exynos_drm_crtc *crtc);
|
||||
@ -162,6 +166,7 @@ struct exynos_drm_crtc {
|
||||
const struct exynos_drm_crtc_ops *ops;
|
||||
void *ctx;
|
||||
struct exynos_drm_clk *pipe_clk;
|
||||
bool i80_mode : 1;
|
||||
};
|
||||
|
||||
static inline void exynos_drm_pipe_clk_enable(struct exynos_drm_crtc *crtc,
|
||||
|
@ -254,7 +254,6 @@ struct exynos_dsi {
|
||||
struct drm_encoder encoder;
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct drm_connector connector;
|
||||
struct device_node *panel_node;
|
||||
struct drm_panel *panel;
|
||||
struct device *dev;
|
||||
|
||||
@ -1329,12 +1328,13 @@ static int exynos_dsi_init(struct exynos_dsi *dsi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
|
||||
static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi,
|
||||
struct device *panel)
|
||||
{
|
||||
int ret;
|
||||
int te_gpio_irq;
|
||||
|
||||
dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
|
||||
dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
|
||||
if (dsi->te_gpio == -ENOENT)
|
||||
return 0;
|
||||
|
||||
@ -1374,85 +1374,6 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
|
||||
}
|
||||
}
|
||||
|
||||
static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *device)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
dsi->lanes = device->lanes;
|
||||
dsi->format = device->format;
|
||||
dsi->mode_flags = device->mode_flags;
|
||||
dsi->panel_node = device->dev.of_node;
|
||||
|
||||
/*
|
||||
* This is a temporary solution and should be made by more generic way.
|
||||
*
|
||||
* If attached panel device is for command mode one, dsi should register
|
||||
* TE interrupt handler.
|
||||
*/
|
||||
if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
|
||||
int ret = exynos_dsi_register_te_irq(dsi);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dsi->connector.dev)
|
||||
drm_helper_hpd_irq_event(dsi->connector.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *device)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
exynos_dsi_unregister_te_irq(dsi);
|
||||
|
||||
dsi->panel_node = NULL;
|
||||
|
||||
if (dsi->connector.dev)
|
||||
drm_helper_hpd_irq_event(dsi->connector.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
struct exynos_dsi_transfer xfer;
|
||||
int ret;
|
||||
|
||||
if (!(dsi->state & DSIM_STATE_ENABLED))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
|
||||
ret = exynos_dsi_init(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
dsi->state |= DSIM_STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
ret = mipi_dsi_create_packet(&xfer.packet, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
xfer.rx_len = msg->rx_len;
|
||||
xfer.rx_payload = msg->rx_buf;
|
||||
xfer.flags = msg->flags;
|
||||
|
||||
ret = exynos_dsi_transfer(dsi, &xfer);
|
||||
return (ret < 0) ? ret : xfer.rx_done;
|
||||
}
|
||||
|
||||
static const struct mipi_dsi_host_ops exynos_dsi_ops = {
|
||||
.attach = exynos_dsi_host_attach,
|
||||
.detach = exynos_dsi_host_detach,
|
||||
.transfer = exynos_dsi_host_transfer,
|
||||
};
|
||||
|
||||
static void exynos_dsi_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct exynos_dsi *dsi = encoder_to_dsi(encoder);
|
||||
@ -1508,25 +1429,7 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
|
||||
static enum drm_connector_status
|
||||
exynos_dsi_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct exynos_dsi *dsi = connector_to_dsi(connector);
|
||||
|
||||
if (!dsi->panel) {
|
||||
dsi->panel = of_drm_find_panel(dsi->panel_node);
|
||||
if (dsi->panel)
|
||||
drm_panel_attach(dsi->panel, &dsi->connector);
|
||||
} else if (!dsi->panel_node) {
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
encoder = platform_get_drvdata(to_platform_device(dsi->dev));
|
||||
exynos_dsi_disable(encoder);
|
||||
drm_panel_detach(dsi->panel);
|
||||
dsi->panel = NULL;
|
||||
}
|
||||
|
||||
if (dsi->panel)
|
||||
return connector_status_connected;
|
||||
|
||||
return connector_status_disconnected;
|
||||
return connector->status;
|
||||
}
|
||||
|
||||
static void exynos_dsi_connector_destroy(struct drm_connector *connector)
|
||||
@ -1575,6 +1478,7 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
|
||||
return ret;
|
||||
}
|
||||
|
||||
connector->status = connector_status_disconnected;
|
||||
drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
@ -1611,6 +1515,105 @@ static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
|
||||
|
||||
static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *device)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
struct drm_device *drm = dsi->connector.dev;
|
||||
|
||||
/*
|
||||
* This is a temporary solution and should be made by more generic way.
|
||||
*
|
||||
* If attached panel device is for command mode one, dsi should register
|
||||
* TE interrupt handler.
|
||||
*/
|
||||
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
|
||||
int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&drm->mode_config.mutex);
|
||||
|
||||
dsi->lanes = device->lanes;
|
||||
dsi->format = device->format;
|
||||
dsi->mode_flags = device->mode_flags;
|
||||
dsi->panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (dsi->panel) {
|
||||
drm_panel_attach(dsi->panel, &dsi->connector);
|
||||
dsi->connector.status = connector_status_connected;
|
||||
}
|
||||
exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
|
||||
!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
|
||||
|
||||
mutex_unlock(&drm->mode_config.mutex);
|
||||
|
||||
if (drm->mode_config.poll_enabled)
|
||||
drm_kms_helper_hotplug_event(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *device)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
struct drm_device *drm = dsi->connector.dev;
|
||||
|
||||
mutex_lock(&drm->mode_config.mutex);
|
||||
|
||||
if (dsi->panel) {
|
||||
exynos_dsi_disable(&dsi->encoder);
|
||||
drm_panel_detach(dsi->panel);
|
||||
dsi->panel = NULL;
|
||||
dsi->connector.status = connector_status_disconnected;
|
||||
}
|
||||
|
||||
mutex_unlock(&drm->mode_config.mutex);
|
||||
|
||||
if (drm->mode_config.poll_enabled)
|
||||
drm_kms_helper_hotplug_event(drm);
|
||||
|
||||
exynos_dsi_unregister_te_irq(dsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
struct exynos_dsi *dsi = host_to_dsi(host);
|
||||
struct exynos_dsi_transfer xfer;
|
||||
int ret;
|
||||
|
||||
if (!(dsi->state & DSIM_STATE_ENABLED))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
|
||||
ret = exynos_dsi_init(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
dsi->state |= DSIM_STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
ret = mipi_dsi_create_packet(&xfer.packet, msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
xfer.rx_len = msg->rx_len;
|
||||
xfer.rx_payload = msg->rx_buf;
|
||||
xfer.flags = msg->flags;
|
||||
|
||||
ret = exynos_dsi_transfer(dsi, &xfer);
|
||||
return (ret < 0) ? ret : xfer.rx_done;
|
||||
}
|
||||
|
||||
static const struct mipi_dsi_host_ops exynos_dsi_ops = {
|
||||
.attach = exynos_dsi_host_attach,
|
||||
.detach = exynos_dsi_host_detach,
|
||||
.transfer = exynos_dsi_host_transfer,
|
||||
};
|
||||
|
||||
static int exynos_dsi_of_read_u32(const struct device_node *np,
|
||||
const char *propname, u32 *out_value)
|
||||
{
|
||||
@ -1662,20 +1665,15 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
|
||||
struct drm_bridge *bridge;
|
||||
int ret;
|
||||
|
||||
ret = exynos_drm_crtc_get_pipe_from_type(drm_dev,
|
||||
EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
encoder->possible_crtcs = 1 << ret;
|
||||
|
||||
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
|
||||
|
||||
drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
|
||||
drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
|
||||
|
||||
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = exynos_dsi_create_connector(encoder);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to create connector ret = %d\n", ret);
|
||||
|
@ -225,4 +225,6 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
|
||||
|
||||
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
|
||||
dev->mode_config.helper_private = &exynos_drm_mode_config_helpers;
|
||||
|
||||
dev->mode_config.allow_fb_modifiers = true;
|
||||
}
|
||||
|
@ -583,18 +583,12 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win,
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
default:
|
||||
val |= WINCON1_BPPMODE_25BPP_A1888
|
||||
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
|
||||
val |= WINCONx_WSWP;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
|
||||
|
||||
val |= WINCON0_BPPMODE_24BPP_888;
|
||||
val |= WINCONx_WSWP;
|
||||
val |= WINCONx_BURSTLEN_16WORD;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -718,13 +712,13 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
|
||||
unsigned long val, size, offset;
|
||||
unsigned int last_x, last_y, buf_offsize, line_size;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int bpp = fb->format->cpp[0];
|
||||
unsigned int cpp = fb->format->cpp[0];
|
||||
unsigned int pitch = fb->pitches[0];
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
offset = state->src.x * bpp;
|
||||
offset = state->src.x * cpp;
|
||||
offset += state->src.y * pitch;
|
||||
|
||||
/* buffer start address */
|
||||
@ -743,8 +737,8 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc,
|
||||
state->crtc.w, state->crtc.h);
|
||||
|
||||
/* buffer size */
|
||||
buf_offsize = pitch - (state->crtc.w * bpp);
|
||||
line_size = state->crtc.w * bpp;
|
||||
buf_offsize = pitch - (state->crtc.w * cpp);
|
||||
line_size = state->crtc.w * cpp;
|
||||
val = VIDW_BUF_SIZE_OFFSET(buf_offsize) |
|
||||
VIDW_BUF_SIZE_PAGEWIDTH(line_size) |
|
||||
VIDW_BUF_SIZE_OFFSET_E(buf_offsize) |
|
||||
|
@ -21,9 +21,12 @@
|
||||
#include <linux/component.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
|
||||
/* Sysreg registers for MIC */
|
||||
#define DSD_CFG_MUX 0x1004
|
||||
#define MIC0_RGB_MUX (1 << 0)
|
||||
@ -85,12 +88,6 @@
|
||||
|
||||
#define MIC_BS_SIZE_2D(x) ((x) & 0x3fff)
|
||||
|
||||
enum {
|
||||
ENDPOINT_DECON_NODE,
|
||||
ENDPOINT_DSI_NODE,
|
||||
NUM_ENDPOINTS
|
||||
};
|
||||
|
||||
static char *clk_names[] = { "pclk_mic0", "sclk_rgb_vclk_to_mic0" };
|
||||
#define NUM_CLKS ARRAY_SIZE(clk_names)
|
||||
static DEFINE_MUTEX(mic_mutex);
|
||||
@ -229,36 +226,6 @@ static void mic_set_reg_on(struct exynos_mic *mic, bool enable)
|
||||
writel(reg, mic->reg + MIC_OP);
|
||||
}
|
||||
|
||||
static int parse_dt(struct exynos_mic *mic)
|
||||
{
|
||||
int ret = 0, i, j;
|
||||
struct device_node *remote_node;
|
||||
struct device_node *nodes[3];
|
||||
|
||||
/*
|
||||
* The order of endpoints does matter.
|
||||
* The first node must be for decon and the second one must be for dsi.
|
||||
*/
|
||||
for (i = 0, j = 0; i < NUM_ENDPOINTS; i++) {
|
||||
remote_node = of_graph_get_remote_node(mic->dev->of_node, i, 0);
|
||||
if (!remote_node) {
|
||||
ret = -EPIPE;
|
||||
goto exit;
|
||||
}
|
||||
nodes[j++] = remote_node;
|
||||
|
||||
if (i == ENDPOINT_DECON_NODE &&
|
||||
of_get_child_by_name(remote_node, "i80-if-timings"))
|
||||
mic->i80_mode = 1;
|
||||
}
|
||||
|
||||
exit:
|
||||
while (--j > -1)
|
||||
of_node_put(nodes[j]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mic_disable(struct drm_bridge *bridge) { }
|
||||
|
||||
static void mic_post_disable(struct drm_bridge *bridge)
|
||||
@ -286,6 +253,7 @@ static void mic_mode_set(struct drm_bridge *bridge,
|
||||
|
||||
mutex_lock(&mic_mutex);
|
||||
drm_display_mode_to_videomode(mode, &mic->vm);
|
||||
mic->i80_mode = to_exynos_crtc(bridge->encoder->crtc)->i80_mode;
|
||||
mutex_unlock(&mic_mutex);
|
||||
}
|
||||
|
||||
@ -417,10 +385,6 @@ static int exynos_mic_probe(struct platform_device *pdev)
|
||||
|
||||
mic->dev = dev;
|
||||
|
||||
ret = parse_dt(mic);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_address_to_resource(dev->of_node, 0, &res);
|
||||
if (ret) {
|
||||
DRM_ERROR("mic: Failed to get mem region for MIC\n");
|
||||
|
@ -178,6 +178,29 @@ static struct drm_plane_funcs exynos_plane_funcs = {
|
||||
.atomic_destroy_state = exynos_drm_plane_destroy_state,
|
||||
};
|
||||
|
||||
static int
|
||||
exynos_drm_plane_check_format(const struct exynos_drm_plane_config *config,
|
||||
struct exynos_drm_plane_state *state)
|
||||
{
|
||||
struct drm_framebuffer *fb = state->base.fb;
|
||||
|
||||
switch (fb->modifier) {
|
||||
case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
|
||||
if (!(config->capabilities & EXYNOS_DRM_PLANE_CAP_TILE))
|
||||
return -ENOTSUPP;
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_MOD_LINEAR:
|
||||
break;
|
||||
|
||||
default:
|
||||
DRM_ERROR("unsupported pixel format modifier");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config,
|
||||
struct exynos_drm_plane_state *state)
|
||||
@ -222,6 +245,10 @@ static int exynos_plane_atomic_check(struct drm_plane *plane,
|
||||
/* translate state into exynos_state */
|
||||
exynos_plane_mode_set(exynos_state);
|
||||
|
||||
ret = exynos_drm_plane_check_format(exynos_plane->config, exynos_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
|
||||
return ret;
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
|
||||
struct exynos_drm_plane *exynos_plane;
|
||||
struct exynos_drm_plane_config plane_config = { 0 };
|
||||
unsigned int i;
|
||||
int pipe, ret;
|
||||
int ret;
|
||||
|
||||
ctx->drm_dev = drm_dev;
|
||||
|
||||
@ -406,20 +406,15 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
|
||||
return PTR_ERR(ctx->crtc);
|
||||
}
|
||||
|
||||
pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
|
||||
EXYNOS_DISPLAY_TYPE_VIDI);
|
||||
if (pipe < 0)
|
||||
return pipe;
|
||||
|
||||
encoder->possible_crtcs = 1 << pipe;
|
||||
|
||||
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
|
||||
|
||||
drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
|
||||
drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs);
|
||||
|
||||
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_VIDI);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = vidi_create_connector(encoder);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to create connector ret = %d\n", ret);
|
||||
|
@ -1697,32 +1697,25 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
||||
struct drm_device *drm_dev = data;
|
||||
struct hdmi_context *hdata = dev_get_drvdata(dev);
|
||||
struct drm_encoder *encoder = &hdata->encoder;
|
||||
struct exynos_drm_crtc *exynos_crtc;
|
||||
struct drm_crtc *crtc;
|
||||
int ret, pipe;
|
||||
struct exynos_drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
hdata->drm_dev = drm_dev;
|
||||
|
||||
pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
|
||||
EXYNOS_DISPLAY_TYPE_HDMI);
|
||||
if (pipe < 0)
|
||||
return pipe;
|
||||
|
||||
hdata->phy_clk.enable = hdmiphy_clk_enable;
|
||||
|
||||
crtc = drm_crtc_from_index(drm_dev, pipe);
|
||||
exynos_crtc = to_exynos_crtc(crtc);
|
||||
exynos_crtc->pipe_clk = &hdata->phy_clk;
|
||||
|
||||
encoder->possible_crtcs = 1 << pipe;
|
||||
|
||||
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
|
||||
|
||||
drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
|
||||
drm_encoder_helper_add(encoder, &exynos_hdmi_encoder_helper_funcs);
|
||||
|
||||
ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_HDMI);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
crtc = exynos_drm_crtc_get_by_type(drm_dev, EXYNOS_DISPLAY_TYPE_HDMI);
|
||||
crtc->pipe_clk = &hdata->phy_clk;
|
||||
|
||||
ret = hdmi_create_connector(encoder);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to create connector ret = %d\n", ret);
|
||||
|
@ -148,7 +148,8 @@ static const struct exynos_drm_plane_config plane_configs[MIXER_WIN_NR] = {
|
||||
.pixel_formats = vp_formats,
|
||||
.num_pixel_formats = ARRAY_SIZE(vp_formats),
|
||||
.capabilities = EXYNOS_DRM_PLANE_CAP_SCALE |
|
||||
EXYNOS_DRM_PLANE_CAP_ZPOS,
|
||||
EXYNOS_DRM_PLANE_CAP_ZPOS |
|
||||
EXYNOS_DRM_PLANE_CAP_TILE,
|
||||
},
|
||||
};
|
||||
|
||||
@ -483,29 +484,18 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
||||
unsigned int priority = state->base.normalized_zpos + 1;
|
||||
unsigned long flags;
|
||||
dma_addr_t luma_addr[2], chroma_addr[2];
|
||||
bool tiled_mode = false;
|
||||
bool crcb_mode = false;
|
||||
bool is_tiled, is_nv21;
|
||||
u32 val;
|
||||
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_NV12:
|
||||
crcb_mode = false;
|
||||
break;
|
||||
case DRM_FORMAT_NV21:
|
||||
crcb_mode = true;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("pixel format for vp is wrong [%d].\n",
|
||||
fb->format->format);
|
||||
return;
|
||||
}
|
||||
is_nv21 = (fb->format->format == DRM_FORMAT_NV21);
|
||||
is_tiled = (fb->modifier == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE);
|
||||
|
||||
luma_addr[0] = exynos_drm_fb_dma_addr(fb, 0);
|
||||
chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
__set_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
if (tiled_mode) {
|
||||
if (is_tiled) {
|
||||
luma_addr[1] = luma_addr[0] + 0x40;
|
||||
chroma_addr[1] = chroma_addr[0] + 0x40;
|
||||
} else {
|
||||
@ -525,14 +515,14 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
||||
vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
|
||||
|
||||
/* setup format */
|
||||
val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
|
||||
val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
|
||||
val = (is_nv21 ? VP_MODE_NV21 : VP_MODE_NV12);
|
||||
val |= (is_tiled ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
|
||||
vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
|
||||
|
||||
/* setting size of input image */
|
||||
vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(fb->pitches[0]) |
|
||||
VP_IMG_VSIZE(fb->height));
|
||||
/* chroma height has to reduced by 2 to avoid chroma distorions */
|
||||
/* chroma plane for NV12/NV21 is half the height of the luma plane */
|
||||
vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(fb->pitches[0]) |
|
||||
VP_IMG_VSIZE(fb->height / 2));
|
||||
|
||||
@ -594,7 +584,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
unsigned long flags;
|
||||
unsigned int win = plane->index;
|
||||
unsigned int x_ratio = 0, y_ratio = 0;
|
||||
unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
|
||||
unsigned int dst_x_offset, dst_y_offset;
|
||||
dma_addr_t dma_addr;
|
||||
unsigned int fmt;
|
||||
u32 val;
|
||||
@ -616,12 +606,9 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
default:
|
||||
fmt = MXR_FORMAT_ARGB8888;
|
||||
break;
|
||||
|
||||
default:
|
||||
DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* ratio is already checked by common plane code */
|
||||
@ -631,12 +618,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
dst_x_offset = state->crtc.x;
|
||||
dst_y_offset = state->crtc.y;
|
||||
|
||||
/* converting dma address base and source offset */
|
||||
/* translate dma address base s.t. the source image offset is zero */
|
||||
dma_addr = exynos_drm_fb_dma_addr(fb, 0)
|
||||
+ (state->src.x * fb->format->cpp[0])
|
||||
+ (state->src.y * fb->pitches[0]);
|
||||
src_x_offset = 0;
|
||||
src_y_offset = 0;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
__set_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
@ -667,11 +652,6 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
||||
val |= MXR_GRP_WH_V_SCALE(y_ratio);
|
||||
mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
|
||||
|
||||
/* setup offsets in source image */
|
||||
val = MXR_GRP_SXY_SX(src_x_offset);
|
||||
val |= MXR_GRP_SXY_SY(src_y_offset);
|
||||
mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
|
||||
|
||||
/* setup offsets in display image */
|
||||
val = MXR_GRP_DXY_DX(dst_x_offset);
|
||||
val |= MXR_GRP_DXY_DY(dst_y_offset);
|
||||
@ -748,6 +728,10 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
||||
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
|
||||
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
|
||||
|
||||
/* set all source image offsets to zero */
|
||||
mixer_reg_write(res, MXR_GRAPHIC_SXY(0), 0);
|
||||
mixer_reg_write(res, MXR_GRAPHIC_SXY(1), 0);
|
||||
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user