[PATCH 4/6] imx-drm: ipuv3-crtc: Implement mode_fixup
From: Steve Longerbeam
Ask the IPU display interface, via ipu_di_adjust_videomode(), to
adjust a video mode to meet any DI restrictions. The function takes
a subsystem independent videomode, so the drm_display_mode must be
converted to videomode first, and then the adjusted mode converted
back to a drm_display_mode.
Signed-off-by: Steve Longerbeam
---
drivers/gpu/drm/imx/ipuv3-crtc.c | 12
1 file changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 11e84a2..fb16026 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -242,6 +242,18 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct videomode vm;
+ int ret;
+
+ videomode_from_drm_display_mode(adjusted_mode, &vm);
+
+ ret = ipu_di_adjust_videomode(ipu_crtc->di, &vm);
+ if (ret)
+ return false;
+
+ drm_display_mode_from_videomode(&vm, adjusted_mode);
+
return true;
}
--
1.7.9.5
[PATCH 0/6] imx-drm: ipuv3-crtc: Implement mode_fixup
From: Steve Longerbeam This patchset implements ->mode_fixup() in the imx ipuv3-crtc driver, using a new support function ipu_di_adjust_videomode(). This new function needs to be subsystem independent, so it accepts a video mode as a 'struct videomode'. Hence ipu-crtc ->mode_fixup() needs another support function to convert a drm_display_mode to a videomode before passing the mode to ipu_di_adjust_videomode() for fixup. Also some related code cleanup: 'struct ipu_di_signal_cfg' should use 'struct videomode' for mode timings. Jiada Wang (1): gpu: ipu-di: Add ipu_di_adjust_videomode() Steve Longerbeam (5): gpu: ipu-di: remove some non-functional code drm_modes: add videomode_from_drm_display_mode imx-drm: ipuv3-crtc: Implement mode_fixup imx-drm: encoder mode_set must use adjusted mode gpu: ipu-v3: Use videomode in struct ipu_di_signal_cfg drivers/gpu/drm/drm_modes.c| 40 +++ drivers/gpu/drm/imx/imx-hdmi.c |4 +- drivers/gpu/drm/imx/imx-ldb.c |4 +- drivers/gpu/drm/imx/imx-tve.c |4 +- drivers/gpu/drm/imx/ipuv3-crtc.c | 39 +- drivers/gpu/drm/imx/parallel-display.c |4 +- drivers/gpu/ipu-v3/ipu-di.c| 124 include/drm/drm_modes.h|2 + include/video/imx-ipu-v3.h | 21 ++ 9 files changed, 150 insertions(+), 92 deletions(-) -- 1.7.9.5
[PATCH 1/6] gpu: ipu-di: Add ipu_di_adjust_videomode()
From: Jiada Wang
On some monitors, high resolution modes are not working, exhibiting
pixel column truncation problems (for example, 1280x1024 displays as
1280x1022).
The function ipu_di_adjust_videomode() aims to fix these issues by
adjusting a passed videomode to IPU restrictions. The function can
be called from the drm_crtc_helper_funcs->mode_fixup() methods.
Signed-off-by: Jiada Wang
Signed-off-by: Deepak Das
Signed-off-by: Steve Longerbeam
---
drivers/gpu/ipu-v3/ipu-di.c | 29 +
include/video/imx-ipu-v3.h |2 ++
2 files changed, 31 insertions(+)
diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index c490ba4..46f9570 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -511,6 +511,35 @@ static void ipu_di_config_clock(struct ipu_di *di,
clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
}
+/*
+ * This function is called to adjust a video mode to IPU restrictions.
+ * It is meant to be called from drm crtc mode_fixup() methods.
+ */
+int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)
+{
+ u32 diff;
+
+ if (mode->vfront_porch >= 2)
+ return 0;
+
+ diff = 2 - mode->vfront_porch;
+
+ if (mode->vback_porch >= diff) {
+ mode->vfront_porch = 2;
+ mode->vback_porch -= diff;
+ } else if (mode->vsync_len > diff) {
+ mode->vfront_porch = 2;
+ mode->vsync_len = mode->vsync_len - diff;
+ } else {
+ dev_warn(di->ipu->dev, "failed to adjust videomode\n");
+ return -EINVAL;
+ }
+
+ dev_warn(di->ipu->dev, "videomode adapted for IPU restrictions\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode);
+
int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
{
u32 reg;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index c74bf4a..d333d54 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
struct ipu_soc;
@@ -236,6 +237,7 @@ void ipu_di_put(struct ipu_di *);
int ipu_di_disable(struct ipu_di *);
int ipu_di_enable(struct ipu_di *);
int ipu_di_get_num(struct ipu_di *);
+int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode);
int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig);
/*
--
1.7.9.5
[PATCH 2/6] gpu: ipu-di: remove some non-functional code
From: Steve Longerbeam h_total and v_total were calculated in ipu_di_init_sync_panel() but never actually used. Remove. Signed-off-by: Steve Longerbeam --- drivers/gpu/ipu-v3/ipu-di.c |6 -- 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c index 46f9570..41df8d7 100644 --- a/drivers/gpu/ipu-v3/ipu-di.c +++ b/drivers/gpu/ipu-v3/ipu-di.c @@ -545,7 +545,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) u32 reg; u32 di_gen, vsync_cnt; u32 div; - u32 h_total, v_total; dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", di->id, sig->width, sig->height); @@ -553,11 +552,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) return -EINVAL; - h_total = sig->width + sig->h_sync_width + sig->h_start_width + - sig->h_end_width; - v_total = sig->height + sig->v_sync_width + sig->v_start_width + - sig->v_end_width; - dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", clk_get_rate(di->clk_ipu), clk_get_rate(di->clk_di), -- 1.7.9.5
[PATCH 3/6] drm_modes: add videomode_from_drm_display_mode
From: Steve Longerbeam
Add conversion from drm_display_mode to videomode.
Signed-off-by: Steve Longerbeam
---
drivers/gpu/drm/drm_modes.c | 40
include/drm/drm_modes.h |2 ++
2 files changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 6d8b941..583a391 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -615,6 +615,46 @@ void drm_display_mode_from_videomode(const struct
videomode *vm,
}
EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
+/**
+ * videomode_from_drm_display_mode - fill in @vm using @dmode,
+ * @dmode: drm_display_mode structure to use as source
+ * @vm: videomode structure to use as destination
+ *
+ * Fills out @vm using the display mode specified in @dmode.
+ */
+void videomode_from_drm_display_mode(const struct drm_display_mode *dmode,
+struct videomode *vm)
+{
+ vm->hactive = dmode->hdisplay;
+ vm->hfront_porch = dmode->hsync_start - dmode->hdisplay;
+ vm->hsync_len = dmode->hsync_end - dmode->hsync_start;
+ vm->hback_porch = dmode->htotal - dmode->hsync_end;
+
+ vm->vactive = dmode->vdisplay;
+ vm->vfront_porch = dmode->vsync_start - dmode->vdisplay;
+ vm->vsync_len = dmode->vsync_end - dmode->vsync_start;
+ vm->vback_porch = dmode->vtotal - dmode->vsync_end;
+
+ vm->pixelclock = dmode->clock * 1000;
+
+ vm->flags = 0;
+ if (dmode->flags & DRM_MODE_FLAG_PHSYNC)
+ vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
+ else if (dmode->flags & DRM_MODE_FLAG_NHSYNC)
+ vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;
+ if (dmode->flags & DRM_MODE_FLAG_PVSYNC)
+ vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+ else if (dmode->flags & DRM_MODE_FLAG_NVSYNC)
+ vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;
+ if (dmode->flags & DRM_MODE_FLAG_INTERLACE)
+ vm->flags |= DISPLAY_FLAGS_INTERLACED;
+ if (dmode->flags & DRM_MODE_FLAG_DBLSCAN)
+ vm->flags |= DISPLAY_FLAGS_DOUBLESCAN;
+ if (dmode->flags & DRM_MODE_FLAG_DBLCLK)
+ vm->flags |= DISPLAY_FLAGS_DOUBLECLK;
+}
+EXPORT_SYMBOL_GPL(videomode_from_drm_display_mode);
+
#ifdef CONFIG_OF
/**
* of_get_drm_display_mode - get a drm_display_mode from devicetree
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index 91d0582..60c0144 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -197,6 +197,8 @@ struct drm_display_mode *drm_gtf_mode_complex(struct
drm_device *dev,
int GTF_K, int GTF_2J);
void drm_display_mode_from_videomode(const struct videomode *vm,
struct drm_display_mode *dmode);
+void videomode_from_drm_display_mode(const struct drm_display_mode *dmode,
+struct videomode *vm);
int of_get_drm_display_mode(struct device_node *np,
struct drm_display_mode *dmode,
int index);
--
1.7.9.5
[PATCH 5/6] imx-drm: encoder mode_set must use adjusted mode
From: Steve Longerbeam
The encoder ->mode_set() methods need to use the hw adjusted mode,
not the original mode.
Signed-off-by: Steve Longerbeam
---
drivers/gpu/drm/imx/imx-hdmi.c |4 ++--
drivers/gpu/drm/imx/imx-ldb.c |4 ++--
drivers/gpu/drm/imx/imx-tve.c |4 ++--
drivers/gpu/drm/imx/parallel-display.c |4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/imx/imx-hdmi.c b/drivers/gpu/drm/imx/imx-hdmi.c
index aaec6b2..32116cc 100644
--- a/drivers/gpu/drm/imx/imx-hdmi.c
+++ b/drivers/gpu/drm/imx/imx-hdmi.c
@@ -1417,8 +1417,8 @@ static struct drm_encoder
*imx_hdmi_connector_best_encoder(struct drm_connector
}
static void imx_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_display_mode *orig_mode,
+ struct drm_display_mode *mode)
{
struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index 4662e00..a88c0e1 100644
--- a/drivers/gpu/drm/imx/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
@@ -246,8 +246,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder
*encoder)
}
static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
-struct drm_display_mode *mode,
-struct drm_display_mode *adjusted_mode)
+struct drm_display_mode *orig_mode,
+struct drm_display_mode *mode)
{
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb;
diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c
index 42c651b..9709bf9a 100644
--- a/drivers/gpu/drm/imx/imx-tve.c
+++ b/drivers/gpu/drm/imx/imx-tve.c
@@ -312,8 +312,8 @@ static void imx_tve_encoder_prepare(struct drm_encoder
*encoder)
}
static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
-struct drm_display_mode *mode,
-struct drm_display_mode *adjusted_mode)
+struct drm_display_mode *orig_mode,
+struct drm_display_mode *mode)
{
struct imx_tve *tve = enc_to_tve(encoder);
unsigned long rounded_rate;
diff --git a/drivers/gpu/drm/imx/parallel-display.c
b/drivers/gpu/drm/imx/parallel-display.c
index 015a454..d0842a4 100644
--- a/drivers/gpu/drm/imx/parallel-display.c
+++ b/drivers/gpu/drm/imx/parallel-display.c
@@ -131,8 +131,8 @@ static void imx_pd_encoder_commit(struct drm_encoder
*encoder)
}
static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
-struct drm_display_mode *mode,
-struct drm_display_mode *adjusted_mode)
+struct drm_display_mode *orig_mode,
+struct drm_display_mode *mode)
{
}
--
1.7.9.5
[PATCH 6/6] gpu: ipu-v3: Use videomode in struct ipu_di_signal_cfg
From: Steve Longerbeam
This patch changes struct ipu_di_signal_cfg to use struct videomode
to define video timings and flags.
Signed-off-by: Steve Longerbeam
---
drivers/gpu/drm/imx/ipuv3-crtc.c | 27 +++-
drivers/gpu/ipu-v3/ipu-di.c | 89 --
include/video/imx-ipu-v3.h | 19 ++--
3 files changed, 57 insertions(+), 78 deletions(-)
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index fb16026..0a50129 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -158,35 +158,20 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
out_pixel_fmt = ipu_crtc->interface_pix_fmt;
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- sig_cfg.interlaced = 1;
- if (mode->flags & DRM_MODE_FLAG_PHSYNC)
- sig_cfg.Hsync_pol = 1;
- if (mode->flags & DRM_MODE_FLAG_PVSYNC)
- sig_cfg.Vsync_pol = 1;
-
sig_cfg.enable_pol = 1;
sig_cfg.clk_pol = 0;
- sig_cfg.width = mode->hdisplay;
- sig_cfg.height = mode->vdisplay;
sig_cfg.pixel_fmt = out_pixel_fmt;
- sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
- sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
- sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
-
- sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
- sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
- sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
- sig_cfg.pixelclock = mode->clock * 1000;
sig_cfg.clkflags = ipu_crtc->di_clkflags;
-
sig_cfg.v_to_h_sync = 0;
-
sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;
- ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
- out_pixel_fmt, mode->hdisplay);
+ videomode_from_drm_display_mode(mode, &sig_cfg.mode);
+
+ ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
+ (mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ true : false,
+ out_pixel_fmt, mode->hdisplay);
if (ret) {
dev_err(ipu_crtc->dev,
"initializing display controller failed with
%d\n",
diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index 41df8d7..d95fbd0 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -207,10 +207,10 @@ static void ipu_di_sync_config(struct ipu_di *di, struct
di_sync_config *config,
static void ipu_di_sync_config_interlaced(struct ipu_di *di,
struct ipu_di_signal_cfg *sig)
{
- u32 h_total = sig->width + sig->h_sync_width +
- sig->h_start_width + sig->h_end_width;
- u32 v_total = sig->height + sig->v_sync_width +
- sig->v_start_width + sig->v_end_width;
+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len +
+ sig->mode.hback_porch + sig->mode.hfront_porch;
+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len +
+ sig->mode.vback_porch + sig->mode.vfront_porch;
u32 reg;
struct di_sync_config cfg[] = {
{
@@ -229,13 +229,13 @@ static void ipu_di_sync_config_interlaced(struct ipu_di
*di,
}, {
.run_count = v_total / 2 - 1,
.run_src = DI_SYNC_HSYNC,
- .offset_count = sig->v_start_width,
+ .offset_count = sig->mode.vback_porch,
.offset_src = DI_SYNC_HSYNC,
.repeat_count = 2,
.cnt_clr_src = DI_SYNC_VSYNC,
}, {
.run_src = DI_SYNC_HSYNC,
- .repeat_count = sig->height / 2,
+ .repeat_count = sig->mode.vactive / 2,
.cnt_clr_src = 4,
}, {
.run_count = v_total - 1,
@@ -249,9 +249,9 @@ static void ipu_di_sync_config_interlaced(struct ipu_di *di,
.cnt_clr_src = DI_SYNC_VSYNC,
}, {
.run_src = DI_SYNC_CLK,
- .offset_count = sig->h_start_width,
+ .offset_count = sig->mode.hback_porch,
.offset_src = DI_SYNC_CLK,
- .repeat_count = sig->width,
+ .repeat_count = sig->mode.hactive,
.cnt_clr_src = 5,
}, {
.run_count = v_total - 1,
@@ -277,10 +277,10 @@ static void ipu_di_sync_config_interlaced(struct ipu_di
*di,
static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
struct ipu_di_signal_cfg *sig, int div)
{
- u32 h_total = sig->width + sig->h_sync_width + sig->h_start_widt
