drm/modesetting: attempt to make fb code more sane
authorDave Airlie <airlied@redhat.com>
Thu, 5 Jun 2008 05:21:07 +0000 (15:21 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 5 Jun 2008 05:21:07 +0000 (15:21 +1000)
linux-core/drm_crtc.c
linux-core/drm_crtc.h
linux-core/intel_fb.c

index 4c6afd1..92a7d51 100644 (file)
@@ -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);
index c1d89ee..043b4a5 100644 (file)
@@ -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;
 
index 9b2e931..2355bf3 100644 (file)
@@ -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;