mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/modes: Allow to specify rotation and reflection on the commandline
Rotations and reflections setup are needed in some scenarios to initialise properly the initial framebuffer. Some drivers already had a bunch of quirks to deal with this, such as either a private kernel command line parameter (omapdss) or on the device tree (various panels). In order to accomodate this, let's create a video mode parameter to deal with the rotation and reflexion. Reviewed-by: Noralf Trønnes <noralf@tronnes.org> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com> Link: https://patchwork.freedesktop.org/patch/msgid/777da16e42db757c1f5b414b5ca34507097fed5c.1560783090.git-series.maxime.ripard@bootlin.com
This commit is contained in:
		
							parent
							
								
									3aeeb13d89
								
							
						
					
					
						commit
						1bf4e09227
					
				
					 4 changed files with 146 additions and 20 deletions
				
			
		| 
						 | 
					@ -51,6 +51,18 @@ To force the VGA output to be enabled and drive a specific mode say:
 | 
				
			||||||
Specifying the option multiple times for different ports is possible, e.g.:
 | 
					Specifying the option multiple times for different ports is possible, e.g.:
 | 
				
			||||||
    video=LVDS-1:d video=HDMI-1:D
 | 
					    video=LVDS-1:d video=HDMI-1:D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Options can also be passed after the mode, using commas as separator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Valid options are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - reflect_x (boolean): Perform an axial symmetry on the X axis
 | 
				
			||||||
 | 
					  - reflect_y (boolean): Perform an axial symmetry on the Y axis
 | 
				
			||||||
 | 
					  - rotate (integer): Rotate the initial framebuffer by x
 | 
				
			||||||
 | 
					    degrees. Valid values are 0, 90, 180 and 270.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo *****
 | 
					***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo *****
 | 
				
			||||||
 | 
					
 | 
				
			||||||
What is the VESA(TM) Coordinated Video Timings (CVT)?
 | 
					What is the VESA(TM) Coordinated Video Timings (CVT)?
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -824,6 +824,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct drm_connector *connector = modeset->connectors[0];
 | 
						struct drm_connector *connector = modeset->connectors[0];
 | 
				
			||||||
	struct drm_plane *plane = modeset->crtc->primary;
 | 
						struct drm_plane *plane = modeset->crtc->primary;
 | 
				
			||||||
 | 
						struct drm_cmdline_mode *cmdline;
 | 
				
			||||||
	u64 valid_mask = 0;
 | 
						u64 valid_mask = 0;
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -844,6 +845,35 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
 | 
				
			||||||
		*rotation = DRM_MODE_ROTATE_0;
 | 
							*rotation = DRM_MODE_ROTATE_0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The panel already defined the default rotation
 | 
				
			||||||
 | 
						 * through its orientation. Whatever has been provided
 | 
				
			||||||
 | 
						 * on the command line needs to be added to that.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Unfortunately, the rotations are at different bit
 | 
				
			||||||
 | 
						 * indices, so the math to add them up are not as
 | 
				
			||||||
 | 
						 * trivial as they could.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Reflections on the other hand are pretty trivial to deal with, a
 | 
				
			||||||
 | 
						 * simple XOR between the two handle the addition nicely.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						cmdline = &connector->cmdline_mode;
 | 
				
			||||||
 | 
						if (cmdline->specified) {
 | 
				
			||||||
 | 
							unsigned int cmdline_rest, panel_rest;
 | 
				
			||||||
 | 
							unsigned int cmdline_rot, panel_rot;
 | 
				
			||||||
 | 
							unsigned int sum_rot, sum_rest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK);
 | 
				
			||||||
 | 
							cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK);
 | 
				
			||||||
 | 
							sum_rot = (panel_rot + cmdline_rot) % 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK;
 | 
				
			||||||
 | 
							cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK;
 | 
				
			||||||
 | 
							sum_rest = panel_rest ^ cmdline_rest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*rotation = (1 << sum_rot) | sum_rest;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * TODO: support 90 / 270 degree hardware rotation,
 | 
						 * TODO: support 90 / 270 degree hardware rotation,
 | 
				
			||||||
	 * depending on the hardware this may require the framebuffer
 | 
						 * depending on the hardware this may require the framebuffer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1554,6 +1554,71 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int drm_mode_parse_cmdline_options(char *str, size_t len,
 | 
				
			||||||
 | 
										  struct drm_connector *connector,
 | 
				
			||||||
 | 
										  struct drm_cmdline_mode *mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int rotation = 0;
 | 
				
			||||||
 | 
						char *sep = str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((sep = strchr(sep, ','))) {
 | 
				
			||||||
 | 
							char *delim, *option;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							option = sep + 1;
 | 
				
			||||||
 | 
							delim = strchr(option, '=');
 | 
				
			||||||
 | 
							if (!delim) {
 | 
				
			||||||
 | 
								delim = strchr(option, ',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!delim)
 | 
				
			||||||
 | 
									delim = str + len;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								switch (deg) {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									rotation |= DRM_MODE_ROTATE_0;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 90:
 | 
				
			||||||
 | 
									rotation |= DRM_MODE_ROTATE_90;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 180:
 | 
				
			||||||
 | 
									rotation |= DRM_MODE_ROTATE_180;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 270:
 | 
				
			||||||
 | 
									rotation |= DRM_MODE_ROTATE_270;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} 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 {
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mode->rotation_reflection = rotation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
 | 
					 * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
 | 
				
			||||||
 * @mode_option: optional per connector mode option
 | 
					 * @mode_option: optional per connector mode option
 | 
				
			||||||
| 
						 | 
					@ -1569,6 +1634,10 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
 | 
					 *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * Additionals options can be provided following the mode, using a comma to
 | 
				
			||||||
 | 
					 * separate each option. Valid options can be found in
 | 
				
			||||||
 | 
					 * Documentation/fb/modedb.txt.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * The intermediate drm_cmdline_mode structure is required to store additional
 | 
					 * The intermediate drm_cmdline_mode structure is required to store additional
 | 
				
			||||||
 * options from the command line modline like the force-enable/disable flag.
 | 
					 * options from the command line modline like the force-enable/disable flag.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -1581,9 +1650,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *name;
 | 
						const char *name;
 | 
				
			||||||
	bool named_mode = false, parse_extras = false;
 | 
						bool named_mode = false, parse_extras = false;
 | 
				
			||||||
	unsigned int bpp_off = 0, refresh_off = 0;
 | 
						unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
 | 
				
			||||||
	unsigned int mode_end = 0;
 | 
						unsigned int mode_end = 0;
 | 
				
			||||||
	char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
 | 
						char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
 | 
				
			||||||
 | 
						char *options_ptr = NULL;
 | 
				
			||||||
	char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
 | 
						char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1632,13 +1702,18 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
 | 
				
			||||||
		mode->refresh_specified = true;
 | 
							mode->refresh_specified = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Locate the start of named options */
 | 
				
			||||||
 | 
						options_ptr = strchr(name, ',');
 | 
				
			||||||
 | 
						if (options_ptr)
 | 
				
			||||||
 | 
							options_off = options_ptr - name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Locate the end of the name / resolution, and parse it */
 | 
						/* Locate the end of the name / resolution, and parse it */
 | 
				
			||||||
	if (bpp_ptr && refresh_ptr) {
 | 
						if (bpp_ptr) {
 | 
				
			||||||
		mode_end = min(bpp_off, refresh_off);
 | 
					 | 
				
			||||||
	} else if (bpp_ptr) {
 | 
					 | 
				
			||||||
		mode_end = bpp_off;
 | 
							mode_end = bpp_off;
 | 
				
			||||||
	} else if (refresh_ptr) {
 | 
						} else if (refresh_ptr) {
 | 
				
			||||||
		mode_end = refresh_off;
 | 
							mode_end = refresh_off;
 | 
				
			||||||
 | 
						} else if (options_ptr) {
 | 
				
			||||||
 | 
							mode_end = options_off;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		mode_end = strlen(name);
 | 
							mode_end = strlen(name);
 | 
				
			||||||
		parse_extras = true;
 | 
							parse_extras = true;
 | 
				
			||||||
| 
						 | 
					@ -1680,24 +1755,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
 | 
				
			||||||
	else if (refresh_ptr)
 | 
						else if (refresh_ptr)
 | 
				
			||||||
		extra_ptr = refresh_end_ptr;
 | 
							extra_ptr = refresh_end_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (extra_ptr) {
 | 
						if (extra_ptr &&
 | 
				
			||||||
		if (!named_mode) {
 | 
						    extra_ptr != options_ptr) {
 | 
				
			||||||
		int len = strlen(name) - (extra_ptr - name);
 | 
							int len = strlen(name) - (extra_ptr - name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
 | 
							ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
 | 
				
			||||||
						   connector, mode);
 | 
											   connector, mode);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			int remaining = strlen(name) - (extra_ptr - name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/*
 | 
					 | 
				
			||||||
			 * We still have characters to process, while
 | 
					 | 
				
			||||||
			 * we shouldn't have any
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			if (remaining > 0)
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (options_ptr) {
 | 
				
			||||||
 | 
							int len = strlen(name) - (options_ptr - name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = drm_mode_parse_cmdline_options(options_ptr, len,
 | 
				
			||||||
 | 
											     connector, mode);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1025,6 +1025,16 @@ struct drm_cmdline_mode {
 | 
				
			||||||
	 * state to one of the DRM_FORCE_* values.
 | 
						 * state to one of the DRM_FORCE_* values.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	enum drm_connector_force force;
 | 
						enum drm_connector_force force;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @rotation_reflection:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Initial rotation and reflection of the mode setup from the
 | 
				
			||||||
 | 
						 * command line. See DRM_MODE_ROTATE_* and
 | 
				
			||||||
 | 
						 * DRM_MODE_REFLECT_*. The only rotations supported are
 | 
				
			||||||
 | 
						 * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						unsigned int rotation_reflection;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue