Add support for monitor hotplug signals/waits
authorAlan Hourihane <alanh@tungstengraphics.com>
Tue, 11 Mar 2008 20:29:37 +0000 (20:29 +0000)
committerAlan Hourihane <alanh@tungstengraphics.com>
Tue, 11 Mar 2008 20:30:25 +0000 (20:30 +0000)
Also adjust i915 irq handling as it follows the 16bit'ism's
of the i8xx series.

linux-core/drmP.h
linux-core/drm_crtc.c
linux-core/drm_drv.c
linux-core/drm_irq.c
linux-core/i915_fence.c
linux-core/intel_crt.c
linux-core/intel_sdvo.c
shared-core/drm.h
shared-core/i915_drv.h
shared-core/i915_irq.c

index 4797485..7b21f8a 100644 (file)
@@ -591,6 +591,13 @@ struct drm_vbl_sig {
        struct task_struct *task;
 };
 
+struct drm_hotplug_sig {
+       struct list_head head;
+       unsigned int counter;
+       struct siginfo info;
+       struct task_struct *task;
+};
+
 /* location of GART table */
 #define DRM_ATI_GART_MAIN 1
 #define DRM_ATI_GART_FB   2
@@ -867,6 +874,15 @@ struct drm_device {
 
        struct work_struct work;
 
+       /** \name HOTPLUG IRQ support */
+       /*@{ */
+       wait_queue_head_t hotplug_queue;        /**< HOTPLUG wait queue */
+       spinlock_t hotplug_lock;
+       struct list_head *hotplug_sigs;         /**< signal list to send on HOTPLUG */
+       atomic_t hotplug_signal_pending;        /* number of signals pending on all crtcs*/
+
+       /*@} */
+
        /** \name VBLANK IRQ support */
        /*@{ */
 
@@ -1193,13 +1209,17 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev);
 extern void drm_driver_irq_postinstall(struct drm_device *dev);
 extern void drm_driver_irq_uninstall(struct drm_device *dev);
 
+extern int drm_hotplug_init(struct drm_device *dev);
+extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);
 extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
 extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp);
+extern int drm_wait_hotplug(struct drm_device *dev, void *data, struct drm_file *filp);
 extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);
 extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*));
 extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
 extern void drm_update_vblank_count(struct drm_device *dev, int crtc);
 extern void drm_handle_vblank(struct drm_device *dev, int crtc);
+extern void drm_handle_hotplug(struct drm_device *dev);
 extern int drm_vblank_get(struct drm_device *dev, int crtc);
 extern void drm_vblank_put(struct drm_device *dev, int crtc);
 
index e0c85ce..0cd0ebd 100644 (file)
@@ -947,6 +947,7 @@ static void drm_pick_crtcs (struct drm_device *dev)
                                                if (drm_mode_equal (modes, modes_equal)) {
                                                        if ((output->possible_clones & output_equal->possible_clones) && (output_equal->crtc == crtc)) {
                                                                printk("Cloning %s (0x%lx) to %s (0x%lx)\n",drm_get_output_name(output),output->possible_clones,drm_get_output_name(output_equal),output_equal->possible_clones);
+                                                               des_mode = modes;
                                                                assigned = 0;
                                                                goto clone;
                                                        }
@@ -1180,7 +1181,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info,
  * @output hotpluged output
  *
  * LOCKING.
- * Caller must hold mode config lock, function might grap struct lock.
+ * Caller must hold mode config lock, function might grab struct lock.
  *
  * Stage two of a hotplug.
  *
@@ -1192,10 +1193,11 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
 {
        int has_config = 0;
 
+       dev->mode_config.hotplug_counter++;
+
        /* We might want to do something more here */
        if (!connected) {
                DRM_DEBUG("not connected\n");
-               dev->mode_config.hotplug_counter++;
                return 0;
        }
 
@@ -1211,10 +1213,10 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
 
        if (!output->crtc || !output->crtc->desired_mode) {
                DRM_DEBUG("could not find a desired mode or crtc for output\n");
-               goto out_err;
+               return 1;
        }
 
-       /* We should realy check if there is a fb using this crtc */
+       /* We should really check if there is a fb using this crtc */
        if (!has_config)
                dev->driver->fb_probe(dev, output->crtc);
        else {
@@ -1226,12 +1228,7 @@ int drm_hotplug_stage_two(struct drm_device *dev, struct drm_output *output,
 
        drm_disable_unused_functions(dev);
 
-       dev->mode_config.hotplug_counter++;
        return 0;
-
-out_err:
-       dev->mode_config.hotplug_counter++;
-       return 1;
 }
 EXPORT_SYMBOL(drm_hotplug_stage_two);
 
index 434789d..09372c7 100644 (file)
@@ -136,6 +136,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_ROOT_ONLY | DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_HOTPLUG, drm_mode_hotplug_ioctl, DRM_CONTROL_ALLOW),
+       DRM_IOCTL_DEF(DRM_IOCTL_WAIT_HOTPLUG, drm_wait_hotplug, 0),
 
        DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl,
                      DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
index 6b740b1..230ef3c 100644 (file)
@@ -90,32 +90,41 @@ static void vblank_disable_fn(unsigned long arg)
 
 static void drm_vblank_cleanup(struct drm_device *dev)
 {
-       /* Bail if the driver didn't call drm_vblank_init() */
-       if (dev->num_crtcs == 0)
-               return;
-
        del_timer(&dev->vblank_disable_timer);
 
        vblank_disable_fn((unsigned long)dev);
 
-       drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
+       if (dev->vbl_queue)
+           drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
+
+       if (dev->vbl_sigs)
+           drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
+
+       if (dev->_vblank_count)
+           drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
+
+       if (dev->vblank_refcount)
+           drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
+
+       if (dev->vblank_enabled)
+           drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
+
+       if (dev->last_vblank)
+           drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
                 DRM_MEM_DRIVER);
-       drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) *
+
+       if (dev->vblank_premodeset)
+           drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) *
                 dev->num_crtcs, DRM_MEM_DRIVER);
-       drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs,
-                DRM_MEM_DRIVER);
 
-       dev->num_crtcs = 0;
+       if (dev->vblank_offset)
+           drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs,
+                DRM_MEM_DRIVER);
 }
 
 int drm_vblank_init(struct drm_device *dev, int num_crtcs)
@@ -182,6 +191,82 @@ err:
 }
 EXPORT_SYMBOL(drm_vblank_init);
 
+int drm_wait_hotplug(struct drm_device *dev, void *data,
+                   struct drm_file *file_priv)
+{
+       union drm_wait_hotplug *hotplugwait = data;
+       struct timeval now;
+       int ret = 0;
+       unsigned int flags;
+
+       if ((!dev->irq) || (!dev->irq_enabled))
+               return -EINVAL;
+
+       flags = hotplugwait->request.type;
+
+       if (flags & _DRM_HOTPLUG_SIGNAL) {
+               unsigned long irqflags;
+               struct list_head *hotplug_sigs = dev->hotplug_sigs;
+               struct drm_hotplug_sig *hotplug_sig;
+
+               hotplug_sig = drm_calloc(1, sizeof(struct drm_hotplug_sig),
+                                    DRM_MEM_DRIVER);
+               if (!hotplug_sig)
+                       return -ENOMEM;
+
+               atomic_inc(&dev->hotplug_signal_pending);
+
+               hotplug_sig->info.si_signo = hotplugwait->request.signal;
+               hotplug_sig->task = current;
+               hotplug_sig->counter = 
+                       hotplugwait->reply.counter = 
+                                       dev->mode_config.hotplug_counter;
+
+               spin_lock_irqsave(&dev->hotplug_lock, irqflags);
+
+               list_add_tail(&hotplug_sig->head, hotplug_sigs);
+
+               spin_unlock_irqrestore(&dev->hotplug_lock, irqflags);
+       } else {
+               int cur_hotplug = dev->mode_config.hotplug_counter;
+
+               DRM_WAIT_ON(ret, dev->hotplug_queue, 3 * DRM_HZ,
+                               dev->mode_config.hotplug_counter > cur_hotplug);
+
+               do_gettimeofday(&now);
+
+               hotplugwait->reply.tval_sec = now.tv_sec;
+               hotplugwait->reply.tval_usec = now.tv_usec;
+               hotplugwait->reply.counter = dev->mode_config.hotplug_counter;
+       }
+
+       return ret;
+}
+
+static void drm_hotplug_cleanup(struct drm_device *dev)
+{
+       if (dev->hotplug_sigs)
+           drm_free(dev->hotplug_sigs, sizeof(*dev->hotplug_sigs),
+                DRM_MEM_DRIVER);
+}
+EXPORT_SYMBOL(drm_hotplug_cleanup);
+
+int drm_hotplug_init(struct drm_device *dev)
+{
+       spin_lock_init(&dev->hotplug_lock);
+       atomic_set(&dev->hotplug_signal_pending, 0);
+
+       dev->hotplug_sigs = drm_alloc(sizeof(struct list_head), DRM_MEM_DRIVER);
+       if (!dev->hotplug_sigs)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(dev->hotplug_sigs);
+       init_waitqueue_head(&dev->hotplug_queue);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_hotplug_init);
+
 /**
  * Install IRQ handler.
  *
@@ -277,6 +362,8 @@ int drm_irq_uninstall(struct drm_device * dev)
 
        drm_vblank_cleanup(dev);
 
+       drm_hotplug_cleanup(dev);
+
        dev->locked_tasklet_func = NULL;
 
        return 0;
@@ -530,7 +617,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        if (flags & _DRM_VBLANK_SIGNAL) {
                unsigned long irqflags;
                struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
-               struct drm_vbl_sig *vbl_sig;
+               struct drm_vbl_sig *vbl_sig, *tmp;
 
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
 
@@ -538,7 +625,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                 * for the same vblank sequence number; nothing to be done in
                 * that case
                 */
-               list_for_each_entry(vbl_sig, vbl_sigs, head) {
+               list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
                        if (vbl_sig->sequence == vblwait->request.sequence
                            && vbl_sig->info.si_signo ==
                            vblwait->request.signal
@@ -660,6 +747,53 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)
 EXPORT_SYMBOL(drm_handle_vblank);
 
 /**
+ * Send the HOTPLUG signals.
+ *
+ * \param dev DRM device.
+ *
+ * Sends a signal for each task in drm_device::hotplug_sigs and empties the list.
+ */
+static void drm_hotplug_send_signals(struct drm_device * dev)
+{
+       struct drm_hotplug_sig *hotplug_sig, *tmp;
+       struct list_head *hotplug_sigs;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->hotplug_lock, flags);
+
+       hotplug_sigs = dev->hotplug_sigs;
+
+       list_for_each_entry_safe(hotplug_sig, tmp, hotplug_sigs, head) {
+           hotplug_sig->info.si_code = hotplug_sig->counter;
+
+           send_sig_info(hotplug_sig->info.si_signo,
+                             &hotplug_sig->info, hotplug_sig->task);
+
+           list_del(&hotplug_sig->head);
+
+           drm_free(hotplug_sig, sizeof(*hotplug_sig),
+                        DRM_MEM_DRIVER);
+           atomic_dec(&dev->hotplug_signal_pending);
+       }
+
+       spin_unlock_irqrestore(&dev->hotplug_lock, flags);
+}
+
+/**
+ * drm_handle_hotplug - handle a hotplug event
+ * @dev: DRM device
+ * @crtc: where this event occurred
+ *
+ * Drivers should call this routine in their hotplug interrupt handlers.
+ */
+void drm_handle_hotplug(struct drm_device *dev)
+{
+       DRM_WAKEUP(&dev->hotplug_queue);
+       drm_hotplug_send_signals(dev);
+}
+EXPORT_SYMBOL(drm_handle_hotplug);
+
+/**
  * Tasklet wrapper function.
  *
  * \param data DRM device in disguise.
index 0f6cdee..f392e8e 100644 (file)
@@ -120,11 +120,11 @@ static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class,
 
                if (dev_priv->fence_irq_on &&
                    !(fc->waiting_types & DRM_FENCE_TYPE_EXE)) {
-                       i915_user_irq_off(dev_priv);
+                       i915_user_irq_off(dev);
                        dev_priv->fence_irq_on = 0;
                } else if (!dev_priv->fence_irq_on &&
                           (fc->waiting_types & DRM_FENCE_TYPE_EXE)) {
-                       i915_user_irq_on(dev_priv);
+                       i915_user_irq_on(dev);
                        dev_priv->fence_irq_on = 1;
                }
        }
index a9fb50a..915e430 100644 (file)
@@ -132,7 +132,7 @@ static void intel_crt_mode_set(struct drm_output *output,
 /**
  * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
  *
- * Only for I945G/GM.
+ * Not for i915G/i915GM
  *
  * \return TRUE if CRT is connected.
  * \return FALSE if CRT is disconnected.
@@ -142,7 +142,7 @@ static bool intel_crt_detect_hotplug(struct drm_output *output)
        struct drm_device *dev = output->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 temp;
-#if 1
+
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 
        temp = I915_READ(PORT_HOTPLUG_EN);
@@ -161,15 +161,6 @@ static bool intel_crt_detect_hotplug(struct drm_output *output)
                return true;
 
        return false;
-#else
-       temp = I915_READ(PORT_HOTPLUG_STAT);
-       DRM_DEBUG("HST 0x%08x\n", temp);
-
-       if (temp & (1 << 8) && temp & (1 << 9))
-               return true;
-
-       return false;
-#endif
 }
 
 static bool intel_crt_detect_ddc(struct drm_output *output)
@@ -187,7 +178,7 @@ static enum drm_output_status intel_crt_detect(struct drm_output *output)
 {
        struct drm_device *dev = output->dev;
        
-       if (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) {
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
                if (intel_crt_detect_hotplug(output))
                        return output_status_connected;
                else
index 3887df0..a8441d8 100644 (file)
@@ -53,7 +53,6 @@ struct intel_sdvo_priv {
        struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
        struct intel_sdvo_dtd save_output_dtd[16];
        u32 save_SDVOX;
-       int hotplug_enabled;
 };
 
 /**
@@ -72,14 +71,8 @@ void intel_sdvo_write_sdvox(struct drm_output *output, u32 val)
 
        if (sdvo_priv->output_device == SDVOB) {
                cval = I915_READ(SDVOC);
-
-               if (sdvo_priv->hotplug_enabled)
-                       bval = bval | (1 << 26);
        } else {
                bval = I915_READ(SDVOB);
-
-               if (sdvo_priv->hotplug_enabled)
-                       cval = cval | (1 << 26);
        }
        /*
         * Write the registers twice for luck. Sometimes,
@@ -933,8 +926,6 @@ int intel_sdvo_supports_hotplug(struct drm_output *output)
 
 void intel_sdvo_set_hotplug(struct drm_output *output, int on)
 {
-       struct intel_output *intel_output = output->driver_private;
-       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
        u8 response[2];
        u8 status;
 
@@ -942,15 +933,11 @@ void intel_sdvo_set_hotplug(struct drm_output *output, int on)
        intel_sdvo_read_response(output, &response, 2);
 
        if (on) {
-               sdvo_priv->hotplug_enabled = 1;
-
                intel_sdvo_write_cmd(output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
                status = intel_sdvo_read_response(output, &response, 2);
 
                intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
        } else {
-               sdvo_priv->hotplug_enabled = 0;
-
                response[0] = 0;
                response[1] = 0;
                intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
@@ -1074,7 +1061,6 @@ void intel_sdvo_init(struct drm_device *dev, int output_device)
        }
 
        sdvo_priv->output_device = output_device;
-       sdvo_priv->hotplug_enabled = 0;
        intel_output->i2c_bus = i2cbus;
        intel_output->dev_priv = sdvo_priv;
 
index 303a84b..a1ebfb9 100644 (file)
@@ -555,6 +555,39 @@ union drm_wait_vblank {
        struct drm_wait_vblank_reply reply;
 };
 
+/* Handle monitor hotplug.
+ *
+ * May want to extend this later to pass reply information which
+ * details the outputs which generated the hotplug event.
+ * Some chipsets can't determine that though, and we'd need to leave
+ * it to the higher levels to determine exactly what changed.
+ */
+enum drm_hotplug_seq_type {
+       _DRM_HOTPLUG_SIGNAL = 0x00000001, /**< Send signal instead of blocking */
+};
+
+struct drm_wait_hotplug_request {
+       enum drm_hotplug_seq_type type;
+       unsigned long signal;
+};
+
+struct drm_wait_hotplug_reply {
+       enum drm_hotplug_seq_type type;
+       unsigned int counter;
+       long tval_sec;
+       long tval_usec;
+};
+
+/**
+ * DRM_IOCTL_WAIT_HOTPLUG ioctl argument type.
+ *
+ * \sa drmWaitHotplug().
+ */
+union drm_wait_hotplug {
+       struct drm_wait_hotplug_request request;
+       struct drm_wait_hotplug_reply reply;
+};
+
 enum drm_modeset_ctl_cmd {
        _DRM_PRE_MODESET = 1,
        _DRM_POST_MODESET = 2,
@@ -1238,6 +1271,7 @@ struct drm_mode_hotplug {
 #define DRM_IOCTL_MODE_GETPROPERTY     DRM_IOWR(0xAB, struct drm_mode_get_property)
 #define DRM_IOCTL_MODE_CURSOR          DRM_IOWR(0xAC, struct drm_mode_cursor)
 #define DRM_IOCTL_MODE_HOTPLUG         DRM_IOWR(0xAD, struct drm_mode_hotplug)
+#define DRM_IOCTL_WAIT_HOTPLUG         DRM_IOWR(0xAE, union drm_wait_hotplug)
 
 /*@}*/
 
index b8dfbc3..fb85593 100644 (file)
@@ -307,8 +307,8 @@ extern void i915_disable_vblank(struct drm_device *dev, int crtc);
 extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
 extern int i915_vblank_swap(struct drm_device *dev, void *data,
                            struct drm_file *file_priv);
-extern void i915_user_irq_on(struct drm_i915_private *dev_priv);
-extern void i915_user_irq_off(struct drm_i915_private *dev_priv);
+extern void i915_user_irq_on(struct drm_device *dev);
+extern void i915_user_irq_off(struct drm_device *dev);
 
 /* i915_mem.c */
 extern int i915_mem_alloc(struct drm_device *dev, void *data,
@@ -536,6 +536,12 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 
 #define I915REG_PIPEASTAT      0x70024
 #define I915REG_PIPEBSTAT      0x71024
+
+#define I915_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
+#define I915_HOTPLUG_INTERRUPT_ENABLE  (1UL<<26)
+#define I915_HOTPLUG_CLEAR             (1UL<<10)
+#define I915_VBLANK_CLEAR              (1UL<<1)
+
 /*
  * The two pipe frame counter registers are not synchronized, so
  * reading a stable value is somewhat tricky. The following code 
@@ -567,9 +573,6 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define PIPE_PIXEL_MASK         0x00ffffff
 #define PIPE_PIXEL_SHIFT        0
 
-#define I915_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
-#define I915_VBLANK_CLEAR              (1UL<<1)
-
 #define GPIOA                  0x5010
 #define GPIOB                  0x5014
 #define GPIOC                  0x5018
@@ -1153,8 +1156,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define SDVOB_PCIE_CONCURRENCY                 (1 << 3)
 #define SDVO_DETECTED                          (1 << 2)
 /* Bits to be preserved when writing */
-#define SDVOB_PRESERVE_MASK                    ((1 << 17) | (1 << 16) | (1 << 14))
-#define SDVOC_PRESERVE_MASK                    (1 << 17)
+#define SDVOB_PRESERVE_MASK                    ((1 << 17) | (1 << 16) | (1 << 14) | (1 << 26))
+#define SDVOC_PRESERVE_MASK                    ((1 << 17) | (1 << 26))
 
 /** @defgroup LVDS
  * @{
index b9d137f..4127383 100644 (file)
@@ -34,7 +34,9 @@
 #include "intel_drv.h"
 
 #define USER_INT_FLAG (1<<1)
+#define EVENT_PIPEB_FLAG (1<<4)
 #define VSYNC_PIPEB_FLAG (1<<5)
+#define EVENT_PIPEA_FLAG (1<<6)
 #define VSYNC_PIPEA_FLAG (1<<7)
 #define HOTPLUG_FLAG (1 << 17)
 
@@ -158,13 +160,14 @@ static void i915_vblank_tasklet(struct drm_device *dev)
        int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
        unsigned counter[2];
        struct drm_drawable_info *drw;
-       struct drm_i915_sarea *sarea_priv;
+       struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv;
        u32 cpp = dev_priv->cpp,  offsets[3];
        u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
                                XY_SRC_COPY_BLT_WRITE_ALPHA |
                                XY_SRC_COPY_BLT_WRITE_RGB)
                             : XY_SRC_COPY_BLT_CMD;
-       u32 pitchropcpp;
+       u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) |
+                                       (cpp << 23) | (1 << 24);
        RING_LOCALS;
 
        counter[0] = drm_vblank_count(dev, 0);
@@ -434,7 +437,7 @@ static struct drm_device *hotplug_dev;
 static int hotplug_cmd = 0;
 static spinlock_t hotplug_lock = SPIN_LOCK_UNLOCKED;
 
-static void i915_hotplug_crt(struct drm_device *dev, bool connected)
+static void i915_hotplug_crt(struct drm_device *dev, bool isconnected)
 {
        struct drm_output *output;
        struct intel_output *iout;
@@ -453,7 +456,7 @@ static void i915_hotplug_crt(struct drm_device *dev, bool connected)
        if (iout == 0)
                goto unlock;
 
-       drm_hotplug_stage_two(dev, output, connected);
+       drm_hotplug_stage_two(dev, output, isconnected);
 
 unlock:
        mutex_unlock(&dev->mode_config.mutex);
@@ -468,10 +471,8 @@ static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB)
 
        output = intel_sdvo_find(dev, sdvoB);
 
-       if (!output) {
-               DRM_ERROR("could not find sdvo%s output\n", sdvoB ? "B" : "C");
+       if (!output)
                goto unlock;
-       }
 
        status = output->funcs->detect(output);
 
@@ -480,7 +481,6 @@ static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB)
        else
                drm_hotplug_stage_two(dev, output, true);
 
-       /* wierd hw bug, sdvo stop sending interupts */
        intel_sdvo_set_hotplug(output, 1);
 
 unlock:
@@ -521,6 +521,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
        if (sdvoC)
                i915_hotplug_sdvo(dev, 0);
 
+       drm_handle_hotplug(dev);
 }
 
 static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat)
@@ -575,45 +576,21 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
        struct drm_i915_master_private *master_priv;
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        u32 temp = 0;
-       u32 temp2;
        u32 pipea_stats, pipeb_stats;
 
-       pipea_stats = I915_READ(I915REG_PIPEASTAT);
-       pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
-
-       /* On i8xx hw the IIR and IER are 16bit on i9xx its 32bit */
-       if (IS_I9XX(dev))
+       /* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
                temp = I915_READ(I915REG_INT_IDENTITY_R);
        else
                temp = I915_READ16(I915REG_INT_IDENTITY_R);
 
-       temp2 = temp;
        temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG);
 
-#if 0
-       /* ugly despamification of pipeb event irq */
-       if (temp & (0xFFFFFFF ^ ((1 << 5) | (1 << 7)))) {
-               DRM_DEBUG("IIR %08x\n", temp2);
-               DRM_DEBUG("MSK %08x\n", dev_priv->irq_enable_reg | USER_INT_FLAG);
-               DRM_DEBUG("M&I %08x\n", temp);
-               DRM_DEBUG("HOT %08x\n", I915_READ(PORT_HOTPLUG_STAT));
-       }
-#else
-#if 0
-       DRM_DEBUG("flag=%08x\n", temp);
-#endif
-#endif
-
        if (temp == 0)
                return IRQ_NONE;
 
-       if (IS_I9XX(dev)) {
-               I915_WRITE(I915REG_INT_IDENTITY_R, temp);
-               (void) I915_READ(I915REG_INT_IDENTITY_R);
-       } else {
-               I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
-               (void) I915_READ16(I915REG_INT_IDENTITY_R);
-       }
+       pipea_stats = I915_READ(I915REG_PIPEASTAT);
+       pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
 
        /*
         * Clear the PIPE(A|B)STAT regs before the IIR otherwise
@@ -621,25 +598,40 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
         */
        if (temp & VSYNC_PIPEA_FLAG) {
                drm_handle_vblank(dev, i915_get_plane(dev, 0));
-               I915_WRITE(I915REG_PIPEASTAT,
-                          pipea_stats | I915_VBLANK_INTERRUPT_ENABLE |
-                          I915_VBLANK_CLEAR);
+
+               pipea_stats |= I915_VBLANK_INTERRUPT_ENABLE |
+                              I915_VBLANK_CLEAR;
        }
 
        if (temp & VSYNC_PIPEB_FLAG) {
                drm_handle_vblank(dev, i915_get_plane(dev, 1));
-               I915_WRITE(I915REG_PIPEBSTAT,
-                          pipeb_stats | I915_VBLANK_INTERRUPT_ENABLE |
-                          I915_VBLANK_CLEAR);
+
+               pipeb_stats |= I915_VBLANK_INTERRUPT_ENABLE |
+                              I915_VBLANK_CLEAR;
        }
 
-       I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
-       (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */
+       if (temp & EVENT_PIPEA_FLAG)
+               pipea_stats |= I915_HOTPLUG_INTERRUPT_ENABLE |
+                              I915_HOTPLUG_CLEAR;
 
-       DRM_READMEMORYBARRIER();
+       if (temp & EVENT_PIPEB_FLAG)
+               pipeb_stats |= I915_HOTPLUG_INTERRUPT_ENABLE |
+                              I915_HOTPLUG_CLEAR;
+
+       I915_WRITE(I915REG_PIPEASTAT, pipea_stats);
+       (void) I915_READ(I915REG_PIPEASTAT);
+       I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats);
+       (void) I915_READ(I915REG_PIPEBSTAT);
+
+       /* Clear the generated interrupt */
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+               I915_WRITE(I915REG_INT_IDENTITY_R, temp);
+               (void) I915_READ(I915REG_INT_IDENTITY_R);
+       } else {
+               I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
+               (void) I915_READ16(I915REG_INT_IDENTITY_R);
+       }
 
-       temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG |
-                VSYNC_PIPEB_FLAG);
 
        if (dev->primary->master) {
                master_priv = dev->primary->master->driver_priv;
@@ -658,15 +650,43 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                        drm_locked_tasklet(dev, i915_vblank_tasklet);
        }
 
-       /* for now lest just ack it */
-       if (temp & (1 << 17)) {
-               DRM_DEBUG("Hotplug event received\n");
+       if (temp & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) {
+               u32 temp2 = 0;
 
-               temp2 = I915_READ(PORT_HOTPLUG_STAT);
+               DRM_INFO("Hotplug event received\n");
 
-               i915_run_hotplug_tasklet(dev, temp2);
+               if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) {
+#if 0
+                       u32 b,c;
+
+                       b = I915_READ(SDVOB) & SDVO_PIPE_B_SELECT;
+                       c = I915_READ(SDVOC) & SDVO_PIPE_B_SELECT;
+
+                       if (temp & EVENT_PIPEA_FLAG) {
+                               if (!b)
+                                       temp2 |= SDVOB_HOTPLUG_INT_STATUS;
+                               if (!c)
+                                       temp2 |= SDVOC_HOTPLUG_INT_STATUS;
+                                       
+                       }
+
+                       if (temp & EVENT_PIPEB_FLAG) {
+                               if (b)
+                                       temp2 |= SDVOB_HOTPLUG_INT_STATUS;
+                               if (c)
+                                       temp2 |= SDVOC_HOTPLUG_INT_STATUS;
+                                       
+                       }
+#else
+                       temp2 |= SDVOB_HOTPLUG_INT_STATUS |
+                                SDVOC_HOTPLUG_INT_STATUS;
+#endif
+               } else {
+                       temp2 = I915_READ(PORT_HOTPLUG_STAT);
 
-               I915_WRITE(PORT_HOTPLUG_STAT,temp2);
+                       I915_WRITE(PORT_HOTPLUG_STAT, temp2);
+               }
+               i915_run_hotplug_tasklet(dev, temp2);
        }
 
        return IRQ_HANDLED;
@@ -691,23 +711,33 @@ int i915_emit_irq(struct drm_device *dev)
        return dev_priv->counter;
 }
 
-void i915_user_irq_on(struct drm_i915_private *dev_priv)
+void i915_user_irq_on(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
        if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
                dev_priv->irq_enable_reg |= USER_INT_FLAG;
-               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+               if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
+                       I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+               else
+                       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
        }
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
 }
                
-void i915_user_irq_off(struct drm_i915_private *dev_priv)
+void i915_user_irq_off(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
        if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
                //              dev_priv->irq_enable_reg &= ~USER_INT_FLAG;
-               //              I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+               //              if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
+               //                      I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+               //              else
+               //                      I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
        }
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 }
@@ -725,10 +755,10 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
        if (READ_BREADCRUMB(dev_priv) >= irq_nr)
                return 0;
 
-       i915_user_irq_on(dev_priv);
+       i915_user_irq_on(dev);
        DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
                    READ_BREADCRUMB(dev_priv) >= irq_nr);
-       i915_user_irq_off(dev_priv);
+       i915_user_irq_off(dev);
 
        if (ret == -EBUSY) {
                DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
@@ -803,7 +833,10 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
                break;
        }
 
-       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
+               I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       else
+               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
 
        return 0;
 }
@@ -826,7 +859,10 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                break;
        }
 
-       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
+               I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       else
+               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
 }
 
 void i915_enable_interrupt (struct drm_device *dev)
@@ -836,40 +872,62 @@ void i915_enable_interrupt (struct drm_device *dev)
 
        dev_priv->irq_enable_reg |= USER_INT_FLAG;
 
-       if (IS_I9XX(dev) && dev->mode_config.num_output) {
-               dev_priv->irq_enable_reg |= HOTPLUG_FLAG;
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+               if (dev->mode_config.num_output)
+                       dev_priv->irq_enable_reg |= HOTPLUG_FLAG;
+       } else {
+               if (dev->mode_config.num_output)
+                       dev_priv->irq_enable_reg |= EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG;
+
+               I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR);
+               I915_WRITE(I915REG_PIPEBSTAT, I915_READ(I915REG_PIPEBSTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR);
+       }
+
+       if (dev_priv->irq_enable_reg & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) {
+               u32 temp = 0;
 
-               /* Activate the CRT */
-               I915_WRITE(PORT_HOTPLUG_EN, CRT_HOTPLUG_INT_EN);
+               if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+                       temp = I915_READ(PORT_HOTPLUG_EN);
 
-               /* SDVOB */
-               o = intel_sdvo_find(dev, 1);
-               if (o && intel_sdvo_supports_hotplug(o)) {
-                       intel_sdvo_set_hotplug(o, 1);
-                       I915_WRITE(PORT_HOTPLUG_EN, SDVOB_HOTPLUG_INT_EN);
+                       /* Activate the CRT */
+                       temp |= CRT_HOTPLUG_INT_EN;
                }
 
-               /* SDVOC */
-               o = intel_sdvo_find(dev, 0);
-               if (o && intel_sdvo_supports_hotplug(o)) {
-                       intel_sdvo_set_hotplug(o, 1);
-                       I915_WRITE(PORT_HOTPLUG_EN, SDVOC_HOTPLUG_INT_EN);
+               if (IS_I9XX(dev)) {
+                       /* SDVOB */
+                       o = intel_sdvo_find(dev, 1);
+                       if (o && intel_sdvo_supports_hotplug(o)) {
+                               intel_sdvo_set_hotplug(o, 1);
+                               temp |= SDVOB_HOTPLUG_INT_EN;
+                       }
+
+                       /* SDVOC */
+                       o = intel_sdvo_find(dev, 0);
+                       if (o && intel_sdvo_supports_hotplug(o)) {
+                               intel_sdvo_set_hotplug(o, 1);
+                               temp |= SDVOC_HOTPLUG_INT_EN;
+                       }
+
+                       I915_WRITE(SDVOB, I915_READ(SDVOB) | SDVO_INTERRUPT_ENABLE);
+                       I915_WRITE(SDVOC, I915_READ(SDVOC) | SDVO_INTERRUPT_ENABLE);
+               } else {
+                       /* DVO ???? */
                }
 
+               if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+                       I915_WRITE(PORT_HOTPLUG_EN, temp);
+
+                       DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN));
+                       DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT));
+
+                       I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+               }
        }
 
-       if (IS_I9XX(dev)) {
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
                I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       } else {
+       else
                I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       }
-
-       DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN));
-       DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT));
-       DRM_DEBUG("IER %08x\n",I915_READ(I915REG_INT_ENABLE_R));
-       DRM_DEBUG("SDB %08x\n",I915_READ(SDVOB));
-
-       I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 
        dev_priv->irq_enabled = 1;
 }
@@ -909,7 +967,11 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       flag = I915_READ(I915REG_INT_ENABLE_R);
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
+               flag = I915_READ(I915REG_INT_ENABLE_R);
+       else
+               flag = I915_READ16(I915REG_INT_ENABLE_R);
+
        pipe->pipe = 0;
        if (flag & VSYNC_PIPEA_FLAG)
                pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
@@ -1086,8 +1148,10 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
 {
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
 
+       I915_WRITE(I915REG_PIPEASTAT, 0xffff);
+       I915_WRITE(I915REG_PIPEBSTAT, 0xffff);
        I915_WRITE16(I915REG_HWSTAM, 0xeffe);
-       if (IS_I9XX(dev)) {
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
                I915_WRITE(I915REG_INT_MASK_R, 0x0);
                I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
        } else {
@@ -1114,6 +1178,10 @@ int i915_driver_irq_postinstall(struct drm_device * dev)
        if (ret)
                return ret;
 
+       ret = drm_hotplug_init(dev);
+       if (ret)
+               return ret;
+
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
 
        i915_enable_interrupt(dev);
@@ -1138,7 +1206,9 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
        dev_priv->irq_enabled = 0;
 
 
-       if(IS_I9XX(dev)) {
+       I915_WRITE(I915REG_PIPEASTAT, 0xffff);
+       I915_WRITE(I915REG_PIPEBSTAT, 0xffff);
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
                I915_WRITE(I915REG_HWSTAM, 0xffffffff);
                I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
                I915_WRITE(I915REG_INT_ENABLE_R, 0x0);