Add support for monitor hotplug signals/waits

Also adjust i915 irq handling as it follows the 16bit'ism's
of the i8xx series.
main
Alan Hourihane 2008-03-11 20:29:37 +00:00
parent 5a7f4b3074
commit 903d9231d6
10 changed files with 396 additions and 160 deletions

View 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);

View 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);

View 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),

View 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,
DRM_MEM_DRIVER);
drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
DRM_MEM_DRIVER);
drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
dev->num_crtcs, DRM_MEM_DRIVER);
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,
DRM_MEM_DRIVER);
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,
if (dev->vbl_queue)
drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
DRM_MEM_DRIVER);
dev->num_crtcs = 0;
if (dev->vbl_sigs)
drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
DRM_MEM_DRIVER);
if (dev->_vblank_count)
drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
dev->num_crtcs, DRM_MEM_DRIVER);
if (dev->vblank_refcount)
drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
dev->num_crtcs, DRM_MEM_DRIVER);
if (dev->vblank_enabled)
drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
dev->num_crtcs, DRM_MEM_DRIVER);
if (dev->last_vblank)
drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
DRM_MEM_DRIVER);
if (dev->vblank_premodeset)
drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) *
dev->num_crtcs, DRM_MEM_DRIVER);
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
@ -659,6 +746,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.
*

View 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;
}
}

View 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

View 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;

View 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)
/*@}*/

View 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
* @{

View 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");
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_run_hotplug_tasklet(dev, temp2);
I915_WRITE(PORT_HOTPLUG_STAT,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;
/* Activate the CRT */
I915_WRITE(PORT_HOTPLUG_EN, CRT_HOTPLUG_INT_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);
}
/* 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)) {
I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
if (dev->mode_config.num_output)
dev_priv->irq_enable_reg |= HOTPLUG_FLAG;
} else {
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
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);
}
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));
if (dev_priv->irq_enable_reg & (HOTPLUG_FLAG | EVENT_PIPEA_FLAG | EVENT_PIPEB_FLAG)) {
u32 temp = 0;
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
temp = I915_READ(PORT_HOTPLUG_EN);
/* Activate the CRT */
temp |= CRT_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) && !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);
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);