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

Commit fdafc41a authored by Padmanabhan Komanduru's avatar Padmanabhan Komanduru Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/dp: add support for DP video pattern link tests



Add support for Display Port video test pattern compliance
tests. The change notifies the userspace to perform a DP teardown
followed by a DP enable sequence with the requested video test
pattern/bpp.

Change-Id: I1a7798ce8638c496dbe13136814175f0dec905d2
Signed-off-by: default avatarPadmanabhan Komanduru <pkomandu@codeaurora.org>
parent 71aec2d3
Loading
Loading
Loading
Loading
+76 −11
Original line number Diff line number Diff line
@@ -490,6 +490,10 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,
		return 0;
	}

	/* reset video pattern flag on disconnect */
	if (!hpd)
		dp->panel->video_test = false;

	dp->dp_display.is_connected = hpd;
	reinit_completion(&dp->notification_comp);
	dp_display_send_hpd_event(&dp->dp_display);
@@ -672,6 +676,17 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
	return rc;
}

static void dp_display_handle_video_request(struct dp_display_private *dp)
{
	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) {
		/* force disconnect followed by connect */
		dp->usbpd->connect(dp->usbpd, false);
		dp->panel->video_test = true;
		dp->usbpd->connect(dp->usbpd, true);
		dp->link->send_test_response(dp->link);
	}
}

static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
{
	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
@@ -687,6 +702,8 @@ static int dp_display_handle_hpd_irq(struct dp_display_private *dp)

	dp->ctrl->handle_sink_request(dp->ctrl);

	dp_display_handle_video_request(dp);

	return 0;
}

@@ -742,6 +759,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
	struct dp_ctrl_in ctrl_in = {
		.dev = dev,
	};
	struct dp_panel_in panel_in = {
		.dev = dev,
	};

	cb->configure  = dp_display_usbpd_configure_cb;
	cb->disconnect = dp_display_usbpd_disconnect_cb;
@@ -782,13 +802,6 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
		goto err;
	}

	dp->panel = dp_panel_get(dev, dp->aux, &dp->catalog->panel);
	if (IS_ERR(dp->panel)) {
		rc = PTR_ERR(dp->panel);
		pr_err("failed to initialize panel, rc = %d\n", rc);
		goto err;
	}

	dp->link = dp_link_get(dev, dp->aux);
	if (IS_ERR(dp->link)) {
		rc = PTR_ERR(dp->link);
@@ -796,6 +809,17 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
		goto err;
	}

	panel_in.aux = dp->aux;
	panel_in.catalog = &dp->catalog->panel;
	panel_in.link = dp->link;

	dp->panel = dp_panel_get(&panel_in);
	if (IS_ERR(dp->panel)) {
		rc = PTR_ERR(dp->panel);
		pr_err("failed to initialize panel, rc = %d\n", rc);
		goto err;
	}

	ctrl_in.link = dp->link;
	ctrl_in.panel = dp->panel;
	ctrl_in.aux = dp->aux;
@@ -1017,17 +1041,56 @@ static int dp_display_validate_mode(struct dp_display *dp,
	return 0;
}

static int dp_display_get_modes(struct dp_display *dp)
static int dp_display_get_modes(struct dp_display *dp,
	struct dp_display_mode *dp_mode)
{
	struct dp_display_private *dp_display;
	int ret = 0;

	if (!dp) {
		pr_err("invalid params\n");
		return 0;
	}

	dp_display = container_of(dp, struct dp_display_private, dp_display);

	ret = dp_display->panel->get_modes(dp_display->panel,
		dp->connector, dp_mode);
	if (dp_mode->timing.pixel_clk_khz)
		dp->max_pclk_khz = dp_mode->timing.pixel_clk_khz;
	return ret;
}

static bool dp_display_check_video_test(struct dp_display *dp)
{
	struct dp_display_private *dp_display;

	if (!dp) {
		pr_err("invalid params\n");
		return false;
	}

	dp_display = container_of(dp, struct dp_display_private, dp_display);

	ret = _sde_edid_update_modes(dp->connector,
		dp_display->panel->edid_ctrl);
	if (dp_display->panel->video_test)
		return true;

	return ret;
	return false;
}

static int dp_display_get_test_bpp(struct dp_display *dp)
{
	struct dp_display_private *dp_display;

	if (!dp) {
		pr_err("invalid params\n");
		return 0;
	}

	dp_display = container_of(dp, struct dp_display_private, dp_display);

	return dp_link_bit_depth_to_bpp(
		dp_display->link->test_video.test_bit_depth);
}

static int dp_display_probe(struct platform_device *pdev)
@@ -1071,6 +1134,8 @@ static int dp_display_probe(struct platform_device *pdev)
	g_dp_display->request_irq   = dp_request_irq;
	g_dp_display->get_debug     = dp_get_debug;
	g_dp_display->send_hpd_event    = dp_display_send_hpd_event;
	g_dp_display->is_video_test = dp_display_check_video_test;
	g_dp_display->get_test_bpp = dp_display_get_test_bpp;

	rc = component_add(&pdev->dev, &dp_display_comp_ops);
	if (rc)
+4 −6
Original line number Diff line number Diff line
@@ -19,11 +19,6 @@

#include "dp_panel.h"

struct dp_display_mode {
	struct dp_panel_info timing;
	u32 capabilities;
};

struct dp_display {
	struct drm_device *drm_dev;
	struct dp_bridge *bridge;
@@ -41,12 +36,15 @@ struct dp_display {
			struct dp_display_mode *mode);
	int (*validate_mode)(struct dp_display *dp_display,
			struct dp_display_mode *mode);
	int (*get_modes)(struct dp_display *dp_display);
	int (*get_modes)(struct dp_display *dp_display,
		struct dp_display_mode *dp_mode);
	int (*prepare)(struct dp_display *dp_display);
	int (*unprepare)(struct dp_display *dp_display);
	int (*request_irq)(struct dp_display *dp_display);
	struct dp_debug *(*get_debug)(struct dp_display *dp_display);
	void (*send_hpd_event)(struct dp_display *dp_display);
	bool (*is_video_test)(struct dp_display *dp_display);
	int (*get_test_bpp)(struct dp_display *dp_display);
};

int dp_display_get_num_of_displays(void);
+43 −4
Original line number Diff line number Diff line
@@ -48,7 +48,15 @@ static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,

	dp_mode->timing.v_front_porch = drm_mode->vsync_start -
					 drm_mode->vdisplay;
	dp_mode->timing.bpp = dp->connector->display_info.bpc * num_components;

	if (dp->is_video_test(dp))
		dp_mode->timing.bpp = dp->get_test_bpp(dp);
	else
		dp_mode->timing.bpp = dp->connector->display_info.bpc *
		num_components;

	if (!dp_mode->timing.bpp)
		dp_mode->timing.bpp = 24;

	dp_mode->timing.refresh_rate = drm_mode->vrefresh;

@@ -395,21 +403,45 @@ int dp_connector_get_modes(struct drm_connector *connector,
{
	int rc = 0;
	struct dp_display *dp;
	struct dp_display_mode *dp_mode = NULL;
	struct drm_display_mode *m, drm_mode;

	if (!connector || !display)
		return -EINVAL;
		return 0;

	dp = display;

	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
	if (!dp_mode)
		return 0;

	/* pluggable case assumes EDID is read when HPD */
	if (dp->is_connected) {
		rc = dp->get_modes(dp);
		rc = dp->get_modes(dp, dp_mode);
		if (!rc)
			pr_err("failed to get DP sink modes, rc=%d\n", rc);

		if (dp_mode->timing.pixel_clk_khz) { /* valid DP mode */
			memset(&drm_mode, 0x0, sizeof(drm_mode));
			convert_to_drm_mode(dp_mode, &drm_mode);
			m = drm_mode_duplicate(connector->dev, &drm_mode);
			if (!m) {
				pr_err("failed to add mode %ux%u\n",
				       drm_mode.hdisplay,
				       drm_mode.vdisplay);
				kfree(dp_mode);
				return 0;
			}
			m->width_mm = connector->display_info.width_mm;
			m->height_mm = connector->display_info.height_mm;
			drm_mode_probed_add(connector, m);
		}
	} else {
		pr_err("No sink connected\n");
	}
	kfree(dp_mode);

	return 0;
	return rc;
}

int dp_drm_bridge_init(void *data, struct drm_encoder *encoder)
@@ -491,6 +523,13 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
		else
			return MODE_ERROR;
	} else {
		if (mode->vrefresh == 0) {
			int vrefresh = (mode->clock * 1000) /
				(mode->vtotal * mode->htotal);
			if (vrefresh > 60)
				return MODE_BAD;
		}

		if (mode->clock > dp_disp->max_pclk_khz)
			return MODE_BAD;
		else
+75 −7
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ struct dp_panel_private {
	struct device *dev;
	struct dp_panel dp_panel;
	struct dp_aux *aux;
	struct dp_link *link;
	struct dp_catalog_panel *catalog;
	bool aux_cfg_update_done;
};
@@ -193,6 +194,72 @@ static u32 dp_panel_get_max_pclk(struct dp_panel *dp_panel)
	return max_pclk_rate_khz;
}

static void dp_panel_set_test_mode(struct dp_panel_private *panel,
		struct dp_display_mode *mode)
{
	struct dp_panel_info *pinfo = NULL;
	struct dp_link_test_video *test_info = NULL;

	if (!panel) {
		pr_err("invalid params\n");
		return;
	}

	pinfo = &mode->timing;
	test_info = &panel->link->test_video;

	pinfo->h_active = test_info->test_h_width;
	pinfo->h_sync_width = test_info->test_hsync_width;
	pinfo->h_back_porch = test_info->test_h_start -
		test_info->test_hsync_width;
	pinfo->h_front_porch = test_info->test_h_total -
		(test_info->test_h_start + test_info->test_h_width);

	pinfo->v_active = test_info->test_v_height;
	pinfo->v_sync_width = test_info->test_vsync_width;
	pinfo->v_back_porch = test_info->test_v_start -
		test_info->test_vsync_width;
	pinfo->v_front_porch = test_info->test_v_total -
		(test_info->test_v_start + test_info->test_v_height);

	pinfo->bpp = dp_link_bit_depth_to_bpp(test_info->test_bit_depth);
	pinfo->h_active_low = test_info->test_hsync_pol;
	pinfo->v_active_low = test_info->test_vsync_pol;

	pinfo->refresh_rate = test_info->test_rr_n;
	pinfo->pixel_clk_khz = test_info->test_h_total *
		test_info->test_v_total * pinfo->refresh_rate;

	if (test_info->test_rr_d == 0)
		pinfo->pixel_clk_khz /= 1000;
	else
		pinfo->pixel_clk_khz /= 1001;

	if (test_info->test_h_width == 640)
		pinfo->pixel_clk_khz = 25170;
}

static int dp_panel_get_modes(struct dp_panel *dp_panel,
	struct drm_connector *connector, struct dp_display_mode *mode)
{
	struct dp_panel_private *panel;

	if (!dp_panel) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);

	if (dp_panel->video_test) {
		dp_panel_set_test_mode(panel, mode);
		return 1;
	} else if (dp_panel->edid_ctrl->edid) {
		return _sde_edid_update_modes(connector, dp_panel->edid_ctrl);
	}
	return 0;
}

static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
{
	int rc = 0;
@@ -351,28 +418,28 @@ static u32 dp_panel_get_min_req_link_rate(struct dp_panel *dp_panel)
	return min_link_rate_khz;
}

struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
				struct dp_catalog_panel *catalog)
struct dp_panel *dp_panel_get(struct dp_panel_in *in)
{
	int rc = 0;
	struct dp_panel_private *panel;
	struct dp_panel *dp_panel;

	if (!dev || !aux || !catalog) {
	if (!in->dev || !in->catalog || !in->aux || !in->link) {
		pr_err("invalid input\n");
		rc = -EINVAL;
		goto error;
	}

	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
	panel = devm_kzalloc(in->dev, sizeof(*panel), GFP_KERNEL);
	if (!panel) {
		rc = -ENOMEM;
		goto error;
	}

	panel->dev = dev;
	panel->aux = aux;
	panel->catalog = catalog;
	panel->dev = in->dev;
	panel->aux = in->aux;
	panel->catalog = in->catalog;
	panel->link = in->link;

	dp_panel = &panel->dp_panel;
	panel->aux_cfg_update_done = false;
@@ -384,6 +451,7 @@ struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
	dp_panel->read_sink_caps = dp_panel_read_sink_caps;
	dp_panel->get_min_req_link_rate = dp_panel_get_min_req_link_rate;
	dp_panel->get_max_pclk = dp_panel_get_max_pclk;
	dp_panel->get_modes = dp_panel_get_modes;

	return dp_panel;
error:
+18 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#define _DP_PANEL_H_

#include "dp_aux.h"
#include "dp_link.h"
#include "dp_usbpd.h"
#include "sde_edid_parser.h"

#define DP_MAX_DOWNSTREAM_PORTS 0x10
@@ -37,6 +39,18 @@ struct dp_panel_info {
	u32 bpp;
};

struct dp_display_mode {
	struct dp_panel_info timing;
	u32 capabilities;
};

struct dp_panel_in {
	struct device *dev;
	struct dp_aux *aux;
	struct dp_link *link;
	struct dp_catalog_panel *catalog;
};

struct dp_panel {
	/* dpcd raw data */
	u8 dpcd[DP_RECEIVER_CAP_SIZE];
@@ -46,6 +60,7 @@ struct dp_panel {
	struct sde_edid_ctrl *edid_ctrl;
	struct drm_connector *connector;
	struct dp_panel_info pinfo;
	bool video_test;

	u32 vic;
	u32 max_pclk_khz;
@@ -58,9 +73,10 @@ struct dp_panel {
		struct drm_connector *connector);
	u32 (*get_min_req_link_rate)(struct dp_panel *dp_panel);
	u32 (*get_max_pclk)(struct dp_panel *dp_panel);
	int (*get_modes)(struct dp_panel *dp_panel,
		struct drm_connector *connector, struct dp_display_mode *mode);
};

struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
				struct dp_catalog_panel *catalog);
struct dp_panel *dp_panel_get(struct dp_panel_in *in);
void dp_panel_put(struct dp_panel *dp_panel);
#endif /* _DP_PANEL_H_ */