diff options
Diffstat (limited to 'drivers/gpu/drm/drm_modes.c')
| -rw-r--r-- | drivers/gpu/drm/drm_modes.c | 255 | 
1 files changed, 128 insertions, 127 deletions
| diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 88232698d7a0..10336b144c72 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -233,7 +233,7 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,  		/* 3) Nominal HSync width (% of line period) - default 8 */  #define CVT_HSYNC_PERCENTAGE	8  		unsigned int hblank_percentage; -		int vsyncandback_porch, vback_porch, hblank; +		int vsyncandback_porch, __maybe_unused vback_porch, hblank;  		/* estimated the horizontal period */  		tmp1 = HV_FACTOR * 1000000  - @@ -386,9 +386,10 @@ drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay,  	int top_margin, bottom_margin;  	int interlace;  	unsigned int hfreq_est; -	int vsync_plus_bp, vback_porch; -	unsigned int vtotal_lines, vfieldrate_est, hperiod; -	unsigned int vfield_rate, vframe_rate; +	int vsync_plus_bp, __maybe_unused vback_porch; +	unsigned int vtotal_lines, __maybe_unused vfieldrate_est; +	unsigned int __maybe_unused hperiod; +	unsigned int vfield_rate, __maybe_unused vframe_rate;  	int left_margin, right_margin;  	unsigned int total_active_pixels, ideal_duty_cycle;  	unsigned int hblank, total_pixels, pixel_freq; @@ -1568,33 +1569,76 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,  	return 0;  } -static int drm_mode_parse_cmdline_options(char *str, size_t len, +static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret) +{ +	const char *value; +	char *endp; + +	/* +	 * delim must point to the '=', otherwise it is a syntax error and +	 * if delim points to the terminating zero, then delim + 1 wil point +	 * past the end of the string. +	 */ +	if (*delim != '=') +		return -EINVAL; + +	value = delim + 1; +	*int_ret = simple_strtol(value, &endp, 10); + +	/* Make sure we have parsed something */ +	if (endp == value) +		return -EINVAL; + +	return 0; +} + +static int drm_mode_parse_panel_orientation(const char *delim, +					    struct drm_cmdline_mode *mode) +{ +	const char *value; + +	if (*delim != '=') +		return -EINVAL; + +	value = delim + 1; +	delim = strchr(value, ','); +	if (!delim) +		delim = value + strlen(value); + +	if (!strncmp(value, "normal", delim - value)) +		mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; +	else if (!strncmp(value, "upside_down", delim - value)) +		mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; +	else if (!strncmp(value, "left_side_up", delim - value)) +		mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; +	else if (!strncmp(value, "right_side_up", delim - value)) +		mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; +	else +		return -EINVAL; + +	return 0; +} + +static int drm_mode_parse_cmdline_options(const char *str, +					  bool freestanding,  					  const struct drm_connector *connector,  					  struct drm_cmdline_mode *mode)  { -	unsigned int rotation = 0; -	char *sep = str; +	unsigned int deg, margin, rotation = 0; +	const char *delim, *option, *sep; -	while ((sep = strchr(sep, ','))) { -		char *delim, *option; - -		option = sep + 1; +	option = str; +	do {  		delim = strchr(option, '=');  		if (!delim) {  			delim = strchr(option, ',');  			if (!delim) -				delim = str + len; +				delim = option + strlen(option);  		}  		if (!strncmp(option, "rotate", delim - option)) { -			const char *value = delim + 1; -			unsigned int deg; - -			deg = simple_strtol(value, &sep, 10); - -			/* Make sure we have parsed something */ -			if (sep == value) +			if (drm_mode_parse_cmdline_int(delim, °))  				return -EINVAL;  			switch (deg) { @@ -1619,58 +1663,40 @@ static int drm_mode_parse_cmdline_options(char *str, size_t len,  			}  		} else if (!strncmp(option, "reflect_x", delim - option)) {  			rotation |= DRM_MODE_REFLECT_X; -			sep = delim;  		} else if (!strncmp(option, "reflect_y", delim - option)) {  			rotation |= DRM_MODE_REFLECT_Y; -			sep = delim;  		} else if (!strncmp(option, "margin_right", delim - option)) { -			const char *value = delim + 1; -			unsigned int margin; - -			margin = simple_strtol(value, &sep, 10); - -			/* Make sure we have parsed something */ -			if (sep == value) +			if (drm_mode_parse_cmdline_int(delim, &margin))  				return -EINVAL;  			mode->tv_margins.right = margin;  		} else if (!strncmp(option, "margin_left", delim - option)) { -			const char *value = delim + 1; -			unsigned int margin; - -			margin = simple_strtol(value, &sep, 10); - -			/* Make sure we have parsed something */ -			if (sep == value) +			if (drm_mode_parse_cmdline_int(delim, &margin))  				return -EINVAL;  			mode->tv_margins.left = margin;  		} else if (!strncmp(option, "margin_top", delim - option)) { -			const char *value = delim + 1; -			unsigned int margin; - -			margin = simple_strtol(value, &sep, 10); - -			/* Make sure we have parsed something */ -			if (sep == value) +			if (drm_mode_parse_cmdline_int(delim, &margin))  				return -EINVAL;  			mode->tv_margins.top = margin;  		} else if (!strncmp(option, "margin_bottom", delim - option)) { -			const char *value = delim + 1; -			unsigned int margin; - -			margin = simple_strtol(value, &sep, 10); - -			/* Make sure we have parsed something */ -			if (sep == value) +			if (drm_mode_parse_cmdline_int(delim, &margin))  				return -EINVAL;  			mode->tv_margins.bottom = margin; +		} else if (!strncmp(option, "panel_orientation", delim - option)) { +			if (drm_mode_parse_panel_orientation(delim, mode)) +				return -EINVAL;  		} else {  			return -EINVAL;  		} -	} +		sep = strchr(delim, ','); +		option = sep + 1; +	} while (sep); + +	if (rotation && freestanding) +		return -EINVAL;  	mode->rotation_reflection = rotation; @@ -1682,17 +1708,6 @@ static const char * const drm_named_modes_whitelist[] = {  	"PAL",  }; -static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size) -{ -	int i; - -	for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) -		if (!strncmp(mode, drm_named_modes_whitelist[i], size)) -			return true; - -	return false; -} -  /**   * drm_mode_parse_command_line_for_connector - parse command line modeline for connector   * @mode_option: optional per connector mode option @@ -1723,72 +1738,30 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,  					       struct drm_cmdline_mode *mode)  {  	const char *name; -	bool named_mode = false, parse_extras = false; +	bool freestanding = false, parse_extras = false;  	unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;  	unsigned int mode_end = 0; -	char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; -	char *options_ptr = NULL; +	const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; +	const char *options_ptr = NULL;  	char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; -	int ret; +	int i, len, ret; -#ifdef CONFIG_FB -	if (!mode_option) -		mode_option = fb_mode_option; -#endif +	memset(mode, 0, sizeof(*mode)); +	mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; -	if (!mode_option) { -		mode->specified = false; +	if (!mode_option)  		return false; -	}  	name = mode_option; -	/* -	 * This is a bit convoluted. To differentiate between the -	 * named modes and poorly formatted resolutions, we need a -	 * bunch of things: -	 *   - We need to make sure that the first character (which -	 *     would be our resolution in X) is a digit. -	 *   - If not, then it's either a named mode or a force on/off. -	 *     To distinguish between the two, we need to run the -	 *     extra parsing function, and if not, then we consider it -	 *     a named mode. -	 * -	 * If this isn't enough, we should add more heuristics here, -	 * and matching unit-tests. -	 */ -	if (!isdigit(name[0]) && name[0] != 'x') { -		unsigned int namelen = strlen(name); - -		/* -		 * Only the force on/off options can be in that case, -		 * and they all take a single character. -		 */ -		if (namelen == 1) { -			ret = drm_mode_parse_cmdline_extra(name, namelen, true, -							   connector, mode); -			if (!ret) -				return true; -		} - -		named_mode = true; -	} -  	/* Try to locate the bpp and refresh specifiers, if any */  	bpp_ptr = strchr(name, '-'); -	if (bpp_ptr) { +	if (bpp_ptr)  		bpp_off = bpp_ptr - name; -		mode->bpp_specified = true; -	}  	refresh_ptr = strchr(name, '@'); -	if (refresh_ptr) { -		if (named_mode) -			return false; - +	if (refresh_ptr)  		refresh_off = refresh_ptr - name; -		mode->refresh_specified = true; -	}  	/* Locate the start of named options */  	options_ptr = strchr(name, ','); @@ -1802,33 +1775,58 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,  		mode_end = refresh_off;  	} else if (options_ptr) {  		mode_end = options_off; +		parse_extras = true;  	} else {  		mode_end = strlen(name);  		parse_extras = true;  	} -	if (named_mode) { -		if (mode_end + 1 > DRM_DISPLAY_MODE_LEN) -			return false; +	/* First check for a named mode */ +	for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) { +		ret = str_has_prefix(name, drm_named_modes_whitelist[i]); +		if (ret == mode_end) { +			if (refresh_ptr) +				return false; /* named + refresh is invalid */ -		if (!drm_named_mode_is_in_whitelist(name, mode_end)) -			return false; +			strcpy(mode->name, drm_named_modes_whitelist[i]); +			mode->specified = true; +			break; +		} +	} -		strscpy(mode->name, name, mode_end + 1); -	} else { +	/* No named mode? Check for a normal mode argument, e.g. 1024x768 */ +	if (!mode->specified && isdigit(name[0])) {  		ret = drm_mode_parse_cmdline_res_mode(name, mode_end,  						      parse_extras,  						      connector,  						      mode);  		if (ret)  			return false; + +		mode->specified = true; +	} + +	/* No mode? Check for freestanding extras and/or options */ +	if (!mode->specified) { +		unsigned int len = strlen(mode_option); + +		if (bpp_ptr || refresh_ptr) +			return false; /* syntax error */ + +		if (len == 1 || (len >= 2 && mode_option[1] == ',')) +			extra_ptr = mode_option; +		else +			options_ptr = mode_option - 1; + +		freestanding = true;  	} -	mode->specified = true;  	if (bpp_ptr) {  		ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);  		if (ret)  			return false; + +		mode->bpp_specified = true;  	}  	if (refresh_ptr) { @@ -1836,6 +1834,8 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,  						     &refresh_end_ptr, mode);  		if (ret)  			return false; + +		mode->refresh_specified = true;  	}  	/* @@ -1849,20 +1849,21 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,  	else if (refresh_ptr)  		extra_ptr = refresh_end_ptr; -	if (extra_ptr && -	    extra_ptr != options_ptr) { -		int len = strlen(name) - (extra_ptr - name); +	if (extra_ptr) { +		if (options_ptr) +			len = options_ptr - extra_ptr; +		else +			len = strlen(extra_ptr); -		ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false, +		ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,  						   connector, mode);  		if (ret)  			return false;  	}  	if (options_ptr) { -		int len = strlen(name) - (options_ptr - name); - -		ret = drm_mode_parse_cmdline_options(options_ptr, len, +		ret = drm_mode_parse_cmdline_options(options_ptr + 1, +						     freestanding,  						     connector, mode);  		if (ret)  			return false; |