Merge branch 'drm-intel-next' of ../anholt-2.6 into drm-linus

This commit is contained in:
Dave Airlie 2009-04-03 10:27:21 +10:00
commit 5f3dbedf27
16 changed files with 292 additions and 123 deletions

View File

@ -505,7 +505,6 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_local_map *map = NULL; struct drm_local_map *map = NULL;
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct drm_hash_item *hash; struct drm_hash_item *hash;
unsigned long prot;
int ret = 0; int ret = 0;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
@ -538,11 +537,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_ops = obj->dev->driver->gem_vm_ops; vma->vm_ops = obj->dev->driver->gem_vm_ops;
vma->vm_private_data = map->handle; vma->vm_private_data = map->handle;
/* FIXME: use pgprot_writecombine when available */ /* FIXME: use pgprot_writecombine when available */
prot = pgprot_val(vma->vm_page_prot); vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
#ifdef CONFIG_X86
prot |= _PAGE_CACHE_WC;
#endif
vma->vm_page_prot = __pgprot(prot);
/* Take a ref for this mapping of the object, so that the fault /* Take a ref for this mapping of the object, so that the fault
* handler can dereference the mmap offset's pointer to the object. * handler can dereference the mmap offset's pointer to the object.

View File

@ -451,6 +451,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
} }
EXPORT_SYMBOL(drm_sysfs_hotplug_event);
/** /**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver * drm_sysfs_device_add - adds a class device to sysfs for a character driver

View File

@ -922,7 +922,7 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
* Some of the preallocated space is taken by the GTT * Some of the preallocated space is taken by the GTT
* and popup. GTT is 1K per MB of aperture size, and popup is 4K. * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
*/ */
if (IS_G4X(dev)) if (IS_G4X(dev) || IS_IGD(dev))
overhead = 4096; overhead = 4096;
else else
overhead = (*aperture_size / 1024) + 4096; overhead = (*aperture_size / 1024) + 4096;
@ -1030,13 +1030,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret) if (ret)
goto destroy_ringbuffer; goto destroy_ringbuffer;
/* FIXME: re-add hotplug support */
#if 0
ret = drm_hotplug_init(dev);
if (ret)
goto destroy_ringbuffer;
#endif
/* Always safe in the mode setting case. */ /* Always safe in the mode setting case. */
/* FIXME: do pre/post-mode set stuff in core KMS code */ /* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = 1; dev->vblank_disable_allowed = 1;

View File

@ -159,6 +159,9 @@ typedef struct drm_i915_private {
u32 irq_mask_reg; u32 irq_mask_reg;
u32 pipestat[2]; u32 pipestat[2];
u32 hotplug_supported_mask;
struct work_struct hotplug_work;
int tex_lru_log_granularity; int tex_lru_log_granularity;
int allow_batchbuffer; int allow_batchbuffer;
struct mem_block *agp_heap; struct mem_block *agp_heap;
@ -297,6 +300,7 @@ typedef struct drm_i915_private {
* *
* A reference is held on the buffer while on this list. * A reference is held on the buffer while on this list.
*/ */
spinlock_t active_list_lock;
struct list_head active_list; struct list_head active_list;
/** /**
@ -810,6 +814,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \ #define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev))) IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev)) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024) #define PRIMARY_RINGBUFFER_SIZE (128*1024)

View File

@ -1072,6 +1072,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
case -EAGAIN: case -EAGAIN:
return VM_FAULT_OOM; return VM_FAULT_OOM;
case -EFAULT: case -EFAULT:
case -EINVAL:
return VM_FAULT_SIGBUS; return VM_FAULT_SIGBUS;
default: default:
return VM_FAULT_NOPAGE; return VM_FAULT_NOPAGE;
@ -1324,8 +1325,10 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno)
obj_priv->active = 1; obj_priv->active = 1;
} }
/* Move from whatever list we were on to the tail of execution. */ /* Move from whatever list we were on to the tail of execution. */
spin_lock(&dev_priv->mm.active_list_lock);
list_move_tail(&obj_priv->list, list_move_tail(&obj_priv->list,
&dev_priv->mm.active_list); &dev_priv->mm.active_list);
spin_unlock(&dev_priv->mm.active_list_lock);
obj_priv->last_rendering_seqno = seqno; obj_priv->last_rendering_seqno = seqno;
} }
@ -1467,6 +1470,7 @@ i915_gem_retire_request(struct drm_device *dev,
/* Move any buffers on the active list that are no longer referenced /* Move any buffers on the active list that are no longer referenced
* by the ringbuffer to the flushing/inactive lists as appropriate. * by the ringbuffer to the flushing/inactive lists as appropriate.
*/ */
spin_lock(&dev_priv->mm.active_list_lock);
while (!list_empty(&dev_priv->mm.active_list)) { while (!list_empty(&dev_priv->mm.active_list)) {
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv; struct drm_i915_gem_object *obj_priv;
@ -1481,7 +1485,7 @@ i915_gem_retire_request(struct drm_device *dev,
* this seqno. * this seqno.
*/ */
if (obj_priv->last_rendering_seqno != request->seqno) if (obj_priv->last_rendering_seqno != request->seqno)
return; goto out;
#if WATCH_LRU #if WATCH_LRU
DRM_INFO("%s: retire %d moves to inactive list %p\n", DRM_INFO("%s: retire %d moves to inactive list %p\n",
@ -1493,6 +1497,8 @@ i915_gem_retire_request(struct drm_device *dev,
else else
i915_gem_object_move_to_inactive(obj); i915_gem_object_move_to_inactive(obj);
} }
out:
spin_unlock(&dev_priv->mm.active_list_lock);
} }
/** /**
@ -1990,20 +1996,23 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
int regnum = obj_priv->fence_reg; int regnum = obj_priv->fence_reg;
uint32_t val; uint32_t val;
uint32_t pitch_val; uint32_t pitch_val;
uint32_t fence_size_bits;
if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || if ((obj_priv->gtt_offset & ~I830_FENCE_START_MASK) ||
(obj_priv->gtt_offset & (obj->size - 1))) { (obj_priv->gtt_offset & (obj->size - 1))) {
WARN(1, "%s: object 0x%08x not 1M or size aligned\n", WARN(1, "%s: object 0x%08x not 512K or size aligned\n",
__func__, obj_priv->gtt_offset); __func__, obj_priv->gtt_offset);
return; return;
} }
pitch_val = (obj_priv->stride / 128) - 1; pitch_val = (obj_priv->stride / 128) - 1;
WARN_ON(pitch_val & ~0x0000000f);
val = obj_priv->gtt_offset; val = obj_priv->gtt_offset;
if (obj_priv->tiling_mode == I915_TILING_Y) if (obj_priv->tiling_mode == I915_TILING_Y)
val |= 1 << I830_FENCE_TILING_Y_SHIFT; val |= 1 << I830_FENCE_TILING_Y_SHIFT;
val |= I830_FENCE_SIZE_BITS(obj->size); fence_size_bits = I830_FENCE_SIZE_BITS(obj->size);
WARN_ON(fence_size_bits & ~0x00000f00);
val |= fence_size_bits;
val |= pitch_val << I830_FENCE_PITCH_SHIFT; val |= pitch_val << I830_FENCE_PITCH_SHIFT;
val |= I830_FENCE_REG_VALID; val |= I830_FENCE_REG_VALID;
@ -2194,7 +2203,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
return -EBUSY; return -EBUSY;
if (alignment == 0) if (alignment == 0)
alignment = i915_gem_get_gtt_alignment(obj); alignment = i915_gem_get_gtt_alignment(obj);
if (alignment & (PAGE_SIZE - 1)) { if (alignment & (i915_gem_get_gtt_alignment(obj) - 1)) {
DRM_ERROR("Invalid object alignment requested %u\n", alignment); DRM_ERROR("Invalid object alignment requested %u\n", alignment);
return -EINVAL; return -EINVAL;
} }
@ -2211,15 +2220,20 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
} }
} }
if (obj_priv->gtt_space == NULL) { if (obj_priv->gtt_space == NULL) {
bool lists_empty;
/* If the gtt is empty and we're still having trouble /* If the gtt is empty and we're still having trouble
* fitting our object in, we're out of memory. * fitting our object in, we're out of memory.
*/ */
#if WATCH_LRU #if WATCH_LRU
DRM_INFO("%s: GTT full, evicting something\n", __func__); DRM_INFO("%s: GTT full, evicting something\n", __func__);
#endif #endif
if (list_empty(&dev_priv->mm.inactive_list) && spin_lock(&dev_priv->mm.active_list_lock);
list_empty(&dev_priv->mm.flushing_list) && lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.active_list)) { list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->mm.active_list));
spin_unlock(&dev_priv->mm.active_list_lock);
if (lists_empty) {
DRM_ERROR("GTT full, but LRU list empty\n"); DRM_ERROR("GTT full, but LRU list empty\n");
return -ENOMEM; return -ENOMEM;
} }
@ -3675,6 +3689,7 @@ i915_gem_idle(struct drm_device *dev)
i915_gem_retire_requests(dev); i915_gem_retire_requests(dev);
spin_lock(&dev_priv->mm.active_list_lock);
if (!dev_priv->mm.wedged) { if (!dev_priv->mm.wedged) {
/* Active and flushing should now be empty as we've /* Active and flushing should now be empty as we've
* waited for a sequence higher than any pending execbuffer * waited for a sequence higher than any pending execbuffer
@ -3701,6 +3716,7 @@ i915_gem_idle(struct drm_device *dev)
obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
i915_gem_object_move_to_inactive(obj_priv->obj); i915_gem_object_move_to_inactive(obj_priv->obj);
} }
spin_unlock(&dev_priv->mm.active_list_lock);
while (!list_empty(&dev_priv->mm.flushing_list)) { while (!list_empty(&dev_priv->mm.flushing_list)) {
struct drm_i915_gem_object *obj_priv; struct drm_i915_gem_object *obj_priv;
@ -3949,7 +3965,10 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
if (ret != 0) if (ret != 0)
return ret; return ret;
spin_lock(&dev_priv->mm.active_list_lock);
BUG_ON(!list_empty(&dev_priv->mm.active_list)); BUG_ON(!list_empty(&dev_priv->mm.active_list));
spin_unlock(&dev_priv->mm.active_list_lock);
BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
BUG_ON(!list_empty(&dev_priv->mm.request_list)); BUG_ON(!list_empty(&dev_priv->mm.request_list));
@ -3993,6 +4012,7 @@ i915_gem_load(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
spin_lock_init(&dev_priv->mm.active_list_lock);
INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.active_list);
INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
INIT_LIST_HEAD(&dev_priv->mm.inactive_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list);

View File

@ -105,12 +105,14 @@ i915_dump_lru(struct drm_device *dev, const char *where)
struct drm_i915_gem_object *obj_priv; struct drm_i915_gem_object *obj_priv;
DRM_INFO("active list %s {\n", where); DRM_INFO("active list %s {\n", where);
spin_lock(&dev_priv->mm.active_list_lock);
list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list_for_each_entry(obj_priv, &dev_priv->mm.active_list,
list) list)
{ {
DRM_INFO(" %p: %08x\n", obj_priv, DRM_INFO(" %p: %08x\n", obj_priv,
obj_priv->last_rendering_seqno); obj_priv->last_rendering_seqno);
} }
spin_unlock(&dev_priv->mm.active_list_lock);
DRM_INFO("}\n"); DRM_INFO("}\n");
DRM_INFO("flushing list %s {\n", where); DRM_INFO("flushing list %s {\n", where);
list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list,

View File

@ -69,10 +69,13 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev; struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv; struct drm_i915_gem_object *obj_priv;
spinlock_t *lock = NULL;
switch (list) { switch (list) {
case ACTIVE_LIST: case ACTIVE_LIST:
seq_printf(m, "Active:\n"); seq_printf(m, "Active:\n");
lock = &dev_priv->mm.active_list_lock;
spin_lock(lock);
head = &dev_priv->mm.active_list; head = &dev_priv->mm.active_list;
break; break;
case INACTIVE_LIST: case INACTIVE_LIST:
@ -104,6 +107,9 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
seq_printf(m, " (fence: %d\n", obj_priv->fence_reg); seq_printf(m, " (fence: %d\n", obj_priv->fence_reg);
seq_printf(m, "\n"); seq_printf(m, "\n");
} }
if (lock)
spin_unlock(lock);
return 0; return 0;
} }

View File

@ -216,6 +216,22 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
else else
tile_width = 512; tile_width = 512;
/* check maximum stride & object size */
if (IS_I965G(dev)) {
/* i965 stores the end address of the gtt mapping in the fence
* reg, so dont bother to check the size */
if (stride / 128 > I965_FENCE_MAX_PITCH_VAL)
return false;
} else if (IS_I9XX(dev)) {
if (stride / tile_width > I830_FENCE_MAX_PITCH_VAL ||
size > (I830_FENCE_MAX_SIZE_VAL << 20))
return false;
} else {
if (stride / 128 > I830_FENCE_MAX_PITCH_VAL ||
size > (I830_FENCE_MAX_SIZE_VAL << 19))
return false;
}
/* 965+ just needs multiples of tile width */ /* 965+ just needs multiples of tile width */
if (IS_I965G(dev)) { if (IS_I965G(dev)) {
if (stride & (tile_width - 1)) if (stride & (tile_width - 1))

View File

@ -48,10 +48,6 @@
/** Interrupts that we mask and unmask at runtime. */ /** Interrupts that we mask and unmask at runtime. */
#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
/** These are all of the interrupts used by the driver */
#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \
I915_INTERRUPT_ENABLE_VAR)
#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\ #define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\
PIPE_VBLANK_INTERRUPT_STATUS) PIPE_VBLANK_INTERRUPT_STATUS)
@ -187,6 +183,19 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
return I915_READ(reg); return I915_READ(reg);
} }
/*
* Handle hotplug events outside the interrupt handler proper.
*/
static void i915_hotplug_work_func(struct work_struct *work)
{
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
hotplug_work);
struct drm_device *dev = dev_priv->dev;
/* Just fire off a uevent and let userspace tell us what to do */
drm_sysfs_hotplug_event(dev);
}
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
@ -244,6 +253,20 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
/* Consume port. Then clear IIR or we'll miss events */
if ((I915_HAS_HOTPLUG(dev)) &&
(iir & I915_DISPLAY_PORT_INTERRUPT)) {
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
DRM_DEBUG("hotplug event received, stat 0x%08x\n",
hotplug_status);
if (hotplug_status & dev_priv->hotplug_supported_mask)
schedule_work(&dev_priv->hotplug_work);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
I915_READ(PORT_HOTPLUG_STAT);
}
I915_WRITE(IIR, iir); I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */ new_iir = I915_READ(IIR); /* Flush posted writes */
@ -528,17 +551,24 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0); atomic_set(&dev_priv->irq_received, 0);
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
}
I915_WRITE(HWSTAM, 0xeffe); I915_WRITE(HWSTAM, 0xeffe);
I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0); I915_WRITE(PIPEBSTAT, 0);
I915_WRITE(IMR, 0xffffffff); I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0); I915_WRITE(IER, 0x0);
(void) I915_READ(IER); (void) I915_READ(IER);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
} }
int i915_driver_irq_postinstall(struct drm_device *dev) int i915_driver_irq_postinstall(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
@ -550,13 +580,35 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
dev_priv->pipestat[0] = 0; dev_priv->pipestat[0] = 0;
dev_priv->pipestat[1] = 0; dev_priv->pipestat[1] = 0;
if (I915_HAS_HOTPLUG(dev)) {
u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
/* Leave other bits alone */
hotplug_en |= HOTPLUG_EN_MASK;
I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
dev_priv->hotplug_supported_mask = CRT_HOTPLUG_INT_STATUS |
TV_HOTPLUG_INT_STATUS | SDVOC_HOTPLUG_INT_STATUS |
SDVOB_HOTPLUG_INT_STATUS;
if (IS_G4X(dev)) {
dev_priv->hotplug_supported_mask |=
HDMIB_HOTPLUG_INT_STATUS |
HDMIC_HOTPLUG_INT_STATUS |
HDMID_HOTPLUG_INT_STATUS;
}
/* Enable in IER... */
enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
/* and unmask in IMR */
i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT);
}
/* Disable pipe interrupt enables, clear pending pipe status */ /* Disable pipe interrupt enables, clear pending pipe status */
I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
/* Clear pending interrupt status */ /* Clear pending interrupt status */
I915_WRITE(IIR, I915_READ(IIR)); I915_WRITE(IIR, I915_READ(IIR));
I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); I915_WRITE(IER, enable_mask);
I915_WRITE(IMR, dev_priv->irq_mask_reg); I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IER); (void) I915_READ(IER);
@ -575,6 +627,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
dev_priv->vblank_pipe = 0; dev_priv->vblank_pipe = 0;
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
}
I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0); I915_WRITE(PIPEBSTAT, 0);

View File

@ -190,6 +190,8 @@
#define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8) #define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8)
#define I830_FENCE_PITCH_SHIFT 4 #define I830_FENCE_PITCH_SHIFT 4
#define I830_FENCE_REG_VALID (1<<0) #define I830_FENCE_REG_VALID (1<<0)
#define I830_FENCE_MAX_PITCH_VAL 0x10
#define I830_FENCE_MAX_SIZE_VAL (1<<8)
#define I915_FENCE_START_MASK 0x0ff00000 #define I915_FENCE_START_MASK 0x0ff00000
#define I915_FENCE_SIZE_BITS(size) ((ffs((size) >> 20) - 1) << 8) #define I915_FENCE_SIZE_BITS(size) ((ffs((size) >> 20) - 1) << 8)
@ -198,6 +200,7 @@
#define I965_FENCE_PITCH_SHIFT 2 #define I965_FENCE_PITCH_SHIFT 2
#define I965_FENCE_TILING_Y_SHIFT 1 #define I965_FENCE_TILING_Y_SHIFT 1
#define I965_FENCE_REG_VALID (1<<0) #define I965_FENCE_REG_VALID (1<<0)
#define I965_FENCE_MAX_PITCH_VAL 0x0400
/* /*
* Instruction and interrupt control regs * Instruction and interrupt control regs
@ -648,6 +651,14 @@
#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) #define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2)
#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) #define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
#define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */ #define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */
#define CRT_FORCE_HOTPLUG_MASK 0xfffffe1f
#define HOTPLUG_EN_MASK (HDMIB_HOTPLUG_INT_EN | \
HDMIC_HOTPLUG_INT_EN | \
HDMID_HOTPLUG_INT_EN | \
SDVOB_HOTPLUG_INT_EN | \
SDVOC_HOTPLUG_INT_EN | \
TV_HOTPLUG_INT_EN | \
CRT_HOTPLUG_INT_EN)
#define PORT_HOTPLUG_STAT 0x61114 #define PORT_HOTPLUG_STAT 0x61114

View File

@ -41,7 +41,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp = I915_READ(ADPA); temp = I915_READ(ADPA);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE; temp |= ADPA_DAC_ENABLE;
switch(mode) { switch(mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
@ -158,7 +158,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
else else
tries = 1; tries = 1;
hotplug_en = I915_READ(PORT_HOTPLUG_EN); hotplug_en = I915_READ(PORT_HOTPLUG_EN);
hotplug_en &= ~(CRT_HOTPLUG_MASK); hotplug_en &= CRT_FORCE_HOTPLUG_MASK;
hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
if (IS_GM45(dev)) if (IS_GM45(dev))

View File

@ -636,7 +636,7 @@ void
intel_wait_for_vblank(struct drm_device *dev) intel_wait_for_vblank(struct drm_device *dev)
{ {
/* Wait for 20ms, i.e. one cycle at 50hz. */ /* Wait for 20ms, i.e. one cycle at 50hz. */
udelay(20000); mdelay(20);
} }
static int static int
@ -1106,6 +1106,26 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
return -EINVAL; return -EINVAL;
} }
/* SDVO TV has fixed PLL values depend on its clock range,
this mirrors vbios setting. */
if (is_sdvo && is_tv) {
if (adjusted_mode->clock >= 100000
&& adjusted_mode->clock < 140500) {
clock.p1 = 2;
clock.p2 = 10;
clock.n = 3;
clock.m1 = 16;
clock.m2 = 8;
} else if (adjusted_mode->clock >= 140500
&& adjusted_mode->clock <= 200000) {
clock.p1 = 1;
clock.p2 = 10;
clock.n = 6;
clock.m1 = 12;
clock.m2 = 8;
}
}
if (IS_IGD(dev)) if (IS_IGD(dev))
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2; fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
else else

View File

@ -76,6 +76,7 @@ int intel_ddc_get_modes(struct intel_output *intel_output)
drm_mode_connector_update_edid_property(&intel_output->base, drm_mode_connector_update_edid_property(&intel_output->base,
edid); edid);
ret = drm_add_edid_modes(&intel_output->base, edid); ret = drm_add_edid_modes(&intel_output->base, edid);
intel_output->base.display_info.raw_edid = NULL;
kfree(edid); kfree(edid);
} }

View File

@ -273,20 +273,20 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int i; int i;
DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
for (i = 0; i < args_len; i++) for (i = 0; i < args_len; i++)
printk("%02X ", ((u8 *)args)[i]); printk(KERN_DEBUG "%02X ", ((u8 *)args)[i]);
for (; i < 8; i++) for (; i < 8; i++)
printk(" "); printk(KERN_DEBUG " ");
for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) { for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
if (cmd == sdvo_cmd_names[i].cmd) { if (cmd == sdvo_cmd_names[i].cmd) {
printk("(%s)", sdvo_cmd_names[i].name); printk(KERN_DEBUG "(%s)", sdvo_cmd_names[i].name);
break; break;
} }
} }
if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0])) if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
printk("(%02X)",cmd); printk(KERN_DEBUG "(%02X)", cmd);
printk("\n"); printk(KERN_DEBUG "\n");
} }
#else #else
#define intel_sdvo_debug_write(o, c, a, l) #define intel_sdvo_debug_write(o, c, a, l)
@ -323,17 +323,18 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
u8 status) u8 status)
{ {
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int i;
DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv)); printk(KERN_DEBUG "%s: R: ", SDVO_NAME(sdvo_priv));
for (i = 0; i < response_len; i++) for (i = 0; i < response_len; i++)
printk("%02X ", ((u8 *)response)[i]); printk(KERN_DEBUG "%02X ", ((u8 *)response)[i]);
for (; i < 8; i++) for (; i < 8; i++)
printk(" "); printk(KERN_DEBUG " ");
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
printk("(%s)", cmd_status_names[status]); printk(KERN_DEBUG "(%s)", cmd_status_names[status]);
else else
printk("(??? %d)", status); printk(KERN_DEBUG "(??? %d)", status);
printk("\n"); printk(KERN_DEBUG "\n");
} }
#else #else
#define intel_sdvo_debug_response(o, r, l, s) #define intel_sdvo_debug_response(o, r, l, s)
@ -588,9 +589,12 @@ intel_sdvo_create_preferred_input_timing(struct intel_output *output,
struct intel_sdvo_preferred_input_timing_args args; struct intel_sdvo_preferred_input_timing_args args;
uint8_t status; uint8_t status;
memset(&args, 0, sizeof(args));
args.clock = clock; args.clock = clock;
args.width = width; args.width = width;
args.height = height; args.height = height;
args.interlace = 0;
args.scaled = 0;
intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
&args, sizeof(args)); &args, sizeof(args));
status = intel_sdvo_read_response(output, NULL, 0); status = intel_sdvo_read_response(output, NULL, 0);
@ -683,7 +687,7 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
dtd->part1.v_high = (((height >> 8) & 0xf) << 4) | dtd->part1.v_high = (((height >> 8) & 0xf) << 4) |
((v_blank_len >> 8) & 0xf); ((v_blank_len >> 8) & 0xf);
dtd->part2.h_sync_off = h_sync_offset; dtd->part2.h_sync_off = h_sync_offset & 0xff;
dtd->part2.h_sync_width = h_sync_len & 0xff; dtd->part2.h_sync_width = h_sync_len & 0xff;
dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
(v_sync_len & 0xf); (v_sync_len & 0xf);
@ -705,27 +709,10 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode, static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
struct intel_sdvo_dtd *dtd) struct intel_sdvo_dtd *dtd)
{ {
uint16_t width, height;
uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
uint16_t h_sync_offset, v_sync_offset;
width = mode->crtc_hdisplay;
height = mode->crtc_vdisplay;
/* do some mode translations */
h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
mode->hdisplay = dtd->part1.h_active; mode->hdisplay = dtd->part1.h_active;
mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8; mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off; mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off;
mode->hsync_start += (dtd->part2.sync_off_width_high & 0xa0) << 2; mode->hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2;
mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width; mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width;
mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4; mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
mode->htotal = mode->hdisplay + dtd->part1.h_blank; mode->htotal = mode->hdisplay + dtd->part1.h_blank;
@ -735,7 +722,7 @@ static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8; mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
mode->vsync_start = mode->vdisplay; mode->vsync_start = mode->vdisplay;
mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf; mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0a) << 2; mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2;
mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0; mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0;
mode->vsync_end = mode->vsync_start + mode->vsync_end = mode->vsync_start +
(dtd->part2.v_sync_off_width & 0xf); (dtd->part2.v_sync_off_width & 0xf);
@ -745,7 +732,7 @@ static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
mode->clock = dtd->part1.clock * 10; mode->clock = dtd->part1.clock * 10;
mode->flags &= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
if (dtd->part2.dtd_flags & 0x2) if (dtd->part2.dtd_flags & 0x2)
mode->flags |= DRM_MODE_FLAG_PHSYNC; mode->flags |= DRM_MODE_FLAG_PHSYNC;
if (dtd->part2.dtd_flags & 0x4) if (dtd->part2.dtd_flags & 0x4)
@ -924,6 +911,27 @@ static void intel_sdvo_set_avi_infoframe(struct intel_output *output,
SDVO_HBUF_TX_VSYNC); SDVO_HBUF_TX_VSYNC);
} }
static void intel_sdvo_set_tv_format(struct intel_output *output)
{
struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
struct intel_sdvo_tv_format *format, unset;
u8 status;
format = &sdvo_priv->tv_format;
memset(&unset, 0, sizeof(unset));
if (memcmp(format, &unset, sizeof(*format))) {
DRM_DEBUG("%s: Choosing default TV format of NTSC-M\n",
SDVO_NAME(sdvo_priv));
format->ntsc_m = 1;
intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, format,
sizeof(*format));
status = intel_sdvo_read_response(output, NULL, 0);
if (status != SDVO_CMD_STATUS_SUCCESS)
DRM_DEBUG("%s: Failed to set TV format\n",
SDVO_NAME(sdvo_priv));
}
}
static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
@ -968,6 +976,12 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
&input_dtd); &input_dtd);
intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
drm_mode_set_crtcinfo(adjusted_mode, 0);
mode->clock = adjusted_mode->clock;
adjusted_mode->clock *=
intel_sdvo_get_pixel_multiplier(mode);
} else { } else {
return false; return false;
} }
@ -1012,7 +1026,12 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
sdvox |= SDVO_AUDIO_ENABLE; sdvox |= SDVO_AUDIO_ENABLE;
} }
intel_sdvo_get_dtd_from_mode(&input_dtd, mode); /* We have tried to get input timing in mode_fixup, and filled into
adjusted_mode */
if (sdvo_priv->is_tv)
intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
else
intel_sdvo_get_dtd_from_mode(&input_dtd, mode);
/* If it's a TV, we already set the output timing in mode_fixup. /* If it's a TV, we already set the output timing in mode_fixup.
* Otherwise, the output timing is equal to the input timing. * Otherwise, the output timing is equal to the input timing.
@ -1027,6 +1046,9 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
/* Set the input timing to the screen. Assume always input 0. */ /* Set the input timing to the screen. Assume always input 0. */
intel_sdvo_set_target_input(output, true, false); intel_sdvo_set_target_input(output, true, false);
if (sdvo_priv->is_tv)
intel_sdvo_set_tv_format(output);
/* We would like to use intel_sdvo_create_preferred_input_timing() to /* We would like to use intel_sdvo_create_preferred_input_timing() to
* provide the device with a timing it can support, if it supports that * provide the device with a timing it can support, if it supports that
* feature. However, presumably we would need to adjust the CRTC to * feature. However, presumably we would need to adjust the CRTC to
@ -1395,7 +1417,7 @@ static void
intel_sdvo_check_tv_format(struct intel_output *output) intel_sdvo_check_tv_format(struct intel_output *output)
{ {
struct intel_sdvo_priv *dev_priv = output->dev_priv; struct intel_sdvo_priv *dev_priv = output->dev_priv;
struct intel_sdvo_tv_format format, unset; struct intel_sdvo_tv_format format;
uint8_t status; uint8_t status;
intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0); intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0);
@ -1403,15 +1425,7 @@ intel_sdvo_check_tv_format(struct intel_output *output)
if (status != SDVO_CMD_STATUS_SUCCESS) if (status != SDVO_CMD_STATUS_SUCCESS)
return; return;
memset(&unset, 0, sizeof(unset)); memcpy(&dev_priv->tv_format, &format, sizeof(format));
if (memcmp(&format, &unset, sizeof(format))) {
DRM_DEBUG("%s: Choosing default TV format of NTSC-M\n",
SDVO_NAME(dev_priv));
format.ntsc_m = true;
intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, NULL, 0);
status = intel_sdvo_read_response(output, NULL, 0);
}
} }
/* /*
@ -1420,68 +1434,70 @@ intel_sdvo_check_tv_format(struct intel_output *output)
* XXX: all 60Hz refresh? * XXX: all 60Hz refresh?
*/ */
struct drm_display_mode sdvo_tv_modes[] = { struct drm_display_mode sdvo_tv_modes[] = {
{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815680, 321, 384, 416, { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815, 320, 321, 384,
200, 0, 232, 201, 233, 4196112, 0, 416, 0, 200, 201, 232, 233, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814080, 321, 384, 416, { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814, 320, 321, 384,
240, 0, 272, 241, 273, 4196112, 0, 416, 0, 240, 241, 272, 273, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910080, 401, 464, 496, { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910, 400, 401, 464,
300, 0, 332, 301, 333, 4196112, 0, 496, 0, 300, 301, 332, 333, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913280, 641, 704, 736, { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913, 640, 641, 704,
350, 0, 382, 351, 383, 4196112, 0, 736, 0, 350, 351, 382, 383, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736, { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121, 640, 641, 704,
400, 0, 432, 401, 433, 4196112, 0, 736, 0, 400, 401, 432, 433, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736, { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 22654, 640, 641, 704,
400, 0, 432, 401, 433, 4196112, 0, 736, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624000, 705, 768, 800, { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624, 704, 705, 768,
480, 0, 512, 481, 513, 4196112, 0, 800, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232000, 705, 768, 800, { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232, 704, 705, 768,
576, 0, 608, 577, 609, 4196112, 0, 800, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751680, 721, 784, 816, { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751, 720, 721, 784,
350, 0, 382, 351, 383, 4196112, 0, 816, 0, 350, 351, 382, 383, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199680, 721, 784, 816, { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199, 720, 721, 784,
400, 0, 432, 401, 433, 4196112, 0, 816, 0, 400, 401, 432, 433, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116480, 721, 784, 816, { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116, 720, 721, 784,
480, 0, 512, 481, 513, 4196112, 0, 816, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054080, 721, 784, 816, { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054, 720, 721, 784,
540, 0, 572, 541, 573, 4196112, 0, 816, 0, 540, 541, 572, 573, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816640, 721, 784, 816, { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816, 720, 721, 784,
576, 0, 608, 577, 609, 4196112, 0, 816, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570560, 769, 832, 864, { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570, 768, 769, 832,
576, 0, 608, 577, 609, 4196112, 0, 864, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030080, 801, 864, 896, { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030, 800, 801, 864,
600, 0, 632, 601, 633, 4196112, 0, 896, 0, 600, 601, 632, 633, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581760, 833, 896, 928, { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581, 832, 833, 896,
624, 0, 656, 625, 657, 4196112, 0, 928, 0, 624, 625, 656, 657, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707040, 921, 984, 1016, { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707, 920, 921, 984,
766, 0, 798, 767, 799, 4196112, 0, 1016, 0, 766, 767, 798, 799, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827200, 1025, 1088, 1120, { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827, 1024, 1025, 1088,
768, 0, 800, 769, 801, 4196112, 0, 1120, 0, 768, 769, 800, 801, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265920, 1281, 1344, 1376, { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265, 1280, 1281, 1344,
1024, 0, 1056, 1025, 1057, 4196112, 0, 1376, 0, 1024, 1025, 1056, 1057, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
}; };
static void intel_sdvo_get_tv_modes(struct drm_connector *connector) static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
{ {
struct intel_output *output = to_intel_output(connector); struct intel_output *output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
struct intel_sdvo_sdtv_resolution_request tv_res;
uint32_t reply = 0; uint32_t reply = 0;
uint8_t status; uint8_t status;
int i = 0; int i = 0;
@ -1491,15 +1507,22 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
/* Read the list of supported input resolutions for the selected TV /* Read the list of supported input resolutions for the selected TV
* format. * format.
*/ */
memset(&tv_res, 0, sizeof(tv_res));
memcpy(&tv_res, &sdvo_priv->tv_format, sizeof(tv_res));
intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
NULL, 0); &tv_res, sizeof(tv_res));
status = intel_sdvo_read_response(output, &reply, 3); status = intel_sdvo_read_response(output, &reply, 3);
if (status != SDVO_CMD_STATUS_SUCCESS) if (status != SDVO_CMD_STATUS_SUCCESS)
return; return;
for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++) for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++)
if (reply & (1 << i)) if (reply & (1 << i)) {
drm_mode_probed_add(connector, &sdvo_tv_modes[i]); struct drm_display_mode *nmode;
nmode = drm_mode_duplicate(connector->dev,
&sdvo_tv_modes[i]);
if (nmode)
drm_mode_probed_add(connector, nmode);
}
} }
static int intel_sdvo_get_modes(struct drm_connector *connector) static int intel_sdvo_get_modes(struct drm_connector *connector)

View File

@ -100,6 +100,9 @@ struct intel_sdvo_preferred_input_timing_args {
u16 clock; u16 clock;
u16 width; u16 width;
u16 height; u16 height;
u8 interlace:1;
u8 scaled:1;
u8 pad:6;
} __attribute__((packed)); } __attribute__((packed));
/* I2C registers for SDVO */ /* I2C registers for SDVO */

View File

@ -1570,33 +1570,49 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct intel_output *intel_output = to_intel_output(connector); struct intel_output *intel_output = to_intel_output(connector);
struct intel_tv_priv *tv_priv = intel_output->dev_priv; struct intel_tv_priv *tv_priv = intel_output->dev_priv;
struct drm_encoder *encoder = &intel_output->enc;
struct drm_crtc *crtc = encoder->crtc;
int ret = 0; int ret = 0;
bool changed = false;
ret = drm_connector_property_set_value(connector, property, val); ret = drm_connector_property_set_value(connector, property, val);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (property == dev->mode_config.tv_left_margin_property) if (property == dev->mode_config.tv_left_margin_property &&
tv_priv->margin[TV_MARGIN_LEFT] != val) {
tv_priv->margin[TV_MARGIN_LEFT] = val; tv_priv->margin[TV_MARGIN_LEFT] = val;
else if (property == dev->mode_config.tv_right_margin_property) changed = true;
} else if (property == dev->mode_config.tv_right_margin_property &&
tv_priv->margin[TV_MARGIN_RIGHT] != val) {
tv_priv->margin[TV_MARGIN_RIGHT] = val; tv_priv->margin[TV_MARGIN_RIGHT] = val;
else if (property == dev->mode_config.tv_top_margin_property) changed = true;
} else if (property == dev->mode_config.tv_top_margin_property &&
tv_priv->margin[TV_MARGIN_TOP] != val) {
tv_priv->margin[TV_MARGIN_TOP] = val; tv_priv->margin[TV_MARGIN_TOP] = val;
else if (property == dev->mode_config.tv_bottom_margin_property) changed = true;
} else if (property == dev->mode_config.tv_bottom_margin_property &&
tv_priv->margin[TV_MARGIN_BOTTOM] != val) {
tv_priv->margin[TV_MARGIN_BOTTOM] = val; tv_priv->margin[TV_MARGIN_BOTTOM] = val;
else if (property == dev->mode_config.tv_mode_property) { changed = true;
} else if (property == dev->mode_config.tv_mode_property) {
if (val >= NUM_TV_MODES) { if (val >= NUM_TV_MODES) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
if (!strcmp(tv_priv->tv_format, tv_modes[val].name))
goto out;
tv_priv->tv_format = tv_modes[val].name; tv_priv->tv_format = tv_modes[val].name;
intel_tv_mode_set(&intel_output->enc, NULL, NULL); changed = true;
} else { } else {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
intel_tv_mode_set(&intel_output->enc, NULL, NULL); if (changed && crtc)
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
crtc->y, crtc->fb);
out: out:
return ret; return ret;
} }