Commit b32b8e64 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt/drm-intel

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt/drm-intel: (22 commits)
  drm/i915: Fix read outside array bounds in restoring the SWF10 range.
  drm/i915: Use our own workqueue to avoid wedging the system along with the GPU.
  drm/i915: Add support for dual-channel LVDS on 8xx.
  drm/i915: Return disconnected for SDVO DVI when there's no digital EDID.
  drm/i915: Choose real sdvo output according to result from detection
  drm/i915: Set preferred mode for integrated TV according to TV format
  drm/i915: fix 845G FIFO size & burst length
  drm/i915: fix VGA detect on IGDNG
  drm/i915: Add eDP support on IGDNG mobile chip
  drm/i915: enable DisplayPort support on IGDNG
  drm/i915: Fix channel ending action for DP aux transaction
  drm/i915: fix issue in display pipe setup on IGDNG
  drm/i915: disable VGA plane reliably
  drm/I915: Fix offset to DVO timings in LVDS data
  drm/i915: hdmi detection according by reading edid
  drm/i915: correct self-refresh calculation in "everything off" case
  drm/i915: handle FIFO oversubsription correctly
  drm/i915: FIFO watermark calculation fixes
  drm/i915: ignore lvds on AOpen Mini PC MP-915
  drm/i915: Allow frame buffers up to 4096x4096 on 915/945 class hardware
  ...
parents d6a0967c 819e0064
...@@ -1186,6 +1186,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1186,6 +1186,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (ret) if (ret)
goto out_iomapfree; goto out_iomapfree;
dev_priv->wq = create_workqueue("i915");
if (dev_priv->wq == NULL) {
DRM_ERROR("Failed to create our workqueue.\n");
ret = -ENOMEM;
goto out_iomapfree;
}
/* enable GEM by default */ /* enable GEM by default */
dev_priv->has_gem = 1; dev_priv->has_gem = 1;
...@@ -1211,7 +1218,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1211,7 +1218,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) { if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev); ret = i915_init_phys_hws(dev);
if (ret != 0) if (ret != 0)
goto out_iomapfree; goto out_workqueue_free;
} }
i915_get_mem_freq(dev); i915_get_mem_freq(dev);
...@@ -1245,7 +1252,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1245,7 +1252,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
ret = i915_load_modeset_init(dev, prealloc_size, agp_size); ret = i915_load_modeset_init(dev, prealloc_size, agp_size);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("failed to init modeset\n"); DRM_ERROR("failed to init modeset\n");
goto out_rmmap; goto out_workqueue_free;
} }
} }
...@@ -1256,6 +1263,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1256,6 +1263,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0; return 0;
out_workqueue_free:
destroy_workqueue(dev_priv->wq);
out_iomapfree: out_iomapfree:
io_mapping_free(dev_priv->mm.gtt_mapping); io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap: out_rmmap:
...@@ -1269,6 +1278,8 @@ int i915_driver_unload(struct drm_device *dev) ...@@ -1269,6 +1278,8 @@ int i915_driver_unload(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
destroy_workqueue(dev_priv->wq);
io_mapping_free(dev_priv->mm.gtt_mapping); io_mapping_free(dev_priv->mm.gtt_mapping);
if (dev_priv->mm.gtt_mtrr >= 0) { if (dev_priv->mm.gtt_mtrr >= 0) {
mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
......
...@@ -219,6 +219,7 @@ typedef struct drm_i915_private { ...@@ -219,6 +219,7 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1; unsigned int lvds_vbt:1;
unsigned int int_crt_support:1; unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1; unsigned int lvds_use_ssc:1;
unsigned int edp_support:1;
int lvds_ssc_freq; int lvds_ssc_freq;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
...@@ -229,6 +230,8 @@ typedef struct drm_i915_private { ...@@ -229,6 +230,8 @@ typedef struct drm_i915_private {
spinlock_t error_lock; spinlock_t error_lock;
struct drm_i915_error_state *first_error; struct drm_i915_error_state *first_error;
struct work_struct error_work;
struct workqueue_struct *wq;
/* Register state */ /* Register state */
u8 saveLBB; u8 saveLBB;
...@@ -888,6 +891,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); ...@@ -888,6 +891,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I915GM(dev))) IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
/* dsparb controlled by hw only */ /* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
......
...@@ -1570,7 +1570,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv, ...@@ -1570,7 +1570,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
} }
if (was_empty && !dev_priv->mm.suspended) if (was_empty && !dev_priv->mm.suspended)
schedule_delayed_work(&dev_priv->mm.retire_work, HZ); queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
return seqno; return seqno;
} }
...@@ -1719,7 +1719,7 @@ i915_gem_retire_work_handler(struct work_struct *work) ...@@ -1719,7 +1719,7 @@ i915_gem_retire_work_handler(struct work_struct *work)
i915_gem_retire_requests(dev); i915_gem_retire_requests(dev);
if (!dev_priv->mm.suspended && if (!dev_priv->mm.suspended &&
!list_empty(&dev_priv->mm.request_list)) !list_empty(&dev_priv->mm.request_list))
schedule_delayed_work(&dev_priv->mm.retire_work, HZ); queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
} }
......
...@@ -343,6 +343,8 @@ static int i915_error_state(struct seq_file *m, void *unused) ...@@ -343,6 +343,8 @@ static int i915_error_state(struct seq_file *m, void *unused)
error = dev_priv->first_error; error = dev_priv->first_error;
seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
error->time.tv_usec);
seq_printf(m, "EIR: 0x%08x\n", error->eir); seq_printf(m, "EIR: 0x%08x\n", error->eir);
seq_printf(m, " PGTBL_ER: 0x%08x\n", error->pgtbl_er); seq_printf(m, " PGTBL_ER: 0x%08x\n", error->pgtbl_er);
seq_printf(m, " INSTPM: 0x%08x\n", error->instpm); seq_printf(m, " INSTPM: 0x%08x\n", error->instpm);
......
...@@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) ...@@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
return ret; return ret;
} }
/**
* i915_error_work_func - do process context error handling work
* @work: work struct
*
* Fire an error uevent so userspace can see that a hang or error
* was detected.
*/
static void i915_error_work_func(struct work_struct *work)
{
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
error_work);
struct drm_device *dev = dev_priv->dev;
char *event_string = "ERROR=1";
char *envp[] = { event_string, NULL };
DRM_DEBUG("generating error event\n");
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
}
/**
* i915_capture_error_state - capture an error record for later analysis
* @dev: drm device
*
* Should be called when an error is detected (either a hang or an error
* interrupt) to capture error state from the time of the error. Fills
* out a structure which becomes available in debugfs for user level tools
* to pick up.
*/
static void i915_capture_error_state(struct drm_device *dev) static void i915_capture_error_state(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
...@@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev) ...@@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev)
error->acthd = I915_READ(ACTHD_I965); error->acthd = I915_READ(ACTHD_I965);
} }
do_gettimeofday(&error->time);
dev_priv->first_error = error; dev_priv->first_error = error;
out: out:
spin_unlock_irqrestore(&dev_priv->error_lock, flags); spin_unlock_irqrestore(&dev_priv->error_lock, flags);
} }
/**
* i915_handle_error - handle an error interrupt
* @dev: drm device
*
* Do some basic checking of regsiter state at error interrupt time and
* dump it to the syslog. Also call i915_capture_error_state() to make
* sure we get a record and make it available in debugfs. Fire a uevent
* so userspace knows something bad happened (should trigger collection
* of a ring dump etc.).
*/
static void i915_handle_error(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 eir = I915_READ(EIR);
u32 pipea_stats = I915_READ(PIPEASTAT);
u32 pipeb_stats = I915_READ(PIPEBSTAT);
i915_capture_error_state(dev);
printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
eir);
if (IS_G4X(dev)) {
if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
u32 ipeir = I915_READ(IPEIR_I965);
printk(KERN_ERR " IPEIR: 0x%08x\n",
I915_READ(IPEIR_I965));
printk(KERN_ERR " IPEHR: 0x%08x\n",
I915_READ(IPEHR_I965));
printk(KERN_ERR " INSTDONE: 0x%08x\n",
I915_READ(INSTDONE_I965));
printk(KERN_ERR " INSTPS: 0x%08x\n",
I915_READ(INSTPS));
printk(KERN_ERR " INSTDONE1: 0x%08x\n",
I915_READ(INSTDONE1));
printk(KERN_ERR " ACTHD: 0x%08x\n",
I915_READ(ACTHD_I965));
I915_WRITE(IPEIR_I965, ipeir);
(void)I915_READ(IPEIR_I965);
}
if (eir & GM45_ERROR_PAGE_TABLE) {
u32 pgtbl_err = I915_READ(PGTBL_ER);
printk(KERN_ERR "page table error\n");
printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
pgtbl_err);
I915_WRITE(PGTBL_ER, pgtbl_err);
(void)I915_READ(PGTBL_ER);
}
}
if (IS_I9XX(dev)) {
if (eir & I915_ERROR_PAGE_TABLE) {
u32 pgtbl_err = I915_READ(PGTBL_ER);
printk(KERN_ERR "page table error\n");
printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
pgtbl_err);
I915_WRITE(PGTBL_ER, pgtbl_err);
(void)I915_READ(PGTBL_ER);
}
}
if (eir & I915_ERROR_MEMORY_REFRESH) {
printk(KERN_ERR "memory refresh error\n");
printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
pipea_stats);
printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
pipeb_stats);
/* pipestat has already been acked */
}
if (eir & I915_ERROR_INSTRUCTION) {
printk(KERN_ERR "instruction error\n");
printk(KERN_ERR " INSTPM: 0x%08x\n",
I915_READ(INSTPM));
if (!IS_I965G(dev)) {
u32 ipeir = I915_READ(IPEIR);
printk(KERN_ERR " IPEIR: 0x%08x\n",
I915_READ(IPEIR));
printk(KERN_ERR " IPEHR: 0x%08x\n",
I915_READ(IPEHR));
printk(KERN_ERR " INSTDONE: 0x%08x\n",
I915_READ(INSTDONE));
printk(KERN_ERR " ACTHD: 0x%08x\n",
I915_READ(ACTHD));
I915_WRITE(IPEIR, ipeir);
(void)I915_READ(IPEIR);
} else {
u32 ipeir = I915_READ(IPEIR_I965);
printk(KERN_ERR " IPEIR: 0x%08x\n",
I915_READ(IPEIR_I965));
printk(KERN_ERR " IPEHR: 0x%08x\n",
I915_READ(IPEHR_I965));
printk(KERN_ERR " INSTDONE: 0x%08x\n",
I915_READ(INSTDONE_I965));
printk(KERN_ERR " INSTPS: 0x%08x\n",
I915_READ(INSTPS));
printk(KERN_ERR " INSTDONE1: 0x%08x\n",
I915_READ(INSTDONE1));
printk(KERN_ERR " ACTHD: 0x%08x\n",
I915_READ(ACTHD_I965));
I915_WRITE(IPEIR_I965, ipeir);
(void)I915_READ(IPEIR_I965);
}
}
I915_WRITE(EIR, eir);
(void)I915_READ(EIR);
eir = I915_READ(EIR);
if (eir) {
/*
* some errors might have become stuck,
* mask them.
*/
DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
I915_WRITE(EMR, I915_READ(EMR) | eir);
I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
}
queue_work(dev_priv->wq, &dev_priv->error_work);
}
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;
...@@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
pipea_stats = I915_READ(PIPEASTAT); pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT); pipeb_stats = I915_READ(PIPEBSTAT);
if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
i915_handle_error(dev);
/* /*
* Clear the PIPE(A|B)STAT regs before the IIR * Clear the PIPE(A|B)STAT regs before the IIR
*/ */
...@@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
DRM_DEBUG("hotplug event received, stat 0x%08x\n", DRM_DEBUG("hotplug event received, stat 0x%08x\n",
hotplug_status); hotplug_status);
if (hotplug_status & dev_priv->hotplug_supported_mask) if (hotplug_status & dev_priv->hotplug_supported_mask)
schedule_work(&dev_priv->hotplug_work); queue_work(dev_priv->wq,
&dev_priv->hotplug_work);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
I915_READ(PORT_HOTPLUG_STAT); I915_READ(PORT_HOTPLUG_STAT);
} }
if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) {
u32 eir = I915_READ(EIR);
i915_capture_error_state(dev);
printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
eir);
if (eir & I915_ERROR_PAGE_TABLE) {
u32 pgtbl_err = I915_READ(PGTBL_ER);
printk(KERN_ERR "page table error\n");
printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
pgtbl_err);
I915_WRITE(PGTBL_ER, pgtbl_err);
(void)I915_READ(PGTBL_ER);
}
if (eir & I915_ERROR_MEMORY_REFRESH) {
printk(KERN_ERR "memory refresh error\n");
printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
pipea_stats);
printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
pipeb_stats);
/* pipestat has already been acked */
}
if (eir & I915_ERROR_INSTRUCTION) {
printk(KERN_ERR "instruction error\n");
printk(KERN_ERR " INSTPM: 0x%08x\n",
I915_READ(INSTPM));
if (!IS_I965G(dev)) {
u32 ipeir = I915_READ(IPEIR);
printk(KERN_ERR " IPEIR: 0x%08x\n",
I915_READ(IPEIR));
printk(KERN_ERR " IPEHR: 0x%08x\n",
I915_READ(IPEHR));
printk(KERN_ERR " INSTDONE: 0x%08x\n",
I915_READ(INSTDONE));
printk(KERN_ERR " ACTHD: 0x%08x\n",
I915_READ(ACTHD));
I915_WRITE(IPEIR, ipeir);
(void)I915_READ(IPEIR);
} else {
u32 ipeir = I915_READ(IPEIR_I965);
printk(KERN_ERR " IPEIR: 0x%08x\n",
I915_READ(IPEIR_I965));
printk(KERN_ERR " IPEHR: 0x%08x\n",
I915_READ(IPEHR_I965));
printk(KERN_ERR " INSTDONE: 0x%08x\n",
I915_READ(INSTDONE_I965));
printk(KERN_ERR " INSTPS: 0x%08x\n",
I915_READ(INSTPS));
printk(KERN_ERR " INSTDONE1: 0x%08x\n",
I915_READ(INSTDONE1));
printk(KERN_ERR " ACTHD: 0x%08x\n",
I915_READ(ACTHD_I965));
I915_WRITE(IPEIR_I965, ipeir);
(void)I915_READ(IPEIR_I965);
}
}
I915_WRITE(EIR, eir);
(void)I915_READ(EIR);
eir = I915_READ(EIR);
if (eir) {
/*
* some errors might have become stuck,
* mask them.
*/
DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
I915_WRITE(EMR, I915_READ(EMR) | eir);
I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
}
}
I915_WRITE(IIR, iir); I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */ new_iir = I915_READ(IIR); /* Flush posted writes */
...@@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) ...@@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0); atomic_set(&dev_priv->irq_received, 0);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
if (IS_IGDNG(dev)) { if (IS_IGDNG(dev)) {
igdng_irq_preinstall(dev); igdng_irq_preinstall(dev);
......
...@@ -1395,6 +1395,7 @@ ...@@ -1395,6 +1395,7 @@
#define TV_V_CHROMA_42 0x684a8 #define TV_V_CHROMA_42 0x684a8
/* Display Port */ /* Display Port */
#define DP_A 0x64000 /* eDP */
#define DP_B 0x64100 #define DP_B 0x64100
#define DP_C 0x64200 #define DP_C 0x64200
#define DP_D 0x64300 #define DP_D 0x64300
...@@ -1437,13 +1438,22 @@ ...@@ -1437,13 +1438,22 @@
/* Mystic DPCD version 1.1 special mode */ /* Mystic DPCD version 1.1 special mode */
#define DP_ENHANCED_FRAMING (1 << 18) #define DP_ENHANCED_FRAMING (1 << 18)
/* eDP */
#define DP_PLL_FREQ_270MHZ (0 << 16)
#define DP_PLL_FREQ_160MHZ (1 << 16)
#define DP_PLL_FREQ_MASK (3 << 16)
/** locked once port is enabled */ /** locked once port is enabled */
#define DP_PORT_REVERSAL (1 << 15) #define DP_PORT_REVERSAL (1 << 15)
/* eDP */
#define DP_PLL_ENABLE (1 << 14)
/** sends the clock on lane 15 of the PEG for debug */ /** sends the clock on lane 15 of the PEG for debug */
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) #define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
#define DP_SCRAMBLING_DISABLE (1 << 12) #define DP_SCRAMBLING_DISABLE (1 << 12)
#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7)
/** limit RGB values to avoid confusing TVs */ /** limit RGB values to avoid confusing TVs */
#define DP_COLOR_RANGE_16_235 (1 << 8) #define DP_COLOR_RANGE_16_235 (1 << 8)
...@@ -1463,6 +1473,13 @@ ...@@ -1463,6 +1473,13 @@
* is 20 bytes in each direction, hence the 5 fixed * is 20 bytes in each direction, hence the 5 fixed
* data registers * data registers
*/ */
#define DPA_AUX_CH_CTL 0x64010
#define DPA_AUX_CH_DATA1 0x64014
#define DPA_AUX_CH_DATA2 0x64018
#define DPA_AUX_CH_DATA3 0x6401c
#define DPA_AUX_CH_DATA4 0x64020
#define DPA_AUX_CH_DATA5 0x64024
#define DPB_AUX_CH_CTL 0x64110 #define DPB_AUX_CH_CTL 0x64110
#define DPB_AUX_CH_DATA1 0x64114 #define DPB_AUX_CH_DATA1 0x64114
#define DPB_AUX_CH_DATA2 0x64118 #define DPB_AUX_CH_DATA2 0x64118
...@@ -1618,7 +1635,7 @@ ...@@ -1618,7 +1635,7 @@
#define I830_FIFO_LINE_SIZE 32 #define I830_FIFO_LINE_SIZE 32
#define I945_FIFO_SIZE 127 /* 945 & 965 */ #define I945_FIFO_SIZE 127 /* 945 & 965 */
#define I915_FIFO_SIZE 95 #define I915_FIFO_SIZE 95
#define I855GM_FIFO_SIZE 255 #define I855GM_FIFO_SIZE 127 /* In cachelines */
#define I830_FIFO_SIZE 95 #define I830_FIFO_SIZE 95
#define I915_MAX_WM 0x3f #define I915_MAX_WM 0x3f
...@@ -1848,6 +1865,8 @@ ...@@ -1848,6 +1865,8 @@
#define PFA_CTL_1 0x68080 #define PFA_CTL_1 0x68080
#define PFB_CTL_1 0x68880 #define PFB_CTL_1 0x68880
#define PF_ENABLE (1<<31) #define PF_ENABLE (1<<31)
#define PFA_WIN_SZ 0x68074
#define PFB_WIN_SZ 0x68874
/* legacy palette */ /* legacy palette */
#define LGC_PALETTE_A 0x4a000 #define LGC_PALETTE_A 0x4a000
...@@ -2208,4 +2227,28 @@ ...@@ -2208,4 +2227,28 @@
#define PCH_PP_OFF_DELAYS 0xc720c #define PCH_PP_OFF_DELAYS 0xc720c
#define PCH_PP_DIVISOR 0xc7210 #define PCH_PP_DIVISOR 0xc7210
#define PCH_DP_B 0xe4100
#define PCH_DPB_AUX_CH_CTL 0xe4110
#define PCH_DPB_AUX_CH_DATA1 0xe4114
#define PCH_DPB_AUX_CH_DATA2 0xe4118
#define PCH_DPB_AUX_CH_DATA3 0xe411c
#define PCH_DPB_AUX_CH_DATA4 0xe4120
#define PCH_DPB_AUX_CH_DATA5 0xe4124
#define PCH_DP_C 0xe4200
#define PCH_DPC_AUX_CH_CTL 0xe4210
#define PCH_DPC_AUX_CH_DATA1 0xe4214
#define PCH_DPC_AUX_CH_DATA2 0xe4218
#define PCH_DPC_AUX_CH_DATA3 0xe421c
#define PCH_DPC_AUX_CH_DATA4 0xe4220
#define PCH_DPC_AUX_CH_DATA5 0xe4224
#define PCH_DP_D 0xe4300
#define PCH_DPD_AUX_CH_CTL 0xe4310
#define PCH_DPD_AUX_CH_DATA1 0xe4314
#define PCH_DPD_AUX_CH_DATA2 0xe4318
#define PCH_DPD_AUX_CH_DATA3 0xe431c
#define PCH_DPD_AUX_CH_DATA4 0xe4320
#define PCH_DPD_AUX_CH_DATA5 0xe4324
#endif /* _I915_REG_H_ */ #endif /* _I915_REG_H_ */
...@@ -598,7 +598,7 @@ int i915_restore_state(struct drm_device *dev) ...@@ -598,7 +598,7 @@ int i915_restore_state(struct drm_device *dev)
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]); I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]);
I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i+7]); I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i]);
} }
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]); I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
......
...@@ -97,14 +97,13 @@ static void ...@@ -97,14 +97,13 @@ static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv, parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb) struct bdb_header *bdb)
{ {
struct drm_device *dev = dev_priv->dev;
struct bdb_lvds_options *lvds_options; struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data; struct bdb_lvds_lfp_data *lvds_lfp_data;
struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
struct bdb_lvds_lfp_data_entry *entry; struct bdb_lvds_lfp_data_entry *entry;
struct lvds_dvo_timing *dvo_timing; struct lvds_dvo_timing *dvo_timing;
struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *panel_fixed_mode;
int lfp_data_size; int lfp_data_size, dvo_timing_offset;
/* Defaults if we can't find VBT info */ /* Defaults if we can't find VBT info */
dev_priv->lvds_dither = 0; dev_priv->lvds_dither = 0;
...@@ -133,14 +132,16 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, ...@@ -133,14 +132,16 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
entry = (struct bdb_lvds_lfp_data_entry *) entry = (struct bdb_lvds_lfp_data_entry *)
((uint8_t *)lvds_lfp_data->data + (lfp_data_size * ((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
lvds_options->panel_type)); lvds_options->panel_type));
dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
/* On IGDNG mobile, LVDS data block removes panel fitting registers. /*
So dec 2 dword from dvo_timing offset */ * the size of fp_timing varies on the different platform.
if (IS_IGDNG(dev)) * So calculate the DVO timing relative offset in LVDS data
dvo_timing = (struct lvds_dvo_timing *) * entry to get the DVO timing entry
((u8 *)&entry->dvo_timing - 8); */
else dvo_timing = (struct lvds_dvo_timing *)
dvo_timing = &entry->dvo_timing; ((unsigned char *)entry + dvo_timing_offset);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
...@@ -295,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, ...@@ -295,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
} }
return; return;
} }
static void
parse_driver_features(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
struct drm_device *dev = dev_priv->dev;
struct bdb_driver_features *driver;
/* set default for chips without eDP */
if (!SUPPORTS_EDP(dev)) {
dev_priv->edp_support = 0;
return;
}
driver = find_section(bdb, BDB_DRIVER_FEATURES);
if (driver && driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
dev_priv->edp_support = 1;
}
/** /**
* intel_init_bios - initialize VBIOS settings & find VBT * intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device * @dev: DRM device
...@@ -345,6 +365,8 @@ intel_init_bios(struct drm_device *dev) ...@@ -345,6 +365,8 @@ intel_init_bios(struct drm_device *dev)
parse_lfp_panel_data(dev_priv, bdb); parse_lfp_panel_data(dev_priv, bdb);
parse_sdvo_panel_data(dev_priv, bdb); parse_sdvo_panel_data(dev_priv, bdb);
parse_sdvo_device_mapping(dev_priv, bdb); parse_sdvo_device_mapping(dev_priv, bdb);
parse_driver_features(dev_priv, bdb);
pci_unmap_rom(pdev, bios); pci_unmap_rom(pdev, bios);
return 0; return 0;
......
...@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options { ...@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options {
} __attribute__((packed)); } __attribute__((packed));
#define BDB_DRIVER_FEATURE_NO_LVDS 0
#define BDB_DRIVER_FEATURE_INT_LVDS 1
#define BDB_DRIVER_FEATURE_SDVO_LVDS 2
#define BDB_DRIVER_FEATURE_EDP 3
struct bdb_driver_features {
u8 boot_dev_algorithm:1;
u8 block_display_switch:1;
u8 allow_display_switch:1;
u8 hotplug_dvo:1;
u8 dual_view_zoom:1;
u8 int15h_hook:1;
u8 sprite_in_clone:1;
u8 primary_lfp_id:1;
u16 boot_mode_x;
u16 boot_mode_y;
u8 boot_mode_bpp;
u8 boot_mode_refresh;
u16 enable_lfp_primary:1;
u16 selective_mode_pruning:1;
u16 dual_frequency:1;
u16 render_clock_freq:1; /* 0: high freq; 1: low freq */
u16 nt_clone_support:1;
u16 power_scheme_ui:1; /* 0: CUI; 1: 3rd party */
u16 sprite_display_assign:1; /* 0: secondary; 1: primary */
u16 cui_aspect_scaling:1;
u16 preserve_aspect_ratio:1;
u16 sdvo_device_power_down:1;
u16 crt_hotplug:1;
u16 lvds_config:2;
u16 tv_hotplug:1;
u16 hdmi_config:2;
u8 static_display:1;
u8 reserved2:7;
u16 legacy_crt_max_x;
u16 legacy_crt_max_y;
u8 legacy_crt_max_refresh;
u8 hdmi_termination;
u8 custom_vbt_version;
} __attribute__((packed));
bool intel_init_bios(struct drm_device *dev); bool intel_init_bios(struct drm_device *dev);
/* /*
......
...@@ -156,6 +156,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector) ...@@ -156,6 +156,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
temp = adpa = I915_READ(PCH_ADPA); temp = adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_DAC_ENABLE;
I915_WRITE(PCH_ADPA, adpa);
adpa &= ~ADPA_CRT_HOTPLUG_MASK; adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 | adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
...@@ -169,13 +172,14 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector) ...@@ -169,13 +172,14 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
DRM_DEBUG("pch crt adpa 0x%x", adpa); DRM_DEBUG("pch crt adpa 0x%x", adpa);
I915_WRITE(PCH_ADPA, adpa); I915_WRITE(PCH_ADPA, adpa);
/* This might not be needed as not specified in spec...*/ while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
udelay(1000); ;
/* Check the status to see if both blue and green are on now */ /* Check the status to see if both blue and green are on now */
adpa = I915_READ(PCH_ADPA); adpa = I915_READ(PCH_ADPA);
if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) == adpa &= ADPA_CRT_HOTPLUG_MONITOR_MASK;
ADPA_CRT_HOTPLUG_MONITOR_COLOR) if ((adpa == ADPA_CRT_HOTPLUG_MONITOR_COLOR) ||
(adpa == ADPA_CRT_HOTPLUG_MONITOR_MONO))
ret = true; ret = true;
else else
ret = false; ret = false;
......
This diff is collapsed.
This diff is collapsed.
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#define INTEL_OUTPUT_TVOUT 5 #define INTEL_OUTPUT_TVOUT 5
#define INTEL_OUTPUT_HDMI 6 #define INTEL_OUTPUT_HDMI 6
#define INTEL_OUTPUT_DISPLAYPORT 7 #define INTEL_OUTPUT_DISPLAYPORT 7
#define INTEL_OUTPUT_EDP 8
#define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1 #define INTEL_DVO_CHIP_LVDS 1
...@@ -121,6 +122,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg); ...@@ -121,6 +122,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode);
extern void intel_edp_link_config (struct intel_output *, int *, int *);
extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder); extern void intel_encoder_prepare (struct drm_encoder *encoder);
......
...@@ -130,16 +130,17 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, ...@@ -130,16 +130,17 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
} }
static enum drm_connector_status static enum drm_connector_status
intel_hdmi_edid_detect(struct drm_connector *connector) intel_hdmi_detect(struct drm_connector *connector)
{ {
struct intel_output *intel_output = to_intel_output(connector); struct intel_output *intel_output = to_intel_output(connector);
struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
struct edid *edid = NULL; struct edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected; enum drm_connector_status status = connector_status_disconnected;
hdmi_priv->has_hdmi_sink = false;
edid = drm_get_edid(&intel_output->base, edid = drm_get_edid(&intel_output->base,
intel_output->ddc_bus); intel_output->ddc_bus);
hdmi_priv->has_hdmi_sink = false;
if (edid) { if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) { if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected; status = connector_status_connected;
...@@ -148,65 +149,8 @@ intel_hdmi_edid_detect(struct drm_connector *connector) ...@@ -148,65 +149,8 @@ intel_hdmi_edid_detect(struct drm_connector *connector)
intel_output->base.display_info.raw_edid = NULL; intel_output->base.display_info.raw_edid = NULL;
kfree(edid); kfree(edid);
} }
return status;
}
static enum drm_connector_status
igdng_hdmi_detect(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
/* FIXME hotplug detect */
hdmi_priv->has_hdmi_sink = false;
return intel_hdmi_edid_detect(connector);
}
static enum drm_connector_status return status;
intel_hdmi_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_output *intel_output = to_intel_output(connector);
struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
u32 temp, bit;
if (IS_IGDNG(dev))
return igdng_hdmi_detect(connector);
temp = I915_READ(PORT_HOTPLUG_EN);
switch (hdmi_priv->sdvox_reg) {
case SDVOB:
temp |= HDMIB_HOTPLUG_INT_EN;
break;
case SDVOC:
temp |= HDMIC_HOTPLUG_INT_EN;
break;
default:
return connector_status_unknown;
}
I915_WRITE(PORT_HOTPLUG_EN, temp);
POSTING_READ(PORT_HOTPLUG_EN);
switch (hdmi_priv->sdvox_reg) {
case SDVOB:
bit = HDMIB_HOTPLUG_INT_STATUS;
break;
case SDVOC:
bit = HDMIC_HOTPLUG_INT_STATUS;
break;
default:
return connector_status_unknown;
}
if ((I915_READ(PORT_HOTPLUG_STAT) & bit) != 0)
return intel_hdmi_edid_detect(connector);
else
return connector_status_disconnected;
} }
static int intel_hdmi_get_modes(struct drm_connector *connector) static int intel_hdmi_get_modes(struct drm_connector *connector)
......
...@@ -778,6 +778,14 @@ static const struct dmi_system_id intel_no_lvds[] = { ...@@ -778,6 +778,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"), DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
}, },
}, },
{
.callback = intel_no_lvds_dmi_callback,
.ident = "AOpen Mini PC MP915",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
},
},
{ {
.callback = intel_no_lvds_dmi_callback, .callback = intel_no_lvds_dmi_callback,
.ident = "Aopen i945GTt-VFA", .ident = "Aopen i945GTt-VFA",
...@@ -884,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev) ...@@ -884,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev)
if (IS_IGDNG(dev)) { if (IS_IGDNG(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return; return;
if (dev_priv->edp_support) {
DRM_DEBUG("disable LVDS for eDP support\n");
return;
}
gpio = PCH_GPIOC; gpio = PCH_GPIOC;
} }
......
This diff is collapsed.
...@@ -1490,6 +1490,27 @@ static struct input_res { ...@@ -1490,6 +1490,27 @@ static struct input_res {
{"1920x1080", 1920, 1080}, {"1920x1080", 1920, 1080},
}; };
/*
* Chose preferred mode according to line number of TV format
*/
static void
intel_tv_chose_preferred_modes(struct drm_connector *connector,
struct drm_display_mode *mode_ptr)
{
struct intel_output *intel_output = to_intel_output(connector);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
else if (tv_mode->nbr_end > 480) {
if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
if (mode_ptr->vdisplay == 720)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
} else if (mode_ptr->vdisplay == 1080)
mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
}
}
/** /**
* Stub get_modes function. * Stub get_modes function.
* *
...@@ -1544,6 +1565,7 @@ intel_tv_get_modes(struct drm_connector *connector) ...@@ -1544,6 +1565,7 @@ intel_tv_get_modes(struct drm_connector *connector)
mode_ptr->clock = (int) tmp; mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER; mode_ptr->type = DRM_MODE_TYPE_DRIVER;
intel_tv_chose_preferred_modes(connector, mode_ptr);
drm_mode_probed_add(connector, mode_ptr); drm_mode_probed_add(connector, mode_ptr);
count++; count++;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment