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

Commit 57aae0d2 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar Committed by Gerrit - the friendly Code Review server
Browse files

msm: mdss: hdmi: add support for hdmi simulation



Some sinks may not support DDC (Display Data Channel),
EDID (Extended Display Identification Data), Hot Plug
or a standard resolution. Add support to control these
functionalities from user modules to support these
hardware specific requirements.

Change-Id: I0340ccab35bbdddd63ef62fb4029d665641780a9
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 1b1ec93f
Loading
Loading
Loading
Loading
+126 −61
Original line number Diff line number Diff line
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2016, 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
@@ -114,6 +114,13 @@ struct hdmi_edid_sink_caps {
	bool ind_view_support;
};

struct hdmi_edid_override_data {
	int scramble;
	int sink_mode;
	int format;
	int vic;
};

struct hdmi_edid_ctrl {
	u8 pt_scan_info;
	u8 it_scan_info;
@@ -134,11 +141,12 @@ struct hdmi_edid_ctrl {
	u8 edid_buf[MAX_EDID_SIZE];
	char vendor_id[EDID_VENDOR_ID_SIZE];
	bool keep_resv_timings;
	u32 edid_override;
	bool edid_override;

	struct hdmi_edid_sink_data sink_data;
	struct hdmi_edid_init_data init_data;
	struct hdmi_edid_sink_caps sink_caps;
	struct hdmi_edid_override_data override_data;
};

static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl,
@@ -315,12 +323,8 @@ static DEVICE_ATTR(spkr_alloc_data_block, S_IRUGO,
static ssize_t hdmi_edid_sysfs_wta_modes(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	int scrambling, vic, format, sink;
	ssize_t ret = strnlen(buf, PAGE_SIZE);
	int rc;
	struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
	struct hdmi_edid_sink_data *sd;
	struct msm_hdmi_mode_timing_info info = {0};

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid ctrl\n", __func__);
@@ -328,52 +332,16 @@ static ssize_t hdmi_edid_sysfs_wta_modes(struct device *dev,
		goto error;
	}

	sd = &edid_ctrl->sink_data;

	if (sscanf(buf, "%d %d %d %d",
		&scrambling, &sink, &format, &vic) != 4) {
		&edid_ctrl->override_data.scramble,
		&edid_ctrl->override_data.sink_mode,
		&edid_ctrl->override_data.format,
		&edid_ctrl->override_data.vic) != 4) {
		DEV_ERR("could not read input\n");
		ret = -EINVAL;
		goto bail;
	}

	if ((sink != SINK_MODE_DVI && sink != SINK_MODE_HDMI) ||
	    !(format & (MSM_HDMI_RGB_888_24BPP_FORMAT |
		MSM_HDMI_YUV_420_12BPP_FORMAT)) ||
	    vic <= HDMI_VFRMT_UNKNOWN || vic >= HDMI_VFRMT_MAX) {
		DEV_ERR("%s: invalid input: sink %d, format %d, vic %d\n",
			__func__, sink, format, vic);
		ret = -EINVAL;
		goto bail;
	}

	rc = hdmi_get_supported_mode(&info,
		&edid_ctrl->init_data.ds_data, vic);
	if (rc) {
		DEV_ERR("%s: error getting res details\n", __func__);
		ret = -EINVAL;
		goto bail;
	}

	if (!hdmi_edid_is_mode_supported(edid_ctrl, &info)) {
		DEV_ERR("%s: %d vic not supported\n", __func__, vic);
		ret = -EINVAL;
		goto bail;
	}

	sd->num_of_elements = 1;
	sd->disp_mode_list[0].video_format = vic;

	if (format & MSM_HDMI_RGB_888_24BPP_FORMAT)
		sd->disp_mode_list[0].rgb_support = true;

	if (format & MSM_HDMI_YUV_420_12BPP_FORMAT)
		sd->disp_mode_list[0].y420_support = true;

	edid_ctrl->sink_mode = sink;
	edid_ctrl->sink_caps.scramble_support = !!scrambling;
	edid_ctrl->sink_caps.scdc_present = !!scrambling;

	edid_ctrl->edid_override = true;
	return ret;
bail:
@@ -389,17 +357,26 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev,
	ssize_t ret = 0;
	int i;
	struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
	u32 num_of_elements = 0;
	struct disp_mode_info *video_mode;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	num_of_elements = edid_ctrl->sink_data.num_of_elements;
	video_mode = edid_ctrl->sink_data.disp_mode_list;

	if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
		num_of_elements = 1;
		edid_ctrl->sink_data.disp_mode_list[0].video_format =
			edid_ctrl->override_data.vic;
	}

	buf[0] = 0;
	if (edid_ctrl->sink_data.num_of_elements) {
		struct disp_mode_info *video_mode =
			edid_ctrl->sink_data.disp_mode_list;
		for (i = 0; i < edid_ctrl->sink_data.num_of_elements; i++) {
	if (num_of_elements) {
		for (i = 0; i < num_of_elements; i++) {
			if (ret > 0)
				ret += scnprintf(buf + ret, PAGE_SIZE - ret,
					",%d", video_mode[i].video_format);
@@ -420,6 +397,65 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev,
static DEVICE_ATTR(edid_modes, S_IRUGO | S_IWUSR, hdmi_edid_sysfs_rda_modes,
	hdmi_edid_sysfs_wta_modes);

static ssize_t hdmi_edid_sysfs_rda_res_info_data(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	u32 i, no_of_elem, offset = 0;
	struct msm_hdmi_mode_timing_info info = {0};
	struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
	struct disp_mode_info *minfo = NULL;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	no_of_elem = edid_ctrl->sink_data.num_of_elements;
	minfo = edid_ctrl->sink_data.disp_mode_list;

	if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
		no_of_elem = 1;
		minfo[0].video_format = edid_ctrl->override_data.vic;
	}

	for (i = 0; i < no_of_elem; i++) {
		ret = hdmi_get_supported_mode(&info,
			&edid_ctrl->init_data.ds_data,
			minfo->video_format);

		if (edid_ctrl->edid_override &&
			(edid_ctrl->override_data.format > 0))
			info.pixel_formats = edid_ctrl->override_data.format;
		else
			info.pixel_formats =
			    (minfo->rgb_support ?
				MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
			    (minfo->y420_support ?
				MSM_HDMI_YUV_420_12BPP_FORMAT : 0);

		minfo++;
		if (ret || !info.supported)
			continue;

		offset += scnprintf(buf + offset, PAGE_SIZE - offset,
			"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
			info.video_format, info.active_h,
			info.front_porch_h, info.pulse_width_h,
			info.back_porch_h, info.active_low_h,
			info.active_v, info.front_porch_v,
			info.pulse_width_v, info.back_porch_v,
			info.active_low_v, info.pixel_freq,
			info.refresh_rate, info.interlaced,
			info.supported, info.ar,
			info.pixel_formats);
	}

	return offset;
}
static DEVICE_ATTR(res_info_data, S_IRUGO, hdmi_edid_sysfs_rda_res_info_data,
	NULL);

static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
@@ -477,14 +513,25 @@ static ssize_t hdmi_edid_sysfs_rda_res_info(struct device *dev,
		}
	}

	if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
		no_of_elem = 1;
		minfo[0].video_format = edid_ctrl->override_data.vic;
	}

	for (; i < no_of_elem && size_to_write < PAGE_SIZE; i++) {
		ret = hdmi_get_supported_mode(&info,
			&edid_ctrl->init_data.ds_data,
			minfo->video_format);

		if (edid_ctrl->edid_override &&
			(edid_ctrl->override_data.format > 0))
			info.pixel_formats = edid_ctrl->override_data.format;
		else
			info.pixel_formats =
		    (minfo->rgb_support ? MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
		    (minfo->y420_support ? MSM_HDMI_YUV_420_12BPP_FORMAT : 0);
			    (minfo->rgb_support ?
				 MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
			    (minfo->y420_support ?
				MSM_HDMI_YUV_420_12BPP_FORMAT : 0);

		minfo++;
		if (ret || !info.supported)
@@ -725,6 +772,7 @@ static struct attribute *hdmi_edid_fs_attrs[] = {
	&dev_attr_edid_audio_latency.attr,
	&dev_attr_edid_video_latency.attr,
	&dev_attr_res_info.attr,
	&dev_attr_res_info_data.attr,
	&dev_attr_add_res.attr,
	NULL,
};
@@ -2102,11 +2150,6 @@ int hdmi_edid_parser(void *input)
		goto err_invalid_data;
	}

	if (edid_ctrl->edid_override) {
		DEV_DBG("edid override enabled\n");
		goto err_invalid_data;
	}

	/* reset edid data for new hdmi connection */
	hdmi_edid_reset_parser(edid_ctrl);

@@ -2244,13 +2287,20 @@ end:
u32 hdmi_edid_get_sink_mode(void *input)
{
	struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
	bool sink_mode;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return 0;
	}

	return edid_ctrl->sink_mode;
	if (edid_ctrl->edid_override &&
		(edid_ctrl->override_data.sink_mode != -1))
		sink_mode = edid_ctrl->override_data.sink_mode;
	else
		sink_mode = edid_ctrl->sink_mode;

	return sink_mode;
} /* hdmi_edid_get_sink_mode */

bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode)
@@ -2280,24 +2330,39 @@ bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode)
bool hdmi_edid_get_scdc_support(void *input)
{
	struct hdmi_edid_ctrl *edid_ctrl = input;
	bool scdc_present;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return false;
	}

	return edid_ctrl->sink_caps.scdc_present;
	if (edid_ctrl->edid_override &&
		(edid_ctrl->override_data.scramble != -1))
		scdc_present = edid_ctrl->override_data.scramble;
	else
		scdc_present = edid_ctrl->sink_caps.scdc_present;

	return scdc_present;
}

bool hdmi_edid_get_sink_scrambler_support(void *input)
{
	struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
	bool scramble_support;

	if (!edid_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return 0;
	}
	return edid_ctrl->sink_caps.scramble_support;

	if (edid_ctrl->edid_override &&
		(edid_ctrl->override_data.scramble != -1))
		scramble_support = edid_ctrl->override_data.scramble;
	else
		scramble_support = edid_ctrl->sink_caps.scramble_support;

	return scramble_support;
}

int hdmi_edid_get_audio_blk(void *input, struct msm_hdmi_audio_edid_blk *blk)
+138 −11
Original line number Diff line number Diff line
@@ -696,6 +696,14 @@ static ssize_t hdmi_tx_sysfs_wta_edid(struct device *dev,
	}

	mutex_lock(&hdmi_ctrl->tx_lock);
	if (edid_size < EDID_BLOCK_SIZE) {
		DEV_DBG("%s: disabling custom edid\n", __func__);

		ret = -EINVAL;
		hdmi_ctrl->custom_edid = false;
		goto end;
	}

	memset(hdmi_ctrl->edid_buf, 0, hdmi_ctrl->edid_buf_size);

	while (edid_size--) {
@@ -806,6 +814,108 @@ end:
	return ret;
}

static ssize_t hdmi_tx_sysfs_wta_hot_plug(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	int hot_plug, rc;
	struct hdmi_tx_ctrl *hdmi_ctrl = NULL;

	hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);

	if (!hdmi_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&hdmi_ctrl->tx_lock);

	rc = kstrtoint(buf, 10, &hot_plug);
	if (rc) {
		DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc);
		goto end;
	}

	hdmi_ctrl->hpd_state = !!hot_plug;

	queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work);

	rc = strnlen(buf, PAGE_SIZE);
end:
	mutex_unlock(&hdmi_ctrl->tx_lock);
	return rc;
}

static ssize_t hdmi_tx_sysfs_rda_sim_mode(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	struct hdmi_tx_ctrl *hdmi_ctrl =
		hdmi_tx_get_drvdata_from_sysfs_dev(dev);

	if (!hdmi_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&hdmi_ctrl->tx_lock);
	ret = snprintf(buf, PAGE_SIZE, "%d\n", hdmi_ctrl->sim_mode);
	DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->sim_mode);
	mutex_unlock(&hdmi_ctrl->tx_lock);

	return ret;
}

static ssize_t hdmi_tx_sysfs_wta_sim_mode(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	int sim_mode, rc;
	struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
	struct dss_io_data *io = NULL;

	hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);

	if (!hdmi_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&hdmi_ctrl->tx_lock);
	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
	if (!io->base) {
		DEV_ERR("%s: core io is not initialized\n", __func__);
		rc = -EINVAL;
		goto end;
	}

	if (!hdmi_ctrl->hpd_initialized) {
		DEV_ERR("%s: hpd not enabled\n", __func__);
		rc = -EINVAL;
		goto end;
	}

	rc = kstrtoint(buf, 10, &sim_mode);
	if (rc) {
		DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc);
		goto end;
	}

	hdmi_ctrl->sim_mode = !!sim_mode;

	if (hdmi_ctrl->sim_mode) {
		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0));
	} else {
		int cable_sense = DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1);

		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0) | BIT(2) |
			(cable_sense ? 0 : BIT(1)));
	}

	rc = strnlen(buf, PAGE_SIZE);
end:
	mutex_unlock(&hdmi_ctrl->tx_lock);
	return rc;
}

static ssize_t hdmi_tx_sysfs_rda_video_mode(struct device *dev,
	struct device_attribute *attr, char *buf)
{
@@ -1267,6 +1377,9 @@ end:

static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL);
static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb);
static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug);
static DEVICE_ATTR(sim_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_sim_mode,
	hdmi_tx_sysfs_wta_sim_mode);
static DEVICE_ATTR(edid, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_edid,
	hdmi_tx_sysfs_wta_edid);
static DEVICE_ATTR(video_mode, S_IRUGO, hdmi_tx_sysfs_rda_video_mode, NULL);
@@ -1286,6 +1399,8 @@ static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v);
static struct attribute *hdmi_tx_fs_attrs[] = {
	&dev_attr_connected.attr,
	&dev_attr_hdmi_audio_cb.attr,
	&dev_attr_hot_plug.attr,
	&dev_attr_sim_mode.attr,
	&dev_attr_edid.attr,
	&dev_attr_video_mode.attr,
	&dev_attr_hpd.attr,
@@ -1869,20 +1984,23 @@ static int hdmi_tx_init_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl)

static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
{
	int status;
	int status = 0;
	void *data;

	if (!hdmi_ctrl) {
		DEV_ERR("%s: invalid input\n", __func__);
		return -EINVAL;
	}

	data = hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID];

	if (!hdmi_tx_is_controller_on(hdmi_ctrl)) {
		DEV_ERR("%s: failed: HDMI controller is off", __func__);
		status = -ENXIO;
		goto error;
	}

	if (!hdmi_ctrl->custom_edid) {
	if (!hdmi_ctrl->custom_edid && !hdmi_ctrl->sim_mode) {
		hdmi_ddc_config(&hdmi_ctrl->ddc_ctrl);

		status = hdmi_tx_read_edid(hdmi_ctrl);
@@ -1890,13 +2008,14 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
			DEV_ERR("%s: error reading edid\n", __func__);
			goto error;
		}
	} else {
		hdmi_ctrl->custom_edid = false;
	}

	status = hdmi_edid_parser(hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]);
	/* parse edid if a valid edid buffer is present */
	if (hdmi_ctrl->custom_edid || !hdmi_ctrl->sim_mode) {
		status = hdmi_edid_parser(data);
		if (status)
			DEV_ERR("%s: edid parse failed\n", __func__);
	}

error:
	return status;
@@ -3773,6 +3892,11 @@ static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
		return;
	}

	if (hdmi_ctrl->sim_mode) {
		DEV_DBG("%s: sim mode enabled\n", __func__);
		return;
	}

	if (polarity)
		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2) | BIT(1));
	else
@@ -4495,11 +4619,14 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
		break;

	case MDSS_EVENT_PANEL_ON:
		if (!hdmi_ctrl->sim_mode) {
			hdmi_tx_update_hdcp_info(hdmi_ctrl);

			rc = hdmi_tx_start_hdcp(hdmi_ctrl);
			if (rc)
			DEV_ERR("%s: hdcp start failed rc=%d\n", __func__, rc);
				DEV_ERR("%s: hdcp start failed rc=%d\n",
					__func__, rc);
		}

		hdmi_ctrl->timing_gen_on = true;

+1 −0
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ struct hdmi_tx_ctrl {
	bool hdcp14_sw_keys;
	bool auth_state;
	bool custom_edid;
	bool sim_mode;
	u32 enc_lvl;

	u8 spd_vendor_name[9];