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

Commit 1794d257 authored by Chris Wilson's avatar Chris Wilson Committed by Dave Airlie
Browse files

drm: Export the command-line mode parser



In the absence of configuration data for providing the fixed mode for
a panel, I would like to be able to pass such modes along a separate
module paramenter. To do so, I then need to parse a modeline from a
string, which drm is already capable of. Export that capability to the
drivers.

Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent bbb0aef5
Loading
Loading
Loading
Loading
+36 −171
Original line number Diff line number Diff line
@@ -70,126 +70,30 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
}
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);

/**
 * drm_fb_helper_connector_parse_command_line - parse command line for connector
 * @connector - connector to parse line for
 * @mode_option - per connector mode option
 *
 * This parses the connector specific then generic command lines for
 * modes and options to configure the connector.
 *
 * This uses the same parameters as the fb modedb.c, except for extra
 *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
 *
 * enable/enable Digital/disable bit at the end
 */
static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
						       const char *mode_option)
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
{
	const char *name;
	unsigned int namelen;
	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
	struct drm_fb_helper_connector *fb_helper_conn;
	int i;
	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
	struct drm_fb_helper_cmdline_mode *cmdline_mode;

	for (i = 0; i < fb_helper->connector_count; i++) {
		struct drm_cmdline_mode *mode;
		struct drm_connector *connector;
		char *option = NULL;

	if (!fb_helper_conn)
		return false;
		fb_helper_conn = fb_helper->connector_info[i];
		connector = fb_helper_conn->connector;
		mode = &fb_helper_conn->cmdline_mode;

	cmdline_mode = &fb_helper_conn->cmdline_mode;
	if (!mode_option)
		mode_option = fb_mode_option;

	if (!mode_option) {
		cmdline_mode->specified = false;
		return false;
	}

	name = mode_option;
	namelen = strlen(name);
	for (i = namelen-1; i >= 0; i--) {
		switch (name[i]) {
		case '@':
			namelen = i;
			if (!refresh_specified && !bpp_specified &&
			    !yres_specified) {
				refresh = simple_strtol(&name[i+1], NULL, 10);
				refresh_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case '-':
			namelen = i;
			if (!bpp_specified && !yres_specified) {
				bpp = simple_strtol(&name[i+1], NULL, 10);
				bpp_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case 'x':
			if (!yres_specified) {
				yres = simple_strtol(&name[i+1], NULL, 10);
				yres_specified = 1;
			} else
				goto done;
		case '0' ... '9':
			break;
		case 'M':
			if (!yres_specified)
				cvt = 1;
			break;
		case 'R':
			if (cvt)
				rb = 1;
			break;
		case 'm':
			if (!cvt)
				margins = 1;
			break;
		case 'i':
			if (!cvt)
				interlace = 1;
			break;
		case 'e':
			force = DRM_FORCE_ON;
			break;
		case 'D':
			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
				force = DRM_FORCE_ON;
			else
				force = DRM_FORCE_ON_DIGITAL;
			break;
		case 'd':
			force = DRM_FORCE_OFF;
			break;
		default:
			goto done;
		}
	}
	if (i < 0 && yres_specified) {
		xres = simple_strtol(name, NULL, 10);
		res_specified = 1;
	}
done:

	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
		drm_get_connector_name(connector), xres, yres,
		(refresh) ? refresh : 60, (rb) ? " reduced blanking" :
		"", (margins) ? " with margins" : "", (interlace) ?
		" interlaced" : "");
		/* do something on return - turn off connector maybe */
		if (fb_get_options(drm_get_connector_name(connector), &option))
			continue;

	if (force) {
		if (drm_mode_parse_command_line_for_connector(option,
							      connector,
							      mode)) {
			if (mode->force) {
				const char *s;
		switch (force) {
				switch (mode->force) {
				case DRM_FORCE_OFF: s = "OFF"; break;
				case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
				default:
@@ -198,46 +102,18 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_conn

				DRM_INFO("forcing %s connector %s\n",
					 drm_get_connector_name(connector), s);
		connector->force = force;
				connector->force = mode->force;
			}

	if (res_specified) {
		cmdline_mode->specified = true;
		cmdline_mode->xres = xres;
		cmdline_mode->yres = yres;
	}

	if (refresh_specified) {
		cmdline_mode->refresh_specified = true;
		cmdline_mode->refresh = refresh;
	}

	if (bpp_specified) {
		cmdline_mode->bpp_specified = true;
		cmdline_mode->bpp = bpp;
	}
	cmdline_mode->rb = rb ? true : false;
	cmdline_mode->cvt = cvt  ? true : false;
	cmdline_mode->interlace = interlace ? true : false;

	return true;
			DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
				      drm_get_connector_name(connector),
				      mode->xres, mode->yres,
				      mode->refresh_specified ? mode->refresh : 60,
				      mode->rb ? " reduced blanking" : "",
				      mode->margins ? " with margins" : "",
				      mode->interlace ?  " interlaced" : "");
		}

static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
{
	struct drm_fb_helper_connector *fb_helper_conn;
	int i;

	for (i = 0; i < fb_helper->connector_count; i++) {
		char *option = NULL;

		fb_helper_conn = fb_helper->connector_info[i];

		/* do something on return - turn off connector maybe */
		if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
			continue;

		drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
	}
	return 0;
}
@@ -901,7 +777,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
	/* first up get a count of crtcs now in use and new min/maxes width/heights */
	for (i = 0; i < fb_helper->connector_count; i++) {
		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
		struct drm_fb_helper_cmdline_mode *cmdline_mode;
		struct drm_cmdline_mode *cmdline_mode;

		cmdline_mode = &fb_helper_conn->cmdline_mode;

@@ -1123,7 +999,7 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_conn

static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
{
	struct drm_fb_helper_cmdline_mode *cmdline_mode;
	struct drm_cmdline_mode *cmdline_mode;
	cmdline_mode = &fb_connector->cmdline_mode;
	return cmdline_mode->specified;
}
@@ -1131,7 +1007,7 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
						      int width, int height)
{
	struct drm_fb_helper_cmdline_mode *cmdline_mode;
	struct drm_cmdline_mode *cmdline_mode;
	struct drm_display_mode *mode = NULL;

	cmdline_mode = &fb_helper_conn->cmdline_mode;
@@ -1163,19 +1039,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
	}

create_mode:
	if (cmdline_mode->cvt)
		mode = drm_cvt_mode(fb_helper_conn->connector->dev,
				    cmdline_mode->xres, cmdline_mode->yres,
				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
				    cmdline_mode->rb, cmdline_mode->interlace,
				    cmdline_mode->margins);
	else
		mode = drm_gtf_mode(fb_helper_conn->connector->dev,
				    cmdline_mode->xres, cmdline_mode->yres,
				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
				    cmdline_mode->interlace,
				    cmdline_mode->margins);
	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
	mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
						 cmdline_mode);
	list_add(&mode->head, &fb_helper_conn->connector->modes);
	return mode;
}
+154 −0
Original line number Diff line number Diff line
@@ -974,3 +974,157 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
	}
}
EXPORT_SYMBOL(drm_mode_connector_list_update);

/**
 * drm_mode_parse_command_line_for_connector - parse command line for connector
 * @mode_option - per connector mode option
 * @connector - connector to parse line for
 *
 * This parses the connector specific then generic command lines for
 * modes and options to configure the connector.
 *
 * This uses the same parameters as the fb modedb.c, except for extra
 *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
 *
 * enable/enable Digital/disable bit at the end
 */
bool drm_mode_parse_command_line_for_connector(const char *mode_option,
					       struct drm_connector *connector,
					       struct drm_cmdline_mode *mode)
{
	const char *name;
	unsigned int namelen;
	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
	int i;
	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;

	if (!mode_option)
		mode_option = fb_mode_option;

	if (!mode_option) {
		mode->specified = false;
		return false;
	}

	name = mode_option;
	namelen = strlen(name);
	for (i = namelen-1; i >= 0; i--) {
		switch (name[i]) {
		case '@':
			namelen = i;
			if (!refresh_specified && !bpp_specified &&
			    !yres_specified) {
				refresh = simple_strtol(&name[i+1], NULL, 10);
				refresh_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case '-':
			namelen = i;
			if (!bpp_specified && !yres_specified) {
				bpp = simple_strtol(&name[i+1], NULL, 10);
				bpp_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case 'x':
			if (!yres_specified) {
				yres = simple_strtol(&name[i+1], NULL, 10);
				yres_specified = 1;
			} else
				goto done;
		case '0' ... '9':
			break;
		case 'M':
			if (!yres_specified)
				cvt = 1;
			break;
		case 'R':
			if (cvt)
				rb = 1;
			break;
		case 'm':
			if (!cvt)
				margins = 1;
			break;
		case 'i':
			if (!cvt)
				interlace = 1;
			break;
		case 'e':
			force = DRM_FORCE_ON;
			break;
		case 'D':
			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
				force = DRM_FORCE_ON;
			else
				force = DRM_FORCE_ON_DIGITAL;
			break;
		case 'd':
			force = DRM_FORCE_OFF;
			break;
		default:
			goto done;
		}
	}
	if (i < 0 && yres_specified) {
		xres = simple_strtol(name, NULL, 10);
		res_specified = 1;
	}
done:
	if (res_specified) {
		mode->specified = true;
		mode->xres = xres;
		mode->yres = yres;
	}

	if (refresh_specified) {
		mode->refresh_specified = true;
		mode->refresh = refresh;
	}

	if (bpp_specified) {
		mode->bpp_specified = true;
		mode->bpp = bpp;
	}
	mode->rb = rb ? true : false;
	mode->cvt = cvt  ? true : false;
	mode->interlace = interlace ? true : false;
	mode->force = force;

	return true;
}
EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);

struct drm_display_mode *
drm_mode_create_from_cmdline_mode(struct drm_device *dev,
				  struct drm_cmdline_mode *cmd)
{
	struct drm_display_mode *mode;

	if (cmd->cvt)
		mode = drm_cvt_mode(dev,
				    cmd->xres, cmd->yres,
				    cmd->refresh_specified ? cmd->refresh : 60,
				    cmd->rb, cmd->interlace,
				    cmd->margins);
	else
		mode = drm_gtf_mode(dev,
				    cmd->xres, cmd->yres,
				    cmd->refresh_specified ? cmd->refresh : 60,
				    cmd->interlace,
				    cmd->margins);
	if (!mode)
		return NULL;

	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
	return mode;
}
EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode);
+25 −0
Original line number Diff line number Diff line
@@ -994,6 +994,22 @@ struct drm_minor {
	struct drm_mode_group mode_group;
};

/* mode specified on the command line */
struct drm_cmdline_mode {
	bool specified;
	bool refresh_specified;
	bool bpp_specified;
	int xres, yres;
	int bpp;
	int refresh;
	bool rb;
	bool interlace;
	bool cvt;
	bool margins;
	enum drm_connector_force force;
};


struct drm_pending_vblank_event {
	struct drm_pending_event base;
	int pipe;
@@ -1389,6 +1405,15 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
						 struct drm_crtc *refcrtc);
extern void drm_calc_timestamping_constants(struct drm_crtc *crtc);

extern bool
drm_mode_parse_command_line_for_connector(const char *mode_option,
					  struct drm_connector *connector,
					  struct drm_cmdline_mode *mode);

extern struct drm_display_mode *
drm_mode_create_from_cmdline_mode(struct drm_device *dev,
				  struct drm_cmdline_mode *cmd);

/* Modesetting support */
extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
+1 −15
Original line number Diff line number Diff line
@@ -40,20 +40,6 @@ struct drm_fb_helper_crtc {
	struct drm_display_mode *desired_mode;
};

/* mode specified on the command line */
struct drm_fb_helper_cmdline_mode {
	bool specified;
	bool refresh_specified;
	bool bpp_specified;
	int xres, yres;
	int bpp;
	int refresh;
	bool rb;
	bool interlace;
	bool cvt;
	bool margins;
};

struct drm_fb_helper_surface_size {
	u32 fb_width;
	u32 fb_height;
@@ -74,8 +60,8 @@ struct drm_fb_helper_funcs {
};

struct drm_fb_helper_connector {
	struct drm_fb_helper_cmdline_mode cmdline_mode;
	struct drm_connector *connector;
	struct drm_cmdline_mode cmdline_mode;
};

struct drm_fb_helper {