Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78acdd4a authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'for-next' of git://people.freedesktop.org/~seanpaul/dogwood into drm-next

This pull request contains the following rockchip drm changes:

  - Introduce support for rk3399 vop/crtc
  - Add PSR framework to the rockchip driver
  - Implement PSR in the rockchip analogix edp driver
  - Fix panel on/off in analogix to avoid damaging panels
  - Some miscellaneous fixes to clean up logs and code readability

* 'for-next' of git://people.freedesktop.org/~seanpaul/dogwood:
  drm/rockchip: analogix_dp: drop unnecessary probe deferral "error" print
  drm/rockchip: Enable vblank without event
  drm/rockchip: Improve analogix-dp psr handling
  drm/rockchip: A couple small fixes to psr
  drm/rockchip: Use a spinlock to protect psr state
  drm/rockchip: Don't use a delayed worker for psr state changes
  drm/rockchip: Convert psr_list_mutex to spinlock and use it
  drm/rockchip: analogix_dp: implement PSR function
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: add an common abstracted PSR driver
  drm/rockchip: vop: export line flag function
  drm/bridge: analogix_dp: Ensure the panel is properly prepared/unprepared
  dt-bindings: add compatible strings for big/little rockchip vops
  dt-bindings: sort Rockchip vop compatible by chip's number
  drm/rockchip: vop: add rk3399 vop support
  drm/rockchip: vop: introduce VOP_REG_MASK
  drm/rockchip: sort registers define by chip's number
parents 0d42204f 80826339
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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
+171 −11
Original line number Diff line number Diff line
@@ -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,
+8 −0
Original line number Diff line number Diff line
@@ -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 */
+51 −0
Original line number Diff line number Diff line
@@ -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);
}
+34 −0
Original line number Diff line number Diff line
@@ -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 */
Loading