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

Commit d50ba256 authored by Dave Airlie's avatar Dave Airlie
Browse files

drm/kms: start adding command line interface using fb.



[note this requires an fb patch posted to linux-fbdev-devel already]

This uses the normal video= command line option to control the kms
output setup at boot time. It is used to override the autodetection
done by kms.

video= normally takes a framebuffer as the first parameter, in kms
it will take a connector name, DVI-I-1, or LVDS-1 etc. If no output
connector is specified the mode string will apply to all connectors.

The mode specification used will match down the probed modes, and if
no mode is found it will add a CVT mode that matches.

video=1024x768 - all connectors match a 1024x768 mode or add a CVT on
video=VGA-1:1024x768, VGA-1 connector gets mode only.

The same strings as used in current fb modedb.c are used, except I've
added three more letters, e, D, d, e = enable, D = enable Digital,
d = disable, which allow a connector to be forced into a certain state.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent f8033035
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -482,6 +482,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
	list_for_each_entry_safe(mode, t, &connector->user_modes, head)
		drm_mode_remove(connector, mode);

	kfree(connector->fb_helper_private);
	mutex_lock(&dev->mode_config.mutex);
	drm_mode_object_put(dev, &connector->base);
	list_del(&connector->head);
+74 −5
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"

static void drm_mode_validate_flag(struct drm_connector *connector,
				   int flags)
@@ -90,6 +91,14 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
	list_for_each_entry_safe(mode, t, &connector->modes, head)
		mode->status = MODE_UNVERIFIED;

	if (connector->force) {
		if (connector->force == DRM_FORCE_ON)
			connector->status = connector_status_connected;
		else
			connector->status = connector_status_disconnected;
		if (connector->funcs->force)
			connector->funcs->force(connector);
	} else
		connector->status = connector->funcs->detect(connector);

	if (connector->status == connector_status_disconnected) {
@@ -267,6 +276,56 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *con
	return NULL;
}

static bool drm_has_cmdline_mode(struct drm_connector *connector)
{
	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
	struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode;
	return cmdline_mode->specified;
}

static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
{
	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
	struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode;
	struct drm_display_mode *mode = NULL;

	if (cmdline_mode->specified == false)
		return mode;

	/* attempt to find a matching mode in the list of modes
	 *  we have gotten so far, if not add a CVT mode that conforms
	 */
	if (cmdline_mode->rb || cmdline_mode->margins)
		goto create_mode;

	list_for_each_entry(mode, &connector->modes, head) {
		/* check width/height */
		if (mode->hdisplay != cmdline_mode->xres ||
		    mode->vdisplay != cmdline_mode->yres)
			continue;

		if (cmdline_mode->refresh_specified) {
			if (mode->vrefresh != cmdline_mode->refresh)
				continue;
		}

		if (cmdline_mode->interlace) {
			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
				continue;
		}
		return mode;
	}

create_mode:
	mode = drm_cvt_mode(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);
	list_add(&mode->head, &connector->modes);
	return mode;
}

static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
{
	bool enable;
@@ -317,10 +376,16 @@ static bool drm_target_preferred(struct drm_device *dev,
			continue;
		}

		DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
			      connector->base.id);

		/* got for command line mode first */
		modes[i] = drm_pick_cmdline_mode(connector, width, height);
		if (!modes[i]) {
			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
				      connector->base.id);
			modes[i] = drm_has_preferred_mode(connector, width, height);
		}
		/* No preferred modes, pick one off the list */
		if (!modes[i] && !list_empty(&connector->modes)) {
			list_for_each_entry(modes[i], &connector->modes, head)
@@ -369,6 +434,8 @@ static int drm_pick_crtcs(struct drm_device *dev,
	my_score = 1;
	if (connector->status == connector_status_connected)
		my_score++;
	if (drm_has_cmdline_mode(connector))
		my_score++;
	if (drm_has_preferred_mode(connector, width, height))
		my_score++;

@@ -943,6 +1010,8 @@ bool drm_helper_initial_config(struct drm_device *dev)
{
	int count = 0;

	drm_fb_helper_parse_command_line(dev);

	count = drm_helper_probe_connector_modes(dev,
						 dev->mode_config.max_width,
						 dev->mode_config.max_height);
@@ -950,7 +1019,7 @@ bool drm_helper_initial_config(struct drm_device *dev)
	/*
	 * we shouldn't end up with no modes here.
	 */
	WARN(!count, "Connected connector with 0 modes\n");
	WARN(!count, "No connectors reported connected with modes\n");

	drm_setup_crtcs(dev);

+4 −2
Original line number Diff line number Diff line
@@ -560,7 +560,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
		vsize = (hsize * 9) / 16;
	/* HDTV hack */
	if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) {
		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
				    false);
		mode->hdisplay = 1366;
		mode->vsync_start = mode->vsync_start - 1;
		mode->vsync_end = mode->vsync_end - 1;
@@ -579,7 +580,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
		break;
	case LEVEL_CVT:
		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
				    false);
		break;
	}
	return mode;
+223 −1
Original line number Diff line number Diff line
@@ -40,6 +40,196 @@ MODULE_LICENSE("GPL and additional rights");

static LIST_HEAD(kernel_fb_helper_list);

int drm_fb_helper_add_connector(struct drm_connector *connector)
{
	connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
	if (!connector->fb_helper_private)
		return -ENOMEM;

	return 0;

}
EXPORT_SYMBOL(drm_fb_helper_add_connector);

static int my_atoi(const char *name)
{
	int val = 0;

	for (;; name++) {
		switch (*name) {
		case '0' ... '9':
			val = 10*val+(*name-'0');
			break;
		default:
			return val;
		}
	}
}

/**
 * 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_connector *connector,
						       const char *mode_option)
{
	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;
	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
	struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_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 = my_atoi(&name[i+1]);
				refresh_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case '-':
			namelen = i;
			if (!bpp_specified && !yres_specified) {
				bpp = my_atoi(&name[i+1]);
				bpp_specified = 1;
				if (cvt || rb)
					cvt = 0;
			} else
				goto done;
			break;
		case 'x':
			if (!yres_specified) {
				yres = my_atoi(&name[i+1]);
				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 = my_atoi(name);
		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" : "");

	if (force) {
		const char *s;
		switch (force) {
		case DRM_FORCE_OFF: s = "OFF"; break;
		case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
		default:
		case DRM_FORCE_ON: s = "ON"; break;
		}

		DRM_INFO("forcing %s connector %s\n",
			 drm_get_connector_name(connector), s);
		connector->force = 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;
}

int drm_fb_helper_parse_command_line(struct drm_device *dev)
{
	struct drm_connector *connector;

	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		char *option = NULL;

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

		drm_fb_helper_connector_parse_command_line(connector, option);
	}
	return 0;
}

bool drm_fb_helper_force_kernel_mode(void)
{
	int i = 0;
@@ -484,6 +674,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
						   uint32_t fb_height,
						   uint32_t surface_width,
						   uint32_t surface_height,
						   uint32_t surface_depth,
						   uint32_t surface_bpp,
						   struct drm_framebuffer **fb_ptr))
{
	struct drm_crtc *crtc;
@@ -497,8 +689,37 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
	struct drm_framebuffer *fb;
	struct drm_mode_set *modeset = NULL;
	struct drm_fb_helper *fb_helper;
	uint32_t surface_depth = 24, surface_bpp = 32;

	/* first up get a count of crtcs now in use and new min/maxes width/heights */
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
		struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode;

		if (cmdline_mode->bpp_specified) {
			switch (cmdline_mode->bpp) {
			case 8:
				surface_depth = surface_bpp = 8;
				break;
			case 15:
				surface_depth = 15;
				surface_bpp = 16;
				break;
			case 16:
				surface_depth = surface_bpp = 16;
				break;
			case 24:
				surface_depth = surface_bpp = 24;
				break;
			case 32:
				surface_depth = 24;
				surface_bpp = 32;
				break;
			}
			break;
		}
	}

	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
		if (drm_helper_crtc_in_use(crtc)) {
			if (crtc->desired_mode) {
@@ -527,7 +748,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
	/* do we have an fb already? */
	if (list_empty(&dev->mode_config.fb_kernel_list)) {
		ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
				   surface_height, &fb);
				   surface_height, surface_depth, surface_bpp,
				   &fb);
		if (ret)
			return -EINVAL;
		new_fb = 1;
+1 −2
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline);
#define HV_FACTOR			1000
struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
				      int vdisplay, int vrefresh,
				      bool reduced, bool interlaced)
				      bool reduced, bool interlaced, bool margins)
{
	/* 1) top/bottom margin size (% of height) - default: 1.8, */
#define	CVT_MARGIN_PERCENTAGE		18
@@ -101,7 +101,6 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay,
	/* Pixel Clock step (kHz) */
#define CVT_CLOCK_STEP			250
	struct drm_display_mode *drm_mode;
	bool margins = false;
	unsigned int vfieldrate, hperiod;
	int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync;
	int interlace;
Loading