Port pipe reservation code for load detection
authorJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Wed, 9 Apr 2008 21:07:55 +0000 (14:07 -0700)
committerJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Wed, 9 Apr 2008 21:13:38 +0000 (14:13 -0700)
TV out needs to do load detection, which means we have to find an
available pipe to use for the detection.  Port over the pipe reservation
code for this purpose.

linux-core/drm_crtc.h
linux-core/intel_display.c
linux-core/intel_drv.h

index ac0d2d5..52e5ab5 100644 (file)
@@ -622,6 +622,7 @@ 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 bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                       int x, int y);
+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,
index 0615c1c..13936ee 100644 (file)
@@ -567,6 +567,8 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
                break;
        }
+
+       intel_crtc->dpms_mode = mode;
 }
 
 static bool intel_crtc_lock(struct drm_crtc *crtc)
@@ -1097,6 +1099,129 @@ static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
        intel_crtc->lut_b[regno] = blue >> 8;
 }
 
+/**
+ * Get a pipe with a simple mode set on it for doing load-based monitor
+ * detection.
+ *
+ * It will be up to the load-detect code to adjust the pipe as appropriate for
+ * its requirements.  The pipe will be connected to no other outputs.
+ *
+ * Currently this code will only succeed if there is a pipe with no outputs
+ * configured for it.  In the future, it could choose to temporarily disable
+ * some outputs to free up a pipe for its use.
+ *
+ * \return crtc, or NULL if no pipes are available.
+ */
+    
+/* VESA 640x480x72Hz mode to set on the pipe */
+static struct drm_display_mode load_detect_mode = {
+       DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
+                704, 832, 0, 480, 489, 491, 520, 0, V_NHSYNC | V_NVSYNC),
+};
+
+struct drm_crtc *intel_get_load_detect_pipe(struct drm_output *output,
+                                           struct drm_display_mode *mode,
+                                           int *dpms_mode)
+{
+       struct drm_device *dev = output->dev;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_crtc *intel_crtc;
+       struct drm_crtc *possible_crtc;
+       struct drm_crtc *supported_crtc =NULL;
+       struct drm_crtc *crtc = NULL;
+       int i = 0;
+
+       /*
+        * Algorithm gets a little messy:
+        *   - if the output already has an assigned crtc, use it (but make
+        *     sure it's on first)
+        *   - try to find the first unused crtc that can drive this output,
+        *     and use that if we find one
+        *   - if there are no unused crtcs available, try to use the first
+        *     one we found that supports the output
+        */
+
+       /* See if we already have a CRTC for this output */
+       if (output->crtc) {
+               crtc = output->crtc;
+               /* Make sure the crtc and output are running */
+               intel_crtc = crtc->driver_private;
+               *dpms_mode = intel_crtc->dpms_mode;
+               if (intel_crtc->dpms_mode != DPMSModeOn) {
+                       crtc->funcs->dpms(crtc, DPMSModeOn);
+                       output->funcs->dpms(output, DPMSModeOn);
+               }
+               return crtc;
+       }
+
+       /* Find an unused one (if possible) */
+       list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+               i++;
+               if (!(output->possible_crtcs & (1 << i)))
+                       continue;
+               if (!possible_crtc->enabled) {
+                       crtc = possible_crtc;
+                       break;
+               }
+               if (!supported_crtc)
+                       supported_crtc = possible_crtc;
+       }
+
+       /*
+        * If we didn't find an unused CRTC, use the first available one
+        * that can drive this output.
+        */
+       if (!crtc) {
+               crtc = supported_crtc;
+               if (!crtc)
+                       return NULL;
+       }
+
+       output->crtc = crtc;
+       intel_output->load_detect_temp = TRUE;
+    
+       intel_crtc = crtc->driver_private;
+       *dpms_mode = intel_crtc->dpms_mode;
+
+       if (!crtc->enabled) {
+               if (!mode)
+                       mode = &load_detect_mode;
+               drm_crtc_set_mode(crtc, mode, 0, 0);
+       } else {
+               if (intel_crtc->dpms_mode != DPMSModeOn)
+                       crtc->funcs->dpms(crtc, DPMSModeOn);
+
+               /* Add this output to the crtc */
+               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);
+
+       return crtc;
+}
+
+void intel_release_load_detect_pipe(struct drm_output *output, int dpms_mode)
+{
+       struct drm_device *dev = output->dev;
+       struct intel_output *intel_output = output->driver_private;
+       struct drm_crtc *crtc = output->crtc;
+    
+       if (intel_output->load_detect_temp) {
+               output->crtc = NULL;
+               intel_output->load_detect_temp = FALSE;
+               crtc->enabled = drm_crtc_in_use(crtc);
+               drm_disable_unused_functions(dev);
+       }
+
+       /* Switch crtc and output back off if necessary */
+       if (crtc->enabled && dpms_mode != DPMSModeOn) {
+               if (output->crtc == crtc)
+                       output->funcs->dpms(output, dpms_mode);
+               crtc->funcs->dpms(crtc, dpms_mode);
+       }
+}
+
 /* Returns the clock of the currently programmed mode of the given pipe. */
 static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
 {
@@ -1246,6 +1371,7 @@ void intel_crtc_init(struct drm_device *dev, int pipe)
        }
 
        intel_crtc->cursor_addr = 0;
+       intel_crtc->dpms_mode = DPMSModeOff;
 
        crtc->driver_private = intel_crtc;
 }
@@ -1292,10 +1418,10 @@ static void intel_setup_outputs(struct drm_device *dev)
                intel_sdvo_init(dev, SDVOB);
                intel_sdvo_init(dev, SDVOC);
        }
-#if 0
+
        if (IS_I9XX(dev) && !IS_I915G(dev))
                intel_tv_init(dev);
-#endif
+
        list_for_each_entry(output, &dev->mode_config.output_list, head) {
                struct intel_output *intel_output = output->driver_private;
                int crtc_mask = 0, clone_mask = 0;
index 62e21a5..51c52c8 100644 (file)
@@ -58,6 +58,7 @@ struct intel_crtc {
        int plane;
        uint32_t cursor_addr;
        u8 lut_r[256], lut_g[256], lut_b[256];
+       int dpms_mode;
 };
 
 struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
@@ -78,6 +79,11 @@ extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
                                                    struct drm_crtc *crtc);
 extern void intel_wait_for_vblank(struct drm_device *dev);
 extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
+extern struct drm_crtc *intel_get_load_detect_pipe(struct drm_output *output,
+                                                  struct drm_display_mode *mode,
+                                                  int *dpms_mode);
+extern void intel_release_load_detect_pipe(struct drm_output *output,
+                                          int dpms_mode);
 
 extern struct drm_output* intel_sdvo_find(struct drm_device *dev, int sdvoB);
 extern int intel_sdvo_supports_hotplug(struct drm_output *output);