summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt4
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c182
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.h8
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c51
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h34
-rw-r--r--drivers/gpu/drm/rockchip/Makefile2
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c70
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h6
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.c12
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_psr.c245
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_psr.h26
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c198
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.h7
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.c242
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.h193
-rw-r--r--include/drm/bridge/analogix_dp.h3
17 files changed, 1168 insertions, 118 deletions
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
index 5489b59..9eb3f0a 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
@@ -6,8 +6,10 @@ buffer to an external LCD interface.
Required properties:
- compatible: value should be one of the following
- "rockchip,rk3288-vop";
"rockchip,rk3036-vop";
+ "rockchip,rk3288-vop";
+ "rockchip,rk3399-vop-big";
+ "rockchip,rk3399-vop-lit";
- interrupts: should contain a list of all VOP IP block interrupts in the
order: VSYNC, LCD_SYSTEM. The interrupt specifier
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..efac8ab 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,83 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
return 0;
}
+int analogix_dp_enable_psr(struct device *dev)
+{
+ struct analogix_dp_device *dp = dev_get_drvdata(dev);
+ struct edp_vsc_psr psr_vsc;
+
+ if (!dp->psr_support)
+ return -EINVAL;
+
+ /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+ memset(&psr_vsc, 0, sizeof(psr_vsc));
+ psr_vsc.sdp_header.HB0 = 0;
+ psr_vsc.sdp_header.HB1 = 0x7;
+ psr_vsc.sdp_header.HB2 = 0x2;
+ psr_vsc.sdp_header.HB3 = 0x8;
+
+ psr_vsc.DB0 = 0;
+ psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
+
+ analogix_dp_send_psr_spd(dp, &psr_vsc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
+
+int analogix_dp_disable_psr(struct device *dev)
+{
+ struct analogix_dp_device *dp = dev_get_drvdata(dev);
+ struct edp_vsc_psr psr_vsc;
+
+ if (!dp->psr_support)
+ return -EINVAL;
+
+ /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+ memset(&psr_vsc, 0, sizeof(psr_vsc));
+ psr_vsc.sdp_header.HB0 = 0;
+ psr_vsc.sdp_header.HB1 = 0x7;
+ psr_vsc.sdp_header.HB2 = 0x2;
+ psr_vsc.sdp_header.HB3 = 0x8;
+
+ psr_vsc.DB0 = 0;
+ psr_vsc.DB1 = 0;
+
+ analogix_dp_send_psr_spd(dp, &psr_vsc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+ unsigned char psr_version;
+
+ analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+ dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+ return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+ unsigned char psr_en;
+
+ /* Disable psr function */
+ analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+ psr_en &= ~DP_PSR_ENABLE;
+ analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+ /* Main-Link transmitter remains active during PSR active states */
+ psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+ analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+ /* Enable psr function */
+ psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+ DP_PSR_CRC_VERIFICATION;
+ analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+ analogix_dp_enable_psr_crc(dp);
+}
+
static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
{
int i;
@@ -921,13 +998,69 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
/* Enable video */
analogix_dp_start_video(dp);
+
+ dp->psr_support = analogix_dp_detect_sink_psr(dp);
+ if (dp->psr_support)
+ analogix_dp_enable_sink_psr(dp);
+}
+
+/*
+ * This function is a bit of a catch-all for panel preparation, hopefully
+ * simplifying the logic of functions that need to prepare/unprepare the panel
+ * below.
+ *
+ * If @prepare is true, this function will prepare the panel. Conversely, if it
+ * is false, the panel will be unprepared.
+ *
+ * If @is_modeset_prepare is true, the function will disregard the current state
+ * of the panel and either prepare/unprepare the panel based on @prepare. Once
+ * it finishes, it will update dp->panel_is_modeset to reflect the current state
+ * of the panel.
+ */
+static int analogix_dp_prepare_panel(struct analogix_dp_device *dp,
+ bool prepare, bool is_modeset_prepare)
+{
+ int ret = 0;
+
+ if (!dp->plat_data->panel)
+ return 0;
+
+ mutex_lock(&dp->panel_lock);
+
+ /*
+ * Exit early if this is a temporary prepare/unprepare and we're already
+ * modeset (since we neither want to prepare twice or unprepare early).
+ */
+ if (dp->panel_is_modeset && !is_modeset_prepare)
+ goto out;
+
+ if (prepare)
+ ret = drm_panel_prepare(dp->plat_data->panel);
+ else
+ ret = drm_panel_unprepare(dp->plat_data->panel);
+
+ if (ret)
+ goto out;
+
+ if (is_modeset_prepare)
+ dp->panel_is_modeset = prepare;
+
+out:
+ mutex_unlock(&dp->panel_lock);
+ return ret;
}
int analogix_dp_get_modes(struct drm_connector *connector)
{
struct analogix_dp_device *dp = to_dp(connector);
struct edid *edid = (struct edid *)dp->edid;
- int num_modes = 0;
+ int ret, num_modes = 0;
+
+ ret = analogix_dp_prepare_panel(dp, true, false);
+ if (ret) {
+ DRM_ERROR("Failed to prepare panel (%d)\n", ret);
+ return 0;
+ }
if (analogix_dp_handle_edid(dp) == 0) {
drm_mode_connector_update_edid_property(&dp->connector, edid);
@@ -940,6 +1073,10 @@ int analogix_dp_get_modes(struct drm_connector *connector)
if (dp->plat_data->get_modes)
num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
+ ret = analogix_dp_prepare_panel(dp, false, false);
+ if (ret)
+ DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
+
return num_modes;
}
@@ -960,11 +1097,23 @@ enum drm_connector_status
analogix_dp_detect(struct drm_connector *connector, bool force)
{
struct analogix_dp_device *dp = to_dp(connector);
+ enum drm_connector_status status = connector_status_disconnected;
+ int ret;
- if (analogix_dp_detect_hpd(dp))
+ ret = analogix_dp_prepare_panel(dp, true, false);
+ if (ret) {
+ DRM_ERROR("Failed to prepare panel (%d)\n", ret);
return connector_status_disconnected;
+ }
+
+ if (!analogix_dp_detect_hpd(dp))
+ status = connector_status_connected;
- return connector_status_connected;
+ ret = analogix_dp_prepare_panel(dp, false, false);
+ if (ret)
+ DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
+
+ return status;
}
static void analogix_dp_connector_destroy(struct drm_connector *connector)
@@ -1035,6 +1184,16 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
return 0;
}
+static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct analogix_dp_device *dp = bridge->driver_private;
+ int ret;
+
+ ret = analogix_dp_prepare_panel(dp, true, true);
+ if (ret)
+ DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+}
+
static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
{
struct analogix_dp_device *dp = bridge->driver_private;
@@ -1058,6 +1217,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
{
struct analogix_dp_device *dp = bridge->driver_private;
+ int ret;
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
@@ -1077,6 +1237,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
pm_runtime_put_sync(dp->dev);
+ ret = analogix_dp_prepare_panel(dp, false, true);
+ if (ret)
+ DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+
dp->dpms_mode = DRM_MODE_DPMS_OFF;
}
@@ -1165,9 +1329,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
}
static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
+ .pre_enable = analogix_dp_bridge_pre_enable,
.enable = analogix_dp_bridge_enable,
.disable = analogix_dp_bridge_disable,
- .pre_enable = analogix_dp_bridge_nop,
.post_disable = analogix_dp_bridge_nop,
.mode_set = analogix_dp_bridge_mode_set,
.attach = analogix_dp_bridge_attach,
@@ -1254,6 +1418,9 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
+ mutex_init(&dp->panel_lock);
+ dp->panel_is_modeset = false;
+
/*
* platform dp driver need containor_of the plat_data to get
* the driver private data, so we need to store the point of
@@ -1333,13 +1500,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
phy_power_on(dp->phy);
- if (dp->plat_data->panel) {
- if (drm_panel_prepare(dp->plat_data->panel)) {
- DRM_ERROR("failed to setup the panel\n");
- return -EBUSY;
- }
- }
-
analogix_dp_init_dp(dp);
ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..473b980 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,10 @@ struct analogix_dp_device {
int hpd_gpio;
bool force_hpd;
unsigned char edid[EDID_BLOCK_LENGTH * 2];
+ bool psr_support;
+
+ struct mutex panel_lock;
+ bool panel_is_modeset;
struct analogix_dp_plat_data *plat_data;
};
@@ -278,4 +282,8 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+ struct edp_vsc_psr *vsc);
+
#endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..52c1b6b 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,54 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
reg |= SCRAMBLING_DISABLE;
writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
}
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+ writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+ struct edp_vsc_psr *vsc)
+{
+ unsigned int val;
+
+ /* don't send info frame */
+ val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+ val &= ~IF_EN;
+ writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+ /* configure single frame update mode */
+ writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
+ dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+ /* configure VSC HB0~HB3 */
+ writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+ writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+ writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+ writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+ /* configure reused VSC PB0~PB3, magic number from vendor */
+ writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+ writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+ writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+ writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+ /* configure DB0 / DB1 values */
+ writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+ writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+ /* set reuse spd inforframe */
+ val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+ val |= REUSE_SPD_EN;
+ writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+ /* mark info frame update */
+ val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+ val = (val | IF_UP) & ~IF_EN;
+ writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+ /* send info frame */
+ val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+ val |= IF_EN;
+ writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..40200c6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
#define ANALOGIX_DP_VIDEO_CTL_8 0x3C
#define ANALOGIX_DP_VIDEO_CTL_10 0x44
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0 0xD8
+
#define ANALOGIX_DP_PLL_REG_1 0xfc
#define ANALOGIX_DP_PLL_REG_2 0x9e4
#define ANALOGIX_DP_PLL_REG_3 0x9e8
@@ -30,6 +32,21 @@
#define ANALOGIX_DP_PD 0x12c
+#define ANALOGIX_DP_IF_TYPE 0x244
+#define ANALOGIX_DP_IF_PKT_DB1 0x254
+#define ANALOGIX_DP_IF_PKT_DB2 0x258
+#define ANALOGIX_DP_SPD_HB0 0x2F8
+#define ANALOGIX_DP_SPD_HB1 0x2FC
+#define ANALOGIX_DP_SPD_HB2 0x300
+#define ANALOGIX_DP_SPD_HB3 0x304
+#define ANALOGIX_DP_SPD_PB0 0x308
+#define ANALOGIX_DP_SPD_PB1 0x30C
+#define ANALOGIX_DP_SPD_PB2 0x310
+#define ANALOGIX_DP_SPD_PB3 0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL 0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0 0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1 0x320
+
#define ANALOGIX_DP_LANE_MAP 0x35C
#define ANALOGIX_DP_ANALOG_CTL_1 0x370
@@ -103,6 +120,8 @@
#define ANALOGIX_DP_SOC_GENERAL_CTL 0x800
+#define ANALOGIX_DP_CRC_CON 0x890
+
/* ANALOGIX_DP_TX_SW_RESET */
#define RESET_DP_TX (0x1 << 0)
@@ -151,6 +170,7 @@
#define VID_CHK_UPDATE_TYPE_SHIFT (4)
#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4)
#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4)
+#define REUSE_SPD_EN (0x1 << 3)
/* ANALOGIX_DP_VIDEO_CTL_8 */
#define VID_HRES_TH(x) (((x) & 0xf) << 4)
@@ -167,6 +187,12 @@
#define REF_CLK_27M (0x0 << 0)
#define REF_CLK_MASK (0x1 << 0)
+/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
+#define PSR_FRAME_UP_TYPE_BURST (0x1 << 0)
+#define PSR_FRAME_UP_TYPE_SINGLE (0x0 << 0)
+#define PSR_CRC_SEL_HARDWARE (0x1 << 1)
+#define PSR_CRC_SEL_MANUALLY (0x0 << 1)
+
/* ANALOGIX_DP_LANE_MAP */
#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6)
#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6)
@@ -376,4 +402,12 @@
#define VIDEO_MODE_SLAVE_MODE (0x1 << 0)
#define VIDEO_MODE_MASTER_MODE (0x0 << 0)
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP (0x1 << 4)
+#define IF_EN (0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH (0x1 << 2)
+#define PSR_VID_CRC_ENABLE (0x1 << 0)
+
#endif /* _ANALOGIX_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
- rockchip_drm_gem.o rockchip_drm_vop.o
+ rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 0e63ee2..e83be15 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
#include <drm/bridge/analogix_dp.h>
#include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
#include "rockchip_drm_vop.h"
#define RK3288_GRF_SOC_CON6 0x25c
@@ -41,6 +42,8 @@
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
+#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100
+
#define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm)
/**
@@ -68,11 +71,62 @@ struct rockchip_dp_device {
struct regmap *grf;
struct reset_control *rst;
+ struct work_struct psr_work;
+ spinlock_t psr_lock;
+ unsigned int psr_state;
+
const struct rockchip_dp_chip_data *data;
struct analogix_dp_plat_data plat_data;
};
+static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+ struct rockchip_dp_device *dp = to_dp(encoder);
+ unsigned long flags;
+
+ dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+ spin_lock_irqsave(&dp->psr_lock, flags);
+ if (enabled)
+ dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+ else
+ dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+ schedule_work(&dp->psr_work);
+ spin_unlock_irqrestore(&dp->psr_lock, flags);
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+ struct rockchip_dp_device *dp =
+ container_of(work, typeof(*dp), psr_work);
+ struct drm_crtc *crtc = dp->encoder.crtc;
+ int psr_state = dp->psr_state;
+ int vact_end;
+ int ret;
+ unsigned long flags;
+
+ if (!crtc)
+ return;
+
+ vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+ ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
+ PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+ if (ret) {
+ dev_err(dp->dev, "line flag interrupt did not arrive\n");
+ return;
+ }
+
+ spin_lock_irqsave(&dp->psr_lock, flags);
+ if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+ analogix_dp_enable_psr(dp->dev);
+ else
+ analogix_dp_disable_psr(dp->dev);
+ spin_unlock_irqrestore(&dp->psr_lock, flags);
+}
+
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
{
reset_control_assert(dp->rst);
@@ -87,6 +141,8 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
struct rockchip_dp_device *dp = to_dp(plat_data);
int ret;
+ cancel_work_sync(&dp->psr_work);
+
ret = clk_prepare_enable(dp->pclk);
if (ret < 0) {
dev_err(dp->dev, "failed to enable pclk %d\n", ret);
@@ -342,12 +398,22 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
dp->plat_data.power_off = rockchip_dp_powerdown;
dp->plat_data.get_modes = rockchip_dp_get_modes;
+ spin_lock_init(&dp->psr_lock);
+ dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+ INIT_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+ rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
}
static void rockchip_dp_unbind(struct device *dev, struct device *master,
void *data)
{
+ struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+ rockchip_drm_psr_unregister(&dp->encoder);
+
return analogix_dp_unbind(dev, master, data);
}
@@ -381,10 +447,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
panel = of_drm_find_panel(panel_node);
of_node_put(panel_node);
- if (!panel) {
- DRM_ERROR("failed to find panel\n");
+ if (!panel)
return -EPROBE_DEFER;
- }
}
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index a822d49..76eaf1d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
drm_dev->dev_private = private;
+ INIT_LIST_HEAD(&private->psr_list);
+ spin_lock_init(&private->psr_list_lock);
+
drm_mode_config_init(drm_dev);
rockchip_drm_mode_config_init(drm_dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..5c69845 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -61,6 +61,9 @@ struct rockchip_drm_private {
struct drm_gem_object *fbdev_bo;
const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
struct drm_atomic_state *state;
+
+ struct list_head psr_list;
+ spinlock_t psr_list_lock;
};
int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
@@ -70,4 +73,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct device *dev);
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+ unsigned int mstimeout);
+
#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 55c5273..ba45d9d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -22,6 +22,7 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
@@ -63,9 +64,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
rockchip_fb->obj[0], handle);
}
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned int flags, unsigned int color,
+ struct drm_clip_rect *clips,
+ unsigned int num_clips)
+{
+ rockchip_drm_psr_flush(fb->dev);
+ return 0;
+}
+
static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
.destroy = rockchip_drm_fb_destroy,
.create_handle = rockchip_drm_fb_create_handle,
+ .dirty = rockchip_drm_fb_dirty,
};
static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..c6ac5d0
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(3000) /* 3 seconds */
+
+enum psr_state {
+ PSR_FLUSH,
+ PSR_ENABLE,
+ PSR_DISABLE,
+};
+
+struct psr_drv {
+ struct list_head list;
+ struct drm_encoder *encoder;
+
+ spinlock_t lock;
+ enum psr_state state;
+
+ struct timer_list flush_timer;
+
+ void (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+ struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
+ struct psr_drv *psr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ list_for_each_entry(psr, &drm_drv->psr_list, list) {
+ if (psr->encoder->crtc == crtc)
+ goto out;
+ }
+ psr = ERR_PTR(-ENODEV);
+
+out:
+ spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ return psr;
+}
+
+static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
+{
+ /*
+ * Allowed finite state machine:
+ *
+ * PSR_ENABLE < = = = = = > PSR_FLUSH
+ * | ^ |
+ * | | |
+ * v | |
+ * PSR_DISABLE < - - - - - - - - -
+ */
+ if (state == psr->state)
+ return;
+
+ /* Requesting a flush when disabled is a noop */
+ if (state == PSR_FLUSH && psr->state == PSR_DISABLE)
+ return;
+
+ psr->state = state;
+
+ /* Already disabled in flush, change the state, but not the hardware */
+ if (state == PSR_DISABLE && psr->state == PSR_FLUSH)
+ return;
+
+ /* Actually commit the state change to hardware */
+ switch (psr->state) {
+ case PSR_ENABLE:
+ psr->set(psr->encoder, true);
+ break;
+
+ case PSR_DISABLE:
+ case PSR_FLUSH:
+ psr->set(psr->encoder, false);
+ break;
+ }
+}
+
+static void psr_set_state(struct psr_drv *psr, enum psr_state state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&psr->lock, flags);
+ psr_set_state_locked(psr, state);
+ spin_unlock_irqrestore(&psr->lock, flags);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+ struct psr_drv *psr = (struct psr_drv *)data;
+ unsigned long flags;
+
+ /* If the state has changed since we initiated the flush, do nothing */
+ spin_lock_irqsave(&psr->lock, flags);
+ if (psr->state == PSR_FLUSH)
+ psr_set_state_locked(psr, PSR_ENABLE);
+ spin_unlock_irqrestore(&psr->lock, flags);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+ struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+ if (IS_ERR(psr))
+ return PTR_ERR(psr);
+
+ psr_set_state(psr, PSR_ENABLE);
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+ struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+ if (IS_ERR(psr))
+ return PTR_ERR(psr);
+
+ psr_set_state(psr, PSR_DISABLE);
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ * @dev: drm device
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
+ * changed during flush time, then keep the state no change after flush
+ * timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(struct drm_device *dev)
+{
+ struct rockchip_drm_private *drm_drv = dev->dev_private;
+ struct psr_drv *psr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ list_for_each_entry(psr, &drm_drv->psr_list, list) {
+ mod_timer(&psr->flush_timer,
+ round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+ psr_set_state(psr, PSR_FLUSH);
+ }
+ spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+ void (*psr_set)(struct drm_encoder *, bool enable))
+{
+ struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+ struct psr_drv *psr;
+ unsigned long flags;
+
+ if (!encoder || !psr_set)
+ return -EINVAL;
+
+ psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+ if (!psr)
+ return -ENOMEM;
+
+ setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+ spin_lock_init(&psr->lock);
+
+ psr->state = PSR_DISABLE;
+ psr->encoder = encoder;
+ psr->set = psr_set;
+
+ spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ list_add_tail(&psr->list, &drm_drv->psr_list);
+ spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+ struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+ struct psr_drv *psr, *n;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
+ if (psr->encoder == encoder) {
+ del_timer(&psr->flush_timer);
+ list_del(&psr->list);
+ kfree(psr);
+ }
+ }
+ spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..c35b688
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(struct drm_device *dev);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+ void (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index efbc41a..d486049 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,17 +34,21 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
#include "rockchip_drm_vop.h"
-#define __REG_SET_RELAXED(x, off, mask, shift, v) \
- vop_mask_write_relaxed(x, off, (mask) << shift, (v) << shift)
-#define __REG_SET_NORMAL(x, off, mask, shift, v) \
- vop_mask_write(x, off, (mask) << shift, (v) << shift)
+#define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
+ vop_mask_write(x, off, mask, shift, v, write_mask, true)
+
+#define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \
+ vop_mask_write(x, off, mask, shift, v, write_mask, false)
#define REG_SET(x, base, reg, v, mode) \
- __REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
+ __REG_SET_##mode(x, base + reg.offset, \
+ reg.mask, reg.shift, v, reg.write_mask)
#define REG_SET_MASK(x, base, reg, mask, v, mode) \
- __REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v)
+ __REG_SET_##mode(x, base + reg.offset, \
+ mask, reg.shift, v, reg.write_mask)
#define VOP_WIN_SET(x, win, name, v) \
REG_SET(x, win->base, win->phy->name, v, RELAXED)
@@ -106,6 +110,7 @@ struct vop {
struct device *dev;
struct drm_device *drm_dev;
bool is_enabled;
+ bool vblank_active;
/* mutex vsync_ work */
struct mutex vsync_mutex;
@@ -116,6 +121,8 @@ struct vop {
/* protected by dev->event_lock */
struct drm_pending_vblank_event *event;
+ struct completion line_flag_completion;
+
const struct vop_data *data;
uint32_t *regsbak;
@@ -162,27 +169,25 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base,
}
static inline void vop_mask_write(struct vop *vop, uint32_t offset,
- uint32_t mask, uint32_t v)
+ uint32_t mask, uint32_t shift, uint32_t v,
+ bool write_mask, bool relaxed)
{
- if (mask) {
- uint32_t cached_val = vop->regsbak[offset >> 2];
-
- cached_val = (cached_val & ~mask) | v;
- writel(cached_val, vop->regs + offset);
- vop->regsbak[offset >> 2] = cached_val;
- }
-}
+ if (!mask)
+ return;
-static inline void vop_mask_write_relaxed(struct vop *vop, uint32_t offset,
- uint32_t mask, uint32_t v)
-{
- if (mask) {
+ if (write_mask) {
+ v = ((v << shift) & 0xffff) | (mask << (shift + 16));
+ } else {
uint32_t cached_val = vop->regsbak[offset >> 2];
- cached_val = (cached_val & ~mask) | v;
- writel_relaxed(cached_val, vop->regs + offset);
- vop->regsbak[offset >> 2] = cached_val;
+ v = (cached_val & ~(mask << shift)) | ((v & mask) << shift);
+ vop->regsbak[offset >> 2] = v;
}
+
+ if (relaxed)
+ writel_relaxed(v, vop->regs + offset);
+ else
+ writel(v, vop->regs + offset);
}
static inline uint32_t vop_get_intr_type(struct vop *vop,
@@ -428,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ * the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ * to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+ |
+ * | |
+ * v v
+ * | Vsync | Vbp | Vactive | Vfp |
+ * ^ ^ ^ ^
+ * | | | |
+ * | | | |
+ * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+ uint32_t line_flag_irq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vop->irq_lock, flags);
+
+ line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+ spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+ return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+ unsigned long flags;
+
+ if (WARN_ON(!vop->is_enabled))
+ return;
+
+ spin_lock_irqsave(&vop->irq_lock, flags);
+
+ VOP_CTRL_SET(vop, line_flag_num[0], line_num);
+ VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+ spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+ unsigned long flags;
+
+ if (WARN_ON(!vop->is_enabled))
+ return;
+
+ spin_lock_irqsave(&vop->irq_lock, flags);
+
+ VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+ spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
static int vop_enable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
@@ -849,6 +919,8 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
+ rockchip_drm_psr_disable(&vop->crtc);
+
return 0;
}
@@ -865,6 +937,8 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+ rockchip_drm_psr_enable(&vop->crtc);
}
static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -908,7 +982,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
u16 vact_end = vact_st + vdisplay;
- uint32_t val;
+ uint32_t pin_pol, val;
int ret;
WARN_ON(vop->event);
@@ -955,21 +1029,26 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
vop_dsp_hold_valid_irq_disable(vop);
}
- val = 0x8;
- val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
- val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
- VOP_CTRL_SET(vop, pin_pol, val);
+ pin_pol = 0x8;
+ pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
+ pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+ VOP_CTRL_SET(vop, pin_pol, pin_pol);
+
switch (s->output_type) {
case DRM_MODE_CONNECTOR_LVDS:
VOP_CTRL_SET(vop, rgb_en, 1);
+ VOP_CTRL_SET(vop, rgb_pin_pol, pin_pol);
break;
case DRM_MODE_CONNECTOR_eDP:
+ VOP_CTRL_SET(vop, edp_pin_pol, pin_pol);
VOP_CTRL_SET(vop, edp_en, 1);
break;
case DRM_MODE_CONNECTOR_HDMIA:
+ VOP_CTRL_SET(vop, hdmi_pin_pol, pin_pol);
VOP_CTRL_SET(vop, hdmi_en, 1);
break;
case DRM_MODE_CONNECTOR_DSI:
+ VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
VOP_CTRL_SET(vop, mipi_en, 1);
break;
default:
@@ -1016,10 +1095,11 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
struct vop *vop = to_vop(crtc);
spin_lock_irq(&crtc->dev->event_lock);
- if (crtc->state->event) {
- WARN_ON(drm_crtc_vblank_get(crtc) != 0);
- WARN_ON(vop->event);
+ vop->vblank_active = true;
+ WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+ WARN_ON(vop->event);
+ if (crtc->state->event) {
vop->event = crtc->state->event;
crtc->state->event = NULL;
}
@@ -1106,12 +1186,14 @@ static void vop_handle_vblank(struct vop *vop)
spin_lock_irqsave(&drm->event_lock, flags);
if (vop->event) {
-
drm_crtc_send_vblank_event(crtc, vop->event);
- drm_crtc_vblank_put(crtc);
vop->event = NULL;
}
+ if (vop->vblank_active) {
+ vop->vblank_active = false;
+ drm_crtc_vblank_put(crtc);
+ }
spin_unlock_irqrestore(&drm->event_lock, flags);
if (!completion_done(&vop->wait_update_complete))
@@ -1149,6 +1231,12 @@ static irqreturn_t vop_isr(int irq, void *data)
ret = IRQ_HANDLED;
}
+ if (active_irqs & LINE_FLAG_INTR) {
+ complete(&vop->line_flag_completion);
+ active_irqs &= ~LINE_FLAG_INTR;
+ ret = IRQ_HANDLED;
+ }
+
if (active_irqs & FS_INTR) {
drm_crtc_handle_vblank(crtc);
vop_handle_vblank(vop);
@@ -1250,6 +1338,7 @@ static int vop_create_crtc(struct vop *vop)
init_completion(&vop->dsp_hold_completion);
init_completion(&vop->wait_update_complete);
+ init_completion(&vop->line_flag_completion);
crtc->port = port;
rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
@@ -1377,6 +1466,7 @@ static int vop_initial(struct vop *vop)
clk_disable(vop->aclk);
vop->is_enabled = false;
+ vop->vblank_active = false;
return 0;
@@ -1406,6 +1496,49 @@ static void vop_win_init(struct vop *vop)
}
}
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+ unsigned int mstimeout)
+{
+ struct vop *vop = to_vop(crtc);
+ unsigned long jiffies_left;
+
+ if (!crtc || !vop->is_enabled)
+ return -ENODEV;
+
+ if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+ return -EINVAL;
+
+ if (vop_line_flag_irq_is_enabled(vop))
+ return -EBUSY;
+
+ reinit_completion(&vop->line_flag_completion);
+ vop_line_flag_irq_enable(vop, line_num);
+
+ jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+ msecs_to_jiffies(mstimeout));
+ vop_line_flag_irq_disable(vop);
+
+ if (jiffies_left == 0) {
+ dev_err(vop->dev, "Timeout waiting for IRQ\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
static int vop_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1474,6 +1607,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
return ret;
pm_runtime_enable(&pdev->dev);
+
return 0;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 071ff0b..1dbc526 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -33,6 +33,7 @@ struct vop_reg {
uint32_t offset;
uint32_t shift;
uint32_t mask;
+ bool write_mask;
};
struct vop_ctrl {
@@ -48,6 +49,10 @@ struct vop_ctrl {
struct vop_reg dither_down;
struct vop_reg dither_up;
struct vop_reg pin_pol;
+ struct vop_reg rgb_pin_pol;
+ struct vop_reg hdmi_pin_pol;
+ struct vop_reg edp_pin_pol;
+ struct vop_reg mipi_pin_pol;
struct vop_reg htotal_pw;
struct vop_reg hact_st_end;
@@ -56,6 +61,8 @@ struct vop_ctrl {
struct vop_reg hpost_st_end;
struct vop_reg vpost_st_end;
+ struct vop_reg line_flag_num[2];
+
struct vop_reg cfg_done;
};
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index b7f59c4..35c51f3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -23,7 +23,14 @@
#define VOP_REG(off, _mask, s) \
{.offset = off, \
.mask = _mask, \
- .shift = s,}
+ .shift = s, \
+ .write_mask = false,}
+
+#define VOP_REG_MASK(off, _mask, s) \
+ {.offset = off, \
+ .mask = _mask, \
+ .shift = s, \
+ .write_mask = true,}
static const uint32_t formats_win_full[] = {
DRM_FORMAT_XRGB8888,
@@ -50,6 +57,89 @@ static const uint32_t formats_win_lite[] = {
DRM_FORMAT_BGR565,
};
+static const struct vop_scl_regs rk3036_win_scl = {
+ .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+ .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+ .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+ .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3036_win0_data = {
+ .scl = &rk3036_win_scl,
+ .data_formats = formats_win_full,
+ .nformats = ARRAY_SIZE(formats_win_full),
+ .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
+ .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
+ .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
+ .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
+ .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
+ .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
+ .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+};
+
+static const struct vop_win_phy rk3036_win1_data = {
+ .data_formats = formats_win_lite,
+ .nformats = ARRAY_SIZE(formats_win_lite),
+ .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
+ .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
+ .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
+ .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
+ .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+};
+
+static const struct vop_win_data rk3036_vop_win_data[] = {
+ { .base = 0x00, .phy = &rk3036_win0_data,
+ .type = DRM_PLANE_TYPE_PRIMARY },
+ { .base = 0x00, .phy = &rk3036_win1_data,
+ .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const int rk3036_vop_intrs[] = {
+ DSP_HOLD_VALID_INTR,
+ FS_INTR,
+ LINE_FLAG_INTR,
+ BUS_ERROR_INTR,
+};
+
+static const struct vop_intr rk3036_intr = {
+ .intrs = rk3036_vop_intrs,
+ .nintrs = ARRAY_SIZE(rk3036_vop_intrs),
+ .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
+ .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
+ .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),
+};
+
+static const struct vop_ctrl rk3036_ctrl_data = {
+ .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30),
+ .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
+ .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
+ .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+ .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
+ .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+ .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+ .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
+ .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
+};
+
+static const struct vop_reg_data rk3036_vop_init_reg_table[] = {
+ {RK3036_DSP_CTRL1, 0x00000000},
+};
+
+static const struct vop_data rk3036_vop = {
+ .init_table = rk3036_vop_init_reg_table,
+ .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table),
+ .ctrl = &rk3036_ctrl_data,
+ .intr = &rk3036_intr,
+ .win = rk3036_vop_win_data,
+ .win_size = ARRAY_SIZE(rk3036_vop_win_data),
+};
+
static const struct vop_scl_extension rk3288_win_full_scl_ext = {
.cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31),
.cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30),
@@ -133,6 +223,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+ .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
};
@@ -190,93 +281,104 @@ static const struct vop_data rk3288_vop = {
.win_size = ARRAY_SIZE(rk3288_vop_win_data),
};
-static const struct vop_scl_regs rk3036_win_scl = {
- .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
- .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
- .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
- .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
-};
-
-static const struct vop_win_phy rk3036_win0_data = {
- .scl = &rk3036_win_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
- .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
- .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
- .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
- .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
- .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
- .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
- .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
- .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
- .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
- .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+static const struct vop_ctrl rk3399_ctrl_data = {
+ .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
+ .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+ .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
+ .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
+ .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
+ .mipi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 15),
+ .dither_down = VOP_REG(RK3399_DSP_CTRL1, 0xf, 1),
+ .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6),
+ .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
+ .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
+ .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+ .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
+ .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
+ .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
+ .htotal_pw = VOP_REG(RK3399_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+ .hact_st_end = VOP_REG(RK3399_DSP_HACT_ST_END, 0x1fff1fff, 0),
+ .vtotal_pw = VOP_REG(RK3399_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+ .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
+ .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+ .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+ .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+ .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
+ .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
};
-static const struct vop_win_phy rk3036_win1_data = {
- .data_formats = formats_win_lite,
- .nformats = ARRAY_SIZE(formats_win_lite),
- .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
- .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
- .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
- .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
- .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
- .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
- .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
- .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
-};
-
-static const struct vop_win_data rk3036_vop_win_data[] = {
- { .base = 0x00, .phy = &rk3036_win0_data,
- .type = DRM_PLANE_TYPE_PRIMARY },
- { .base = 0x00, .phy = &rk3036_win1_data,
- .type = DRM_PLANE_TYPE_CURSOR },
-};
-
-static const int rk3036_vop_intrs[] = {
- DSP_HOLD_VALID_INTR,
+static const int rk3399_vop_intrs[] = {
FS_INTR,
+ 0, 0,
LINE_FLAG_INTR,
+ 0,
BUS_ERROR_INTR,
+ 0, 0, 0, 0, 0, 0, 0,
+ DSP_HOLD_VALID_INTR,
};
-static const struct vop_intr rk3036_intr = {
- .intrs = rk3036_vop_intrs,
- .nintrs = ARRAY_SIZE(rk3036_vop_intrs),
- .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
- .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
- .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),
+static const struct vop_intr rk3399_vop_intr = {
+ .intrs = rk3399_vop_intrs,
+ .nintrs = ARRAY_SIZE(rk3399_vop_intrs),
+ .status = VOP_REG_MASK(RK3399_INTR_STATUS0, 0xffff, 0),
+ .enable = VOP_REG_MASK(RK3399_INTR_EN0, 0xffff, 0),
+ .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0),
};
-static const struct vop_ctrl rk3036_ctrl_data = {
- .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30),
- .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
- .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
- .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
- .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
- .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
- .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
- .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
+static const struct vop_reg_data rk3399_init_reg_table[] = {
+ {RK3399_SYS_CTRL, 0x2000f800},
+ {RK3399_DSP_CTRL0, 0x00000000},
+ {RK3399_WIN0_CTRL0, 0x00000080},
+ {RK3399_WIN1_CTRL0, 0x00000080},
+ /* TODO: Win2/3 support multiple area function, but we haven't found
+ * a suitable way to use it yet, so let's just use them as other windows
+ * with only area 0 enabled.
+ */
+ {RK3399_WIN2_CTRL0, 0x00000010},
+ {RK3399_WIN3_CTRL0, 0x00000010},
};
-static const struct vop_reg_data rk3036_vop_init_reg_table[] = {
- {RK3036_DSP_CTRL1, 0x00000000},
+static const struct vop_data rk3399_vop_big = {
+ .init_table = rk3399_init_reg_table,
+ .table_size = ARRAY_SIZE(rk3399_init_reg_table),
+ .intr = &rk3399_vop_intr,
+ .ctrl = &rk3399_ctrl_data,
+ /*
+ * rk3399 vop big windows register layout is same as rk3288.
+ */
+ .win = rk3288_vop_win_data,
+ .win_size = ARRAY_SIZE(rk3288_vop_win_data),
};
-static const struct vop_data rk3036_vop = {
- .init_table = rk3036_vop_init_reg_table,
- .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table),
- .ctrl = &rk3036_ctrl_data,
- .intr = &rk3036_intr,
- .win = rk3036_vop_win_data,
- .win_size = ARRAY_SIZE(rk3036_vop_win_data),
+static const struct vop_win_data rk3399_vop_lit_win_data[] = {
+ { .base = 0x00, .phy = &rk3288_win01_data,
+ .type = DRM_PLANE_TYPE_PRIMARY },
+ { .base = 0x00, .phy = &rk3288_win23_data,
+ .type = DRM_PLANE_TYPE_CURSOR},
+};
+
+static const struct vop_data rk3399_vop_lit = {
+ .init_table = rk3399_init_reg_table,
+ .table_size = ARRAY_SIZE(rk3399_init_reg_table),
+ .intr = &rk3399_vop_intr,
+ .ctrl = &rk3399_ctrl_data,
+ /*
+ * rk3399 vop lit windows register layout is same as rk3288,
+ * but cut off the win1 and win3 windows.
+ */
+ .win = rk3399_vop_lit_win_data,
+ .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data),
};
static const struct of_device_id vop_driver_dt_match[] = {
- { .compatible = "rockchip,rk3288-vop",
- .data = &rk3288_vop },
{ .compatible = "rockchip,rk3036-vop",
.data = &rk3036_vop },
+ { .compatible = "rockchip,rk3288-vop",
+ .data = &rk3288_vop },
+ { .compatible = "rockchip,rk3399-vop-big",
+ .data = &rk3399_vop_big },
+ { .compatible = "rockchip,rk3399-vop-lit",
+ .data = &rk3399_vop_lit },
{},
};
MODULE_DEVICE_TABLE(of, vop_driver_dt_match);
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index d4b46cb..cd19726 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -166,4 +166,197 @@
#define RK3036_HWC_LUT_ADDR 0x800
/* rk3036 register definition end */
+/* rk3399 register definition */
+#define RK3399_REG_CFG_DONE 0x00000
+#define RK3399_VERSION_INFO 0x00004
+#define RK3399_SYS_CTRL 0x00008
+#define RK3399_SYS_CTRL1 0x0000c
+#define RK3399_DSP_CTRL0 0x00010
+#define RK3399_DSP_CTRL1 0x00014
+#define RK3399_DSP_BG 0x00018
+#define RK3399_MCU_CTRL 0x0001c
+#define RK3399_WB_CTRL0 0x00020
+#define RK3399_WB_CTRL1 0x00024
+#define RK3399_WB_YRGB_MST 0x00028
+#define RK3399_WB_CBR_MST 0x0002c
+#define RK3399_WIN0_CTRL0 0x00030
+#define RK3399_WIN0_CTRL1 0x00034
+#define RK3399_WIN0_COLOR_KEY 0x00038
+#define RK3399_WIN0_VIR 0x0003c
+#define RK3399_WIN0_YRGB_MST 0x00040
+#define RK3399_WIN0_CBR_MST 0x00044
+#define RK3399_WIN0_ACT_INFO 0x00048
+#define RK3399_WIN0_DSP_INFO 0x0004c
+#define RK3399_WIN0_DSP_ST 0x00050
+#define RK3399_WIN0_SCL_FACTOR_YRGB 0x00054
+#define RK3399_WIN0_SCL_FACTOR_CBR 0x00058
+#define RK3399_WIN0_SCL_OFFSET 0x0005c
+#define RK3399_WIN0_SRC_ALPHA_CTRL 0x00060
+#define RK3399_WIN0_DST_ALPHA_CTRL 0x00064
+#define RK3399_WIN0_FADING_CTRL 0x00068
+#define RK3399_WIN0_CTRL2 0x0006c
+#define RK3399_WIN1_CTRL0 0x00070
+#define RK3399_WIN1_CTRL1 0x00074
+#define RK3399_WIN1_COLOR_KEY 0x00078
+#define RK3399_WIN1_VIR 0x0007c
+#define RK3399_WIN1_YRGB_MST 0x00080
+#define RK3399_WIN1_CBR_MST 0x00084
+#define RK3399_WIN1_ACT_INFO 0x00088
+#define RK3399_WIN1_DSP_INFO 0x0008c
+#define RK3399_WIN1_DSP_ST 0x00090
+#define RK3399_WIN1_SCL_FACTOR_YRGB 0x00094
+#define RK3399_WIN1_SCL_FACTOR_CBR 0x00098
+#define RK3399_WIN1_SCL_OFFSET 0x0009c
+#define RK3399_WIN1_SRC_ALPHA_CTRL 0x000a0
+#define RK3399_WIN1_DST_ALPHA_CTRL 0x000a4
+#define RK3399_WIN1_FADING_CTRL 0x000a8
+#define RK3399_WIN1_CTRL2 0x000ac
+#define RK3399_WIN2_CTRL0 0x000b0
+#define RK3399_WIN2_CTRL1 0x000b4
+#define RK3399_WIN2_VIR0_1 0x000b8
+#define RK3399_WIN2_VIR2_3 0x000bc
+#define RK3399_WIN2_MST0 0x000c0
+#define RK3399_WIN2_DSP_INFO0 0x000c4
+#define RK3399_WIN2_DSP_ST0 0x000c8
+#define RK3399_WIN2_COLOR_KEY 0x000cc
+#define RK3399_WIN2_MST1 0x000d0
+#define RK3399_WIN2_DSP_INFO1 0x000d4
+#define RK3399_WIN2_DSP_ST1 0x000d8
+#define RK3399_WIN2_SRC_ALPHA_CTRL 0x000dc
+#define RK3399_WIN2_MST2 0x000e0
+#define RK3399_WIN2_DSP_INFO2 0x000e4
+#define RK3399_WIN2_DSP_ST2 0x000e8
+#define RK3399_WIN2_DST_ALPHA_CTRL 0x000ec
+#define RK3399_WIN2_MST3 0x000f0
+#define RK3399_WIN2_DSP_INFO3 0x000f4
+#define RK3399_WIN2_DSP_ST3 0x000f8
+#define RK3399_WIN2_FADING_CTRL 0x000fc
+#define RK3399_WIN3_CTRL0 0x00100
+#define RK3399_WIN3_CTRL1 0x00104
+#define RK3399_WIN3_VIR0_1 0x00108
+#define RK3399_WIN3_VIR2_3 0x0010c
+#define RK3399_WIN3_MST0 0x00110
+#define RK3399_WIN3_DSP_INFO0 0x00114
+#define RK3399_WIN3_DSP_ST0 0x00118
+#define RK3399_WIN3_COLOR_KEY 0x0011c
+#define RK3399_WIN3_MST1 0x00120
+#define RK3399_WIN3_DSP_INFO1 0x00124
+#define RK3399_WIN3_DSP_ST1 0x00128
+#define RK3399_WIN3_SRC_ALPHA_CTRL 0x0012c
+#define RK3399_WIN3_MST2 0x00130
+#define RK3399_WIN3_DSP_INFO2 0x00134
+#define RK3399_WIN3_DSP_ST2 0x00138
+#define RK3399_WIN3_DST_ALPHA_CTRL 0x0013c
+#define RK3399_WIN3_MST3 0x00140
+#define RK3399_WIN3_DSP_INFO3 0x00144
+#define RK3399_WIN3_DSP_ST3 0x00148
+#define RK3399_WIN3_FADING_CTRL 0x0014c
+#define RK3399_HWC_CTRL0 0x00150
+#define RK3399_HWC_CTRL1 0x00154
+#define RK3399_HWC_MST 0x00158
+#define RK3399_HWC_DSP_ST 0x0015c
+#define RK3399_HWC_SRC_ALPHA_CTRL 0x00160
+#define RK3399_HWC_DST_ALPHA_CTRL 0x00164
+#define RK3399_HWC_FADING_CTRL 0x00168
+#define RK3399_HWC_RESERVED1 0x0016c
+#define RK3399_POST_DSP_HACT_INFO 0x00170
+#define RK3399_POST_DSP_VACT_INFO 0x00174
+#define RK3399_POST_SCL_FACTOR_YRGB 0x00178
+#define RK3399_POST_RESERVED 0x0017c
+#define RK3399_POST_SCL_CTRL 0x00180
+#define RK3399_POST_DSP_VACT_INFO_F1 0x00184
+#define RK3399_DSP_HTOTAL_HS_END 0x00188
+#define RK3399_DSP_HACT_ST_END 0x0018c
+#define RK3399_DSP_VTOTAL_VS_END 0x00190
+#define RK3399_DSP_VACT_ST_END 0x00194
+#define RK3399_DSP_VS_ST_END_F1 0x00198
+#define RK3399_DSP_VACT_ST_END_F1 0x0019c
+#define RK3399_PWM_CTRL 0x001a0
+#define RK3399_PWM_PERIOD_HPR 0x001a4
+#define RK3399_PWM_DUTY_LPR 0x001a8
+#define RK3399_PWM_CNT 0x001ac
+#define RK3399_BCSH_COLOR_BAR 0x001b0
+#define RK3399_BCSH_BCS 0x001b4
+#define RK3399_BCSH_H 0x001b8
+#define RK3399_BCSH_CTRL 0x001bc
+#define RK3399_CABC_CTRL0 0x001c0
+#define RK3399_CABC_CTRL1 0x001c4
+#define RK3399_CABC_CTRL2 0x001c8
+#define RK3399_CABC_CTRL3 0x001cc
+#define RK3399_CABC_GAUSS_LINE0_0 0x001d0
+#define RK3399_CABC_GAUSS_LINE0_1 0x001d4
+#define RK3399_CABC_GAUSS_LINE1_0 0x001d8
+#define RK3399_CABC_GAUSS_LINE1_1 0x001dc
+#define RK3399_CABC_GAUSS_LINE2_0 0x001e0
+#define RK3399_CABC_GAUSS_LINE2_1 0x001e4
+#define RK3399_FRC_LOWER01_0 0x001e8
+#define RK3399_FRC_LOWER01_1 0x001ec
+#define RK3399_FRC_LOWER10_0 0x001f0
+#define RK3399_FRC_LOWER10_1 0x001f4
+#define RK3399_FRC_LOWER11_0 0x001f8
+#define RK3399_FRC_LOWER11_1 0x001fc
+#define RK3399_AFBCD0_CTRL 0x00200
+#define RK3399_AFBCD0_HDR_PTR 0x00204
+#define RK3399_AFBCD0_PIC_SIZE 0x00208
+#define RK3399_AFBCD0_STATUS 0x0020c
+#define RK3399_AFBCD1_CTRL 0x00220
+#define RK3399_AFBCD1_HDR_PTR 0x00224
+#define RK3399_AFBCD1_PIC_SIZE 0x00228
+#define RK3399_AFBCD1_STATUS 0x0022c
+#define RK3399_AFBCD2_CTRL 0x00240
+#define RK3399_AFBCD2_HDR_PTR 0x00244
+#define RK3399_AFBCD2_PIC_SIZE 0x00248
+#define RK3399_AFBCD2_STATUS 0x0024c
+#define RK3399_AFBCD3_CTRL 0x00260
+#define RK3399_AFBCD3_HDR_PTR 0x00264
+#define RK3399_AFBCD3_PIC_SIZE 0x00268
+#define RK3399_AFBCD3_STATUS 0x0026c
+#define RK3399_INTR_EN0 0x00280
+#define RK3399_INTR_CLEAR0 0x00284
+#define RK3399_INTR_STATUS0 0x00288
+#define RK3399_INTR_RAW_STATUS0 0x0028c
+#define RK3399_INTR_EN1 0x00290
+#define RK3399_INTR_CLEAR1 0x00294
+#define RK3399_INTR_STATUS1 0x00298
+#define RK3399_INTR_RAW_STATUS1 0x0029c
+#define RK3399_LINE_FLAG 0x002a0
+#define RK3399_VOP_STATUS 0x002a4
+#define RK3399_BLANKING_VALUE 0x002a8
+#define RK3399_MCU_BYPASS_PORT 0x002ac
+#define RK3399_WIN0_DSP_BG 0x002b0
+#define RK3399_WIN1_DSP_BG 0x002b4
+#define RK3399_WIN2_DSP_BG 0x002b8
+#define RK3399_WIN3_DSP_BG 0x002bc
+#define RK3399_YUV2YUV_WIN 0x002c0
+#define RK3399_YUV2YUV_POST 0x002c4
+#define RK3399_AUTO_GATING_EN 0x002cc
+#define RK3399_WIN0_CSC_COE 0x003a0
+#define RK3399_WIN1_CSC_COE 0x003c0
+#define RK3399_WIN2_CSC_COE 0x003e0
+#define RK3399_WIN3_CSC_COE 0x00400
+#define RK3399_HWC_CSC_COE 0x00420
+#define RK3399_BCSH_R2Y_CSC_COE 0x00440
+#define RK3399_BCSH_Y2R_CSC_COE 0x00460
+#define RK3399_POST_YUV2YUV_Y2R_COE 0x00480
+#define RK3399_POST_YUV2YUV_3X3_COE 0x004a0
+#define RK3399_POST_YUV2YUV_R2Y_COE 0x004c0
+#define RK3399_WIN0_YUV2YUV_Y2R 0x004e0
+#define RK3399_WIN0_YUV2YUV_3X3 0x00500
+#define RK3399_WIN0_YUV2YUV_R2Y 0x00520
+#define RK3399_WIN1_YUV2YUV_Y2R 0x00540
+#define RK3399_WIN1_YUV2YUV_3X3 0x00560
+#define RK3399_WIN1_YUV2YUV_R2Y 0x00580
+#define RK3399_WIN2_YUV2YUV_Y2R 0x005a0
+#define RK3399_WIN2_YUV2YUV_3X3 0x005c0
+#define RK3399_WIN2_YUV2YUV_R2Y 0x005e0
+#define RK3399_WIN3_YUV2YUV_Y2R 0x00600
+#define RK3399_WIN3_YUV2YUV_3X3 0x00620
+#define RK3399_WIN3_YUV2YUV_R2Y 0x00640
+#define RK3399_WIN2_LUT_ADDR 0x01000
+#define RK3399_WIN3_LUT_ADDR 0x01400
+#define RK3399_HWC_LUT_ADDR 0x01800
+#define RK3399_CABC_GAMMA_LUT_ADDR 0x01c00
+#define RK3399_GAMMA_LUT_ADDR 0x02000
+/* rk3399 register definition end */
+
#endif /* _ROCKCHIP_VOP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..9cd8838 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
struct drm_connector *);
};
+int analogix_dp_enable_psr(struct device *dev);
+int analogix_dp_disable_psr(struct device *dev);
+
int analogix_dp_resume(struct device *dev);
int analogix_dp_suspend(struct device *dev);