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

Commit a2feba2b 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/dsi-staging: add support for configuring display topology" into msm-4.9

parents 5fba1de3 6b345acf
Loading
Loading
Loading
Loading
+34 −66
Original line number Diff line number Diff line
@@ -456,28 +456,6 @@ Optional properties:
					with the supply entry index. For a detailed description of
					fields in the supply entry, refer to the qcom,ctrl-supply-entries
					binding above.
- qcom,config-select:			Optional property to select default configuration.

[[Optional config sub-nodes]]		These subnodes provide different configurations for a given same panel.
					Default configuration can be chosen by specifying phandle of the
					selected subnode in the qcom,config-select.
Required properties for sub-nodes:	None
Optional properites:
- qcom,lm-split:			An array of two values indicating MDP should use two layer
					mixers to reduce power.
					Ex: Normally 1080x1920 display uses single DSI and thus one layer
					    mixer. But if we use two layer mixers then mux the output of
					    those two mixers into single stream and route it to single DSI
					    then we can lower the clock requirements of MDP. To use this
					    configuration we need two fill this array with <540 540>.
					Both values doesn't have to be same, but recommended, however sum of
					both values has to be equal to the panel-width.
					By default two mixer streams are merged using 2D mux, however if
					2 DSC encoders are used then merge is performed within compression
					engine.
- qcom,split-mode:			String property indicating which split mode MDP should use. Valid
					entries are "pingpong-split" and "dualctl-split".
					This property is mutually exclusive with qcom,lm-split.
- qcom,mdss-dsc-version:		An 8 bit value indicates the DSC version supported by panel. Bits[0.3]
					provides information about minor version while Bits[4.7] provides
					major version information. It supports only DSC rev 1(Major).1(Minor)
@@ -500,6 +478,21 @@ Optional properites:
- qcom,mdss-dsc-block-prediction-enable: A boolean value to enable/disable the block prediction at decoder.
- qcom,mdss-dsc-config-by-manufacture-cmd: A boolean to indicates panel use manufacture command to setup pps
					instead of standard dcs type 0x0A.
- qcom,display-topology:  		Array of u32 values which specifies the	list of topologies available
					for the display. A display topology is defined by a
					set of 3 values in the order:
					- number of mixers
					- number of compression encoders
					- number of interfaces
					Therefore, the array should always contain a tuple of 3 elements.
- qcom,default-topology-index:          An u32 value which indexes the topology set
					specified by the node "qcom,display-topology"
					to identify the default topology for the
					display. The first set is indexed by the
					value 0.

Required properties for sub-nodes:	None
Optional properties:
- qcom,dba-panel:	Indicates whether the current panel is used as a display bridge
					to a non-DSI interface.
- qcom,bridge-name:			A string to indicate the name of the bridge chip connected to DSI. qcom,bridge-name
@@ -692,10 +685,6 @@ Example:
					29 00 00 00 00 00 02 F1 00];
				qcom,mdss-dsi-timing-switch-command-state = "dsi_lp_mode";

				qcom,config-select = <&dsi_sim_vid_config0>;
				dsi_sim_vid_config0: config0 {
					qcom,lm-split = <360 360>;
					qcom,mdss-dsc-encoders = <2>;
				qcom,mdss-dsc-slice-height = <16>;
				qcom,mdss-dsc-slice-width = <360>;
				qcom,mdss-dsc-slice-per-pkt = <2>;
@@ -703,7 +692,8 @@ Example:
				qcom,mdss-dsc-bit-per-pixel = <8>;
				qcom,mdss-dsc-block-prediction-enable;
				qcom,mdss-dsc-config-by-manufacture-cmd;
				};
				qcom,display-topology = <1 1 1>;
				qcom,default-topology-index = <0>;
			};
		};
		qcom,panel-supply-entries {
@@ -737,15 +727,10 @@ Example:
			};
		};

		qcom,config-select = <&dsi_sim_vid_config0>;
		qcom,dba-panel;
		qcom,bridge-name = "adv7533";
		qcom,mdss-dsc-version = <0x11>;
		qcom,mdss-dsc-scr-version = <0x1>;

		dsi_sim_vid_config0: config0 {
			qcom,lm-split = <360 360>;
			qcom,mdss-dsc-encoders = <2>;
		qcom,mdss-dsc-slice-height = <16>;
		qcom,mdss-dsc-slice-width = <360>;
		qcom,mdss-dsc-slice-per-pkt = <2>;
@@ -753,25 +738,8 @@ Example:
		qcom,mdss-dsc-bit-per-pixel = <8>;
		qcom,mdss-dsc-block-prediction-enable;
		qcom,mdss-dsc-config-by-manufacture-cmd;
		};

		dsi_sim_vid_config1: config1 {
			qcom,mdss-dsc-encoders = <1>;
			qcom,mdss-dsc-slice-height = <16>;
			qcom,mdss-dsc-slice-width = <360>;
			qcom,mdss-dsc-slice-per-pkt = <2>;
			qcom,mdss-dsc-bit-per-component = <8>;
			qcom,mdss-dsc-bit-per-pixel = <8>;
			qcom,mdss-dsc-block-prediction-enable;
			qcom,mdss-dsc-config-by-manufacture-cmd;
		};

		dsi_sim_vid_config2: config2 {
			qcom,split-mode = "dualctl-split";
		};

		dsi_sim_vid_config3: config3 {
			qcom,split-mode = "pingpong-split";
		};
		qcom,display-topology = <1 1 1>,
			                <2 2 1>;
		qcom,default-topology-index = <0>;
	};
};
+1 −0
Original line number Diff line number Diff line
@@ -408,6 +408,7 @@ struct dsi_display_mode {
	u32 pixel_clk_khz;
	enum dsi_op_mode panel_mode;
	u32 dsi_mode_flags;
	struct msm_mode_info *mode_info;
};

#endif /* _DSI_DEFS_H_ */
+24 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode,
	dsi_mode->pixel_clk_khz = drm_mode->clock;
	dsi_mode->panel_mode = 0; /* TODO: Panel Mode */

	dsi_mode->mode_info = (struct msm_mode_info *)drm_mode->private;

	if (msm_is_mode_seamless(drm_mode))
		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_SEAMLESS;
	if (msm_is_mode_dynamic_fps(drm_mode))
@@ -81,6 +83,8 @@ static void convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
	drm_mode->vrefresh = dsi_mode->timing.refresh_rate;
	drm_mode->clock = dsi_mode->pixel_clk_khz;

	drm_mode->private = (int *)dsi_mode->mode_info;

	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS)
		drm_mode->flags |= DRM_MODE_FLAG_SEAMLESS;
	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DFPS)
@@ -255,6 +259,26 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge,
	return ret;
}

int dsi_conn_get_topology(const struct drm_display_mode *drm_mode,
	struct msm_display_topology *topology,
	u32 max_mixer_width)
{
	struct dsi_display_mode dsi_mode;

	if (!drm_mode || !topology)
		return -EINVAL;

	convert_to_dsi_mode(drm_mode, &dsi_mode);

	if (!dsi_mode.mode_info)
		return -EINVAL;

	memcpy(topology, &dsi_mode.mode_info->topology,
			sizeof(struct msm_display_topology));

	return 0;
}

static const struct drm_bridge_funcs dsi_bridge_ops = {
	.attach       = dsi_bridge_attach,
	.mode_fixup   = dsi_bridge_mode_fixup,
+11 −0
Original line number Diff line number Diff line
@@ -63,6 +63,17 @@ enum drm_connector_status dsi_conn_detect(struct drm_connector *conn,
int dsi_connector_get_modes(struct drm_connector *connector,
		void *display);

/**
 * dsi_conn_get_topology - retrieve current topology for the mode selected
 * @drm_mode: Display mode set for the display
 * @topology: Out parameter. Topology for the mode.
 * @max_mixer_width: max width supported by HW layer mixer
 * Returns: Zero on success
 */
int dsi_conn_get_topology(const struct drm_display_mode *drm_mode,
	struct msm_display_topology *topology,
	u32 max_mixer_width);

/**
 * dsi_conn_mode_valid - callback to determine if specified mode is valid
 * @connector: Pointer to drm connector structure
+139 −13
Original line number Diff line number Diff line
@@ -20,6 +20,19 @@
#include "dsi_panel.h"
#include "dsi_ctrl_hw.h"

#define MAX_CMDLINE_PARAM_LEN 256
static char display_config[MAX_CMDLINE_PARAM_LEN];

/**
 * topology is currently defined by a set of following 3 values:
 * 1. num of layer mixers
 * 2. num of compression encoders
 * 3. num of interfaces
 */
#define TOPOLOGY_SET_LEN 3
#define INT_BASE_10 10
#define MAX_TOPOLOGY 5

#define DSI_PANEL_DEFAULT_LABEL  "Default dsi panel"

#define DEFAULT_MDP_TRANSFER_TIME 14000
@@ -1912,25 +1925,18 @@ int dsi_panel_parse_dsc_params(struct dsi_panel *panel,
	u32 data;
	int rc = -EINVAL;
	int intf_width;
	struct device_node *dsc_np = NULL;

	if (!panel->dsc_enabled)
		return 0;

	dsc_np = of_parse_phandle(of_node, "qcom,config-select", 0);
	if (!dsc_np) {
		pr_err("no dsc config found\n");
		goto error;
	}

	rc = of_property_read_u32(dsc_np, "qcom,mdss-dsc-slice-height", &data);
	rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-height", &data);
	if (rc) {
		pr_err("failed to parse qcom,mdss-dsc-slice-height\n");
		goto error;
	}
	panel->dsc.slice_height = data;

	rc = of_property_read_u32(dsc_np, "qcom,mdss-dsc-slice-width", &data);
	rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-width", &data);
	if (rc) {
		pr_err("failed to parse qcom,mdss-dsc-slice-width\n");
		goto error;
@@ -1946,14 +1952,15 @@ int dsi_panel_parse_dsc_params(struct dsi_panel *panel,
	panel->dsc.pic_width = panel->mode.timing.h_active;
	panel->dsc.pic_height = panel->mode.timing.v_active;

	rc = of_property_read_u32(dsc_np, "qcom,mdss-dsc-slice-per-pkt", &data);
	rc = of_property_read_u32(of_node, "qcom,mdss-dsc-slice-per-pkt",
			&data);
	if (rc) {
		pr_err("failed to parse qcom,mdss-dsc-slice-per-pkt\n");
		goto error;
	}
	panel->dsc.slice_per_pkt = data;

	rc = of_property_read_u32(dsc_np, "qcom,mdss-dsc-bit-per-component",
	rc = of_property_read_u32(of_node, "qcom,mdss-dsc-bit-per-component",
		&data);
	if (rc) {
		pr_err("failed to parse qcom,mdss-dsc-bit-per-component\n");
@@ -1961,14 +1968,15 @@ int dsi_panel_parse_dsc_params(struct dsi_panel *panel,
	}
	panel->dsc.bpc = data;

	rc = of_property_read_u32(dsc_np, "qcom,mdss-dsc-bit-per-pixel", &data);
	rc = of_property_read_u32(of_node, "qcom,mdss-dsc-bit-per-pixel",
			&data);
	if (rc) {
		pr_err("failed to parse qcom,mdss-dsc-bit-per-pixel\n");
		goto error;
	}
	panel->dsc.bpp = data;

	panel->dsc.block_pred_enable = of_property_read_bool(dsc_np,
	panel->dsc.block_pred_enable = of_property_read_bool(of_node,
		"qcom,mdss-dsc-block-prediction-enable");

	panel->dsc.full_frame_slices = DIV_ROUND_UP(intf_width,
@@ -2027,6 +2035,112 @@ static int dsi_panel_parse_hdr_config(struct dsi_panel *panel,
	return 0;
}

static int dsi_get_cmdline_top_override(void)
{
	char *str = display_config;
	int top_index = -1;

	/*
	 * This module need to be updated with needed cmd line argument parsing
	 * for other dsi parameters.
	 */
	if (strlcat(str, "\0", sizeof(str)) > sizeof(str))
		return -EINVAL;

	str = strnstr(display_config, "config", strlen(display_config));
	if (!str)
		return -EINVAL;

	if (kstrtol(str + strlen("config"), INT_BASE_10,
				(unsigned long *)&top_index))
		return -EINVAL;

	return top_index;
}

static int dsi_panel_parse_topology(struct dsi_panel *panel,
		struct device_node *of_node)
{
	struct msm_display_topology *topology;
	u32 top_count, top_sel, *array = NULL;
	int i, len = 0;
	int rc = -EINVAL;

	len = of_property_count_u32_elems(of_node, "qcom,display-topology");
	if (len <= 0 || len % TOPOLOGY_SET_LEN ||
			len > (TOPOLOGY_SET_LEN * MAX_TOPOLOGY)) {
		pr_err("invalid topology list for the panel, rc = %d\n", rc);
		return rc;
	}

	top_count = len / TOPOLOGY_SET_LEN;

	array = kcalloc(len, sizeof(u32), GFP_KERNEL);
	if (!array)
		return -ENOMEM;

	rc = of_property_read_u32_array(of_node,
			"qcom,display-topology", array, len);
	if (rc) {
		pr_err("unable to read the display topologies, rc = %d\n", rc);
		goto read_fail;
	}

	topology = kcalloc(top_count, sizeof(*topology), GFP_KERNEL);
	if (!topology) {
		rc = -ENOMEM;
		goto read_fail;
	}

	for (i = 0; i < top_count; i++) {
		struct msm_display_topology *top = &topology[i];

		top->num_lm = array[i * TOPOLOGY_SET_LEN];
		top->num_enc = array[i * TOPOLOGY_SET_LEN + 1];
		top->num_intf = array[i * TOPOLOGY_SET_LEN + 2];
	};

	top_sel = dsi_get_cmdline_top_override();
	if (top_sel >= 0 && top_sel < top_count) {
		pr_info("overidden topology: lm: %d comp_enc:%d intf: %d\n",
			topology[top_sel].num_lm,
			topology[top_sel].num_enc,
			topology[top_sel].num_intf);
		goto parse_done;
	}

	rc = of_property_read_u32(of_node,
			"qcom,default-topology-index", &top_sel);
	if (rc) {
		pr_err("no default topology selected, rc = %d\n", rc);
		goto parse_fail;
	}

	if (top_sel >= top_count) {
		rc = -EINVAL;
		pr_err("default topology is specified is not valid, rc = %d\n",
			rc);
		goto parse_fail;
	}

	pr_info("default topology: lm: %d comp_enc:%d intf: %d\n",
		topology[top_sel].num_lm,
		topology[top_sel].num_enc,
		topology[top_sel].num_intf);

parse_done:
	panel->mode.mode_info = kzalloc(sizeof(struct msm_mode_info),
			GFP_KERNEL);
	memcpy(&panel->mode.mode_info->topology, &topology[top_sel],
		sizeof(struct msm_display_topology));
parse_fail:
	kfree(topology);
read_fail:
	kfree(array);

	return rc;
}

struct dsi_panel *dsi_panel_get(struct device *parent,
				struct device_node *of_node)
{
@@ -2084,6 +2198,13 @@ struct dsi_panel *dsi_panel_get(struct device *parent,
	panel->mode.pixel_clk_khz = (DSI_H_TOTAL(&panel->mode.timing) *
				    DSI_V_TOTAL(&panel->mode.timing) *
				    panel->mode.timing.refresh_rate) / 1000;

	rc = dsi_panel_parse_topology(panel, of_node);
	if (rc) {
		pr_err("failed to parse panel topology, rc=%d\n", rc);
		goto error;
	}

	rc = dsi_panel_parse_host_config(panel, of_node);
	if (rc) {
		pr_err("failed to parse host configuration, rc=%d\n", rc);
@@ -2153,6 +2274,8 @@ void dsi_panel_put(struct dsi_panel *panel)
	for (i = 0; i < DSI_CMD_SET_MAX; i++)
		dsi_panel_destroy_cmd_packets(&panel->cmd_sets[i]);

	kfree(panel->mode.mode_info);

	/* TODO:  more free */
	kfree(panel);
}
@@ -2611,3 +2734,6 @@ int dsi_panel_post_unprepare(struct dsi_panel *panel)
	mutex_unlock(&panel->panel_lock);
	return rc;
}

module_param_string(display_param, display_config, MAX_CMDLINE_PARAM_LEN, 0600);
MODULE_PARM_DESC(display_param, "format: configx - x indexes the selected topology from the display topology list. Index 0 corresponds to the first topology in the list");
Loading