LF-4887-6 drm/imx: lcdif: Add MPU interface(8080 mode) support

This patch adds MPU interface(8080 mode) support in the LCDIF driver
so that LCDIF may send frames to the sec-dsim MIPI DSI controller embedded
in i.MX8mm/mn via that interface.  From the MIPI DSI controller perspective,
the input interface is called S-i80(Synchronous i80 Interface).

The general idea is to leverage the self refresh(SR) helpers offered by the
DRM core to make the external display device enter self refresh mode and
hence disable LCDIF when appropriate.  The relevant DRM connectors'
conn_state->self_refresh_aware flags are inspected to determine whether to
use MPU interface or DOTCLK interface(also called RGB interface).

Since LCDIF will be disabled when the self refresh is active and the data
is transferred at modest rate, there are below known limitations:

1) The cur_frame_done interrupt comes at a lower frequency than the
   display vsync signal frequency due to the low data transfer rate.
   So, vblank events cannot reflect the real vsync pulses of the external
   display.

2) Screen tearing phenomenon can be seen, because the frame update is
   asynchronous to the external display self refresh and the LCDIF
   controller does not support the 'Tearing Effect' mechanism.

3) The fbdev emulation and boot logo can not be updated onto screen in
   time, because the external display often enters self refresh mode at
   those stages.  The boot logo will show up until the next time when
   frame is sent to the external display via the MPU interface, e.g., when
   the next framebuffer pan display operation is done after the logo is
   ready in framebuffer.  Like the below command line, writing to the
   framebuffer sys node 'pan' frequently at background may make the
   framebuffer emulation be a bit responsive.

while true; do echo 0,0 > /sys/class/graphics/fb0/pan; sleep 1; done &

Cc: Sandor Yu <Sandor.yu@nxp.com>
Cc: Wujian sun <wujian.sun_1@nxp.com>
Reviewed-by: Sandor Yu <Sandor.yu@nxp.com>
Signed-off-by: Liu Ying <victor.liu@nxp.com>
This commit is contained in:
Liu Ying 2021-07-27 16:08:34 +08:00 committed by Dong Aisheng
parent 52e179861e
commit 13c71ee35e
6 changed files with 120 additions and 29 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,7 +22,9 @@
#include <drm/drm_atomic.h>
#include <drm/drm_edid.h>
#include <drm/drm_vblank.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_self_refresh_helper.h>
#include <video/imx-lcdif.h>
#include <video/videomode.h>
@ -159,6 +161,7 @@ static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state);
struct videomode vm;
bool use_i80 = lcdif_drm_connector_is_self_refresh_aware(state);
drm_display_mode_to_videomode(mode, &vm);
@ -174,7 +177,7 @@ static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
pm_runtime_get_sync(lcdif_crtc->dev->parent);
lcdif_set_mode(lcdif, &vm);
lcdif_set_mode(lcdif, &vm, use_i80);
/* config LCDIF output bus format */
lcdif_set_bus_fmt(lcdif, imx_crtc_state->bus_format);
@ -188,8 +191,14 @@ static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc,
static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
crtc);
struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc);
struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent);
bool use_i80 = lcdif_drm_connector_is_self_refresh_aware(state);
if (old_crtc_state->self_refresh_active)
return;
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
@ -200,7 +209,7 @@ static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc,
drm_crtc_vblank_off(crtc);
lcdif_disable_controller(lcdif);
lcdif_disable_controller(lcdif, use_i80);
pm_runtime_put(lcdif_crtc->dev->parent);
}
@ -337,6 +346,13 @@ static int lcdif_crtc_init(struct lcdif_crtc *lcdif_crtc,
disable_irq(lcdif_crtc->vbl_irq);
ret = drm_self_refresh_helper_init(&lcdif_crtc->base);
if (ret) {
dev_err(lcdif_crtc->dev,
"failed to init self refresh helper: %d\n", ret);
goto primary_plane_deinit;
}
return 0;
primary_plane_deinit:
@ -380,6 +396,8 @@ static void lcdif_crtc_unbind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct lcdif_crtc *lcdif_crtc = dev_get_drvdata(dev);
drm_self_refresh_helper_cleanup(&lcdif_crtc->base);
lcdif_plane_deinit(drm, lcdif_crtc->plane[0]);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,7 +15,25 @@
#ifndef __LCDIF_KMS_H
#define __LCDIF_KMS_H
#include <drm/drm_atomic.h>
#include <drm/drm_connector.h>
extern const struct drm_mode_config_funcs lcdif_drm_mode_config_funcs;
extern struct drm_mode_config_helper_funcs lcdif_drm_mode_config_helpers;
static inline bool
lcdif_drm_connector_is_self_refresh_aware(struct drm_atomic_state *state)
{
struct drm_connector *conn;
struct drm_connector_state *conn_state;
int i;
for_each_new_connector_in_state(state, conn, conn_state, i) {
if (conn_state->self_refresh_aware)
return true;
}
return false;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -27,6 +27,7 @@
#include <video/imx-lcdif.h>
#include "lcdif-plane.h"
#include "lcdif-kms.h"
static uint32_t lcdif_pixel_formats[] = {
DRM_FORMAT_XRGB8888,
@ -55,6 +56,7 @@ static int lcdif_plane_atomic_check(struct drm_plane *plane,
struct drm_crtc_state *crtc_state;
struct drm_display_mode *mode;
struct drm_rect clip = { 0 };
bool use_i80;
/* 'fb' should also be NULL which has been checked in
* the core sanity check function 'drm_atomic_plane_check()'
@ -98,6 +100,17 @@ static int lcdif_plane_atomic_check(struct drm_plane *plane,
crtc_state->mode_changed = true;
}
/* Add affected connectors to check if we use i80 mode or not. */
ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
if (ret)
return ret;
use_i80 = lcdif_drm_connector_is_self_refresh_aware(state);
/* Do not support cropping in i80 mode. */
if (use_i80 && (plane_state->src_w >> 16 != fb->width))
return -EINVAL;
return 0;
}
@ -112,6 +125,7 @@ static void lcdif_plane_atomic_update(struct drm_plane *plane,
struct drm_gem_dma_object *gem_obj = NULL;
u32 fb_addr, src_off, src_w, fb_idx, cpp, stride;
bool crop;
bool use_i80 = lcdif_drm_connector_is_self_refresh_aware(state);
/* plane and crtc is disabling */
if (!fb)
@ -135,7 +149,7 @@ static void lcdif_plane_atomic_update(struct drm_plane *plane,
return;
}
lcdif_set_fb_addr(lcdif, fb_idx, fb_addr);
lcdif_set_fb_addr(lcdif, fb_idx, fb_addr, use_i80);
/* Config pixel format and horizontal cropping
* if CRTC needs a full modeset which needs to
@ -154,7 +168,9 @@ static void lcdif_plane_atomic_update(struct drm_plane *plane,
crop = src_w != stride ? true : false;
lcdif_set_fb_hcrop(lcdif, src_w, stride, crop);
lcdif_enable_controller(lcdif);
lcdif_enable_controller(lcdif, use_i80);
} else if (use_i80) {
lcdif_enable_controller(lcdif, use_i80);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -360,12 +360,15 @@ void lcdif_set_bus_fmt(struct lcdif_soc *lcdif, u32 bus_format)
}
EXPORT_SYMBOL(lcdif_set_bus_fmt);
void lcdif_set_fb_addr(struct lcdif_soc *lcdif, int id, u32 addr)
void lcdif_set_fb_addr(struct lcdif_soc *lcdif, int id, u32 addr, bool use_i80)
{
switch (id) {
case 0:
/* primary plane */
writel(addr, lcdif->base + LCDIF_NEXT_BUF);
if (use_i80)
writel(addr, lcdif->base + LCDIF_CUR_BUF);
else
writel(addr, lcdif->base + LCDIF_NEXT_BUF);
break;
default:
/* TODO: add overlay support */
@ -432,7 +435,8 @@ void lcdif_set_fb_hcrop(struct lcdif_soc *lcdif, u32 src_w,
EXPORT_SYMBOL(lcdif_set_fb_hcrop);
void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode)
void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode,
bool use_i80)
{
const struct of_device_id *of_id =
of_match_device(imx_lcdif_dt_ids, lcdif->dev);
@ -457,6 +461,12 @@ void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode)
TRANSFER_COUNT_SET_HCOUNT(vmode->hactive),
lcdif->base + LCDIF_TRANSFER_COUNT);
if (use_i80) {
/* use MPU 8080 mode */
writel(CTRL1_MODE86, lcdif->base + LCDIF_CTRL1 + REG_CLR);
return;
}
vdctrl0 = VDCTRL0_ENABLE_PRESENT |
VDCTRL0_VSYNC_PERIOD_UNIT |
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
@ -514,20 +524,36 @@ void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode)
}
EXPORT_SYMBOL(lcdif_set_mode);
void lcdif_enable_controller(struct lcdif_soc *lcdif)
void lcdif_enable_controller(struct lcdif_soc *lcdif, bool use_i80)
{
u32 ctrl2, vdctrl4;
u32 ctrl2, vdctrl4, timing;
ctrl2 = readl(lcdif->base + LCDIF_CTRL2);
vdctrl4 = readl(lcdif->base + LCDIF_VDCTRL4);
ctrl2 &= ~CTRL2_OUTSTANDING_REQS(0x7);
ctrl2 |= CTRL2_OUTSTANDING_REQS(REQ_16);
ctrl2 |= CTRL2_OUTSTANDING_REQS(use_i80 ? REQ_8 : REQ_16);
writel(ctrl2, lcdif->base + LCDIF_CTRL2);
/* Continous dotclock mode */
writel(CTRL_BYPASS_COUNT | CTRL_DOTCLK_MODE,
lcdif->base + LCDIF_CTRL + REG_SET);
if (use_i80) {
/* MPU 8080 write mode */
writel(CTRL_DATA_SELECT, lcdif->base + LCDIF_CTRL + REG_SET);
writel(CTRL_READ_WRITEB, lcdif->base + LCDIF_CTRL + REG_CLR);
writel(CTRL_BYPASS_COUNT, lcdif->base + LCDIF_CTRL + REG_CLR);
writel(CTRL_DVI_MODE | CTRL_VSYNC_MODE | CTRL_DOTCLK_MODE,
lcdif->base + LCDIF_CTRL + REG_CLR);
writel(CTRL1_COMBINE_MPU_WR_STRB,
lcdif->base + LCDIF_CTRL1 + REG_CLR);
timing = TIMING_CMD_HOLD(2) | TIMING_CMD_SETUP(1) |
TIMING_DATA_HOLD(2) | TIMING_DATA_SETUP(1);
writel(timing, lcdif->base + LCDIF_TIMING);
} else {
/* Continuous dotclock mode */
writel(CTRL_BYPASS_COUNT | CTRL_DOTCLK_MODE,
lcdif->base + LCDIF_CTRL + REG_SET);
}
/* enable the SYNC signals first, then the DMA engine */
vdctrl4 |= VDCTRL4_SYNC_SIGNALS_ON;
@ -543,18 +569,21 @@ void lcdif_enable_controller(struct lcdif_soc *lcdif)
}
EXPORT_SYMBOL(lcdif_enable_controller);
void lcdif_disable_controller(struct lcdif_soc *lcdif)
void lcdif_disable_controller(struct lcdif_soc *lcdif, bool use_i80)
{
int ret;
u32 ctrl, vdctrl4;
writel(CTRL_RUN, lcdif->base + LCDIF_CTRL + REG_CLR);
writel(CTRL_DOTCLK_MODE, lcdif->base + LCDIF_CTRL + REG_CLR);
ret = readl_poll_timeout(lcdif->base + LCDIF_CTRL, ctrl,
!(ctrl & CTRL_RUN), 0, 1000);
if (WARN_ON(ret))
dev_err(lcdif->dev, "disable lcdif run timeout\n");
if (!use_i80) {
writel(CTRL_DOTCLK_MODE, lcdif->base + LCDIF_CTRL + REG_CLR);
ret = readl_poll_timeout(lcdif->base + LCDIF_CTRL, ctrl,
!(ctrl & CTRL_RUN), 0, 1000);
if (WARN_ON(ret))
dev_err(lcdif->dev, "disable lcdif run timeout\n");
}
writel(CTRL_MASTER, lcdif->base + LCDIF_CTRL + REG_CLR);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -50,8 +50,10 @@
/* regs bit fields */
#define CTRL_SFTRST BIT(31)
#define CTRL_CLKGATE BIT(30)
#define CTRL_READ_WRITEB BIT(28)
#define CTRL_SHIFT_DIR(x) REG_PUT((x), 26, 26)
#define CTRL_SHIFT_NUM(x) REG_PUT((x), 25, 21)
#define CTRL_DVI_MODE BIT(20)
#define CTRL_BYPASS_COUNT BIT(19)
#define CTRL_VSYNC_MODE BIT(18)
#define CTRL_DOTCLK_MODE BIT(17)
@ -69,12 +71,14 @@
#define CTRL_DF24 BIT(1)
#define CTRL_RUN BIT(0)
#define CTRL1_COMBINE_MPU_WR_STRB BIT(27)
#define CTRL1_RECOVERY_ON_UNDERFLOW BIT(24)
#define CTRL1_FIFO_CLEAR BIT(21)
#define CTRL1_SET_BYTE_PACKAGING(x) REG_PUT((x), 19, 16)
#define CTRL1_GET_BYTE_PACKAGING(x) REG_GET((x), 19, 16)
#define CTRL1_CUR_FRAME_DONE_IRQ_EN BIT(13)
#define CTRL1_CUR_FRAME_DONE_IRQ BIT(9)
#define CTRL1_MODE86 BIT(1)
#define REQ_1 0
#define REQ_2 1
@ -91,6 +95,11 @@
#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
#define TIMING_CMD_HOLD(x) REG_PUT((x), 31, 24)
#define TIMING_CMD_SETUP(x) REG_PUT((x), 23, 16)
#define TIMING_DATA_HOLD(x) REG_PUT((x), 15, 8)
#define TIMING_DATA_SETUP(x) REG_PUT((x), 7, 0)
#define VDCTRL0_ENABLE_PRESENT BIT(28)
#define VDCTRL0_VSYNC_ACT_HIGH BIT(27)
#define VDCTRL0_HSYNC_ACT_HIGH BIT(26)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 NXP
* Copyright 2018,2021 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -31,12 +31,13 @@ int lcdif_get_bus_fmt_from_pix_fmt(struct lcdif_soc *lcdif,
uint32_t format);
int lcdif_set_pix_fmt(struct lcdif_soc *lcdif, u32 format);
void lcdif_set_bus_fmt(struct lcdif_soc *lcdif, u32 bus_format);
void lcdif_set_fb_addr(struct lcdif_soc *lcdif, int id, u32 addr);
void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode);
void lcdif_set_fb_addr(struct lcdif_soc *lcdif, int id, u32 addr, bool use_i80);
void lcdif_set_mode(struct lcdif_soc *lcdif, struct videomode *vmode,
bool use_i80);
void lcdif_set_fb_hcrop(struct lcdif_soc *lcdif, u32 src_w,
u32 fb_w, bool crop);
void lcdif_enable_controller(struct lcdif_soc *lcdif);
void lcdif_disable_controller(struct lcdif_soc *lcdif);
void lcdif_enable_controller(struct lcdif_soc *lcdif, bool use_i80);
void lcdif_disable_controller(struct lcdif_soc *lcdif, bool use_i80);
void lcdif_dump_registers(struct lcdif_soc *lcdif);
long lcdif_pix_clk_round_rate(struct lcdif_soc *lcdif,
unsigned long rate);