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

Commit 7f54de41 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/dp: protect controller functionalities when powered off"

parents 907d8ea1 420e7e1f
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -71,6 +71,8 @@ struct dp_ctrl_private {
	struct completion video_comp;

	bool orientation;
	bool power_on;

	atomic_t aborted;

	u32 pixel_rate;
@@ -128,6 +130,11 @@ static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
		pr_err("CTRL off, return\n");
		return;
	}

	reinit_completion(&ctrl->idle_comp);
	dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);

@@ -833,7 +840,7 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)

	tries = 0;
	old_v_level = ctrl->link->phy_params.v_level;
	while (1) {
	while (!atomic_read(&ctrl->aborted)) {
		drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);

		ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -960,7 +967,7 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
			ret = -EINVAL;
			break;
		}
	} while (1);
	} while (!atomic_read(&ctrl->aborted));

	return ret;
}
@@ -1180,6 +1187,11 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
		pr_err("CTRL off, return\n");
		return -EINVAL;
	}

	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);

@@ -1341,7 +1353,6 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
	atomic_set(&ctrl->aborted, 0);
	rate = ctrl->panel->link_info.rate;

	ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
	ctrl->catalog->hpd_config(ctrl->catalog, true);

	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
@@ -1396,6 +1407,7 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
		dp_ctrl_send_phy_test_pattern(ctrl);

	ctrl->power_on = true;
	pr_debug("End-\n");

end:
@@ -1419,6 +1431,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)

	dp_ctrl_disable_mainlink_clocks(ctrl);

	ctrl->power_on = false;
	pr_debug("DP off done\n");
}

+3 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -665,6 +665,8 @@ static ssize_t dp_debug_write_hdr(struct file *file,
		pr_err("invalid input\n");
		len = -EINVAL;
	}

	debug->panel->setup_hdr(debug->panel, &c_state->hdr_meta);
end:
	return len;
}
+66 −43
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,8 @@
#include <linux/of_irq.h>
#include <linux/hdcp_qseecom.h>

#include "sde_connector.h"

#include "msm_drv.h"
#include "dp_usbpd.h"
#include "dp_parser.h"
@@ -87,11 +89,12 @@ struct dp_display_private {

	struct workqueue_struct *wq;
	struct delayed_work hdcp_cb_work;
	struct work_struct connect_work;
	struct delayed_work connect_work;
	struct work_struct attention_work;
	struct mutex hdcp_mutex;
	struct mutex session_lock;
	int hdcp_status;
	unsigned long audio_status;
};

static const struct of_device_id dp_dt_match[] = {
@@ -99,6 +102,11 @@ static const struct of_device_id dp_dt_match[] = {
	{}
};

static bool dp_display_framework_ready(struct dp_display_private *dp)
{
	return dp->dp_display.post_open ? false : true;
}

static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
	return dp->hdcp.feature_enabled &&
@@ -448,31 +456,30 @@ static void dp_display_post_open(struct dp_display *dp_display)
	}

	/* if cable is already connected, send notification */
	if (dp_display->is_connected)
		dp_display_send_hpd_event(dp);
	if (dp->usbpd->hpd_high)
		queue_delayed_work(dp->wq, &dp->connect_work, HZ * 10);
	else
		dp_display->post_open = NULL;

}

static int dp_display_send_hpd_notification(struct dp_display_private *dp,
		bool hpd)
{
	u32 timeout_sec;

	dp->dp_display.is_connected = hpd;

	/* in case, framework is not yet up, don't notify hpd */
	if (dp->dp_display.post_open)
		return 0;
	if  (dp_display_framework_ready(dp))
		timeout_sec = 5;
	else
		timeout_sec = 10;

	reinit_completion(&dp->notification_comp);
	dp_display_send_hpd_event(dp);

	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
	if (!wait_for_completion_timeout(&dp->notification_comp,
						HZ * timeout_sec)) {
		pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
		/* cancel any pending request */
		dp->ctrl->abort(dp->ctrl);
		dp->aux->abort(dp->aux);

		return -EINVAL;
	}

@@ -497,26 +504,21 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
	rc = dp->panel->read_sink_caps(dp->panel,
		dp->dp_display.connector, dp->usbpd->multi_func);
	if (rc) {
		if (rc == -ETIMEDOUT) {
			pr_err("Sink cap read failed, skip notification\n");
		/*
		 * ETIMEDOUT --> cable may have been removed
		 * ENOTCONN --> no downstream device connected
		 */
		if (rc == -ETIMEDOUT || rc == -ENOTCONN)
			goto end;
		} else {
		else
			goto notify;
	}
	}

	dp->link->process_request(dp->link);

	if (dp_display_is_sink_count_zero(dp)) {
		pr_debug("no downstream devices connected\n");
		rc = -EINVAL;
		goto end;
	}

	edid = dp->panel->edid_ctrl->edid;

	dp->audio_supported = drm_detect_monitor_audio(edid);

	dp->link->process_request(dp->link);
	dp->panel->handle_sink_request(dp->panel);

	dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
@@ -573,9 +575,9 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
	if (dp->audio_supported)
		dp->audio->off(dp->audio);

	rc = dp_display_send_hpd_notification(dp, false);
	dp->audio_status = -ENODEV;

	dp->aux->deinit(dp->aux);
	rc = dp_display_send_hpd_notification(dp, false);

	dp->panel->video_test = false;

@@ -602,8 +604,9 @@ static int dp_display_usbpd_configure_cb(struct device *dev)

	dp_display_host_init(dp);

	if (dp->usbpd->hpd_high)
		queue_work(dp->wq, &dp->connect_work);
	/* check for hpd high and framework ready */
	if  (dp->usbpd->hpd_high && dp_display_framework_ready(dp))
		queue_delayed_work(dp->wq, &dp->connect_work, 0);
end:
	return rc;
}
@@ -620,6 +623,8 @@ static void dp_display_clean(struct dp_display_private *dp)

	dp->ctrl->push_idle(dp->ctrl);
	dp->ctrl->off(dp->ctrl);
	dp->panel->deinit(dp->panel);
	dp->aux->deinit(dp->aux);
	dp->power_on = false;
}

@@ -667,6 +672,7 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
	dp->aux->abort(dp->aux);

	/* wait for idle state */
	cancel_delayed_work(&dp->connect_work);
	flush_workqueue(dp->wq);

	dp_display_handle_disconnect(dp);
@@ -678,13 +684,13 @@ static void dp_display_handle_maintenance_req(struct dp_display_private *dp)
{
	mutex_lock(&dp->audio->ops_lock);

	if (dp->audio_supported)
	if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
		dp->audio->off(dp->audio);

	dp->ctrl->link_maintenance(dp->ctrl);

	if (dp->audio_supported)
		dp->audio->on(dp->audio);
	if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
		dp->audio_status = dp->audio->on(dp->audio);

	mutex_unlock(&dp->audio->ops_lock);
}
@@ -707,7 +713,7 @@ static void dp_display_attention_work(struct work_struct *work)
			return;
		}

		queue_work(dp->wq, &dp->connect_work);
		queue_delayed_work(dp->wq, &dp->connect_work, 0);
		return;
	}

@@ -753,17 +759,19 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
		return -ENODEV;
	}

	if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high) {
	if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high &&
	    dp->power_on) {
		dp->link->process_request(dp->link);
		queue_work(dp->wq, &dp->attention_work);
	} else if (dp->usbpd->hpd_high) {
		queue_work(dp->wq, &dp->connect_work);
		queue_delayed_work(dp->wq, &dp->connect_work, 0);
	} else {
		/* cancel any pending request */
		dp->ctrl->abort(dp->ctrl);
		dp->aux->abort(dp->aux);

		/* wait for idle state */
		cancel_delayed_work(&dp->connect_work);
		flush_workqueue(dp->wq);

		dp_display_handle_disconnect(dp);
@@ -774,7 +782,8 @@ static int dp_display_usbpd_attention_cb(struct device *dev)

static void dp_display_connect_work(struct work_struct *work)
{
	struct dp_display_private *dp = container_of(work,
	struct delayed_work *dw = to_delayed_work(work);
	struct dp_display_private *dp = container_of(dw,
			struct dp_display_private, connect_work);

	if (dp->dp_display.is_connected) {
@@ -1070,7 +1079,7 @@ static int dp_display_post_enable(struct dp_display *dp_display)
	if (dp->audio_supported) {
		dp->audio->bw_code = dp->link->link_params.bw_code;
		dp->audio->lane_count = dp->link->link_params.lane_count;
		dp->audio->on(dp->audio);
		dp->audio_status = dp->audio->on(dp->audio);
	}

	dp_display_update_hdcp_info(dp);
@@ -1081,6 +1090,8 @@ static int dp_display_post_enable(struct dp_display *dp_display)
		dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
		queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
	}

	dp->panel->setup_hdr(dp->panel, NULL);
end:
	/* clear framework event notifier */
	dp_display->post_open = NULL;
@@ -1125,6 +1136,8 @@ static int dp_display_pre_disable(struct dp_display *dp_display)
static int dp_display_disable(struct dp_display *dp_display)
{
	struct dp_display_private *dp;
	struct drm_connector *connector;
	struct sde_connector_state *c_state;

	if (!dp_display) {
		pr_err("invalid input\n");
@@ -1132,6 +1145,8 @@ static int dp_display_disable(struct dp_display *dp_display)
	}

	dp = container_of(dp_display, struct dp_display_private, dp_display);
	connector = dp->dp_display.connector;
	c_state = to_sde_connector_state(connector->state);

	mutex_lock(&dp->session_lock);

@@ -1142,6 +1157,15 @@ static int dp_display_disable(struct dp_display *dp_display)

	dp->ctrl->off(dp->ctrl);
	dp->panel->deinit(dp->panel);
	dp->aux->deinit(dp->aux);

	connector->hdr_eotf = 0;
	connector->hdr_metadata_type_one = 0;
	connector->hdr_max_luminance = 0;
	connector->hdr_avg_luminance = 0;
	connector->hdr_min_luminance = 0;

	memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));

	dp->power_on = false;

@@ -1252,8 +1276,7 @@ static int dp_display_get_modes(struct dp_display *dp,
	return ret;
}


static int dp_display_pre_kickoff(struct dp_display *dp_display,
static int dp_display_config_hdr(struct dp_display *dp_display,
			struct drm_msm_ext_hdr_metadata *hdr)
{
	int rc = 0;
@@ -1266,7 +1289,6 @@ static int dp_display_pre_kickoff(struct dp_display *dp_display,

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

	if (hdr->hdr_supported && dp->panel->hdr_supported(dp->panel))
	rc = dp->panel->setup_hdr(dp->panel, hdr);

	return rc;
@@ -1281,7 +1303,7 @@ static int dp_display_create_workqueue(struct dp_display_private *dp)
	}

	INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
	INIT_WORK(&dp->connect_work, dp_display_connect_work);
	INIT_DELAYED_WORK(&dp->connect_work, dp_display_connect_work);
	INIT_WORK(&dp->attention_work, dp_display_attention_work);

	return 0;
@@ -1308,6 +1330,7 @@ static int dp_display_probe(struct platform_device *pdev)

	dp->pdev = pdev;
	dp->name = "drm_dp";
	dp->audio_status = -ENODEV;

	rc = dp_display_create_workqueue(dp);
	if (rc) {
@@ -1332,7 +1355,7 @@ static int dp_display_probe(struct platform_device *pdev)
	g_dp_display->get_debug     = dp_get_debug;
	g_dp_display->post_open     = dp_display_post_open;
	g_dp_display->post_init     = dp_display_post_init;
	g_dp_display->pre_kickoff   = dp_display_pre_kickoff;
	g_dp_display->config_hdr    = dp_display_config_hdr;

	rc = component_add(&pdev->dev, &dp_display_comp_ops);
	if (rc) {
+2 −2
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,7 @@ struct dp_display {
	int (*request_irq)(struct dp_display *dp_display);
	struct dp_debug *(*get_debug)(struct dp_display *dp_display);
	void (*post_open)(struct dp_display *dp_display);
	int (*pre_kickoff)(struct dp_display *dp_display,
	int (*config_hdr)(struct dp_display *dp_display,
				struct drm_msm_ext_hdr_metadata *hdr_meta);
	void (*post_init)(struct dp_display *dp_display);
};
+5 −6
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -276,18 +276,17 @@ static const struct drm_bridge_funcs dp_bridge_ops = {
	.mode_set     = dp_bridge_mode_set,
};

int dp_connector_pre_kickoff(struct drm_connector *connector,
		void *display,
		struct msm_display_kickoff_params *params)
int dp_connector_config_hdr(void *display,
	struct sde_connector_state *c_state)
{
	struct dp_display *dp = display;

	if (!connector || !display || !params) {
	if (!display || !c_state) {
		pr_err("invalid params\n");
		return -EINVAL;
	}

	return dp->pre_kickoff(dp, params->hdr_meta);
	return dp->config_hdr(dp, &c_state->hdr_meta);
}

int dp_connector_post_init(struct drm_connector *connector, void *display)
Loading