modesetting: reorganise code into core and helper functions.
This splits a lot of the core modesetting code out into a file of helper functions, that are only called from themselves and/or the driver. The driver gets called into more often or can call these functions from itself if it is a helper using driver. I've broken framebuffer resize doing this but I didn't like the API for that in any case.main
parent
ee5afc6342
commit
df8cd54286
|
@ -15,7 +15,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
|
|||
drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \
|
||||
drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \
|
||||
drm_edid.o drm_modes.o drm_bo_lock.o drm_regman.o \
|
||||
drm_vm_nopage_compat.o
|
||||
drm_vm_nopage_compat.o drm_crtc_helper.o
|
||||
tdfx-objs := tdfx_drv.o
|
||||
r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o
|
||||
mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
|
||||
|
|
|
@ -418,133 +418,6 @@ void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_crtc_probe_output_modes);
|
||||
|
||||
/**
|
||||
* drm_crtc_set_mode - set a mode
|
||||
* @crtc: CRTC to program
|
||||
* @mode: mode to use
|
||||
* @x: width of mode
|
||||
* @y: height of mode
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*
|
||||
* Try to set @mode on @crtc. Give @crtc and its associated outputs a chance
|
||||
* to fixup or reject the mode prior to trying to set it.
|
||||
*
|
||||
* RETURNS:
|
||||
* True if the mode was set successfully, or false otherwise.
|
||||
*/
|
||||
bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
int x, int y)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_display_mode *adjusted_mode, saved_mode;
|
||||
int saved_x, saved_y;
|
||||
struct drm_output *output;
|
||||
bool ret = true;
|
||||
|
||||
adjusted_mode = drm_mode_duplicate(dev, mode);
|
||||
|
||||
crtc->enabled = drm_crtc_in_use(crtc);
|
||||
|
||||
if (!crtc->enabled)
|
||||
return true;
|
||||
|
||||
saved_mode = crtc->mode;
|
||||
saved_x = crtc->x;
|
||||
saved_y = crtc->y;
|
||||
|
||||
/* Update crtc values up front so the driver can rely on them for mode
|
||||
* setting.
|
||||
*/
|
||||
crtc->mode = *mode;
|
||||
crtc->x = x;
|
||||
crtc->y = y;
|
||||
|
||||
if (drm_mode_equal(&saved_mode, &crtc->mode)) {
|
||||
if (saved_x != crtc->x || saved_y != crtc->y) {
|
||||
crtc->funcs->mode_set_base(crtc, crtc->x, crtc->y);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass our mode to the outputs and the CRTC to give them a chance to
|
||||
* adjust it according to limitations or output properties, and also
|
||||
* a chance to reject the mode entirely.
|
||||
*/
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
if (!(ret = output->funcs->mode_fixup(output, mode, adjusted_mode))) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ret = crtc->funcs->mode_fixup(crtc, mode, adjusted_mode))) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Prepare the outputs and CRTCs before setting the mode. */
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
/* Disable the output as the first thing we do. */
|
||||
output->funcs->prepare(output);
|
||||
}
|
||||
|
||||
crtc->funcs->prepare(crtc);
|
||||
|
||||
/* Set up the DPLL and any output state that needs to adjust or depend
|
||||
* on the DPLL.
|
||||
*/
|
||||
crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y);
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
DRM_INFO("%s: set mode %s %x\n", drm_get_output_name(output), mode->name, mode->mode_id);
|
||||
|
||||
output->funcs->mode_set(output, mode, adjusted_mode);
|
||||
}
|
||||
|
||||
/* Now, enable the clocks, plane, pipe, and outputs that we set up. */
|
||||
crtc->funcs->commit(crtc);
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
output->funcs->commit(output);
|
||||
|
||||
#if 0 // TODO def RANDR_12_INTERFACE
|
||||
if (output->randr_output)
|
||||
RRPostPendingProperties (output->randr_output);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX free adjustedmode */
|
||||
drm_mode_destroy(dev, adjusted_mode);
|
||||
/* TODO */
|
||||
// if (scrn->pScreen)
|
||||
// drm_crtc_set_screen_sub_pixel_order(dev);
|
||||
|
||||
done:
|
||||
if (!ret) {
|
||||
crtc->mode = saved_mode;
|
||||
crtc->x = saved_x;
|
||||
crtc->y = saved_y;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_set_mode);
|
||||
|
||||
/**
|
||||
* drm_disable_unused_functions - disable unused objects
|
||||
|
@ -896,171 +769,8 @@ out_err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pick_crtcs - pick crtcs for output devices
|
||||
* @dev: DRM device
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*/
|
||||
static void drm_pick_crtcs (struct drm_device *dev)
|
||||
{
|
||||
int c, o, assigned;
|
||||
struct drm_output *output, *output_equal;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_display_mode *des_mode = NULL, *modes, *modes_equal;
|
||||
int found;
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
output->crtc = NULL;
|
||||
|
||||
/* Don't hook up outputs that are disconnected ??
|
||||
*
|
||||
* This is debateable. Do we want fixed /dev/fbX or
|
||||
* dynamic on hotplug (need mode code for that though) ?
|
||||
*
|
||||
* If we don't hook up outputs now, then we only create
|
||||
* /dev/fbX for the output that's enabled, that's good as
|
||||
* the users console will be on that output.
|
||||
*
|
||||
* If we do hook up outputs that are disconnected now, then
|
||||
* the user may end up having to muck about with the fbcon
|
||||
* map flags to assign his console to the enabled output. Ugh.
|
||||
*/
|
||||
if (output->status != output_status_connected)
|
||||
continue;
|
||||
|
||||
if (list_empty(&output->modes))
|
||||
continue;
|
||||
|
||||
des_mode = NULL;
|
||||
found = 0;
|
||||
list_for_each_entry(des_mode, &output->modes, head) {
|
||||
if (des_mode->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No preferred mode, let's just select the first available */
|
||||
if (!found) {
|
||||
des_mode = NULL;
|
||||
list_for_each_entry(des_mode, &output->modes, head) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c = -1;
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
assigned = 0;
|
||||
|
||||
c++;
|
||||
if ((output->possible_crtcs & (1 << c)) == 0)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
|
||||
if (output->id == output_equal->id)
|
||||
continue;
|
||||
|
||||
/* Find out if crtc has been assigned before */
|
||||
if (output_equal->crtc == crtc)
|
||||
assigned = 1;
|
||||
}
|
||||
|
||||
#if 1 /* continue for now */
|
||||
if (assigned)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
o = -1;
|
||||
list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
|
||||
o++;
|
||||
if (output->id == output_equal->id)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(modes, &output->modes, head) {
|
||||
list_for_each_entry(modes_equal, &output_equal->modes, head) {
|
||||
if (drm_mode_equal (modes, modes_equal)) {
|
||||
if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) {
|
||||
printk("Cloning %s (0x%lx) to %s (0x%lx)\n",drm_get_output_name(output),output->possible_clones,drm_get_output_name(output_equal),output_equal->possible_clones);
|
||||
des_mode = modes;
|
||||
assigned = 0;
|
||||
goto clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clone:
|
||||
/* crtc has been assigned skip it */
|
||||
if (assigned)
|
||||
continue;
|
||||
|
||||
/* Found a CRTC to attach to, do it ! */
|
||||
output->crtc = crtc;
|
||||
output->crtc->desired_mode = des_mode;
|
||||
output->initial_x = 0;
|
||||
output->initial_y = 0;
|
||||
DRM_DEBUG("Desired mode for CRTC %d is 0x%x:%s\n",c,des_mode->mode_id, des_mode->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_pick_crtcs);
|
||||
|
||||
/**
|
||||
* drm_initial_config - setup a sane initial output configuration
|
||||
* @dev: DRM device
|
||||
* @can_grow: this configuration is growable
|
||||
*
|
||||
* LOCKING:
|
||||
* Called at init time, must take mode config lock.
|
||||
*
|
||||
* Scan the CRTCs and outputs and try to put together an initial setup.
|
||||
* At the moment, this is a cloned configuration across all heads with
|
||||
* a new framebuffer object as the backing store.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero if everything went ok, nonzero otherwise.
|
||||
*/
|
||||
bool drm_initial_config(struct drm_device *dev, bool can_grow)
|
||||
{
|
||||
struct drm_output *output;
|
||||
struct drm_crtc *crtc;
|
||||
int ret = false;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
drm_crtc_probe_output_modes(dev, 2048, 2048);
|
||||
|
||||
drm_pick_crtcs(dev);
|
||||
|
||||
/* This is a little screwy, as we've already walked the outputs
|
||||
* above, but it's a little bit of magic too. There's the potential
|
||||
* for things not to get setup above if an existing device gets
|
||||
* re-assigned thus confusing the hardware. By walking the outputs
|
||||
* this fixes up their crtc's.
|
||||
*/
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
/* can't setup the output if there's no assigned mode */
|
||||
if (!output->crtc || !output->crtc->desired_mode)
|
||||
continue;
|
||||
|
||||
dev->driver->fb_probe(dev, output->crtc, output);
|
||||
|
||||
/* and needs an attached fb */
|
||||
if (output->crtc->fb)
|
||||
drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0);
|
||||
}
|
||||
|
||||
drm_disable_unused_functions(dev);
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_initial_config);
|
||||
|
||||
/**
|
||||
* drm_mode_config_cleanup - free up DRM mode_config info
|
||||
|
@ -1105,179 +815,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_mode_config_cleanup);
|
||||
|
||||
/**
|
||||
* drm_crtc_set_config - set a new config from userspace
|
||||
* @crtc: CRTC to setup
|
||||
* @crtc_info: user provided configuration
|
||||
* @new_mode: new mode to set
|
||||
* @output_set: set of outputs for the new config
|
||||
* @fb: new framebuffer
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*
|
||||
* Setup a new configuration, provided by the user in @crtc_info, and enable
|
||||
* it.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero. (FIXME)
|
||||
*/
|
||||
int drm_crtc_set_config(struct drm_mode_set *set)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct drm_crtc **save_crtcs, *new_crtc;
|
||||
bool save_enabled;
|
||||
bool changed = false;
|
||||
bool flip_or_move = false;
|
||||
struct drm_output *output;
|
||||
int count = 0, ro;
|
||||
|
||||
DRM_DEBUG("\n");
|
||||
|
||||
if (!set)
|
||||
return -EINVAL;
|
||||
|
||||
if (!set->crtc)
|
||||
return -EINVAL;
|
||||
|
||||
DRM_DEBUG("crtc: %p fb: %p outputs: %p num_outputs: %i (x, y) (%i, %i)\n", set->crtc, set->fb, set->outputs, set->num_outputs, set->x, set->y);
|
||||
dev = set->crtc->dev;
|
||||
|
||||
/* save previous config */
|
||||
save_enabled = set->crtc->enabled;
|
||||
|
||||
/* this is meant to be num_output not num_crtc */
|
||||
save_crtcs = kzalloc(dev->mode_config.num_output * sizeof(struct drm_crtc *), GFP_KERNEL);
|
||||
if (!save_crtcs)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We should be able to check here if the fb has the same properties
|
||||
* and then just flip_or_move it */
|
||||
if (set->crtc->fb != set->fb)
|
||||
flip_or_move = true;
|
||||
|
||||
if (set->x != set->crtc->x || set->y != set->crtc->y)
|
||||
flip_or_move = true;
|
||||
|
||||
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
|
||||
DRM_DEBUG("modes are different\n");
|
||||
drm_mode_debug_printmodeline(&set->crtc->mode);
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
save_crtcs[count++] = output->crtc;
|
||||
|
||||
if (output->crtc == set->crtc)
|
||||
new_crtc = NULL;
|
||||
else
|
||||
new_crtc = output->crtc;
|
||||
|
||||
for (ro = 0; ro < set->num_outputs; ro++) {
|
||||
if (set->outputs[ro] == output)
|
||||
new_crtc = set->crtc;
|
||||
}
|
||||
if (new_crtc != output->crtc) {
|
||||
changed = true;
|
||||
output->crtc = new_crtc;
|
||||
}
|
||||
}
|
||||
|
||||
/* mode_set_base is not a required function */
|
||||
if (flip_or_move && !set->crtc->funcs->mode_set_base)
|
||||
changed = true;
|
||||
|
||||
if (changed) {
|
||||
set->crtc->fb = set->fb;
|
||||
set->crtc->enabled = (set->mode != NULL);
|
||||
if (set->mode != NULL) {
|
||||
DRM_DEBUG("attempting to set mode from userspace\n");
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
if (!drm_crtc_set_mode(set->crtc, set->mode, set->x,
|
||||
set->y)) {
|
||||
set->crtc->enabled = save_enabled;
|
||||
count = 0;
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head)
|
||||
output->crtc = save_crtcs[count++];
|
||||
kfree(save_crtcs);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* TODO are these needed? */
|
||||
set->crtc->desired_x = set->x;
|
||||
set->crtc->desired_y = set->y;
|
||||
set->crtc->desired_mode = set->mode;
|
||||
}
|
||||
drm_disable_unused_functions(dev);
|
||||
} else if (flip_or_move) {
|
||||
if (set->crtc->fb != set->fb)
|
||||
set->crtc->fb = set->fb;
|
||||
set->crtc->funcs->mode_set_base(set->crtc, set->x, set->y);
|
||||
}
|
||||
|
||||
kfree(save_crtcs);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_set_config);
|
||||
|
||||
/**
|
||||
* drm_hotplug_stage_two
|
||||
* @dev DRM device
|
||||
* @output hotpluged output
|
||||
*
|
||||
* LOCKING.
|
||||
* Caller must hold mode config lock, function might grab struct lock.
|
||||
*
|
||||
* Stage two of a hotplug.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero on success, errno on failure.
|
||||
*/
|
||||
int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
|
||||
bool connected)
|
||||
{
|
||||
int has_config = 0;
|
||||
|
||||
dev->mode_config.hotplug_counter++;
|
||||
|
||||
/* We might want to do something more here */
|
||||
if (!connected) {
|
||||
DRM_DEBUG("not connected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (output->crtc && output->crtc->desired_mode) {
|
||||
DRM_DEBUG("drm thinks that the output already has a config\n");
|
||||
has_config = 1;
|
||||
}
|
||||
|
||||
drm_crtc_probe_output_modes(dev, 2048, 2048);
|
||||
|
||||
if (!has_config)
|
||||
drm_pick_crtcs(dev);
|
||||
|
||||
if (!output->crtc || !output->crtc->desired_mode) {
|
||||
DRM_DEBUG("could not find a desired mode or crtc for output\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* We should really check if there is a fb using this crtc */
|
||||
if (!has_config)
|
||||
dev->driver->fb_probe(dev, output->crtc, output);
|
||||
else {
|
||||
dev->driver->fb_resize(dev, output->crtc);
|
||||
|
||||
if (!drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0))
|
||||
DRM_ERROR("failed to set mode after hotplug\n");
|
||||
}
|
||||
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
|
||||
drm_disable_unused_functions(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hotplug_stage_two);
|
||||
|
||||
int drm_mode_hotplug_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *file_priv)
|
||||
|
@ -1740,7 +1278,7 @@ int drm_mode_setcrtc(struct drm_device *dev,
|
|||
set.outputs = output_set;
|
||||
set.num_outputs = crtc_req->count_outputs;
|
||||
set.fb =fb;
|
||||
ret = drm_crtc_set_config(&set);
|
||||
ret = crtc->funcs->set_config(&set);
|
||||
|
||||
out:
|
||||
kfree(output_set);
|
||||
|
@ -2535,7 +2073,6 @@ int drm_mode_replacefb(struct drm_device *dev,
|
|||
{
|
||||
struct drm_mode_fb_cmd *r = data;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_buffer_object *bo;
|
||||
int found = 0;
|
||||
struct drm_framebuffer *fbl = NULL;
|
||||
|
@ -2574,12 +2111,18 @@ int drm_mode_replacefb(struct drm_device *dev,
|
|||
fb->depth = r->depth;
|
||||
fb->bo = bo;
|
||||
|
||||
if (dev->mode_config.funcs->resize_fb)
|
||||
dev->mode_config.funcs->resize_fb(dev, fb);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
#if 0
|
||||
/* find all crtcs connected to this fb */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
if (crtc->fb->id == r->buffer_id) {
|
||||
crtc->funcs->mode_set_base(crtc, crtc->x, crtc->y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/fb.h>
|
||||
|
||||
struct drm_device;
|
||||
struct drm_mode_set;
|
||||
|
||||
/*
|
||||
* Note on terminology: here, for brevity and convenience, we refer to output
|
||||
|
@ -313,20 +314,6 @@ struct drm_crtc_funcs {
|
|||
/* Restore CRTC state */
|
||||
void (*restore)(struct drm_crtc *crtc); /* resume? */
|
||||
|
||||
void (*prepare)(struct drm_crtc *crtc);
|
||||
void (*commit)(struct drm_crtc *crtc);
|
||||
|
||||
/* Provider can fixup or change mode timings before modeset occurs */
|
||||
bool (*mode_fixup)(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
/* Actually set the mode */
|
||||
void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode, int x, int y);
|
||||
|
||||
/* Move the crtc on the current fb to the given position *optional* */
|
||||
void (*mode_set_base)(struct drm_crtc *crtc, int x, int y);
|
||||
|
||||
/* cursor controls */
|
||||
int (*cursor_set)(struct drm_crtc *crtc, struct drm_buffer_object *bo,
|
||||
uint32_t width, uint32_t height);
|
||||
|
@ -337,6 +324,8 @@ struct drm_crtc_funcs {
|
|||
int regno);
|
||||
/* Driver cleanup routine */
|
||||
void (*cleanup)(struct drm_crtc *crtc);
|
||||
|
||||
int (*set_config)(struct drm_mode_set *set);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -371,6 +360,9 @@ struct drm_crtc {
|
|||
int desired_x, desired_y;
|
||||
const struct drm_crtc_funcs *funcs;
|
||||
void *driver_private;
|
||||
|
||||
/* if you are using the helper */
|
||||
void *helper_private;
|
||||
};
|
||||
|
||||
extern struct drm_crtc *drm_crtc_create(struct drm_device *dev,
|
||||
|
@ -399,21 +391,14 @@ struct drm_output_funcs {
|
|||
void (*dpms)(struct drm_output *output, int mode);
|
||||
void (*save)(struct drm_output *output);
|
||||
void (*restore)(struct drm_output *output);
|
||||
int (*mode_valid)(struct drm_output *output,
|
||||
struct drm_display_mode *mode);
|
||||
bool (*mode_fixup)(struct drm_output *output,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
void (*prepare)(struct drm_output *output);
|
||||
void (*commit)(struct drm_output *output);
|
||||
void (*mode_set)(struct drm_output *output,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
enum drm_output_status (*detect)(struct drm_output *output);
|
||||
int (*get_modes)(struct drm_output *output);
|
||||
bool (*set_property)(struct drm_output *output, struct drm_property *property,
|
||||
uint64_t val);
|
||||
void (*cleanup)(struct drm_output *output);
|
||||
int (*mode_valid)(struct drm_output *output,
|
||||
struct drm_display_mode *mode);
|
||||
|
||||
};
|
||||
|
||||
#define DRM_OUTPUT_MAX_UMODES 16
|
||||
|
@ -468,6 +453,8 @@ struct drm_output {
|
|||
struct drm_property_blob *edid_blob_ptr;
|
||||
u32 property_ids[DRM_OUTPUT_MAX_PROPERTY];
|
||||
uint64_t property_values[DRM_OUTPUT_MAX_PROPERTY];
|
||||
|
||||
void *helper_private;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -501,7 +488,7 @@ struct drm_mode_set
|
|||
* the CRTC<->output mappings as needed and update its view of the screen.
|
||||
*/
|
||||
struct drm_mode_config_funcs {
|
||||
bool (*resize)(struct drm_device *dev, int width, int height);
|
||||
bool (*resize_fb)(struct drm_device *dev, struct drm_framebuffer *fb);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -597,18 +584,14 @@ extern int drm_output_property_get_value(struct drm_output *output,
|
|||
struct drm_property *property,
|
||||
uint64_t *value);
|
||||
extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev);
|
||||
extern bool drm_initial_config(struct drm_device *dev, bool cangrow);
|
||||
extern void drm_framebuffer_set_object(struct drm_device *dev,
|
||||
unsigned long handle);
|
||||
extern struct drm_framebuffer *drm_framebuffer_create(struct drm_device *dev);
|
||||
extern void drm_framebuffer_destroy(struct drm_framebuffer *fb);
|
||||
extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
|
||||
extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
|
||||
extern int drm_crtc_set_config(struct drm_mode_set *set);
|
||||
extern bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
int x, int y);
|
||||
extern void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY);
|
||||
extern bool drm_crtc_in_use(struct drm_crtc *crtc);
|
||||
extern int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output, bool connected);
|
||||
|
||||
extern int drm_output_attach_property(struct drm_output *output,
|
||||
struct drm_property *property, uint64_t init_val);
|
||||
|
|
|
@ -0,0 +1,515 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2006-2007 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
*
|
||||
* DRM core CRTC related functions
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that copyright
|
||||
* notice and this permission notice appear in supporting documentation, and
|
||||
* that the name of the copyright holders not be used in advertising or
|
||||
* publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no representations
|
||||
* about the suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THIS SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Keith Packard
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
|
||||
/**
|
||||
* drm_pick_crtcs - pick crtcs for output devices
|
||||
* @dev: DRM device
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*/
|
||||
static void drm_pick_crtcs (struct drm_device *dev)
|
||||
{
|
||||
int c, o, assigned;
|
||||
struct drm_output *output, *output_equal;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_display_mode *des_mode = NULL, *modes, *modes_equal;
|
||||
int found;
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
output->crtc = NULL;
|
||||
|
||||
/* Don't hook up outputs that are disconnected ??
|
||||
*
|
||||
* This is debateable. Do we want fixed /dev/fbX or
|
||||
* dynamic on hotplug (need mode code for that though) ?
|
||||
*
|
||||
* If we don't hook up outputs now, then we only create
|
||||
* /dev/fbX for the output that's enabled, that's good as
|
||||
* the users console will be on that output.
|
||||
*
|
||||
* If we do hook up outputs that are disconnected now, then
|
||||
* the user may end up having to muck about with the fbcon
|
||||
* map flags to assign his console to the enabled output. Ugh.
|
||||
*/
|
||||
if (output->status != output_status_connected)
|
||||
continue;
|
||||
|
||||
if (list_empty(&output->modes))
|
||||
continue;
|
||||
|
||||
des_mode = NULL;
|
||||
found = 0;
|
||||
list_for_each_entry(des_mode, &output->modes, head) {
|
||||
if (des_mode->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No preferred mode, let's just select the first available */
|
||||
if (!found) {
|
||||
des_mode = NULL;
|
||||
list_for_each_entry(des_mode, &output->modes, head) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c = -1;
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
assigned = 0;
|
||||
|
||||
c++;
|
||||
if ((output->possible_crtcs & (1 << c)) == 0)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
|
||||
if (output->id == output_equal->id)
|
||||
continue;
|
||||
|
||||
/* Find out if crtc has been assigned before */
|
||||
if (output_equal->crtc == crtc)
|
||||
assigned = 1;
|
||||
}
|
||||
|
||||
#if 1 /* continue for now */
|
||||
if (assigned)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
o = -1;
|
||||
list_for_each_entry(output_equal, &dev->mode_config.output_list, head) {
|
||||
o++;
|
||||
if (output->id == output_equal->id)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(modes, &output->modes, head) {
|
||||
list_for_each_entry(modes_equal, &output_equal->modes, head) {
|
||||
if (drm_mode_equal (modes, modes_equal)) {
|
||||
if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) {
|
||||
printk("Cloning %s (0x%lx) to %s (0x%lx)\n",drm_get_output_name(output),output->possible_clones,drm_get_output_name(output_equal),output_equal->possible_clones);
|
||||
des_mode = modes;
|
||||
assigned = 0;
|
||||
goto clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clone:
|
||||
/* crtc has been assigned skip it */
|
||||
if (assigned)
|
||||
continue;
|
||||
|
||||
/* Found a CRTC to attach to, do it ! */
|
||||
output->crtc = crtc;
|
||||
output->crtc->desired_mode = des_mode;
|
||||
output->initial_x = 0;
|
||||
output->initial_y = 0;
|
||||
DRM_DEBUG("Desired mode for CRTC %d is 0x%x:%s\n",c,des_mode->mode_id, des_mode->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_pick_crtcs);
|
||||
|
||||
/**
|
||||
* drm_crtc_set_mode - set a mode
|
||||
* @crtc: CRTC to program
|
||||
* @mode: mode to use
|
||||
* @x: width of mode
|
||||
* @y: height of mode
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*
|
||||
* Try to set @mode on @crtc. Give @crtc and its associated outputs a chance
|
||||
* to fixup or reject the mode prior to trying to set it.
|
||||
*
|
||||
* RETURNS:
|
||||
* True if the mode was set successfully, or false otherwise.
|
||||
*/
|
||||
bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
int x, int y)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_display_mode *adjusted_mode, saved_mode;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
struct drm_output_helper_funcs *output_funcs;
|
||||
int saved_x, saved_y;
|
||||
struct drm_output *output;
|
||||
bool ret = true;
|
||||
|
||||
adjusted_mode = drm_mode_duplicate(dev, mode);
|
||||
|
||||
crtc->enabled = drm_crtc_in_use(crtc);
|
||||
|
||||
if (!crtc->enabled)
|
||||
return true;
|
||||
|
||||
saved_mode = crtc->mode;
|
||||
saved_x = crtc->x;
|
||||
saved_y = crtc->y;
|
||||
|
||||
/* Update crtc values up front so the driver can rely on them for mode
|
||||
* setting.
|
||||
*/
|
||||
crtc->mode = *mode;
|
||||
crtc->x = x;
|
||||
crtc->y = y;
|
||||
|
||||
if (drm_mode_equal(&saved_mode, &crtc->mode)) {
|
||||
if (saved_x != crtc->x || saved_y != crtc->y) {
|
||||
crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass our mode to the outputs and the CRTC to give them a chance to
|
||||
* adjust it according to limitations or output properties, and also
|
||||
* a chance to reject the mode entirely.
|
||||
*/
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
output_funcs = output->helper_private;
|
||||
if (!(ret = output_funcs->mode_fixup(output, mode, adjusted_mode))) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Prepare the outputs and CRTCs before setting the mode. */
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
output_funcs = output->helper_private;
|
||||
/* Disable the output as the first thing we do. */
|
||||
output_funcs->prepare(output);
|
||||
}
|
||||
|
||||
crtc_funcs->prepare(crtc);
|
||||
|
||||
/* Set up the DPLL and any output state that needs to adjust or depend
|
||||
* on the DPLL.
|
||||
*/
|
||||
crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y);
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
DRM_INFO("%s: set mode %s %x\n", drm_get_output_name(output), mode->name, mode->mode_id);
|
||||
output_funcs = output->helper_private;
|
||||
output_funcs->mode_set(output, mode, adjusted_mode);
|
||||
}
|
||||
|
||||
/* Now, enable the clocks, plane, pipe, and outputs that we set up. */
|
||||
crtc_funcs->commit(crtc);
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
output_funcs = output->helper_private;
|
||||
output_funcs->commit(output);
|
||||
|
||||
#if 0 // TODO def RANDR_12_INTERFACE
|
||||
if (output->randr_output)
|
||||
RRPostPendingProperties (output->randr_output);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX free adjustedmode */
|
||||
drm_mode_destroy(dev, adjusted_mode);
|
||||
/* TODO */
|
||||
// if (scrn->pScreen)
|
||||
// drm_crtc_set_screen_sub_pixel_order(dev);
|
||||
|
||||
done:
|
||||
if (!ret) {
|
||||
crtc->mode = saved_mode;
|
||||
crtc->x = saved_x;
|
||||
crtc->y = saved_y;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_helper_set_mode);
|
||||
|
||||
|
||||
/**
|
||||
* drm_crtc_helper_set_config - set a new config from userspace
|
||||
* @crtc: CRTC to setup
|
||||
* @crtc_info: user provided configuration
|
||||
* @new_mode: new mode to set
|
||||
* @output_set: set of outputs for the new config
|
||||
* @fb: new framebuffer
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*
|
||||
* Setup a new configuration, provided by the user in @crtc_info, and enable
|
||||
* it.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero. (FIXME)
|
||||
*/
|
||||
int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct drm_crtc **save_crtcs, *new_crtc;
|
||||
bool save_enabled;
|
||||
bool changed = false;
|
||||
bool flip_or_move = false;
|
||||
struct drm_output *output;
|
||||
int count = 0, ro;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
|
||||
DRM_DEBUG("\n");
|
||||
|
||||
if (!set)
|
||||
return -EINVAL;
|
||||
|
||||
if (!set->crtc)
|
||||
return -EINVAL;
|
||||
|
||||
if (!set->crtc->helper_private)
|
||||
return -EINVAL;
|
||||
|
||||
crtc_funcs = set->crtc->helper_private;
|
||||
|
||||
DRM_DEBUG("crtc: %p fb: %p outputs: %p num_outputs: %i (x, y) (%i, %i)\n", set->crtc, set->fb, set->outputs, set->num_outputs, set->x, set->y);
|
||||
dev = set->crtc->dev;
|
||||
|
||||
/* save previous config */
|
||||
save_enabled = set->crtc->enabled;
|
||||
|
||||
/* this is meant to be num_output not num_crtc */
|
||||
save_crtcs = kzalloc(dev->mode_config.num_output * sizeof(struct drm_crtc *), GFP_KERNEL);
|
||||
if (!save_crtcs)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We should be able to check here if the fb has the same properties
|
||||
* and then just flip_or_move it */
|
||||
if (set->crtc->fb != set->fb)
|
||||
flip_or_move = true;
|
||||
|
||||
if (set->x != set->crtc->x || set->y != set->crtc->y)
|
||||
flip_or_move = true;
|
||||
|
||||
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
|
||||
DRM_DEBUG("modes are different\n");
|
||||
drm_mode_debug_printmodeline(&set->crtc->mode);
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
save_crtcs[count++] = output->crtc;
|
||||
|
||||
if (output->crtc == set->crtc)
|
||||
new_crtc = NULL;
|
||||
else
|
||||
new_crtc = output->crtc;
|
||||
|
||||
for (ro = 0; ro < set->num_outputs; ro++) {
|
||||
if (set->outputs[ro] == output)
|
||||
new_crtc = set->crtc;
|
||||
}
|
||||
if (new_crtc != output->crtc) {
|
||||
changed = true;
|
||||
output->crtc = new_crtc;
|
||||
}
|
||||
}
|
||||
|
||||
/* mode_set_base is not a required function */
|
||||
if (flip_or_move && !crtc_funcs->mode_set_base)
|
||||
changed = true;
|
||||
|
||||
if (changed) {
|
||||
set->crtc->fb = set->fb;
|
||||
set->crtc->enabled = (set->mode != NULL);
|
||||
if (set->mode != NULL) {
|
||||
DRM_DEBUG("attempting to set mode from userspace\n");
|
||||
drm_mode_debug_printmodeline(set->mode);
|
||||
if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x,
|
||||
set->y)) {
|
||||
set->crtc->enabled = save_enabled;
|
||||
count = 0;
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head)
|
||||
output->crtc = save_crtcs[count++];
|
||||
kfree(save_crtcs);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* TODO are these needed? */
|
||||
set->crtc->desired_x = set->x;
|
||||
set->crtc->desired_y = set->y;
|
||||
set->crtc->desired_mode = set->mode;
|
||||
}
|
||||
drm_disable_unused_functions(dev);
|
||||
} else if (flip_or_move) {
|
||||
if (set->crtc->fb != set->fb)
|
||||
set->crtc->fb = set->fb;
|
||||
crtc_funcs->mode_set_base(set->crtc, set->x, set->y);
|
||||
}
|
||||
|
||||
kfree(save_crtcs);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_helper_set_config);
|
||||
|
||||
/**
|
||||
* drm_initial_config - setup a sane initial output configuration
|
||||
* @dev: DRM device
|
||||
* @can_grow: this configuration is growable
|
||||
*
|
||||
* LOCKING:
|
||||
* Called at init time, must take mode config lock.
|
||||
*
|
||||
* Scan the CRTCs and outputs and try to put together an initial setup.
|
||||
* At the moment, this is a cloned configuration across all heads with
|
||||
* a new framebuffer object as the backing store.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero if everything went ok, nonzero otherwise.
|
||||
*/
|
||||
bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
|
||||
{
|
||||
struct drm_output *output;
|
||||
int ret = false;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
drm_crtc_probe_output_modes(dev, 2048, 2048);
|
||||
|
||||
drm_pick_crtcs(dev);
|
||||
|
||||
/* This is a little screwy, as we've already walked the outputs
|
||||
* above, but it's a little bit of magic too. There's the potential
|
||||
* for things not to get setup above if an existing device gets
|
||||
* re-assigned thus confusing the hardware. By walking the outputs
|
||||
* this fixes up their crtc's.
|
||||
*/
|
||||
list_for_each_entry(output, &dev->mode_config.output_list, head) {
|
||||
|
||||
/* can't setup the output if there's no assigned mode */
|
||||
if (!output->crtc || !output->crtc->desired_mode)
|
||||
continue;
|
||||
|
||||
dev->driver->fb_probe(dev, output->crtc, output);
|
||||
|
||||
/* and needs an attached fb */
|
||||
if (output->crtc->fb)
|
||||
drm_crtc_helper_set_mode(output->crtc, output->crtc->desired_mode, 0, 0);
|
||||
}
|
||||
|
||||
drm_disable_unused_functions(dev);
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_initial_config);
|
||||
|
||||
/**
|
||||
* drm_hotplug_stage_two
|
||||
* @dev DRM device
|
||||
* @output hotpluged output
|
||||
*
|
||||
* LOCKING.
|
||||
* Caller must hold mode config lock, function might grab struct lock.
|
||||
*
|
||||
* Stage two of a hotplug.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero on success, errno on failure.
|
||||
*/
|
||||
int drm_helper_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
|
||||
bool connected)
|
||||
{
|
||||
int has_config = 0;
|
||||
|
||||
dev->mode_config.hotplug_counter++;
|
||||
|
||||
/* We might want to do something more here */
|
||||
if (!connected) {
|
||||
DRM_DEBUG("not connected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (output->crtc && output->crtc->desired_mode) {
|
||||
DRM_DEBUG("drm thinks that the output already has a config\n");
|
||||
has_config = 1;
|
||||
}
|
||||
|
||||
drm_crtc_probe_output_modes(dev, 2048, 2048);
|
||||
|
||||
if (!has_config)
|
||||
drm_pick_crtcs(dev);
|
||||
|
||||
if (!output->crtc || !output->crtc->desired_mode) {
|
||||
DRM_DEBUG("could not find a desired mode or crtc for output\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* We should really check if there is a fb using this crtc */
|
||||
if (!has_config)
|
||||
dev->driver->fb_probe(dev, output->crtc, output);
|
||||
else {
|
||||
dev->driver->fb_resize(dev, output->crtc);
|
||||
|
||||
#if 0
|
||||
if (!drm_crtc_set_mode(output->crtc, output->crtc->desired_mode, 0, 0))
|
||||
DRM_ERROR("failed to set mode after hotplug\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
|
||||
drm_disable_unused_functions(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright © 2006 Keith Packard
|
||||
* Copyright © 2007 Intel Corporation
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* The DRM mode setting helper functions are common code for drivers to use if they wish.
|
||||
* Drivers are not forced to use this code in their implementations but it would be useful
|
||||
* if they code they do use at least provides a consistent interface and operation to userspace
|
||||
*/
|
||||
|
||||
#ifndef __DRM_CRTC_HELPER_H__
|
||||
#define __DRM_CRTC_HELPER_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
|
||||
struct drm_crtc_helper_funcs {
|
||||
void (*prepare)(struct drm_crtc *crtc);
|
||||
void (*commit)(struct drm_crtc *crtc);
|
||||
|
||||
/* Provider can fixup or change mode timings before modeset occurs */
|
||||
bool (*mode_fixup)(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
/* Actually set the mode */
|
||||
void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode, int x, int y);
|
||||
|
||||
/* Move the crtc on the current fb to the given position *optional* */
|
||||
void (*mode_set_base)(struct drm_crtc *crtc, int x, int y);
|
||||
};
|
||||
|
||||
struct drm_output_helper_funcs {
|
||||
bool (*mode_fixup)(struct drm_output *output,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
void (*prepare)(struct drm_output *output);
|
||||
void (*commit)(struct drm_output *output);
|
||||
void (*mode_set)(struct drm_output *output,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
};
|
||||
|
||||
extern int drm_helper_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
|
||||
bool connected);
|
||||
extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow);
|
||||
extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
|
||||
extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
int x, int y);
|
||||
|
||||
static inline void drm_crtc_helper_add(struct drm_crtc *crtc, const struct drm_crtc_helper_funcs *funcs)
|
||||
{
|
||||
crtc->helper_private = (void *)funcs;
|
||||
}
|
||||
|
||||
static inline void drm_output_helper_add(struct drm_output *output, const struct drm_output_helper_funcs *funcs)
|
||||
{
|
||||
output->helper_private = (void *)funcs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -28,6 +28,7 @@
|
|||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "intel_drv.h"
|
||||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
|
@ -220,19 +221,24 @@ static bool intel_crt_set_property(struct drm_output *output,
|
|||
/*
|
||||
* Routines for controlling stuff on the analog port
|
||||
*/
|
||||
|
||||
static const struct drm_output_helper_funcs intel_crt_helper_funcs = {
|
||||
.mode_fixup = intel_crt_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.commit = intel_output_commit,
|
||||
.mode_set = intel_crt_mode_set,
|
||||
};
|
||||
|
||||
static const struct drm_output_funcs intel_crt_output_funcs = {
|
||||
.dpms = intel_crt_dpms,
|
||||
.save = intel_crt_save,
|
||||
.restore = intel_crt_restore,
|
||||
.mode_valid = intel_crt_mode_valid,
|
||||
.mode_fixup = intel_crt_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_crt_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
.detect = intel_crt_detect,
|
||||
.get_modes = intel_crt_get_modes,
|
||||
.cleanup = intel_crt_destroy,
|
||||
.set_property = intel_crt_set_property,
|
||||
.mode_valid = intel_crt_mode_valid,
|
||||
|
||||
};
|
||||
|
||||
void intel_crt_init(struct drm_device *dev)
|
||||
|
@ -261,6 +267,7 @@ void intel_crt_init(struct drm_device *dev)
|
|||
output->interlace_allowed = 0;
|
||||
output->doublescan_allowed = 0;
|
||||
|
||||
drm_output_helper_add(output, &intel_crt_helper_funcs);
|
||||
drm_sysfs_output_add(output);
|
||||
|
||||
drm_output_attach_property(output, dev->mode_config.connector_type_property, ConnectorVGA);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
|
||||
|
||||
typedef struct {
|
||||
|
@ -1090,6 +1092,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct drm_output *output,
|
|||
struct drm_crtc *possible_crtc;
|
||||
struct drm_crtc *supported_crtc =NULL;
|
||||
struct drm_crtc *crtc = NULL;
|
||||
struct drm_output_helper_funcs *output_funcs;
|
||||
int i = -1;
|
||||
|
||||
/*
|
||||
|
@ -1147,14 +1150,15 @@ struct drm_crtc *intel_get_load_detect_pipe(struct drm_output *output,
|
|||
if (!crtc->enabled) {
|
||||
if (!mode)
|
||||
mode = &load_detect_mode;
|
||||
drm_crtc_set_mode(crtc, mode, 0, 0);
|
||||
drm_crtc_helper_set_mode(crtc, mode, 0, 0);
|
||||
} else {
|
||||
if (intel_crtc->dpms_mode != DPMSModeOn)
|
||||
crtc->funcs->dpms(crtc, DPMSModeOn);
|
||||
|
||||
output_funcs = output->helper_private;
|
||||
/* Add this output to the crtc */
|
||||
output->funcs->mode_set(output, &crtc->mode, &crtc->mode);
|
||||
output->funcs->commit(output);
|
||||
output_funcs->mode_set(output, &crtc->mode, &crtc->mode);
|
||||
output_funcs->commit(output);
|
||||
}
|
||||
/* let the output get through one full cycle before testing */
|
||||
intel_wait_for_vblank(dev);
|
||||
|
@ -1293,16 +1297,20 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
|
|||
return mode;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs intel_crtc_funcs = {
|
||||
.dpms = intel_crtc_dpms,
|
||||
static const struct drm_crtc_helper_funcs intel_helper_funcs = {
|
||||
.mode_fixup = intel_crtc_mode_fixup,
|
||||
.mode_set = intel_crtc_mode_set,
|
||||
.mode_set_base = intel_pipe_set_base,
|
||||
.prepare = intel_crtc_prepare,
|
||||
.commit = intel_crtc_commit,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs intel_crtc_funcs = {
|
||||
.dpms = intel_crtc_dpms,
|
||||
.cursor_set = intel_crtc_cursor_set,
|
||||
.cursor_move = intel_crtc_cursor_move,
|
||||
.gamma_set = intel_crtc_gamma_set,
|
||||
.prepare = intel_crtc_prepare,
|
||||
.commit = intel_crtc_commit,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
};
|
||||
|
||||
|
||||
|
@ -1331,6 +1339,7 @@ void intel_crtc_init(struct drm_device *dev, int pipe)
|
|||
|
||||
intel_crtc->cursor_addr = 0;
|
||||
intel_crtc->dpms_mode = DPMSModeOff;
|
||||
drm_crtc_helper_add(crtc, &intel_helper_funcs);
|
||||
|
||||
crtc->driver_private = intel_crtc;
|
||||
}
|
||||
|
@ -1418,6 +1427,10 @@ static void intel_setup_outputs(struct drm_device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs intel_mode_funcs = {
|
||||
.resize_fb = NULL,
|
||||
};
|
||||
|
||||
void intel_modeset_init(struct drm_device *dev)
|
||||
{
|
||||
int num_pipe;
|
||||
|
@ -1428,6 +1441,8 @@ void intel_modeset_init(struct drm_device *dev)
|
|||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
|
||||
dev->mode_config.funcs = (void *)&intel_mode_funcs;
|
||||
|
||||
if (IS_I965G(dev)) {
|
||||
dev->mode_config.max_width = 8192;
|
||||
dev->mode_config.max_height = 8192;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/i2c-algo-bit.h>
|
||||
#include "drm_crtc.h"
|
||||
|
||||
#include "drm_crtc_helper.h"
|
||||
/*
|
||||
* Display related stuff
|
||||
*/
|
||||
|
|
|
@ -325,19 +325,21 @@ static struct drm_crtc *intel_dvo_get_crtc(struct drm_output *output)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct drm_output_helper_funcs intel_dvo_helper_funcs = {
|
||||
.mode_fixup = intel_dvo_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_dvo_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
};
|
||||
|
||||
static const struct drm_output_funcs intel_dvo_output_funcs = {
|
||||
.dpms = intel_dvo_dpms,
|
||||
.save = intel_dvo_save,
|
||||
.restore = intel_dvo_restore,
|
||||
.mode_valid = intel_dvo_mode_valid,
|
||||
.mode_fixup = intel_dvo_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_dvo_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
.detect = intel_dvo_detect,
|
||||
.get_modes = intel_dvo_get_modes,
|
||||
.cleanup = intel_dvo_destroy
|
||||
.cleanup = intel_dvo_destroy,
|
||||
.mode_valid = intel_dvo_mode_valid,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -457,6 +459,7 @@ void intel_dvo_init(struct drm_device *dev)
|
|||
goto free_i2c;
|
||||
}
|
||||
|
||||
drm_output_helper_add(output, &intel_dvo_helper_funcs);
|
||||
output->driver_private = intel_output;
|
||||
output->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
output->interlace_allowed = false;
|
||||
|
|
|
@ -341,7 +341,7 @@ static int intelfb_set_par(struct fb_info *info)
|
|||
|
||||
return 0;
|
||||
#else
|
||||
return drm_crtc_set_config(&par->set);
|
||||
return par->set.crtc->funcs->set_config(&par->set);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -496,11 +496,11 @@ static int intelfb_pan_display(struct fb_var_screeninfo *var,
|
|||
struct intelfb_par *par = info->par;
|
||||
int ret;
|
||||
DRM_DEBUG("\n");
|
||||
|
||||
|
||||
par->set.x = var->xoffset;
|
||||
par->set.y = var->yoffset;
|
||||
|
||||
ret = drm_crtc_set_config(&par->set);
|
||||
ret = par->set.crtc->funcs->set_config(&par->set);
|
||||
|
||||
if (!ret) {
|
||||
info->var.xoffset = var->xoffset;
|
||||
|
|
|
@ -332,18 +332,21 @@ static void intel_lvds_destroy(struct drm_output *output)
|
|||
kfree(output->driver_private);
|
||||
}
|
||||
|
||||
static const struct drm_output_funcs intel_lvds_output_funcs = {
|
||||
.dpms = intel_lvds_dpms,
|
||||
.save = intel_lvds_save,
|
||||
.restore = intel_lvds_restore,
|
||||
.mode_valid = intel_lvds_mode_valid,
|
||||
static const struct drm_output_helper_funcs intel_lvds_helper_funcs = {
|
||||
.mode_fixup = intel_lvds_mode_fixup,
|
||||
.prepare = intel_lvds_prepare,
|
||||
.mode_set = intel_lvds_mode_set,
|
||||
.commit = intel_lvds_commit,
|
||||
};
|
||||
|
||||
static const struct drm_output_funcs intel_lvds_output_funcs = {
|
||||
.dpms = intel_lvds_dpms,
|
||||
.save = intel_lvds_save,
|
||||
.restore = intel_lvds_restore,
|
||||
.detect = intel_lvds_detect,
|
||||
.get_modes = intel_lvds_get_modes,
|
||||
.cleanup = intel_lvds_destroy
|
||||
.cleanup = intel_lvds_destroy,
|
||||
.mode_valid = intel_lvds_mode_valid,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -375,6 +378,7 @@ void intel_lvds_init(struct drm_device *dev)
|
|||
}
|
||||
|
||||
intel_output->type = INTEL_OUTPUT_LVDS;
|
||||
drm_output_helper_add(output, &intel_lvds_helper_funcs);
|
||||
output->driver_private = intel_output;
|
||||
output->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
output->interlace_allowed = FALSE;
|
||||
|
|
|
@ -972,18 +972,21 @@ static void intel_sdvo_destroy(struct drm_output *output)
|
|||
}
|
||||
}
|
||||
|
||||
static const struct drm_output_funcs intel_sdvo_output_funcs = {
|
||||
.dpms = intel_sdvo_dpms,
|
||||
.save = intel_sdvo_save,
|
||||
.restore = intel_sdvo_restore,
|
||||
.mode_valid = intel_sdvo_mode_valid,
|
||||
static const struct drm_output_helper_funcs intel_sdvo_helper_funcs = {
|
||||
.mode_fixup = intel_sdvo_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_sdvo_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
};
|
||||
|
||||
static const struct drm_output_funcs intel_sdvo_output_funcs = {
|
||||
.dpms = intel_sdvo_dpms,
|
||||
.save = intel_sdvo_save,
|
||||
.restore = intel_sdvo_restore,
|
||||
.detect = intel_sdvo_detect,
|
||||
.get_modes = intel_sdvo_get_modes,
|
||||
.cleanup = intel_sdvo_destroy
|
||||
.cleanup = intel_sdvo_destroy,
|
||||
.mode_valid = intel_sdvo_mode_valid,
|
||||
};
|
||||
|
||||
void intel_sdvo_init(struct drm_device *dev, int output_device)
|
||||
|
@ -1010,6 +1013,7 @@ void intel_sdvo_init(struct drm_device *dev, int output_device)
|
|||
|
||||
sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
|
||||
intel_output->type = INTEL_OUTPUT_SDVO;
|
||||
drm_output_helper_add(output, &intel_sdvo_helper_funcs);
|
||||
output->driver_private = intel_output;
|
||||
output->interlace_allowed = 0;
|
||||
output->doublescan_allowed = 0;
|
||||
|
|
|
@ -1590,15 +1590,18 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_output_helper_funcs intel_tv_helper_funcs = {
|
||||
.mode_fixup = intel_tv_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_tv_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
};
|
||||
|
||||
static const struct drm_output_funcs intel_tv_output_funcs = {
|
||||
.dpms = intel_tv_dpms,
|
||||
.save = intel_tv_save,
|
||||
.restore = intel_tv_restore,
|
||||
.mode_valid = intel_tv_mode_valid,
|
||||
.mode_fixup = intel_tv_mode_fixup,
|
||||
.prepare = intel_output_prepare,
|
||||
.mode_set = intel_tv_mode_set,
|
||||
.commit = intel_output_commit,
|
||||
.detect = intel_tv_detect,
|
||||
.get_modes = intel_tv_get_modes,
|
||||
.cleanup = intel_tv_destroy,
|
||||
|
@ -1674,6 +1677,7 @@ intel_tv_init(struct drm_device *dev)
|
|||
|
||||
tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL);
|
||||
|
||||
drm_output_helper_add(output, &intel_tv_helper_funcs);
|
||||
output->driver_private = intel_output;
|
||||
output->interlace_allowed = FALSE;
|
||||
output->doublescan_allowed = FALSE;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
#include "intel_bios.h"
|
||||
#include "intel_drv.h"
|
||||
|
||||
/**
|
||||
* i915_probe_agp - get AGP bootup configuration
|
||||
|
@ -232,7 +233,7 @@ int i915_load_modeset_init(struct drm_device *dev)
|
|||
}
|
||||
|
||||
intel_modeset_init(dev);
|
||||
drm_initial_config(dev, false);
|
||||
drm_helper_initial_config(dev, false);
|
||||
|
||||
drm_mm_print(&dev->bm.man[DRM_BO_MEM_VRAM].manager, "VRAM");
|
||||
drm_mm_print(&dev->bm.man[DRM_BO_MEM_TT].manager, "TT");
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "i915_drm.h"
|
||||
#include "i915_drv.h"
|
||||
#include "intel_drv.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
#define MAX_NOPID ((u32)~0)
|
||||
|
||||
|
@ -471,8 +472,8 @@ static void i915_hotplug_tv(struct drm_device *dev)
|
|||
goto unlock;
|
||||
|
||||
status = output->funcs->detect(output);
|
||||
drm_hotplug_stage_two(dev, output,
|
||||
status == output_status_connected ? 1 : 0);
|
||||
drm_helper_hotplug_stage_two(dev, output,
|
||||
status == output_status_connected ? 1 : 0);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
@ -497,7 +498,7 @@ static void i915_hotplug_crt(struct drm_device *dev, bool isconnected)
|
|||
if (iout == 0)
|
||||
goto unlock;
|
||||
|
||||
drm_hotplug_stage_two(dev, output, isconnected);
|
||||
drm_helper_hotplug_stage_two(dev, output, isconnected);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
@ -518,9 +519,9 @@ static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB)
|
|||
status = output->funcs->detect(output);
|
||||
|
||||
if (status != output_status_connected)
|
||||
drm_hotplug_stage_two(dev, output, false);
|
||||
drm_helper_hotplug_stage_two(dev, output, false);
|
||||
else
|
||||
drm_hotplug_stage_two(dev, output, true);
|
||||
drm_helper_hotplug_stage_two(dev, output, true);
|
||||
|
||||
intel_sdvo_set_hotplug(output, 1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue