- 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:
Dave Airlie 2017-08-29 10:37:36 +10:00
commit 7ebdb0dd52
18 changed files with 299 additions and 327 deletions

View File

@ -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 {
};
};

View File

@ -280,9 +280,6 @@ &cpu4 {
&decon {
status = "okay";
i80-if-timings {
};
};
&decon_tv {
@ -1116,9 +1113,6 @@ &pwm {
&mic {
status = "okay";
i80-if-timings {
};
};
&pmu_system_controller {

View File

@ -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 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc)
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) {

View File

@ -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));

View File

@ -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);

View File

@ -13,6 +13,7 @@
*/
#include <drm/drmP.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"

View File

@ -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 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
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)

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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) |

View File

@ -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");

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}