diff options
author | Alan Hourihane <alanh@tungstengraphics.com> | 2008-03-11 20:29:37 +0000 |
---|---|---|
committer | Alan Hourihane <alanh@tungstengraphics.com> | 2008-03-11 20:30:25 +0000 |
commit | 903d9231d6f998657cc80ee6f20ded4df68e691b (patch) | |
tree | 594b3c4465fb938e4baf145930662eff2a13cf83 | |
parent | 5a7f4b3074d5cda909fc7329bc91da11d89181e1 (diff) |
Add support for monitor hotplug signals/waits
Also adjust i915 irq handling as it follows the 16bit'ism's
of the i8xx series.
-rw-r--r-- | linux-core/drmP.h | 20 | ||||
-rw-r--r-- | linux-core/drm_crtc.c | 15 | ||||
-rw-r--r-- | linux-core/drm_drv.c | 1 | ||||
-rw-r--r-- | linux-core/drm_irq.c | 166 | ||||
-rw-r--r-- | linux-core/i915_fence.c | 4 | ||||
-rw-r--r-- | linux-core/intel_crt.c | 15 | ||||
-rw-r--r-- | linux-core/intel_sdvo.c | 14 | ||||
-rw-r--r-- | shared-core/drm.h | 34 | ||||
-rw-r--r-- | shared-core/i915_drv.h | 17 | ||||
-rw-r--r-- | shared-core/i915_irq.c | 246 |
10 files changed, 384 insertions, 148 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 47974856..7b21f8a1 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -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); diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index e0c85cef..0cd0ebd0 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -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); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 434789dd..09372c71 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -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), diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 6b740b18..230ef3cc 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -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. diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index 0f6cdeef..f392e8e6 100644 --- a/linux-core/i915_fence.c +++ b/linux-core/i915_fence.c @@ -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; } } diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index a9fb50a3..915e430d 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -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 diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 3887df00..a8441d8f 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -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; diff --git a/shared-core/drm.h b/shared-core/drm.h index 303a84b6..a1ebfb93 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -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) /*@}*/ diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index b8dfbc34..fb855933 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -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 * @{ diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index b9d137f4..4127383a 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -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); |