summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2008-06-05 15:21:07 +1000
committerDave Airlie <airlied@redhat.com>2008-06-05 15:21:07 +1000
commitefcf066eff690887ace33c0f1192168a31115805 (patch)
tree541f47ebd3175c52ea53a10f1587c94c007d88fe
parentf73e54bbf0b97a8f5184ede64d4f263020d623ee (diff)
drm/modesetting: attempt to make fb code more sane
-rw-r--r--linux-core/drm_crtc.c1
-rw-r--r--linux-core/drm_crtc.h6
-rw-r--r--linux-core/intel_fb.c234
3 files changed, 149 insertions, 92 deletions
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;