From efcf066eff690887ace33c0f1192168a31115805 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2008 15:21:07 +1000 Subject: drm/modesetting: attempt to make fb code more sane --- linux-core/drm_crtc.c | 1 + linux-core/drm_crtc.h | 6 +- linux-core/intel_fb.c | 234 +++++++++++++++++++++++++++++++------------------- 3 files changed, 149 insertions(+), 92 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 4c6afd16..92a7d513 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -561,6 +561,7 @@ void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); INIT_LIST_HEAD(&dev->mode_config.fb_list); + INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); INIT_LIST_HEAD(&dev->mode_config.encoder_list); diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index c1d89ee8..043b4a57 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -498,8 +498,7 @@ struct drm_connector { * * This is used to set modes. */ -struct drm_mode_set -{ +struct drm_mode_set { struct list_head head; struct drm_framebuffer *fb; @@ -557,6 +556,9 @@ struct drm_mode_config { struct list_head property_list; + /* in-kernel framebuffers - hung of filp_head in drm_framebuffer */ + struct list_head fb_kernel_list; + /* currently in use generation id */ int current_generation; diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c index 9b2e9314..2355bf3d 100644 --- a/linux-core/intel_fb.c +++ b/linux-core/intel_fb.c @@ -585,6 +585,8 @@ int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height return -EINVAL; } + list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + intel_fb = to_intel_framebuffer(fb); *intel_fb_p = intel_fb; @@ -721,129 +723,181 @@ int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height return 0; } -int intelfb_probe(struct drm_device *dev) +#define INTELFB_CONN_LIMIT 4 + +static int intelfb_create_crtcmodesets(struct intel_framebuffer *intel_fb, int num_sets, int crtc_base) { - struct fb_info *info; + int i,j; + struct drm_device *dev = intel_fb->base.dev; struct intelfb_par *par; - struct device *device = &dev->pdev->dev; - struct intel_framebuffer *intel_fb; struct drm_mode_set *modeset; struct drm_crtc *crtc; - struct drm_connector *connector; - int ret; - int crtc_count = 0, i; - unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; - unsigned int surface_width = 0, surface_height = 0; - - DRM_DEBUG("\n"); - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->desired_mode) { - if (crtc->desired_mode->hdisplay < fb_width) - fb_width = crtc->desired_mode->hdisplay; + struct fb_info *info; - if (crtc->desired_mode->vdisplay < fb_height) - fb_height = crtc->desired_mode->vdisplay; + info = intel_fb->base.fbdev; + par = info->par; - if (crtc->desired_mode->hdisplay > surface_width) - surface_width = crtc->desired_mode->hdisplay; + for (i = 0; i < num_sets; i++) { + modeset = kzalloc(sizeof(struct drm_mode_set) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); + if (!modeset) + goto fail; - if (crtc->desired_mode->vdisplay > surface_height) - surface_height = crtc->desired_mode->vdisplay; + /* attach a CRTC to the modeset */ + j = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (j == i + crtc_base) { + modeset->crtc = crtc; + break; + } + j++; } - crtc_count++; + modeset->fb = &intel_fb->base; + + modeset->connectors = (struct drm_connector **)(modeset + 1); + list_add_tail(&modeset->head, &par->mode_set_list); } - if (fb_width == -1 || fb_height == -1) { - return -EINVAL; +fail: + list_for_each_entry(modeset, &par->mode_set_list, head) { + list_del(&modeset->head); + kfree(modeset); } - DRM_DEBUG("here %d %d %d %d\n", fb_width, fb_height, surface_width, surface_height); - ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); - if (ret) - return -EINVAL; + return -ENOMEM; +} - DRM_DEBUG("here %p\n", intel_fb); - info = intel_fb->base.fbdev; - par = info->par; +static int intelfb_single_fb_probe(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_connector *connector; + unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; + unsigned int surface_width = 0, surface_height = 0; + int new_fb = 0; + int crtc_count = 0; + int ret, i, conn_count = 0; + struct intel_framebuffer *intel_fb; + struct fb_info *info; + struct intelfb_par *par; + struct drm_mode_set *modeset; - DRM_DEBUG("crtc count is %d\n", crtc_count); - /* make up a couple of config sets */ - for (i = 0; i < crtc_count; i++) { - int conn_count = 0; + /* first up get a count of crtcs now in use and new min/maxes width/heights */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (drm_helper_crtc_in_use(crtc)) { + if (crtc->desired_mode) { + if (crtc->desired_mode->hdisplay < fb_width) + fb_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay < fb_height) + fb_height = crtc->desired_mode->vdisplay; + + if (crtc->desired_mode->hdisplay > surface_width) + surface_width = crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay > surface_height) + surface_height = crtc->desired_mode->vdisplay; + } + crtc_count++; + } + } - modeset = kzalloc(sizeof(struct drm_mode_set), GFP_KERNEL); - modeset->fb = &intel_fb->base; + if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { + /* hmm everyone went away - assume VGA cable just fell out + and will come back later. */ + return 0; + } - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (intel_crtc->pipe == i) - modeset->crtc = crtc; + /* do we have an fb already? */ + if (list_empty(&dev->mode_config.fb_kernel_list)) { + /* create an fb if we don't have one */ + ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb); + if (ret) + return -EINVAL; + + /* create a set per crtc connected to this fb */ + ret = intelfb_create_crtcmodesets(intel_fb, dev->mode_config.num_crtc, 0); + if (ret) + return ret; + new_fb = 1; + } else { + struct drm_framebuffer *fb; + fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head); + intel_fb = to_intel_framebuffer(fb); + + /* check if we are going to have a size issue */ + /* do a surface compare */ + if ((fb->width < surface_width) || (fb->height < surface_height)) { + DRM_ERROR("NEED BIGGER SURFACE DUDE\n"); + return -EINVAL; } + } + info = intel_fb->base.fbdev; + par = info->par; - DRM_DEBUG("1\n"); - /* find out how many connectors are on this */ + /* okay we need to setup new connector sets in the crtcs */ + list_for_each_entry(modeset, &par->mode_set_list, head) { + + conn_count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->encoder) - if (connector->encoder->crtc == modeset->crtc) + if(connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = connector; conn_count++; + if (conn_count > INTELFB_CONN_LIMIT) + BUG(); + } } - list_add_tail(&modeset->head, &par->mode_set_list); - - if (!conn_count) - continue; - - DRM_DEBUG("cc %d\n", conn_count); - modeset->connectors = kcalloc(conn_count, sizeof(struct drm_connector *), GFP_KERNEL); - if (!modeset->connectors) { - ret = -ENOMEM; - goto fail; - } + for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; modeset->num_connectors = conn_count; - - conn_count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder) - if (connector->encoder->crtc == modeset->crtc) - modeset->connectors[conn_count++] = connector; - } - modeset->mode = modeset->crtc->desired_mode; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; } - - info->var.pixclock = -1; -#if 0 - info->var.right_margin = mode->hsync_start - mode->hdisplay; - info->var.hsync_len = mode->hsync_end - mode->hsync_start; - info->var.left_margin = mode->htotal - mode->hsync_end; - info->var.lower_margin = mode->vsync_start - mode->vdisplay; - info->var.vsync_len = mode->vsync_end - mode->vsync_start; - info->var.upper_margin = mode->vtotal - mode->vsync_end; - info->var.pixclock = KHZ2PICOS(mode->clock); - if (mode->flags & V_PHSYNC) - info->var.sync |= FB_SYNC_HOR_HIGH_ACT; + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + return 0; +} + +int intelfb_probe(struct drm_device *dev) +{ + int ret; - if (mode->flags & V_PVSYNC) - info->var.sync |= FB_SYNC_VERT_HIGH_ACT; + DRM_DEBUG("\n"); - if (mode->flags & V_INTERLACE) - info->var.vmode = FB_VMODE_INTERLACED; - else if (mode->flags & V_DBLSCAN) - info->var.vmode = FB_VMODE_DOUBLE; - else - info->var.vmode = FB_VMODE_NONINTERLACED; + /* something has changed in the lower levels of hell - deal with it + here */ + + /* two modes : a) 1 fb to rule all crtcs. + b) one fb per crtc. + two actions 1) new connected device + 2) device removed. + case a/1 : if the fb surface isn't big enough - resize the surface fb. + if the fb size isn't big enough - resize fb into surface. + if everything big enough configure the new crtc/etc. + case a/2 : undo the configuration + possibly resize down the fb to fit the new configuration. + case b/1 : see if it is on a new crtc - setup a new fb and add it. + case b/2 : teardown the new fb. + */ -#endif + /* mode a first */ + /* search for an fb */ + if (i915_fbpercrtc == 1) { + ret = -EINVAL; + goto fail; + } - if (register_framebuffer(info) < 0) - return -EINVAL; + ret = intelfb_single_fb_probe(dev); - printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, - info->fix.id); - return 0; fail: /* TODO */ return ret; -- cgit v1.2.3