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
  ...
This commit is contained in:
Linus Torvalds 2009-08-07 19:03:59 -07:00
commit b32b8e645e
17 changed files with 1232 additions and 524 deletions

View file

@ -1186,6 +1186,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (ret)
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 */
dev_priv->has_gem = 1;
@ -1211,7 +1218,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
goto out_iomapfree;
goto out_workqueue_free;
}
i915_get_mem_freq(dev);
@ -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);
if (ret < 0) {
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)
return 0;
out_workqueue_free:
destroy_workqueue(dev_priv->wq);
out_iomapfree:
io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap:
@ -1269,6 +1278,8 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
destroy_workqueue(dev_priv->wq);
io_mapping_free(dev_priv->mm.gtt_mapping);
if (dev_priv->mm.gtt_mtrr >= 0) {
mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,

View file

@ -219,6 +219,7 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1;
unsigned int edp_support:1;
int lvds_ssc_freq;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
@ -229,6 +230,8 @@ typedef struct drm_i915_private {
spinlock_t error_lock;
struct drm_i915_error_state *first_error;
struct work_struct error_work;
struct workqueue_struct *wq;
/* Register state */
u8 saveLBB;
@ -888,6 +891,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I915GM(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_EDP(dev) (IS_IGDNG_M(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
/* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))

View file

@ -1570,7 +1570,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
}
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;
}
@ -1719,7 +1719,7 @@ i915_gem_retire_work_handler(struct work_struct *work)
i915_gem_retire_requests(dev);
if (!dev_priv->mm.suspended &&
!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);
}

View file

@ -343,6 +343,8 @@ static int i915_error_state(struct seq_file *m, void *unused)
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, " PGTBL_ER: 0x%08x\n", error->pgtbl_er);
seq_printf(m, " INSTPM: 0x%08x\n", error->instpm);

View file

@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
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)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev)
error->acthd = I915_READ(ACTHD_I965);
}
do_gettimeofday(&error->time);
dev_priv->first_error = error;
out:
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)
{
struct drm_device *dev = (struct drm_device *) arg;
@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
pipea_stats = I915_READ(PIPEASTAT);
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
*/
@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
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);
queue_work(dev_priv->wq,
&dev_priv->hotplug_work);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
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);
new_iir = I915_READ(IIR); /* Flush posted writes */
@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
if (IS_IGDNG(dev)) {
igdng_irq_preinstall(dev);

View file

@ -1395,6 +1395,7 @@
#define TV_V_CHROMA_42 0x684a8
/* Display Port */
#define DP_A 0x64000 /* eDP */
#define DP_B 0x64100
#define DP_C 0x64200
#define DP_D 0x64300
@ -1437,13 +1438,22 @@
/* Mystic DPCD version 1.1 special mode */
#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 */
#define DP_PORT_REVERSAL (1 << 15)
/* eDP */
#define DP_PLL_ENABLE (1 << 14)
/** sends the clock on lane 15 of the PEG for debug */
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
#define DP_SCRAMBLING_DISABLE (1 << 12)
#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7)
/** limit RGB values to avoid confusing TVs */
#define DP_COLOR_RANGE_16_235 (1 << 8)
@ -1463,6 +1473,13 @@
* is 20 bytes in each direction, hence the 5 fixed
* 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_DATA1 0x64114
#define DPB_AUX_CH_DATA2 0x64118
@ -1618,7 +1635,7 @@
#define I830_FIFO_LINE_SIZE 32
#define I945_FIFO_SIZE 127 /* 945 & 965 */
#define I915_FIFO_SIZE 95
#define I855GM_FIFO_SIZE 255
#define I855GM_FIFO_SIZE 127 /* In cachelines */
#define I830_FIFO_SIZE 95
#define I915_MAX_WM 0x3f
@ -1848,6 +1865,8 @@
#define PFA_CTL_1 0x68080
#define PFB_CTL_1 0x68880
#define PF_ENABLE (1<<31)
#define PFA_WIN_SZ 0x68074
#define PFB_WIN_SZ 0x68874
/* legacy palette */
#define LGC_PALETTE_A 0x4a000
@ -2208,4 +2227,28 @@
#define PCH_PP_OFF_DELAYS 0xc720c
#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_ */

View file

@ -598,7 +598,7 @@ int i915_restore_state(struct drm_device *dev)
for (i = 0; i < 16; 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++)
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);

View file

@ -97,14 +97,13 @@ static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
struct drm_device *dev = dev_priv->dev;
struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data;
struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
struct bdb_lvds_lfp_data_entry *entry;
struct lvds_dvo_timing *dvo_timing;
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 */
dev_priv->lvds_dither = 0;
@ -133,14 +132,16 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
entry = (struct bdb_lvds_lfp_data_entry *)
((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
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 */
if (IS_IGDNG(dev))
dvo_timing = (struct lvds_dvo_timing *)
((u8 *)&entry->dvo_timing - 8);
else
dvo_timing = &entry->dvo_timing;
/*
* the size of fp_timing varies on the different platform.
* So calculate the DVO timing relative offset in LVDS data
* entry to get the DVO timing entry
*/
dvo_timing = (struct lvds_dvo_timing *)
((unsigned char *)entry + dvo_timing_offset);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
@ -295,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
}
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
* @dev: DRM device
@ -345,6 +365,8 @@ intel_init_bios(struct drm_device *dev)
parse_lfp_panel_data(dev_priv, bdb);
parse_sdvo_panel_data(dev_priv, bdb);
parse_sdvo_device_mapping(dev_priv, bdb);
parse_driver_features(dev_priv, bdb);
pci_unmap_rom(pdev, bios);
return 0;

View file

@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options {
} __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);
/*

View file

@ -156,6 +156,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
temp = adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_DAC_ENABLE;
I915_WRITE(PCH_ADPA, adpa);
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
@ -169,13 +172,14 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
DRM_DEBUG("pch crt adpa 0x%x", adpa);
I915_WRITE(PCH_ADPA, adpa);
/* This might not be needed as not specified in spec...*/
udelay(1000);
while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
;
/* Check the status to see if both blue and green are on now */
adpa = I915_READ(PCH_ADPA);
if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
ADPA_CRT_HOTPLUG_MONITOR_COLOR)
adpa &= ADPA_CRT_HOTPLUG_MONITOR_MASK;
if ((adpa == ADPA_CRT_HOTPLUG_MONITOR_COLOR) ||
(adpa == ADPA_CRT_HOTPLUG_MONITOR_MONO))
ret = true;
else
ret = false;

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,8 @@
#define DP_LINK_CONFIGURATION_SIZE 9
#define IS_eDP(i) ((i)->type == INTEL_OUTPUT_EDP)
struct intel_dp_priv {
uint32_t output_reg;
uint32_t DP;
@ -63,6 +65,19 @@ intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
static void
intel_dp_link_down(struct intel_output *intel_output, uint32_t DP);
void
intel_edp_link_config (struct intel_output *intel_output,
int *lane_num, int *link_bw)
{
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
*lane_num = dp_priv->lane_count;
if (dp_priv->link_bw == DP_LINK_BW_1_62)
*link_bw = 162000;
else if (dp_priv->link_bw == DP_LINK_BW_2_7)
*link_bw = 270000;
}
static int
intel_dp_max_lane_count(struct intel_output *intel_output)
{
@ -206,7 +221,13 @@ intel_dp_aux_ch(struct intel_output *intel_output,
* and would like to run at 2MHz. So, take the
* hrawclk value and divide by 2 and use that
*/
aux_clock_divider = intel_hrawclk(dev) / 2;
if (IS_eDP(intel_output))
aux_clock_divider = 225; /* eDP input clock at 450Mhz */
else if (IS_IGDNG(dev))
aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
else
aux_clock_divider = intel_hrawclk(dev) / 2;
/* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) {
/* Load the send data into the aux channel data registers */
@ -236,7 +257,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
}
/* Clear done status and any errors */
I915_WRITE(ch_ctl, (ctl |
I915_WRITE(ch_ctl, (status |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR));
@ -295,7 +316,7 @@ intel_dp_aux_native_write(struct intel_output *intel_output,
return -1;
msg[0] = AUX_NATIVE_WRITE << 4;
msg[1] = address >> 8;
msg[2] = address;
msg[2] = address & 0xff;
msg[3] = send_bytes - 1;
memcpy(&msg[4], send, send_bytes);
msg_bytes = send_bytes + 4;
@ -387,8 +408,8 @@ intel_dp_i2c_init(struct intel_output *intel_output, const char *name)
memset(&dp_priv->adapter, '\0', sizeof (dp_priv->adapter));
dp_priv->adapter.owner = THIS_MODULE;
dp_priv->adapter.class = I2C_CLASS_DDC;
strncpy (dp_priv->adapter.name, name, sizeof dp_priv->adapter.name - 1);
dp_priv->adapter.name[sizeof dp_priv->adapter.name - 1] = '\0';
strncpy (dp_priv->adapter.name, name, sizeof(dp_priv->adapter.name) - 1);
dp_priv->adapter.name[sizeof(dp_priv->adapter.name) - 1] = '\0';
dp_priv->adapter.algo_data = &dp_priv->algo;
dp_priv->adapter.dev.parent = &intel_output->base.kdev;
@ -493,22 +514,40 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
intel_dp_compute_m_n(3, lane_count,
mode->clock, adjusted_mode->clock, &m_n);
if (intel_crtc->pipe == 0) {
I915_WRITE(PIPEA_GMCH_DATA_M,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(PIPEA_GMCH_DATA_N,
m_n.gmch_n);
I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
if (IS_IGDNG(dev)) {
if (intel_crtc->pipe == 0) {
I915_WRITE(TRANSA_DATA_M1,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
} else {
I915_WRITE(TRANSB_DATA_M1,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
}
} else {
I915_WRITE(PIPEB_GMCH_DATA_M,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(PIPEB_GMCH_DATA_N,
m_n.gmch_n);
I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
if (intel_crtc->pipe == 0) {
I915_WRITE(PIPEA_GMCH_DATA_M,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(PIPEA_GMCH_DATA_N,
m_n.gmch_n);
I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
} else {
I915_WRITE(PIPEB_GMCH_DATA_M,
((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
m_n.gmch_m);
I915_WRITE(PIPEB_GMCH_DATA_N,
m_n.gmch_n);
I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
}
}
}
@ -556,8 +595,38 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (intel_crtc->pipe == 1)
dp_priv->DP |= DP_PIPEB_SELECT;
if (IS_eDP(intel_output)) {
/* don't miss out required setting for eDP */
dp_priv->DP |= DP_PLL_ENABLE;
if (adjusted_mode->clock < 200000)
dp_priv->DP |= DP_PLL_FREQ_160MHZ;
else
dp_priv->DP |= DP_PLL_FREQ_270MHZ;
}
}
static void igdng_edp_backlight_on (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
DRM_DEBUG("\n");
pp = I915_READ(PCH_PP_CONTROL);
pp |= EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
static void igdng_edp_backlight_off (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
DRM_DEBUG("\n");
pp = I915_READ(PCH_PP_CONTROL);
pp &= ~EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
static void
intel_dp_dpms(struct drm_encoder *encoder, int mode)
@ -569,11 +638,17 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
uint32_t dp_reg = I915_READ(dp_priv->output_reg);
if (mode != DRM_MODE_DPMS_ON) {
if (dp_reg & DP_PORT_EN)
if (dp_reg & DP_PORT_EN) {
intel_dp_link_down(intel_output, dp_priv->DP);
if (IS_eDP(intel_output))
igdng_edp_backlight_off(dev);
}
} else {
if (!(dp_reg & DP_PORT_EN))
if (!(dp_reg & DP_PORT_EN)) {
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
if (IS_eDP(intel_output))
igdng_edp_backlight_on(dev);
}
}
dp_priv->dpms_mode = mode;
}
@ -935,6 +1010,23 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
DRM_DEBUG("\n");
if (IS_eDP(intel_output)) {
DP &= ~DP_PLL_ENABLE;
I915_WRITE(dp_priv->output_reg, DP);
POSTING_READ(dp_priv->output_reg);
udelay(100);
}
DP &= ~DP_LINK_TRAIN_MASK;
I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
POSTING_READ(dp_priv->output_reg);
udelay(17000);
if (IS_eDP(intel_output))
DP |= DP_LINK_TRAIN_OFF;
I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
POSTING_READ(dp_priv->output_reg);
}
@ -978,6 +1070,24 @@ intel_dp_check_link_status(struct intel_output *intel_output)
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
static enum drm_connector_status
igdng_dp_detect(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
enum drm_connector_status status;
status = connector_status_disconnected;
if (intel_dp_aux_native_read(intel_output,
0x000, dp_priv->dpcd,
sizeof (dp_priv->dpcd)) == sizeof (dp_priv->dpcd))
{
if (dp_priv->dpcd[0] != 0)
status = connector_status_connected;
}
return status;
}
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
*
@ -996,6 +1106,9 @@ intel_dp_detect(struct drm_connector *connector)
dp_priv->has_audio = false;
if (IS_IGDNG(dev))
return igdng_dp_detect(connector);
temp = I915_READ(PORT_HOTPLUG_EN);
I915_WRITE(PORT_HOTPLUG_EN,
@ -1039,11 +1152,27 @@ intel_dp_detect(struct drm_connector *connector)
static int intel_dp_get_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct drm_device *dev = intel_output->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
/* We should parse the EDID data and find out if it has an audio sink
*/
return intel_ddc_get_modes(intel_output);
ret = intel_ddc_get_modes(intel_output);
if (ret)
return ret;
/* if eDP has no EDID, try to use fixed panel mode from VBT */
if (IS_eDP(intel_output)) {
if (dev_priv->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
drm_mode_probed_add(connector, mode);
return 1;
}
}
return 0;
}
static void
@ -1106,6 +1235,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_dp_priv *dp_priv;
const char *name = NULL;
intel_output = kcalloc(sizeof(struct intel_output) +
sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
@ -1119,7 +1249,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
if (output_reg == DP_A)
intel_output->type = INTEL_OUTPUT_EDP;
else
intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
@ -1139,12 +1272,41 @@ intel_dp_init(struct drm_device *dev, int output_reg)
drm_sysfs_connector_add(connector);
/* Set up the DDC bus. */
intel_dp_i2c_init(intel_output,
(output_reg == DP_B) ? "DPDDC-B" :
(output_reg == DP_C) ? "DPDDC-C" : "DPDDC-D");
switch (output_reg) {
case DP_A:
name = "DPDDC-A";
break;
case DP_B:
case PCH_DP_B:
name = "DPDDC-B";
break;
case DP_C:
case PCH_DP_C:
name = "DPDDC-C";
break;
case DP_D:
case PCH_DP_D:
name = "DPDDC-D";
break;
}
intel_dp_i2c_init(intel_output, name);
intel_output->ddc_bus = &dp_priv->adapter;
intel_output->hot_plug = intel_dp_hot_plug;
if (output_reg == DP_A) {
/* initialize panel mode from VBT if available for eDP */
if (dev_priv->lfp_lvds_vbt_mode) {
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (dev_priv->panel_fixed_mode) {
dev_priv->panel_fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
}
}
}
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached.

View file

@ -55,6 +55,7 @@
#define INTEL_OUTPUT_TVOUT 5
#define INTEL_OUTPUT_HDMI 6
#define INTEL_OUTPUT_DISPLAYPORT 7
#define INTEL_OUTPUT_EDP 8
#define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1
@ -121,6 +122,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *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_encoder_prepare (struct drm_encoder *encoder);

View file

@ -130,16 +130,17 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
}
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_hdmi_priv *hdmi_priv = intel_output->dev_priv;
struct edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected;
hdmi_priv->has_hdmi_sink = false;
edid = drm_get_edid(&intel_output->base,
intel_output->ddc_bus);
hdmi_priv->has_hdmi_sink = false;
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
@ -148,67 +149,10 @@ intel_hdmi_edid_detect(struct drm_connector *connector)
intel_output->base.display_info.raw_edid = NULL;
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
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)
{
struct intel_output *intel_output = to_intel_output(connector);

View file

@ -778,6 +778,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
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,
.ident = "Aopen i945GTt-VFA",
@ -884,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev)
if (IS_IGDNG(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return;
if (dev_priv->edp_support) {
DRM_DEBUG("disable LVDS for eDP support\n");
return;
}
gpio = PCH_GPIOC;
}

View file

@ -31,6 +31,7 @@
#include "drm.h"
#include "drm_crtc.h"
#include "intel_drv.h"
#include "drm_edid.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_sdvo_regs.h"
@ -55,6 +56,12 @@ struct intel_sdvo_priv {
/* Pixel clock limitations reported by the SDVO device, in kHz */
int pixel_clock_min, pixel_clock_max;
/*
* For multiple function SDVO device,
* this is for current attached outputs.
*/
uint16_t attached_output;
/**
* This is set if we're going to treat the device as TV-out.
*
@ -114,6 +121,9 @@ struct intel_sdvo_priv {
u32 save_SDVOX;
};
static bool
intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags);
/**
* Writes the SDVOB or SDVOC with the given value, but always writes both
* SDVOB and SDVOC to work around apparent hardware issues (according to
@ -1435,41 +1445,96 @@ void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
intel_sdvo_read_response(intel_output, &response, 2);
}
static void
intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
static bool
intel_sdvo_multifunc_encoder(struct intel_output *intel_output)
{
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
int caps = 0;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_SVID0))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_CVBS1))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_YPRPB1))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1))
caps++;
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1))
caps++;
return (caps > 1);
}
enum drm_connector_status
intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
enum drm_connector_status status = connector_status_connected;
struct edid *edid = NULL;
edid = drm_get_edid(&intel_output->base,
intel_output->ddc_bus);
if (edid != NULL) {
sdvo_priv->is_hdmi = drm_detect_hdmi_monitor(edid);
/* Don't report the output as connected if it's a DVI-I
* connector with a non-digital EDID coming out.
*/
if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
if (edid->input & DRM_EDID_INPUT_DIGITAL)
sdvo_priv->is_hdmi =
drm_detect_hdmi_monitor(edid);
else
status = connector_status_disconnected;
}
kfree(edid);
intel_output->base.display_info.raw_edid = NULL;
}
} else if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
status = connector_status_disconnected;
return status;
}
static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)
{
u8 response[2];
uint16_t response;
u8 status;
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
status = intel_sdvo_read_response(intel_output, &response, 2);
DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
DRM_DEBUG("SDVO response %d %d\n", response & 0xff, response >> 8);
if (status != SDVO_CMD_STATUS_SUCCESS)
return connector_status_unknown;
if ((response[0] != 0) || (response[1] != 0)) {
intel_sdvo_hdmi_sink_detect(connector);
return connector_status_connected;
} else
if (response == 0)
return connector_status_disconnected;
if (intel_sdvo_multifunc_encoder(intel_output) &&
sdvo_priv->attached_output != response) {
if (sdvo_priv->controlled_output != response &&
intel_sdvo_output_setup(intel_output, response) != true)
return connector_status_unknown;
sdvo_priv->attached_output = response;
}
return intel_sdvo_hdmi_sink_detect(connector, response);
}
static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
@ -1866,16 +1931,101 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
return 0x72;
}
static bool
intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
{
struct drm_connector *connector = &intel_output->base;
struct drm_encoder *encoder = &intel_output->enc;
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
bool ret = true, registered = false;
sdvo_priv->is_tv = false;
intel_output->needs_tv_clock = false;
sdvo_priv->is_lvds = false;
if (device_is_registered(&connector->kdev)) {
drm_sysfs_connector_remove(connector);
registered = true;
}
if (flags &
(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
else
sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
if (intel_sdvo_get_supp_encode(intel_output,
&sdvo_priv->encode) &&
intel_sdvo_get_digital_encoding_mode(intel_output) &&
sdvo_priv->is_hdmi) {
/* enable hdmi encoding mode if supported */
intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
intel_sdvo_set_colorimetry(intel_output,
SDVO_COLORIMETRY_RGB256);
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
}
} else if (flags & SDVO_OUTPUT_SVID0) {
sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
sdvo_priv->is_tv = true;
intel_output->needs_tv_clock = true;
} else if (flags & SDVO_OUTPUT_RGB0) {
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
encoder->encoder_type = DRM_MODE_ENCODER_DAC;
connector->connector_type = DRM_MODE_CONNECTOR_VGA;
} else if (flags & SDVO_OUTPUT_RGB1) {
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
encoder->encoder_type = DRM_MODE_ENCODER_DAC;
connector->connector_type = DRM_MODE_CONNECTOR_VGA;
} else if (flags & SDVO_OUTPUT_LVDS0) {
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
sdvo_priv->is_lvds = true;
} else if (flags & SDVO_OUTPUT_LVDS1) {
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
sdvo_priv->is_lvds = true;
} else {
unsigned char bytes[2];
sdvo_priv->controlled_output = 0;
memcpy(bytes, &sdvo_priv->caps.output_flags, 2);
DRM_DEBUG_KMS(I915_SDVO,
"%s: Unknown SDVO output type (0x%02x%02x)\n",
SDVO_NAME(sdvo_priv),
bytes[0], bytes[1]);
ret = false;
}
if (ret && registered)
ret = drm_sysfs_connector_add(connector) == 0 ? true : false;
return ret;
}
bool intel_sdvo_init(struct drm_device *dev, int output_device)
{
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_sdvo_priv *sdvo_priv;
int connector_type;
u8 ch[0x40];
int i;
int encoder_type;
intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
if (!intel_output) {
@ -1925,88 +2075,28 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
/* In defaut case sdvo lvds is false */
sdvo_priv->is_lvds = false;
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
if (sdvo_priv->caps.output_flags &
(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
else
sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
encoder_type = DRM_MODE_ENCODER_TMDS;
connector_type = DRM_MODE_CONNECTOR_DVID;
if (intel_sdvo_get_supp_encode(intel_output,
&sdvo_priv->encode) &&
intel_sdvo_get_digital_encoding_mode(intel_output) &&
sdvo_priv->is_hdmi) {
/* enable hdmi encoding mode if supported */
intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
intel_sdvo_set_colorimetry(intel_output,
SDVO_COLORIMETRY_RGB256);
connector_type = DRM_MODE_CONNECTOR_HDMIA;
}
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
encoder_type = DRM_MODE_ENCODER_TVDAC;
connector_type = DRM_MODE_CONNECTOR_SVIDEO;
sdvo_priv->is_tv = true;
intel_output->needs_tv_clock = true;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
encoder_type = DRM_MODE_ENCODER_DAC;
connector_type = DRM_MODE_CONNECTOR_VGA;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
encoder_type = DRM_MODE_ENCODER_LVDS;
connector_type = DRM_MODE_CONNECTOR_LVDS;
sdvo_priv->is_lvds = true;
}
else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1)
{
sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
encoder_type = DRM_MODE_ENCODER_LVDS;
connector_type = DRM_MODE_CONNECTOR_LVDS;
sdvo_priv->is_lvds = true;
}
else
{
unsigned char bytes[2];
sdvo_priv->controlled_output = 0;
memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
DRM_DEBUG_KMS(I915_SDVO,
"%s: Unknown SDVO output type (0x%02x%02x)\n",
SDVO_NAME(sdvo_priv),
bytes[0], bytes[1]);
encoder_type = DRM_MODE_ENCODER_NONE;
connector_type = DRM_MODE_CONNECTOR_Unknown;
if (intel_sdvo_output_setup(intel_output,
sdvo_priv->caps.output_flags) != true) {
DRM_DEBUG("SDVO output failed to setup on SDVO%c\n",
output_device == SDVOB ? 'B' : 'C');
goto err_i2c;
}
connector = &intel_output->base;
drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
connector_type);
connector->connector_type);
drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
drm_encoder_init(dev, &intel_output->enc,
&intel_sdvo_enc_funcs, intel_output->enc.encoder_type);
drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);

View file

@ -1490,6 +1490,27 @@ static struct input_res {
{"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.
*
@ -1544,6 +1565,7 @@ intel_tv_get_modes(struct drm_connector *connector)
mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
intel_tv_chose_preferred_modes(connector, mode_ptr);
drm_mode_probed_add(connector, mode_ptr);
count++;
}