52,9 → 52,36 |
* mode setting driver. They can be used mostly independantely from the crtc |
* helper functions used by many drivers to implement the kernel mode setting |
* interfaces. |
* |
* Initialization is done as a three-step process with drm_fb_helper_init(), |
* drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). |
* Drivers with fancier requirements than the default beheviour can override the |
* second step with their own code. Teardown is done with drm_fb_helper_fini(). |
* |
* At runtime drivers should restore the fbdev console by calling |
* drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They |
* should also notify the fb helper code from updates to the output |
* configuration by calling drm_fb_helper_hotplug_event(). For easier |
* integration with the output polling code in drm_crtc_helper.c the modeset |
* code proves a ->output_poll_changed callback. |
* |
* All other functions exported by the fb helper library can be used to |
* implement the fbdev driver interface by the driver. |
*/ |
|
/* simple single crtc case helper function */ |
/** |
* drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev |
* emulation helper |
* @fb_helper: fbdev initialized with drm_fb_helper_init |
* |
* This functions adds all the available connectors for use with the given |
* fb_helper. This is a separate step to allow drivers to freely assign |
* connectors to the fbdev, e.g. if some are reserved for special purposes or |
* not adequate to be used for the fbcon. |
* |
* Since this is part of the initial setup before the fbdev is published, no |
* locking is required. |
*/ |
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
{ |
struct drm_device *dev = fb_helper->dev; |
110,7 → 137,24 |
} |
|
|
static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) |
{ |
struct drm_device *dev = fb_helper->dev; |
struct drm_crtc *crtc; |
int bound = 0, crtcs_bound = 0; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb) |
crtcs_bound++; |
if (crtc->fb == fb_helper->fb) |
bound++; |
} |
|
if (bound < crtcs_bound) |
return false; |
return true; |
} |
|
static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) |
{ |
struct drm_fb_helper *fb_helper = info->par; |
120,9 → 164,20 |
int i, j; |
|
/* |
* fbdev->blank can be called from irq context in case of a panic. |
* Since we already have our own special panic handler which will |
* restore the fbdev console mode completely, just bail out early. |
*/ |
|
/* |
* For each CRTC in this fb, turn the connectors on/off. |
*/ |
mutex_lock(&dev->mode_config.mutex); |
drm_modeset_lock_all(dev); |
if (!drm_fb_helper_is_bound(fb_helper)) { |
drm_modeset_unlock_all(dev); |
return; |
} |
|
for (i = 0; i < fb_helper->crtc_count; i++) { |
crtc = fb_helper->crtc_info[i].mode_set.crtc; |
|
137,9 → 192,14 |
dev->mode_config.dpms_property, dpms_mode); |
} |
} |
mutex_unlock(&dev->mode_config.mutex); |
drm_modeset_unlock_all(dev); |
} |
|
/** |
* drm_fb_helper_blank - implementation for ->fb_blank |
* @blank: desired blanking state |
* @info: fbdev registered by the helper |
*/ |
int drm_fb_helper_blank(int blank, struct fb_info *info) |
{ |
switch (blank) { |
183,6 → 243,24 |
kfree(helper->crtc_info); |
} |
|
/** |
* drm_fb_helper_init - initialize a drm_fb_helper structure |
* @dev: drm device |
* @fb_helper: driver-allocated fbdev helper structure to initialize |
* @crtc_count: maximum number of crtcs to support in this fbdev emulation |
* @max_conn_count: max connector count |
* |
* This allocates the structures for the fbdev helper with the given limits. |
* Note that this won't yet touch the hardware (through the driver interfaces) |
* nor register the fbdev. This is only done in drm_fb_helper_initial_config() |
* to allow driver writes more control over the exact init sequence. |
* |
* Drivers must set fb_helper->funcs before calling |
* drm_fb_helper_initial_config(). |
* |
* RETURNS: |
* Zero if everything went ok, nonzero otherwise. |
*/ |
int drm_fb_helper_init(struct drm_device *dev, |
struct drm_fb_helper *fb_helper, |
int crtc_count, int max_conn_count) |
294,6 → 372,11 |
return 0; |
} |
|
/** |
* drm_fb_helper_setcmap - implementation for ->fb_setcmap |
* @cmap: cmap to set |
* @info: fbdev registered by the helper |
*/ |
int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
{ |
struct drm_fb_helper *fb_helper = info->par; |
333,6 → 416,11 |
} |
EXPORT_SYMBOL(drm_fb_helper_setcmap); |
|
/** |
* drm_fb_helper_check_var - implementation for ->fb_check_var |
* @var: screeninfo to check |
* @info: fbdev registered by the helper |
*/ |
int drm_fb_helper_check_var(struct fb_var_screeninfo *var, |
struct fb_info *info) |
{ |
425,13 → 513,19 |
} |
EXPORT_SYMBOL(drm_fb_helper_check_var); |
|
/* this will let fbcon do the mode init */ |
/** |
* drm_fb_helper_set_par - implementation for ->fb_set_par |
* @info: fbdev registered by the helper |
* |
* This will let fbcon do the mode init and is called at initialization time by |
* the fbdev core when registering the driver, and later on through the hotplug |
* callback. |
*/ |
int drm_fb_helper_set_par(struct fb_info *info) |
{ |
struct drm_fb_helper *fb_helper = info->par; |
struct drm_device *dev = fb_helper->dev; |
struct fb_var_screeninfo *var = &info->var; |
struct drm_crtc *crtc; |
int ret; |
int i; |
|
440,25 → 534,29 |
return -EINVAL; |
} |
|
mutex_lock(&dev->mode_config.mutex); |
drm_modeset_lock_all(dev); |
for (i = 0; i < fb_helper->crtc_count; i++) { |
crtc = fb_helper->crtc_info[i].mode_set.crtc; |
ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); |
ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); |
if (ret) { |
mutex_unlock(&dev->mode_config.mutex); |
drm_modeset_unlock_all(dev); |
return ret; |
} |
} |
mutex_unlock(&dev->mode_config.mutex); |
drm_modeset_unlock_all(dev); |
|
if (fb_helper->delayed_hotplug) { |
fb_helper->delayed_hotplug = false; |
// drm_fb_helper_hotplug_event(fb_helper); |
drm_fb_helper_hotplug_event(fb_helper); |
} |
return 0; |
} |
EXPORT_SYMBOL(drm_fb_helper_set_par); |
|
/** |
* drm_fb_helper_pan_display - implementation for ->fb_pan_display |
* @var: updated screen information |
* @info: fbdev registered by the helper |
*/ |
int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, |
struct fb_info *info) |
{ |
469,7 → 567,12 |
int ret = 0; |
int i; |
|
mutex_lock(&dev->mode_config.mutex); |
drm_modeset_lock_all(dev); |
if (!drm_fb_helper_is_bound(fb_helper)) { |
drm_modeset_unlock_all(dev); |
return -EBUSY; |
} |
|
for (i = 0; i < fb_helper->crtc_count; i++) { |
crtc = fb_helper->crtc_info[i].mode_set.crtc; |
|
479,7 → 582,7 |
modeset->y = var->yoffset; |
|
if (modeset->num_connectors) { |
ret = crtc->funcs->set_config(modeset); |
ret = drm_mode_set_config_internal(modeset); |
if (!ret) { |
info->var.xoffset = var->xoffset; |
info->var.yoffset = var->yoffset; |
486,15 → 589,20 |
} |
} |
} |
mutex_unlock(&dev->mode_config.mutex); |
drm_modeset_unlock_all(dev); |
return ret; |
} |
EXPORT_SYMBOL(drm_fb_helper_pan_display); |
|
int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, |
/* |
* Allocates the backing storage and sets up the fbdev info structure through |
* the ->fb_probe callback and then registers the fbdev and sets up the panic |
* notifier. |
*/ |
static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, |
int preferred_bpp) |
{ |
int new_fb = 0; |
int ret = 0; |
int crtc_count = 0; |
int i; |
struct fb_info *info; |
572,34 → 680,44 |
} |
|
/* push down into drivers */ |
new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); |
if (new_fb < 0) |
return new_fb; |
ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); |
if (ret < 0) |
return ret; |
|
info = fb_helper->fbdev; |
|
/* set the fb pointer */ |
/* |
* Set the fb pointer - usually drm_setup_crtcs does this for hotplug |
* events, but at init time drm_setup_crtcs needs to be called before |
* the fb is allocated (since we need to figure out the desired size of |
* the fb before we can allocate it ...). Hence we need to fix things up |
* here again. |
*/ |
for (i = 0; i < fb_helper->crtc_count; i++) |
if (fb_helper->crtc_info[i].mode_set.num_connectors) |
fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; |
|
if (new_fb) { |
|
info->var.pixclock = 0; |
|
// dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", |
// info->node, info->fix.id); |
|
} else { |
drm_fb_helper_set_par(info); |
} |
|
|
if (new_fb) |
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); |
|
return 0; |
} |
EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); |
|
/** |
* drm_fb_helper_fill_fix - initializes fixed fbdev information |
* @info: fbdev registered by the helper |
* @pitch: desired pitch |
* @depth: desired depth |
* |
* Helper to fill in the fixed fbdev information useful for a non-accelerated |
* fbdev emulations. Drivers which support acceleration methods which impose |
* additional constraints need to set up their own limits. |
* |
* Drivers should call this (or their equivalent setup code) from their |
* ->fb_probe callback. |
*/ |
void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
uint32_t depth) |
{ |
620,6 → 738,20 |
} |
EXPORT_SYMBOL(drm_fb_helper_fill_fix); |
|
/** |
* drm_fb_helper_fill_var - initalizes variable fbdev information |
* @info: fbdev instance to set up |
* @fb_helper: fb helper instance to use as template |
* @fb_width: desired fb width |
* @fb_height: desired fb height |
* |
* Sets up the variable fbdev metainformation from the given fb helper instance |
* and the drm framebuffer allocated in fb_helper->fb. |
* |
* Drivers should call this (or their equivalent setup code) from their |
* ->fb_probe callback after having allocated the fbdev backing |
* storage framebuffer. |
*/ |
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, |
uint32_t fb_width, uint32_t fb_height) |
{ |
937,6 → 1069,7 |
for (i = 0; i < fb_helper->crtc_count; i++) { |
modeset = &fb_helper->crtc_info[i].mode_set; |
modeset->num_connectors = 0; |
modeset->fb = NULL; |
} |
|
for (i = 0; i < fb_helper->connector_count; i++) { |
953,9 → 1086,21 |
modeset->mode = drm_mode_duplicate(dev, |
fb_crtc->desired_mode); |
modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; |
modeset->fb = fb_helper->fb; |
} |
} |
|
/* Clear out any old modes if there are no more connected outputs. */ |
for (i = 0; i < fb_helper->crtc_count; i++) { |
modeset = &fb_helper->crtc_info[i].mode_set; |
if (modeset->num_connectors == 0) { |
BUG_ON(modeset->fb); |
BUG_ON(modeset->num_connectors); |
if (modeset->mode) |
drm_mode_destroy(dev, modeset->mode); |
modeset->mode = NULL; |
} |
} |
out: |
kfree(crtcs); |
kfree(modes); |
963,18 → 1108,23 |
} |
|
/** |
* drm_helper_initial_config - setup a sane initial connector configuration |
* drm_fb_helper_initial_config - setup a sane initial connector configuration |
* @fb_helper: fb_helper device struct |
* @bpp_sel: bpp value to use for the framebuffer configuration |
* |
* LOCKING: |
* Called at init time by the driver to set up the @fb_helper initial |
* configuration, must take the mode config lock. |
* |
* Scans the CRTCs and connectors and tries 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. |
* |
* Note that this also registers the fbdev and so allows userspace to call into |
* the driver through the fbdev interfaces. |
* |
* This function will call down into the ->fb_probe callback to let |
* the driver allocate and initialize the fbdev info structure and the drm |
* framebuffer used to back the fbdev. drm_fb_helper_fill_var() and |
* drm_fb_helper_fill_fix() are provided as helpers to setup simple default |
* values for the fbdev info structure. |
* |
* RETURNS: |
* Zero if everything went ok, nonzero otherwise. |
*/ |
983,9 → 1133,6 |
struct drm_device *dev = fb_helper->dev; |
int count = 0; |
|
/* disable all the possible outputs/crtcs before entering KMS mode */ |
drm_helper_disable_unused_functions(fb_helper->dev); |
|
// drm_fb_helper_parse_command_line(fb_helper); |
|
count = drm_fb_helper_probe_connector_modes(fb_helper, |
1003,18 → 1150,22 |
} |
EXPORT_SYMBOL(drm_fb_helper_initial_config); |
|
#if 0 |
/** |
* drm_fb_helper_hotplug_event - respond to a hotplug notification by |
* probing all the outputs attached to the fb |
* @fb_helper: the drm_fb_helper |
* |
* LOCKING: |
* Called at runtime, must take mode config lock. |
* |
* Scan the connectors attached to the fb_helper and try to put together a |
* setup after *notification of a change in output configuration. |
* |
* Called at runtime, takes the mode config locks to be able to check/change the |
* modeset configuration. Must be run from process context (which usually means |
* either the output polling work or a work item launched from the driver's |
* hotplug interrupt). |
* |
* Note that the driver must ensure that this is only called _after_ the fb has |
* been fully set up, i.e. after the call to drm_fb_helper_initial_config. |
* |
* RETURNS: |
* 0 on success and a non-zero error code otherwise. |
*/ |
1023,23 → 1174,14 |
struct drm_device *dev = fb_helper->dev; |
int count = 0; |
u32 max_width, max_height, bpp_sel; |
int bound = 0, crtcs_bound = 0; |
struct drm_crtc *crtc; |
|
if (!fb_helper->fb) |
return 0; |
|
mutex_lock(&dev->mode_config.mutex); |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb) |
crtcs_bound++; |
if (crtc->fb == fb_helper->fb) |
bound++; |
} |
|
if (bound < crtcs_bound) { |
mutex_lock(&fb_helper->dev->mode_config.mutex); |
if (!drm_fb_helper_is_bound(fb_helper)) { |
fb_helper->delayed_hotplug = true; |
mutex_unlock(&dev->mode_config.mutex); |
mutex_unlock(&fb_helper->dev->mode_config.mutex); |
return 0; |
} |
DRM_DEBUG_KMS("\n"); |
1050,13 → 1192,16 |
|
count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, |
max_height); |
mutex_unlock(&fb_helper->dev->mode_config.mutex); |
|
drm_modeset_lock_all(dev); |
drm_setup_crtcs(fb_helper); |
mutex_unlock(&dev->mode_config.mutex); |
drm_modeset_unlock_all(dev); |
drm_fb_helper_set_par(fb_helper->fbdev); |
|
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); |
return 0; |
} |
EXPORT_SYMBOL(drm_fb_helper_hotplug_event); |
#endif |
|
|
|