mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-10-22 23:23:03 +02:00
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:
parent
52e179861e
commit
13c71ee35e
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user