summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
authorAlan Hourihane <alanh@tungstengraphics.com>2008-03-11 20:29:37 +0000
committerAlan Hourihane <alanh@tungstengraphics.com>2008-03-11 20:30:25 +0000
commit903d9231d6f998657cc80ee6f20ded4df68e691b (patch)
tree594b3c4465fb938e4baf145930662eff2a13cf83 /linux-core
parent5a7f4b3074d5cda909fc7329bc91da11d89181e1 (diff)
Add support for monitor hotplug signals/waits
Also adjust i915 irq handling as it follows the 16bit'ism's of the i8xx series.
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drmP.h20
-rw-r--r--linux-core/drm_crtc.c15
-rw-r--r--linux-core/drm_drv.c1
-rw-r--r--linux-core/drm_irq.c166
-rw-r--r--linux-core/i915_fence.c4
-rw-r--r--linux-core/intel_crt.c15
-rw-r--r--linux-core/intel_sdvo.c14
7 files changed, 182 insertions, 53 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;