Initial import of modesetting for intel driver in DRM
authorDave Airlie <airlied@linux.ie>
Thu, 5 Apr 2007 01:21:06 +0000 (11:21 +1000)
committerDave Airlie <airlied@linux.ie>
Thu, 5 Apr 2007 01:21:06 +0000 (11:21 +1000)
17 files changed:
linux-core/Makefile.kernel
linux-core/drmP.h
linux-core/drm_crtc.c [new file with mode: 0644]
linux-core/drm_crtc.h [new file with mode: 0644]
linux-core/drm_edid.c [new file with mode: 0644]
linux-core/drm_modes.c [new file with mode: 0644]
linux-core/i915_drv.c
linux-core/intel_crt.c [new file with mode: 0644]
linux-core/intel_display.c [new file with mode: 0644]
linux-core/intel_drv.h [new file with mode: 0644]
linux-core/intel_i2c.c [new file with mode: 0644]
linux-core/intel_lvds.c [new file with mode: 0644]
linux-core/intel_modes.c [new file with mode: 0644]
linux-core/intel_sdvo.c [new file with mode: 0644]
linux-core/intel_sdvo_regs.h [new file with mode: 0644]
shared-core/i915_dma.c
shared-core/i915_drv.h

index 6f5b021..ac403f6 100644 (file)
@@ -13,13 +13,15 @@ drm-objs    := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
                drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \
                drm_memory_debug.o ati_pcigart.o drm_sman.o \
                drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \
-               drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o
+               drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \
+               drm_edid.o drm_modes.o
 tdfx-objs   := tdfx_drv.o
 r128-objs   := r128_drv.o r128_cce.o r128_state.o r128_irq.o
 mga-objs    := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
 i810-objs   := i810_drv.o i810_dma.o
 i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \
-               i915_buffer.o
+               i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \
+               intel_sdvo.o intel_modes.o intel_i2c.o
 nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
                nouveau_object.o nouveau_irq.o \
                nv04_timer.o \
index 648e29b..db62ab8 100644 (file)
 
 #include "drm_compat.h"
 
+#include "drm_crtc.h"
+
 /***********************************************************************/
 /** \name Macros to make printk easier */
 /*@{*/
@@ -827,6 +829,9 @@ typedef struct drm_device {
        unsigned int drw_info_length;
        drm_drawable_info_t **drw_info;
        /*@} */
+
+       /* DRM mode setting */
+       struct drm_crtc_config crtc_config;
 } drm_device_t;
 
 #if __OS_HAS_AGP
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
new file mode 100644 (file)
index 0000000..a52d82b
--- /dev/null
@@ -0,0 +1,540 @@
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev)
+{
+       struct drm_framebuffer *fb;
+
+       spin_lock(&dev->crtc_config.config_lock);
+       /* Limit to single framebuffer for now */
+       if (dev->crtc_config.num_fb > 1) {
+               DRM_ERROR("Attempt to add multiple framebuffers failed\n");
+               return NULL;
+       }
+       spin_unlock(&dev->crtc_config.config_lock);
+
+       fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
+       if (!fb) {
+
+               return NULL;
+       }
+
+       fb->dev = dev;
+       spin_lock(&dev->crtc_config.config_lock);
+       dev->crtc_config.num_fb++;
+       list_add(&fb->head, &dev->crtc_config.fb_list);
+       spin_unlock(&dev->crtc_config.config_lock);
+
+       return fb;
+}
+
+void drm_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+       drm_device_t *dev = fb->dev;
+
+       spin_lock(&dev->crtc_config.config_lock);
+       list_del(&fb->head);
+       dev->crtc_config.num_fb--;
+       spin_unlock(&dev->crtc_config.config_lock);
+
+       kfree(fb);
+}
+
+struct drm_crtc *drm_crtc_create(drm_device_t *dev,
+                                const struct drm_crtc_funcs *funcs)
+{
+       struct drm_crtc *crtc = NULL;
+       crtc = kmalloc(sizeof(struct drm_crtc), GFP_KERNEL);
+       if (!crtc)
+               return NULL;
+
+       crtc->dev = dev;
+       crtc->funcs = funcs;
+
+       spin_lock(&dev->crtc_config.config_lock);
+
+       list_add_tail(&crtc->head, &dev->crtc_config.crtc_list);
+       dev->crtc_config.num_crtc++;
+
+       spin_unlock(&dev->crtc_config.config_lock);
+
+       return crtc;
+}
+EXPORT_SYMBOL(drm_crtc_create);
+
+void drm_crtc_destroy(struct drm_crtc *crtc)
+{
+       drm_device_t *dev = crtc->dev;
+
+       if (crtc->funcs->cleanup)
+               (*crtc->funcs->cleanup)(crtc);
+
+       spin_lock(&dev->crtc_config.config_lock);
+       list_del(&crtc->head);
+       dev->crtc_config.num_crtc--;
+       spin_unlock(&dev->crtc_config.config_lock);
+       kfree(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_destroy);
+
+bool drm_crtc_in_use(struct drm_crtc *crtc)
+{
+       struct drm_output *output;
+       drm_device_t *dev = crtc->dev;
+       list_for_each_entry(output, &dev->crtc_config.output_list, head)
+               if (output->crtc == crtc)
+                       return true;
+       return false;
+}
+EXPORT_SYMBOL(drm_crtc_in_use);
+
+void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
+{
+       struct drm_output *output;
+       struct drm_display_mode *mode, *t;
+       int ret;
+       //if (maxX == 0 || maxY == 0) 
+       // TODO
+
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               
+               list_for_each_entry_safe(mode, t, &output->modes, head)
+                       drm_mode_remove(output, mode);
+               
+               output->status = (*output->funcs->detect)(output);
+
+               if (output->status == output_status_disconnected) {
+                       /* TODO set EDID to NULL */
+                       continue;
+               }
+
+               ret = (*output->funcs->get_modes)(output);
+
+               if (ret) {
+                       /* move the modes over to the main mode list */
+                       drm_mode_list_concat(&output->probed_modes,
+                                            &output->modes);
+               }
+
+               if (maxX && maxY)
+                       drm_mode_validate_size(dev, &output->modes, maxX,
+                                              maxY, 0);
+               list_for_each_entry_safe(mode, t, &output->modes, head) {
+                       if (mode->status == MODE_OK)
+                               mode->status = (*output->funcs->mode_valid)(output,mode);
+               }
+               
+
+               drm_mode_prune_invalid(dev, &output->modes, TRUE);
+
+               if (list_empty(&output->modes))
+                       continue;
+
+               drm_mode_sort(&output->modes);
+
+               DRM_DEBUG("Probed modes for %s\n", output->name);
+               list_for_each_entry_safe(mode, t, &output->modes, head) {
+                       mode->vrefresh = drm_mode_vrefresh(mode);
+
+                       drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+                       drm_mode_debug_printmodeline(dev, mode);
+               }
+       }
+}
+
+bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                      int x, int y)
+{
+       drm_device_t *dev = crtc->dev;
+       struct drm_display_mode *adjusted_mode, saved_mode;
+       int saved_x, saved_y;
+       bool didLock = false;
+       bool ret = false;
+       struct drm_output *output;
+
+       adjusted_mode = drm_mode_duplicate(mode);
+
+       crtc->enabled = drm_crtc_in_use(crtc);
+
+       if (!crtc->enabled) {
+               return true;
+       }
+
+       didLock = crtc->funcs->lock(crtc);
+
+       saved_mode = crtc->mode;
+       saved_x = crtc->x;
+       saved_y = crtc->y;
+       
+       /* Update crtc values up front so the driver can rely on them for mode
+        * setting.
+        */
+       crtc->mode = *mode;
+       crtc->x = x;
+       crtc->y = y;
+
+       /* XXX short-circuit changes to base location only */
+       
+       /* Pass our mode to the outputs and the CRTC to give them a chance to
+        * adjust it according to limitations or output properties, and also
+        * a chance to reject the mode entirely.
+        */
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               
+               if (output->crtc != crtc)
+                       continue;
+               
+               if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
+                       goto done;
+               }
+       }
+       
+       if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
+               goto done;
+       }
+
+       /* Prepare the outputs and CRTCs before setting the mode. */
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+
+               if (output->crtc != crtc)
+                       continue;
+               
+               /* Disable the output as the first thing we do. */
+               output->funcs->prepare(output);
+       }
+       
+       crtc->funcs->prepare(crtc);
+       
+       /* Set up the DPLL and any output state that needs to adjust or depend
+        * on the DPLL.
+        */
+       crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y);
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               if (output->crtc == crtc)
+                       output->funcs->mode_set(output, mode, adjusted_mode);
+       }
+       
+       /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
+       crtc->funcs->commit(crtc);
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               if (output->crtc == crtc)
+               {
+                       output->funcs->commit(output);
+#if 0 // TODO def RANDR_12_INTERFACE
+                       if (output->randr_output)
+                               RRPostPendingProperties (output->randr_output);
+#endif
+               }
+       }
+       
+       /* XXX free adjustedmode */
+       ret = TRUE;
+       /* TODO */
+//     if (scrn->pScreen)
+//             drm_crtc_set_screen_sub_pixel_order(dev);
+
+done:
+       if (!ret) {
+               crtc->x = saved_x;
+               crtc->y = saved_y;
+               crtc->mode = saved_mode;
+       }
+       
+       if (didLock)
+               crtc->funcs->unlock (crtc);
+       
+       return ret;
+}
+
+bool drm_set_desired_modes(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+       struct drm_output *output, *list_output;
+
+       list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+               output = NULL;
+
+               list_for_each_entry(list_output, &dev->crtc_config.output_list, head) {
+                       if (list_output->crtc == crtc) {
+                               output = list_output;
+                               break;
+                       }
+               }
+               /* Skip disabled crtcs */
+               if (!output)
+                       continue;
+
+               memset(&crtc->mode, 0, sizeof(crtc->mode));
+               if (!crtc->desired_mode.crtc_hdisplay) {
+                       
+               }
+               if (!drm_crtc_set_mode(crtc, &crtc->desired_mode,
+                                      crtc->desired_x, crtc->desired_y))
+                       return false;
+       }
+
+       drm_disable_unused_functions(dev);
+       return true;
+}
+EXPORT_SYMBOL(drm_set_desired_modes);
+
+void drm_disable_unused_functions(struct drm_device *dev)
+{
+       struct drm_output *output;
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               if (!output->crtc)
+                       (*output->funcs->dpms)(output, DPMSModeOff);
+       }
+
+       list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+               if (!crtc->enabled)
+                       crtc->funcs->dpms(crtc, DPMSModeOff);
+       }
+}
+       
+/**
+ * drm_mode_probed_add - add a mode to the specified output's probed mode list
+ * @output: output the new mode
+ * @mode: mode data
+ *
+ * Add @mode to @output's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode)
+{
+       printk(KERN_ERR "adding DDC mode %s to output %s\n", mode->name,
+              output->name);
+       spin_lock(&output->modes_lock);
+       list_add(&mode->head, &output->probed_modes);
+       spin_unlock(&output->modes_lock);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @output: output list to modify
+ * @mode: mode to remove
+ *
+ * Remove @mode from @output's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode)
+{
+       spin_lock(&output->modes_lock);
+       list_del(&mode->head);
+       spin_unlock(&output->modes_lock);
+       kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/*
+ * Probably belongs in the DRM device structure
+ */
+struct drm_output *drm_output_create(drm_device_t *dev,
+                                    const struct drm_output_funcs *funcs,
+                                    const char *name)
+{
+       struct drm_output *output = NULL;
+
+       output = kmalloc(sizeof(struct drm_output), GFP_KERNEL);
+       if (!output)
+               return NULL;
+               
+       output->dev = dev;
+       output->funcs = funcs;
+       if (name)
+               strncpy(output->name, name, DRM_OUTPUT_LEN);
+       output->name[DRM_OUTPUT_LEN - 1] = 0;
+       output->subpixel_order = SubPixelUnknown;
+       INIT_LIST_HEAD(&output->probed_modes);
+       INIT_LIST_HEAD(&output->modes);
+       spin_lock_init(&output->modes_lock);
+       /* randr_output? */
+       /* output_set_monitor(output)? */
+       /* check for output_ignored(output)? */
+
+       spin_lock(&dev->crtc_config.config_lock);
+       list_add_tail(&output->head, &dev->crtc_config.output_list);
+       dev->crtc_config.num_output++;
+
+       spin_unlock(&dev->crtc_config.config_lock);
+
+       return output;
+
+}
+EXPORT_SYMBOL(drm_output_create);
+
+void drm_output_destroy(struct drm_output *output)
+{
+       struct drm_device *dev = output->dev;
+       struct drm_display_mode *mode, *t;
+
+       if (*output->funcs->cleanup)
+               (*output->funcs->cleanup)(output);
+
+       list_for_each_entry_safe(mode, t, &output->probed_modes, head)
+               drm_mode_remove(output, mode);
+
+       list_for_each_entry_safe(mode, t, &output->modes, head)
+               drm_mode_remove(output, mode);
+
+       spin_lock(&dev->crtc_config.config_lock);
+       list_del(&output->head);
+       spin_unlock(&dev->crtc_config.config_lock);
+       kfree(output);
+}
+EXPORT_SYMBOL(drm_output_destroy);
+
+bool drm_output_rename(struct drm_output *output, const char *name)
+{
+       if (!name)
+               return false;
+
+       strncpy(output->name, name, DRM_OUTPUT_LEN);
+       output->name[DRM_OUTPUT_LEN - 1] = 0;
+//     drm_output_set_monitor(output);
+//     if (drm_output_ignored(output))
+//             return FALSE;
+       return TRUE;
+}
+EXPORT_SYMBOL(drm_output_rename);
+
+void drm_crtc_config_init(drm_device_t *dev)
+{
+       spin_lock_init(&dev->crtc_config.config_lock);
+       INIT_LIST_HEAD(&dev->crtc_config.fb_list);
+       INIT_LIST_HEAD(&dev->crtc_config.crtc_list);
+       INIT_LIST_HEAD(&dev->crtc_config.output_list);
+}
+EXPORT_SYMBOL(drm_crtc_config_init);
+
+void drm_framebuffer_set_object(drm_device_t *dev, unsigned long handle)
+{
+       struct drm_framebuffer *fb;
+       drm_user_object_t *uo;
+       drm_hash_item_t *hash;
+       drm_buffer_object_t *bo;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_ht_find_item(&dev->object_hash, handle, &hash);
+       if (ret) {
+               DRM_ERROR("Couldn't find handle.\n");
+               goto out_err;
+       }
+
+       uo = drm_hash_entry(hash, drm_user_object_t, hash);
+       if (uo->type != drm_buffer_type) {
+               ret = -EINVAL;
+               goto out_err;
+       }
+
+       bo = drm_user_object_entry(uo, drm_buffer_object_t, base);
+       
+       /* get the first fb */
+       list_for_each_entry(fb, &dev->crtc_config.fb_list, head) {
+               fb->offset = bo->offset;
+               break;
+       }
+       ret = 0;
+out_err:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(drm_framebuffer_set_object);
+
+bool drm_initial_config(drm_device_t *dev, bool can_grow)
+{
+       /* do a hardcoded initial configuration here */
+       struct drm_crtc *crtc, *vga_crtc = NULL, *dvi_crtc = NULL;
+       struct drm_framebuffer *fb;
+       struct drm_output *output, *use_output = NULL;
+
+       fb = drm_framebuffer_create(dev);
+       if (!fb)
+               return false;
+
+       fb->pitch = 1024;
+       fb->width = 1024;
+       fb->height = 768;
+       fb->depth = 24;
+       fb->bits_per_pixel = 32;
+       
+       /* bind both CRTCs to this fb */
+       /* only initialise one crtc to enabled state */
+       list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+               crtc->fb = fb;
+               if (!vga_crtc) {
+                       vga_crtc = crtc;
+                       crtc->enabled = 1;
+                       crtc->desired_x = 0;
+                       crtc->desired_y = 0;
+               } 
+#if 0
+               else if (!dvi_crtc) {
+                       dvi_crtc = crtc;
+                       crtc->enabled = 1;
+                       crtc->desired_x = 0;
+                       crtc->desired_y = 0;
+               }
+#endif
+       }
+
+       drm_crtc_probe_output_modes(dev, 1024, 768);
+
+       /* hard bind the CRTCS */
+
+       /* bind analog output to one crtc */
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               struct drm_display_mode *des_mode;
+
+               if (strncmp(output->name, "VGA", 3)) {
+                       output->crtc = vga_crtc;
+                       /* just pull the first mode out of that hat */
+                       list_for_each_entry(des_mode, &output->modes, head)
+                               break;
+                       DRM_DEBUG("Setting desired mode for output %s\n", output->name);
+                       drm_mode_debug_printmodeline(dev, des_mode);
+                       output->crtc->desired_mode = *des_mode;
+                       output->initial_x = 0;
+                       output->initial_y = 0;
+                       use_output = output;
+               } else if (strncmp(output->name, "TMDS", 4)) {
+                       output->crtc = vga_crtc;
+#if 0
+                       /* just pull the first mode out of that hat */
+                       list_for_each_entry(des_mode, &output->modes, head)
+                               break;
+                       DRM_DEBUG("Setting desired mode for output %s\n", output->name);
+                       drm_mode_debug_printmodeline(dev, des_mode);
+                       output->crtc->desired_mode = *des_mode;
+#endif
+                       output->initial_x = 0;
+                       output->initial_y = 0;
+               } else
+                       output->crtc = NULL;
+              
+       }
+
+       return false;
+}
+EXPORT_SYMBOL(drm_initial_config);
+
+void drm_crtc_config_cleanup(drm_device_t *dev)
+{
+       struct drm_output *output, *ot;
+       struct drm_crtc *crtc, *ct;
+       
+       list_for_each_entry_safe(output, ot, &dev->crtc_config.output_list, head) {
+               drm_output_destroy(output);
+       }
+
+       
+       list_for_each_entry_safe(crtc, ct, &dev->crtc_config.crtc_list, head) {
+               drm_crtc_destroy(crtc);
+       }
+}
+EXPORT_SYMBOL(drm_crtc_config_cleanup);
+
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
new file mode 100644 (file)
index 0000000..1be115d
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright Â© 2006 Keith Packard
+ * Copyright Â© 2007 Intel Corporation
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ */
+#ifndef __DRM_CRTC_H__
+#define __DRM_CRTC_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include "drmP.h"
+#include "drm.h"
+
+struct drm_device;
+
+/*
+ * Note on terminology:  here, for brevity and convenience, we refer to output
+ * control chips as 'CRTCs'.  They can control any type of output, VGA, LVDS,
+ * DVI, etc.  And 'screen' refers to the whole of the visible display, which
+ * may span multiple monitors (and therefore multiple CRTC and output
+ * structures).
+ */
+
+enum drm_mode_status {
+    MODE_OK    = 0,    /* Mode OK */
+    MODE_HSYNC,                /* hsync out of range */
+    MODE_VSYNC,                /* vsync out of range */
+    MODE_H_ILLEGAL,    /* mode has illegal horizontal timings */
+    MODE_V_ILLEGAL,    /* mode has illegal horizontal timings */
+    MODE_BAD_WIDTH,    /* requires an unsupported linepitch */
+    MODE_NOMODE,       /* no mode with a maching name */
+    MODE_NO_INTERLACE, /* interlaced mode not supported */
+    MODE_NO_DBLESCAN,  /* doublescan mode not supported */
+    MODE_NO_VSCAN,     /* multiscan mode not supported */
+    MODE_MEM,          /* insufficient video memory */
+    MODE_VIRTUAL_X,    /* mode width too large for specified virtual size */
+    MODE_VIRTUAL_Y,    /* mode height too large for specified virtual size */
+    MODE_MEM_VIRT,     /* insufficient video memory given virtual size */
+    MODE_NOCLOCK,      /* no fixed clock available */
+    MODE_CLOCK_HIGH,   /* clock required is too high */
+    MODE_CLOCK_LOW,    /* clock required is too low */
+    MODE_CLOCK_RANGE,  /* clock/mode isn't in a ClockRange */
+    MODE_BAD_HVALUE,   /* horizontal timing was out of range */
+    MODE_BAD_VVALUE,   /* vertical timing was out of range */
+    MODE_BAD_VSCAN,    /* VScan value out of range */
+    MODE_HSYNC_NARROW, /* horizontal sync too narrow */
+    MODE_HSYNC_WIDE,   /* horizontal sync too wide */
+    MODE_HBLANK_NARROW,        /* horizontal blanking too narrow */
+    MODE_HBLANK_WIDE,  /* horizontal blanking too wide */
+    MODE_VSYNC_NARROW, /* vertical sync too narrow */
+    MODE_VSYNC_WIDE,   /* vertical sync too wide */
+    MODE_VBLANK_NARROW,        /* vertical blanking too narrow */
+    MODE_VBLANK_WIDE,  /* vertical blanking too wide */
+    MODE_PANEL,         /* exceeds panel dimensions */
+    MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */
+    MODE_ONE_WIDTH,     /* only one width is supported */
+    MODE_ONE_HEIGHT,    /* only one height is supported */
+    MODE_ONE_SIZE,      /* only one resolution is supported */
+    MODE_NO_REDUCED,    /* monitor doesn't accept reduced blanking */
+    MODE_BAD = -2,     /* unspecified reason */
+    MODE_ERROR = -1    /* error condition */
+};
+
+#define DRM_MODE_TYPE_BUILTIN  (1<<0)
+#define DRM_MODE_TYPE_CLOCK_C  ((1<<1) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_CRTC_C   ((1<<2) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_PREFERRED        (1<<3)
+#define DRM_MODE_TYPE_DEFAULT  (1<<4)
+#define DRM_MODE_TYPE_USERDEF  (1<<5)
+#define DRM_MODE_TYPE_DRIVER   (1<<6)
+
+#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \
+                                   DRM_MODE_TYPE_CRTC_C)
+
+#define DRM_DISPLAY_MODE_LEN 32
+
+#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \
+       .name = nm, .status = 0, .type = (t), .clock = (c), \
+       .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \
+       .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \
+       .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
+       .vscan = (vs), .flags = (f)
+
+struct drm_display_mode {
+       /* Header */
+       struct list_head head;
+       char name[DRM_DISPLAY_MODE_LEN];
+       enum drm_mode_status status;
+       int type;
+
+       /* Proposed mode values */
+       int clock;
+       int hdisplay;
+       int hsync_start;
+       int hsync_end;
+       int htotal;
+       int hskew;
+       int vdisplay;
+       int vsync_start;
+       int vsync_end;
+       int vtotal;
+       int vscan;
+       unsigned int flags;
+
+       /* Actual mode we give to hw */
+       int clock_index;
+       int synth_clock;
+       int crtc_hdisplay;
+       int crtc_hblank_start;
+       int crtc_hblank_end;
+       int crtc_hsync_start;
+       int crtc_hsync_end;
+       int crtc_htotal;
+       int crtc_hskew;
+       int crtc_vdisplay;
+       int crtc_vblank_start;
+       int crtc_vblank_end;
+       int crtc_vsync_start;
+       int crtc_vsync_end;
+       int crtc_vtotal;
+       int crtc_hadjusted;
+       int crtc_vadjusted;
+
+       /* Driver private mode info */
+       int private_size;
+       int *private;
+       int private_flags;
+
+       int vrefresh;
+       float hsync;//, vrefresh;
+};
+
+/* Video mode flags */
+#define V_PHSYNC       (1<<0)
+#define V_NHSYNC       (1<<1)
+#define V_PVSYNC       (1<<2)
+#define V_NVSYNC       (1<<3)
+#define V_INTERLACE    (1<<4)
+#define V_DBLSCAN      (1<<5)
+#define V_CSYNC                (1<<6)
+#define V_PCSYNC       (1<<7)
+#define V_NCSYNC       (1<<8)
+#define V_HSKEW                (1<<9) /* hskew provided */
+#define V_BCAST                (1<<10)
+#define V_PIXMUX       (1<<11)
+#define V_DBLCLK       (1<<12)
+#define V_CLKDIV2      (1<<13)
+
+#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+#define DPMSModeOn 0
+#define DPMSModeStandby 1
+#define DPMSModeSuspend 2
+#define DPMSModeOff 3
+
+enum drm_output_status {
+       output_status_connected,
+       output_status_disconnected,
+       output_status_unknown,
+};
+
+enum subpixel_order {
+       SubPixelUnknown = 0,
+       SubPixelHorizontalRGB,
+       SubPixelHorizontalBGR,
+       SubPixelVerticalRGB,
+       SubPixelVerticalBGR,
+       SubPixelNone,
+};
+
+struct drm_framebuffer {
+       struct drm_device *dev;
+       struct list_head head;
+       unsigned int pitch;
+       unsigned long offset;
+       unsigned int width;
+       unsigned int height;
+       /* depth can be 15 or 16 */
+       unsigned int depth;
+       int bits_per_pixel;
+       int flags;
+};
+struct drm_crtc;
+struct drm_output;
+
+/**
+ * drm_crtc_funcs - control CRTCs for a given device
+ * @dpms: control display power levels
+ * @save: save CRTC state
+ * @resore: restore CRTC state
+ * @lock: lock the CRTC
+ * @unlock: unlock the CRTC
+ * @shadow_allocate: allocate shadow pixmap
+ * @shadow_create: create shadow pixmap for rotation support
+ * @shadow_destroy: free shadow pixmap
+ * @mode_fixup: fixup proposed mode
+ * @mode_set: set the desired mode on the CRTC
+ * @gamma_set: specify color ramp for CRTC
+ * @cleanup: cleanup driver private state prior to close
+ *
+ * The drm_crtc_funcs structure is the central CRTC management structure
+ * in the DRM.  Each CRTC controls one or more outputs (note that the name
+ * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc.
+ * outputs, not just CRTs).
+ *
+ * Each driver is responsible for filling out this structure at startup time,
+ * in addition to providing other modesetting features, like i2c and DDC
+ * bus accessors.
+ */
+struct drm_crtc_funcs {
+       /*
+        * Control power levels on the CRTC.  If the mode passed in is
+        * unsupported, the provider must use the next lowest power level.
+        */
+       void (*dpms)(struct drm_crtc *crtc, int mode);
+
+       /* JJJ:  Are these needed? */
+       /* Save CRTC state */
+       void (*save)(struct drm_crtc *crtc); /* suspend? */
+       /* Restore CRTC state */
+       void (*restore)(struct drm_crtc *crtc); /* resume? */
+       bool (*lock)(struct drm_crtc *crtc);
+       void (*unlock)(struct drm_crtc *crtc);
+
+       void (*prepare)(struct drm_crtc *crtc);
+       void (*commit)(struct drm_crtc *crtc);
+
+       /* Provider can fixup or change mode timings before modeset occurs */
+       bool (*mode_fixup)(struct drm_crtc *crtc,
+                          struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+       /* Actually set the mode */
+       void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode, int x, int y);
+       /* Set gamma on the CRTC */
+       void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+                         int size);
+       /* Driver cleanup routine */
+       void (*cleanup)(struct drm_crtc *crtc);
+};
+
+/**
+ * drm_crtc - central CRTC control structure
+ * @enabled: is this CRTC enabled?
+ * @x: x position on screen
+ * @y: y position on screen
+ * @desired_mode: new desired mode
+ * @desired_x: desired x for desired_mode
+ * @desired_y: desired y for desired_mode
+ * @funcs: CRTC control functions
+ * @driver_private: arbitrary driver data
+ *
+ * Each CRTC may have one or more outputs associated with it.  This structure
+ * allows the CRTC to be controlled.
+ */
+struct drm_crtc {
+       struct drm_device *dev;
+       struct list_head head;
+
+       /* framebuffer the CRTC is currently bound to */
+       struct drm_framebuffer *fb;
+
+       bool enabled;
+
+       /* JJJ: are these needed? */
+       bool cursor_in_range;
+       bool cursor_shown;
+
+       struct drm_display_mode mode;
+
+       int x, y;
+       struct drm_display_mode desired_mode;
+       int desired_x, desired_y;
+       const struct drm_crtc_funcs *funcs;
+       void *driver_private;
+
+       /* RRCrtcPtr randr_crtc? */
+};
+
+extern struct drm_crtc *drm_crtc_create(struct drm_device *dev,
+                                       const struct drm_crtc_funcs *funcs);
+
+/**
+ * drm_output_funcs - control outputs on a given device
+ * @init: setup this output
+ * @dpms: set power state (see drm_crtc_funcs above)
+ * @save: save output state
+ * @restore: restore output state
+ * @mode_valid: is this mode valid on the given output?
+ * @mode_fixup: try to fixup proposed mode for this output
+ * @mode_set: set this mode
+ * @detect: is this output active?
+ * @get_modes: get mode list for this output
+ * @set_property: property for this output may need update
+ * @cleanup: output is going away, cleanup
+ *
+ * Each CRTC may have one or more outputs attached to it.  The functions
+ * below allow the core DRM code to control outputs, enumerate available modes,
+ * etc.
+ */
+struct drm_output_funcs {
+       void (*init)(struct drm_output *output);
+       void (*dpms)(struct drm_output *output, int mode);
+       void (*save)(struct drm_output *output);
+       void (*restore)(struct drm_output *output);
+       int (*mode_valid)(struct drm_output *output,
+                         struct drm_display_mode *mode);
+       bool (*mode_fixup)(struct drm_output *output,
+                          struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+       void (*prepare)(struct drm_output *output);
+       void (*commit)(struct drm_output *output);
+       void (*mode_set)(struct drm_output *output,
+                        struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode);
+       enum drm_output_status (*detect)(struct drm_output *output);
+       int (*get_modes)(struct drm_output *output);
+       /* JJJ: type checking for properties via property value type */
+       bool (*set_property)(struct drm_output *output, int prop, void *val);
+       void (*cleanup)(struct drm_output *output);
+};
+
+#define DRM_OUTPUT_LEN 32
+/**
+ * drm_output - central DRM output control structure
+ * @crtc: CRTC this output is currently connected to, NULL if none
+ * @possible_crtcs: bitmap of CRTCS this output could be attached to
+ * @possible_clones: bitmap of possible outputs this output could clone
+ * @interlace_allowed: can this output handle interlaced modes?
+ * @doublescan_allowed: can this output handle doublescan?
+ * @available_modes: modes available on this output (from get_modes() + user)
+ * @initial_x: initial x position for this output
+ * @initial_y: initial y position for this output
+ * @status: output connected?
+ * @subpixel_order: for this output
+ * @mm_width: displayable width of output in mm
+ * @mm_height: displayable height of output in mm
+ * @name: name of output (should be one of a few standard names)
+ * @funcs: output control functions
+ * @driver_private: private driver data
+ *
+ * Each output may be connected to one or more CRTCs, or may be clonable by
+ * another output if they can share a CRTC.  Each output also has a specific
+ * position in the broader display (referred to as a 'screen' though it could
+ * span multiple monitors).
+ */
+struct drm_output {
+       struct drm_device *dev;
+       struct list_head head;
+       struct drm_crtc *crtc;
+       unsigned long possible_crtcs;
+       unsigned long possible_clones;
+       bool interlace_allowed;
+       bool doublescan_allowed;
+       spinlock_t modes_lock;
+       struct list_head modes; /* list of modes on this output */
+       /*
+         OptionInfoPtr options;
+         XF86ConfMonitorPtr conf_monitor;
+        */
+       int initial_x, initial_y;
+       enum drm_output_status status;
+
+       /* these are modes added by probing with DDC or the BIOS */
+       struct list_head probed_modes;
+       
+       /* xf86MonPtr MonInfo; */
+       enum subpixel_order subpixel_order;
+       int mm_width, mm_height;
+       char name[DRM_OUTPUT_LEN];
+       const struct drm_output_funcs *funcs;
+       void *driver_private;
+       /* RROutputPtr randr_output? */
+};
+
+/**
+ * struct drm_crtc_config_funcs - configure CRTCs for a given screen layout
+ * @resize: adjust CRTCs as necessary for the proposed layout
+ *
+ * Currently only a resize hook is available.  DRM will call back into the
+ * driver with a new screen width and height.  If the driver can't support
+ * the proposed size, it can return false.  Otherwise it should adjust
+ * the CRTC<->output mappings as needed and update its view of the screen.
+ */
+struct drm_crtc_config_funcs {
+       bool (*resize)(struct drm_device *dev, int width, int height);
+};
+
+/**
+ * drm_crtc_config - CRTC configuration control structure
+ *
+ */
+struct drm_crtc_config {
+       spinlock_t config_lock;
+       /* this is limited to one for now */
+       int num_fb;
+       struct list_head fb_list;
+       int num_output;
+       struct list_head output_list;
+
+       /* int compat_output? */
+       int num_crtc;
+       struct list_head crtc_list;
+
+       int min_width, min_height;
+       int max_width, max_height;
+       /* DamagePtr rotationDamage? */
+       /* DGA stuff? */
+       struct drm_crtc_config_funcs *funcs;
+};
+
+struct drm_output *drm_output_create(struct drm_device *dev,
+                                    const struct drm_output_funcs *funcs,
+                                    const char *name);
+void drm_output_destroy(struct drm_output *output);
+bool drm_output_rename(struct drm_output *output, const char *name);
+
+int drm_add_edid_modes(struct drm_output *output,
+                       struct i2c_adapter *adapter);
+void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode);
+void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode);
+extern struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode);
+extern void drm_mode_debug_printmodeline(struct drm_device *dev,
+                                        struct drm_display_mode *mode);
+extern void drm_crtc_config_init(struct drm_device *dev);
+extern void drm_crtc_config_cleanup(struct drm_device *dev);
+extern void drm_disable_unused_functions(struct drm_device *dev);
+#endif /* __DRM_CRTC_H__ */
diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c
new file mode 100644 (file)
index 0000000..3c12375
--- /dev/null
@@ -0,0 +1,515 @@
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/*
+ * DDC/EDID probing rippped off from FB layer
+ */
+
+#include "edid.h"
+#define DDC_ADDR 0x50
+
+#ifdef BIG_ENDIAN
+#error "EDID structure is little endian, need big endian versions"
+#endif
+
+struct est_timings {
+       u8 t1;
+       u8 t2;
+       u8 mfg_rsvd;
+} __attribute__((packed));
+
+struct std_timing {
+       u8 hsize; /* need to multiply by 8 then add 248 */
+       u8 vfreq:6; /* need to add 60 */
+       u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */
+} __attribute__((packed));
+
+/* If detailed data is pixel timing */
+struct detailed_pixel_timing {
+       u8 hactive_lo;
+       u8 hblank_lo;
+       u8 hblank_hi:4;
+       u8 hactive_hi:4;
+       u8 vactive_lo;
+       u8 vblank_lo;
+       u8 vblank_hi:4;
+       u8 vactive_hi:4;
+       u8 hsync_offset_lo;
+       u8 hsync_pulse_width_lo;
+       u8 vsync_pulse_width_lo:4;
+       u8 vsync_offset_lo:4;
+       u8 hsync_pulse_width_hi:2;
+       u8 hsync_offset_hi:2;
+       u8 vsync_pulse_width_hi:2;
+       u8 vsync_offset_hi:2;
+       u8 width_mm_lo;
+       u8 height_mm_lo;
+       u8 height_mm_hi:4;
+       u8 width_mm_hi:4;
+       u8 hborder;
+       u8 vborder;
+       u8 unknown0:1;
+       u8 vsync_positive:1;
+       u8 hsync_positive:1;
+       u8 separate_sync:2;
+       u8 stereo:1;
+       u8 unknown6:1;
+       u8 interlaced:1;
+} __attribute__((packed));
+
+/* If it's not pixel timing, it'll be one of the below */
+struct detailed_data_string {
+       u8 str[13];
+} __attribute__((packed));
+
+struct detailed_data_monitor_range {
+       u8 min_vfreq;
+       u8 max_vfreq;
+       u8 min_hfreq_khz;
+       u8 max_hfreq_khz;
+       u8 pixel_clock_mhz; /* need to multiply by 10 */
+       u16 sec_gtf_toggle; /* A000=use above, 20=use below */
+       u8 hfreq_start_khz; /* need to multiply by 2 */
+       u8 c; /* need to divide by 2 */
+       u16 m;
+       u8 k;
+       u8 j; /* need to divide by 2 */
+} __attribute__((packed));
+
+struct detailed_data_wpindex {
+       u8 white_y_lo:2;
+       u8 white_x_lo:2;
+       u8 pad:4;
+       u8 white_x_hi;
+       u8 white_y_hi;
+       u8 gamma; /* need to divide by 100 then add 1 */
+} __attribute__((packed));
+
+struct detailed_data_color_point {
+       u8 windex1;
+       u8 wpindex1[3];
+       u8 windex2;
+       u8 wpindex2[3];
+} __attribute__((packed));
+
+struct detailed_non_pixel {
+       u8 pad1;
+       u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
+                   fb=color point data, fa=standard timing data,
+                   f9=undefined, f8=mfg. reserved */
+       u8 pad2;
+       union {
+               struct detailed_data_string str;
+               struct detailed_data_monitor_range range;
+               struct detailed_data_wpindex color;
+               struct std_timing timings[5];
+       } data;
+} __attribute__((packed));
+
+#define EDID_DETAIL_STD_MODES 0xfa
+#define EDID_DETAIL_CPDATA 0xfb
+#define EDID_DETAIL_NAME 0xfc
+#define EDID_DETAIL_RANGE 0xfd
+#define EDID_DETAIL_STRING 0xfe
+#define EDID_DETAIL_SERIAL 0xff
+
+struct detailed_timing {
+       u16 pixel_clock; /* need to multiply by 10 KHz */
+       union {
+               struct detailed_pixel_timing pixel_data;
+               struct detailed_non_pixel other_data;
+       } data;
+} __attribute__((packed));
+
+struct edid {
+       u8 header[8];
+       /* Vendor & product info */
+       u16 mfg_id;
+       u16 prod_code;
+       u32 serial;
+       u8 mfg_week;
+       u8 mfg_year;
+       /* EDID version */
+       u8 version;
+       u8 revision;
+       /* Display info: */
+       /*   input definition */
+       u8 serration_vsync:1;
+       u8 sync_on_green:1;
+       u8 composite_sync:1;
+       u8 separate_syncs:1;
+       u8 blank_to_black:1;
+       u8 video_level:2;
+       u8 digital:1; /* bits below must be zero if set */
+       u8 width_cm;
+       u8 height_cm;
+       u8 gamma;
+       /*   feature support */
+       u8 default_gtf:1;
+       u8 preferred_timing:1;
+       u8 standard_color:1;
+       u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */
+       u8 pm_active_off:1;
+       u8 pm_suspend:1;
+       u8 pm_standby:1;
+       /* Color characteristics */
+       u8 red_green_lo;
+       u8 black_white_lo;
+       u8 red_x;
+       u8 red_y;
+       u8 green_x;
+       u8 green_y;
+       u8 blue_x;
+       u8 blue_y;
+       u8 white_x;
+       u8 white_y;
+       /* Est. timings and mfg rsvd timings*/
+       struct est_timings established_timings;
+       /* Standard timings 1-8*/
+       struct std_timing standard_timings[8];
+       /* Detailing timings 1-4 */
+       struct detailed_timing detailed_timings[4];
+       /* Number of 128 byte ext. blocks */
+       u8 extensions;
+       /* Checksum */
+       u8 checksum;
+} __attribute__((packed));
+
+static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+static bool edid_valid(struct edid *edid)
+{
+       int i;
+       u8 csum = 0;
+       u8 *raw_edid = (u8 *)edid;
+
+       if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+               goto bad;
+       if (edid->version != 1)
+               goto bad;
+       if (edid->revision <= 0 || edid->revision > 3)
+               goto bad;
+
+       for (i = 0; i < EDID_LENGTH; i++)
+               csum += raw_edid[i];
+       if (csum)
+               goto bad;
+
+       return 1;
+
+bad:
+       return 0;
+}
+
+/**
+ * drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @t: standard timing params
+ *
+ * Take the standard timing params (in this case width, aspect, and refresh)
+ * and convert them into a real mode using CVT.
+ *
+ * Punts for now.
+ */
+struct drm_display_mode *drm_mode_std(struct std_timing *t)
+{
+//     struct fb_videomode mode;
+
+//     fb_find_mode_cvt(&mode, 0, 0);
+       /* JJJ:  convert to drm_display_mode */
+       struct drm_display_mode *mode;
+       int hsize = t->hsize * 8 + 248, vsize;
+
+       mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+       if (!mode)
+               return NULL;
+
+       if (t->aspect_ratio == 0)
+               vsize = (hsize * 10) / 16;
+       else if (t->aspect_ratio == 1)
+               vsize = (hsize * 3) / 4;
+       else if (t->aspect_ratio == 2)
+               vsize = (hsize * 4) / 5;
+       else
+               vsize = (hsize * 9) / 16;
+
+       snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", hsize, vsize);
+
+       return mode;
+}
+
+struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing,
+                                          bool preferred)
+{
+       struct drm_display_mode *mode;
+       struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+
+       if (pt->stereo) {
+               printk(KERN_ERR "stereo mode not supported\n");
+               return NULL;
+       }
+       if (!pt->separate_sync) {
+               printk(KERN_ERR "integrated sync not supported\n");
+               return NULL;
+       }
+
+       mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+       if (!mode)
+               return NULL;
+
+       mode->type = DRM_MODE_TYPE_DRIVER;
+       mode->type |= preferred ? DRM_MODE_TYPE_PREFERRED : 0;
+       mode->clock = timing->pixel_clock / 100;
+
+       mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo;
+       mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) |
+                                             pt->hsync_offset_lo);
+       mode->hsync_end = mode->hsync_start +
+               ((pt->hsync_pulse_width_hi << 8) |
+                pt->hsync_pulse_width_lo);
+       mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo);
+
+       mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo;
+       mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) |
+                                             pt->vsync_offset_lo);
+       mode->vsync_end = mode->vsync_start +
+               ((pt->vsync_pulse_width_hi << 8) |
+                pt->vsync_pulse_width_lo);
+       mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo);
+
+       snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
+                mode->vdisplay);
+
+       if (pt->interlaced)
+               mode->flags |= V_INTERLACE;
+
+       mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC;
+       mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC;
+
+       return mode;
+}
+
+static struct drm_display_mode established_modes[] = {
+       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+                  968, 1056, 0, 600, 601, 605, 628, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */
+       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+                  896, 1024, 0, 600, 601, 603,  625, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 800x600@56Hz */
+       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+                  720, 840, 0, 480, 481, 484, 500, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 640x480@75Hz */
+       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+                  704,  832, 0, 480, 489, 491, 520, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 640x480@72Hz */
+       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+                  768,  864, 0, 480, 483, 486, 525, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 640x480@67Hz */
+       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+                  752, 800, 0, 480, 490, 492, 525, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+                  846, 900, 0, 400, 421, 423,  449, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 720x400@88Hz */
+       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+                  846,  900, 0, 400, 412, 414, 449, 0,
+                  V_NHSYNC | V_PVSYNC) }, /* 720x400@70Hz */
+       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+                  1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+                  1136, 1312, 0,  768, 769, 772, 800, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 1024x768@75Hz */
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+                  1184, 1328, 0,  768, 771, 777, 806, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 1024x768@70Hz */
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+                  1184, 1344, 0,  768, 771, 777, 806, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 1024x768@60Hz */
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+                  1208, 1264, 0, 768, 768, 776, 817, 0,
+                  V_PHSYNC | V_PVSYNC | V_INTERLACE) }, /* 1024x768@43Hz */
+       { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+                  928, 1152, 0, 624, 625, 628, 667, 0,
+                  V_NHSYNC | V_NVSYNC) }, /* 832x624@75Hz */
+       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+                  896, 1056, 0, 600, 601, 604,  625, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 800x600@75Hz */
+       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+                  976, 1040, 0, 600, 637, 643, 666, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 800x600@72Hz */
+       { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+                  1344, 1600, 0,  864, 865, 868, 900, 0,
+                  V_PHSYNC | V_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above).  Tease them out and add them to the global modes list.
+ */
+static int add_established_modes(struct drm_output *output, struct edid *edid)
+{
+       unsigned long est_bits = edid->established_timings.t1 |
+               (edid->established_timings.t2 << 8) |
+               ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+       int i, modes = 0;
+
+       for (i = 0; i <= EDID_EST_TIMINGS; i++)
+               if (est_bits & (1<<i)) {
+                       drm_mode_probed_add(output,
+                                   drm_mode_duplicate(&established_modes[i]));
+                       modes++;
+               }
+
+       return modes;
+}
+
+/**
+ * add_established_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the CVT standard.  Grab them from
+ * @edid, calculate them, and add them to the list.
+ */
+static int add_standard_modes(struct drm_output *output, struct edid *edid)
+{
+       int i, modes = 0;
+
+       for (i = 0; i < EDID_STD_TIMINGS; i++) {
+               struct std_timing *t = &edid->standard_timings[i];
+
+               /* If std timings bytes are 1, 1 it's empty */
+               if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1)
+                       continue;
+
+               drm_mode_probed_add(output,
+                                drm_mode_std(&edid->standard_timings[i]));
+               modes++;
+       }
+
+       return modes;
+}
+
+/**
+ * add_detailed_modes - get detailed mode info from EDID data
+ * @edid: EDID block to scan
+ *
+ * Some of the detailed timing sections may contain mode information.  Grab
+ * it and add it to the list.
+ */
+static int add_detailed_info(struct drm_output *output, struct edid *edid)
+{
+       int i, j, modes = 0;
+       bool preferred = 0;
+
+       for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+               struct detailed_timing *timing = &edid->detailed_timings[i];
+               struct detailed_non_pixel *data = &timing->data.other_data;
+
+               /* EDID up to and including 1.2 may put monitor info here */
+               if (edid->version == 1 && edid->revision < 3)
+                       continue;
+
+               /* Detailed mode timing */
+               if (timing->pixel_clock) {
+                       if (i == 0 && edid->preferred_timing)
+                               preferred = 1;
+                       drm_mode_probed_add(output,
+                                        drm_mode_detailed(timing, preferred));
+                       modes++;
+                       continue;
+               }
+
+               /* Other timing or info */
+               switch (data->type) {
+               case EDID_DETAIL_SERIAL:
+                       break;
+               case EDID_DETAIL_STRING:
+                       break;
+               case EDID_DETAIL_RANGE:
+                       break;
+               case EDID_DETAIL_NAME:
+                       break;
+               case EDID_DETAIL_CPDATA:
+                       break;
+               case EDID_DETAIL_STD_MODES:
+                       /* Five modes per detailed section */
+                       for (j = 0; j < 5; i++) {
+                               struct std_timing *std;
+
+                               std = &data->data.timings[j];
+                               drm_mode_probed_add(output, drm_mode_std(std));
+                               modes++;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return modes;
+}
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @output: output we're probing
+ * @adapter: i2c adapter to use for DDC
+ *
+ * Poke the given output's i2c channel to grab EDID data if possible.  If we
+ * get any, add the specified modes to the output's mode list.
+ *
+ * Return number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter)
+{
+       struct edid *edid;
+       u8 *raw_edid;
+       int i, est_modes, std_modes, det_modes;
+
+       edid = (struct edid *)fb_ddc_read(adapter);
+
+       if (!edid) {
+               dev_warn(&output->dev->pdev->dev, "no EDID data\n");
+               goto out_err;
+       }
+
+       if (!edid_valid(edid)) {
+               dev_warn(&output->dev->pdev->dev, "EDID invalid, ignoring.\n");
+               goto out_err;
+       }
+
+       est_modes = add_established_modes(output, edid);
+       std_modes = add_standard_modes(output, edid);
+       det_modes = add_detailed_info(output, edid);
+       printk(KERN_ERR "est modes: %d, std_modes: %d, det_modes: %d\n",
+              est_modes, std_modes, det_modes);
+
+       raw_edid = (u8 *)edid;
+       printk(KERN_ERR "EDID:\n" KERN_ERR);
+       for (i = 0; i < EDID_LENGTH; i++) {
+               if (i != 0 && ((i % 16) == 0))
+                   printk("\n" KERN_ERR);
+               printk("%02x", raw_edid[i] & 0xff);
+       }
+       printk("\n");
+
+       printk(KERN_ERR "EDID info:\n");
+       printk(KERN_ERR "  mfg_id: 0x%04x\n", edid->mfg_id);
+       printk(KERN_ERR "  digital?  %s\n", edid->digital ? "Yes" : "No");
+       printk(KERN_ERR "  extensions: %d\n", edid->extensions);
+
+       return est_modes + std_modes + det_modes;
+
+out_err:
+       kfree(edid);
+       return 0;
+}
+EXPORT_SYMBOL(drm_add_edid_modes);
diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c
new file mode 100644 (file)
index 0000000..2347a66
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright Â© 2007 Dave Airlie
+ * 
+ * Based on code from X.org - Copyright (c) 1997-2003 by The XFree86 Project, Inc.
+ */
+
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+void drm_mode_debug_printmodeline(struct drm_device *dev,
+                                 struct drm_display_mode *mode)
+{
+       DRM_DEBUG("Modeline \"%s\" %d %d %d %d %d %d %d %d %d %d\n",
+                 mode->name, mode->vrefresh, mode->clock,
+                 mode->hdisplay, mode->hsync_start,
+                 mode->hsync_end, mode->htotal,
+                 mode->vdisplay, mode->vsync_start,
+                 mode->vsync_end, mode->vtotal);
+}
+EXPORT_SYMBOL(drm_mode_debug_printmodeline);
+
+void drm_mode_list_concat(struct list_head *head, struct list_head *new)
+{
+
+       struct list_head *entry, *tmp;
+
+       list_for_each_safe(entry, tmp, head) {
+               list_move_tail(entry, new);
+       }
+}
+
+int drm_mode_width(struct drm_display_mode *mode)
+{
+       return mode->hdisplay;
+
+}
+EXPORT_SYMBOL(drm_mode_width);
+
+int drm_mode_height(struct drm_display_mode *mode)
+{
+       return mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_mode_height);
+
+int drm_mode_vrefresh(struct drm_display_mode *mode)
+{
+       int refresh = 0;
+
+       if (mode->vrefresh > 0)
+               refresh = mode->vrefresh;
+       else if (mode->htotal > 0 && mode->vtotal > 0) {
+               refresh = ((mode->clock * 1000) * 1000) / mode->htotal / mode->vtotal;
+               if (mode->flags & V_INTERLACE)
+                       refresh *= 2;
+               if (mode->flags & V_DBLSCAN)
+                       refresh /= 2;
+               if (mode->vscan > 1)
+                       refresh /= mode->vscan;
+       }
+       return refresh;
+}
+EXPORT_SYMBOL(drm_mode_vrefresh);
+       
+
+void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
+{
+       if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
+               return;
+
+       p->crtc_hdisplay = p->hdisplay;
+       p->crtc_hsync_start = p->hsync_start;
+       p->crtc_hsync_end = p->hsync_end;
+       p->crtc_htotal = p->htotal;
+       p->crtc_hskew = p->hskew;
+       p->crtc_vdisplay = p->vdisplay;
+       p->crtc_vsync_start = p->vsync_start;
+       p->crtc_vsync_end = p->vsync_end;
+       p->crtc_vtotal = p->vtotal;
+
+       if (p->flags & V_INTERLACE) {
+               if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
+                       p->crtc_vdisplay /= 2;
+                       p->crtc_vsync_start /= 2;
+                       p->crtc_vsync_end /= 2;
+                       p->crtc_vtotal /= 2;
+               }
+
+               p->crtc_vtotal |= 1;
+       }
+
+       if (p->flags & V_DBLSCAN) {
+               p->crtc_vdisplay *= 2;
+               p->crtc_vsync_start *= 2;
+               p->crtc_vsync_end *= 2;
+               p->crtc_vtotal *= 2;
+       }
+
+       if (p->vscan > 1) {
+               p->crtc_vdisplay *= p->vscan;
+               p->crtc_vsync_start *= p->vscan;
+               p->crtc_vsync_end *= p->vscan;
+               p->crtc_vtotal *= p->vscan;
+       }
+
+       p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
+       p->crtc_vblank_end = min(p->crtc_vsync_end, p->crtc_vtotal);
+       p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
+       p->crtc_hblank_end = min(p->crtc_hsync_end, p->crtc_htotal);
+
+       p->crtc_hadjusted = false;
+       p->crtc_vadjusted = false;
+}
+EXPORT_SYMBOL(drm_mode_set_crtcinfo);
+
+
+struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode)
+{
+       struct drm_display_mode *nmode;
+
+       nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+       if (!nmode)
+               return NULL;
+
+       *nmode = *mode;
+       INIT_LIST_HEAD(&nmode->head);
+       return nmode;
+}
+EXPORT_SYMBOL(drm_mode_duplicate);
+
+bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2)
+{
+       if (mode1->clock == mode2->clock &&
+           mode1->hdisplay == mode2->hdisplay &&
+           mode1->hsync_start == mode2->hsync_start &&
+           mode1->hsync_end == mode2->hsync_end &&
+           mode1->htotal == mode2->htotal &&
+           mode1->hskew == mode2->hskew &&
+           mode1->vdisplay == mode2->vdisplay &&
+           mode1->vsync_start == mode2->vsync_start &&
+           mode1->vsync_end == mode2->vsync_end &&
+           mode1->vtotal == mode2->vtotal &&
+           mode1->vscan == mode2->vscan &&
+           mode1->flags == mode2->flags)
+               return true;
+       
+       return false;
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+void drm_mode_validate_size(struct drm_device *dev,
+                           struct list_head *mode_list,
+                           int maxX, int maxY, int maxPitch)
+{
+       struct drm_display_mode *mode;
+
+       list_for_each_entry(mode, mode_list, head) {
+               if (maxPitch > 0 && mode->hdisplay > maxPitch)
+                       mode->status = MODE_BAD_WIDTH;
+               
+               if (maxX > 0 && mode->hdisplay > maxX)
+                       mode->status = MODE_VIRTUAL_X;
+
+               if (maxY > 0 && mode->vdisplay > maxY)
+                       mode->status = MODE_VIRTUAL_Y;
+       }
+}
+EXPORT_SYMBOL(drm_mode_validate_size);
+
+void drm_mode_validate_clocks(struct drm_device *dev,
+                             struct list_head *mode_list,
+                             int *min, int *max, int n_ranges)
+{
+       struct drm_display_mode *mode;
+       int i;
+
+       list_for_each_entry(mode, mode_list, head) {
+               bool good = false;
+               for (i = 0; i < n_ranges; i++) {
+                       if (mode->clock >= min[i] && mode->clock <= max[i]) {
+                               good = true;
+                               break;
+                       }
+               }
+               if (!good)
+                       mode->status = MODE_CLOCK_RANGE;
+       }
+}
+EXPORT_SYMBOL(drm_mode_validate_clocks);
+
+void drm_mode_prune_invalid(struct drm_device *dev,
+                           struct list_head *mode_list, bool verbose)
+{
+       struct drm_display_mode *mode, *t;
+
+       list_for_each_entry_safe(mode, t, mode_list, head) {
+               if (mode->status != MODE_OK) {
+                       list_del(&mode->head);
+                       if (verbose)
+                               DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+                       kfree(mode);
+               }
+       }
+}
+
+static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
+{
+       struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
+       struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
+       int diff;
+
+       diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
+               ((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
+       if (diff)
+               return diff;
+       diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
+       if (diff)
+               return diff;
+       diff = b->clock - a->clock;
+       return diff;
+}
+
+/* list sort from Mark J Roberts (mjr@znex.org) */
+void list_sort(struct list_head *head, int (*cmp)(struct list_head *a, struct list_head *b))
+{
+       struct list_head *p, *q, *e, *list, *tail, *oldhead;
+       int insize, nmerges, psize, qsize, i;
+       
+       list = head->next;
+       list_del(head);
+       insize = 1;
+       for (;;) {
+               p = oldhead = list;
+               list = tail = NULL;
+               nmerges = 0;
+               
+               while (p) {
+                       nmerges++;
+                       q = p;
+                       psize = 0;
+                       for (i = 0; i < insize; i++) {
+                               psize++;
+                               q = q->next == oldhead ? NULL : q->next;
+                               if (!q)
+                                       break;
+                       }
+                       
+                       qsize = insize;
+                       while (psize > 0 || (qsize > 0 && q)) {
+                               if (!psize) {
+                                       e = q;
+                                       q = q->next;
+                                       qsize--;
+                                       if (q == oldhead)
+                                               q = NULL;
+                               } else if (!qsize || !q) {
+                                       e = p;
+                                       p = p->next;
+                                       psize--;
+                                       if (p == oldhead)
+                                               p = NULL;
+                               } else if (cmp(p, q) <= 0) {
+                                       e = p;
+                                       p = p->next;
+                                       psize--;
+                                       if (p == oldhead)
+                                               p = NULL;
+                               } else {
+                                       e = q;
+                                       q = q->next;
+                                       qsize--;
+                                       if (q == oldhead)
+                                               q = NULL;
+                               }
+                               if (tail)
+                                       tail->next = e;
+                               else
+                                       list = e;
+                               e->prev = tail;
+                               tail = e;
+                       }
+                       p = q;
+               }
+               
+               tail->next = list;
+               list->prev = tail;
+               
+               if (nmerges <= 1)
+                       break;
+               
+               insize *= 2;
+       }
+       
+       head->next = list;
+       head->prev = list->prev;
+       list->prev->next = head;
+       list->prev = head;
+}
+
+void drm_mode_sort(struct list_head *mode_list)
+{
+       list_sort(mode_list, drm_mode_compare);
+}
index 7fdb083..b9e624f 100644 (file)
@@ -79,6 +79,7 @@ static struct drm_driver driver = {
            DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL |
            DRIVER_IRQ_VBL2,
        .load = i915_driver_load,
+       .unload = i915_driver_unload,
        .firstopen = i915_driver_firstopen,
        .lastclose = i915_driver_lastclose,
        .preclose = i915_driver_preclose,
diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c
new file mode 100644 (file)
index 0000000..5ff9f79
--- /dev/null
@@ -0,0 +1,226 @@
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+static void intel_crt_dpms(struct drm_output *output, int mode)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 temp;
+       
+       temp = I915_READ(ADPA);
+       temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+       temp &= ~ADPA_DAC_ENABLE;
+       
+       switch(mode) {
+       case DPMSModeOn:
+               temp |= ADPA_DAC_ENABLE;
+               break;
+       case DPMSModeStandby:
+               temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+               break;
+       case DPMSModeSuspend:
+               temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+               break;
+       case DPMSModeOff:
+               temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+               break;
+       }
+       
+       I915_WRITE(ADPA, temp);
+}
+
+static void intel_crt_save(struct drm_output *output)
+{
+       
+}
+
+static void intel_crt_restore(struct drm_output *output)
+{
+
+}
+
+static int intel_crt_mode_valid(struct drm_output *output,
+                               struct drm_display_mode *mode)
+{
+       if (mode->flags & V_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       if (mode->clock > 400000 || mode->clock < 25000)
+               return MODE_CLOCK_RANGE;
+
+       return MODE_OK;
+}
+
+static bool intel_crt_mode_fixup(struct drm_output *output,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void intel_crt_mode_set(struct drm_output *output,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode)
+{
+       drm_device_t *dev = output->dev;
+       struct drm_crtc *crtc = output->crtc;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int dpll_md_reg;
+       u32 adpa, dpll_md;
+
+       if (intel_crtc->pipe == 0) 
+               dpll_md_reg = DPLL_A_MD;
+       else
+               dpll_md_reg = DPLL_B_MD;
+
+       /*
+        * Disable separate mode multiplier used when cloning SDVO to CRT
+        * XXX this needs to be adjusted when we really are cloning
+        */
+       if (IS_I965G(dev))
+       {
+               dpll_md = I915_READ(dpll_md_reg);
+               I915_WRITE(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+       }
+       
+       adpa = 0;
+       if (adjusted_mode->flags & V_PHSYNC)
+               adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+       if (adjusted_mode->flags & V_PVSYNC)
+               adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+       
+       if (intel_crtc->pipe == 0)
+               adpa |= ADPA_PIPE_A_SELECT;
+       else
+               adpa |= ADPA_PIPE_B_SELECT;
+       
+       I915_WRITE(ADPA, adpa);
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
+ *
+ * Only for I945G/GM.
+ *
+ * \return TRUE if CRT is connected.
+ * \return FALSE if CRT is disconnected.
+ */
+static bool intel_crt_detect_hotplug(struct drm_output *output)
+{
+       drm_device_t *dev = output->dev;
+//     struct intel_output *intel_output = output->driver_private;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 temp;
+       const int timeout_ms = 1000;
+       int starttime, curtime;
+
+       temp = I915_READ(PORT_HOTPLUG_EN);
+
+       I915_WRITE(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+#if 0  
+       for (curtime = starttime = GetTimeInMillis();
+            (curtime - starttime) < timeout_ms; curtime = GetTimeInMillis())
+       {
+               if ((I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0)
+                       break;
+       }
+#endif
+       if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
+           CRT_HOTPLUG_MONITOR_COLOR)
+       {
+               return true;
+       } else {
+               return false;
+       }
+}
+
+static bool intel_crt_detect_ddc(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+
+       /* CRT should always be at 0, but check anyway */
+       if (intel_output->type != INTEL_OUTPUT_ANALOG)
+               return false;
+       
+       return intel_ddc_probe(output);
+}
+
+static enum drm_output_status intel_crt_detect(struct drm_output *output)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       
+       if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) {
+               if (intel_crt_detect_hotplug(output))
+                       return output_status_connected;
+               else
+                       return output_status_disconnected;
+       }
+
+       if (intel_crt_detect_ddc(output))
+               return output_status_connected;
+
+       /* TODO use load detect */
+       return output_status_unknown;
+}
+
+static void intel_crt_destroy(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+
+       intel_i2c_destroy(intel_output->ddc_bus);
+
+       if (output->driver_private)
+               kfree(output->driver_private);
+}
+
+/*
+ * Routines for controlling stuff on the analog port
+ */
+static const struct drm_output_funcs intel_crt_output_funcs = {
+       .dpms = intel_crt_dpms,
+       .save = intel_crt_save,
+       .restore = intel_crt_restore,
+       .mode_valid = intel_crt_mode_valid,
+       .mode_fixup = intel_crt_mode_fixup,
+       .prepare = intel_output_prepare,
+       .mode_set = intel_crt_mode_set,
+       .commit = intel_output_commit,
+       .detect = intel_crt_detect,
+       .get_modes = intel_ddc_get_modes,
+       .cleanup = intel_crt_destroy,
+};
+
+void intel_crt_init(drm_device_t *dev)
+{
+       struct drm_output *output;
+       struct intel_output *intel_output;
+       int modes;
+
+       output = drm_output_create (dev, &intel_crt_output_funcs, "VGA");
+
+       intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL);
+       if (!intel_output) {
+               drm_output_destroy(output);
+               return;
+       }
+
+       intel_output->type = INTEL_OUTPUT_ANALOG;
+       output->driver_private = intel_output;
+       output->interlace_allowed = 0;
+       output->doublescan_allowed = 0;
+
+       /* Set up the DDC bus. */
+       intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+       if (!intel_output->ddc_bus) {
+               dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+                          "failed.\n");
+               return;
+       }
+}
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
new file mode 100644 (file)
index 0000000..495f470
--- /dev/null
@@ -0,0 +1,1087 @@
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+
+typedef struct {
+    /* given values */    
+    int n;
+    int m1, m2;
+    int p1, p2;
+    /* derived values */
+    int        dot;
+    int        vco;
+    int        m;
+    int        p;
+} intel_clock_t;
+
+typedef struct {
+    int        min, max;
+} intel_range_t;
+
+typedef struct {
+    int        dot_limit;
+    int        p2_slow, p2_fast;
+} intel_p2_t;
+
+#define INTEL_P2_NUM                 2
+
+typedef struct {
+    intel_range_t   dot, vco, n, m, m1, m2, p, p1;
+    intel_p2_t     p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN             25000
+#define I8XX_DOT_MAX            350000
+#define I8XX_VCO_MIN            930000
+#define I8XX_VCO_MAX           1400000
+#define I8XX_N_MIN                   3
+#define I8XX_N_MAX                  16
+#define I8XX_M_MIN                  96
+#define I8XX_M_MAX                 140
+#define I8XX_M1_MIN                 18
+#define I8XX_M1_MAX                 26
+#define I8XX_M2_MIN                  6
+#define I8XX_M2_MAX                 16
+#define I8XX_P_MIN                   4
+#define I8XX_P_MAX                 128
+#define I8XX_P1_MIN                  2
+#define I8XX_P1_MAX                 33
+#define I8XX_P1_LVDS_MIN             1
+#define I8XX_P1_LVDS_MAX             6
+#define I8XX_P2_SLOW                 4
+#define I8XX_P2_FAST                 2
+#define I8XX_P2_LVDS_SLOW            14
+#define I8XX_P2_LVDS_FAST            14 /* No fast option */
+#define I8XX_P2_SLOW_LIMIT      165000
+
+#define I9XX_DOT_MIN             20000
+#define I9XX_DOT_MAX            400000
+#define I9XX_VCO_MIN           1400000
+#define I9XX_VCO_MAX           2800000
+#define I9XX_N_MIN                   3
+#define I9XX_N_MAX                   8
+#define I9XX_M_MIN                  70
+#define I9XX_M_MAX                 120
+#define I9XX_M1_MIN                 10
+#define I9XX_M1_MAX                 20
+#define I9XX_M2_MIN                  5
+#define I9XX_M2_MAX                  9
+#define I9XX_P_SDVO_DAC_MIN          5
+#define I9XX_P_SDVO_DAC_MAX         80
+#define I9XX_P_LVDS_MIN                      7
+#define I9XX_P_LVDS_MAX                     98
+#define I9XX_P1_MIN                  1
+#define I9XX_P1_MAX                  8
+#define I9XX_P2_SDVO_DAC_SLOW               10
+#define I9XX_P2_SDVO_DAC_FAST                5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT     200000
+#define I9XX_P2_LVDS_SLOW                   14
+#define I9XX_P2_LVDS_FAST                    7
+#define I9XX_P2_LVDS_SLOW_LIMIT                 112000
+
+#define INTEL_LIMIT_I8XX_DVO_DAC    0
+#define INTEL_LIMIT_I8XX_LVDS      1
+#define INTEL_LIMIT_I9XX_SDVO_DAC   2
+#define INTEL_LIMIT_I9XX_LVDS      3
+
+static const intel_limit_t intel_limits[] = {
+    { /* INTEL_LIMIT_I8XX_DVO_DAC */
+        .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
+        .vco = { .min = I8XX_VCO_MIN,          .max = I8XX_VCO_MAX },
+        .n   = { .min = I8XX_N_MIN,            .max = I8XX_N_MAX },
+        .m   = { .min = I8XX_M_MIN,            .max = I8XX_M_MAX },
+        .m1  = { .min = I8XX_M1_MIN,           .max = I8XX_M1_MAX },
+        .m2  = { .min = I8XX_M2_MIN,           .max = I8XX_M2_MAX },
+        .p   = { .min = I8XX_P_MIN,            .max = I8XX_P_MAX },
+        .p1  = { .min = I8XX_P1_MIN,           .max = I8XX_P1_MAX },
+       .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+                .p2_slow = I8XX_P2_SLOW,       .p2_fast = I8XX_P2_FAST },
+    },
+    { /* INTEL_LIMIT_I8XX_LVDS */
+        .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
+        .vco = { .min = I8XX_VCO_MIN,          .max = I8XX_VCO_MAX },
+        .n   = { .min = I8XX_N_MIN,            .max = I8XX_N_MAX },
+        .m   = { .min = I8XX_M_MIN,            .max = I8XX_M_MAX },
+        .m1  = { .min = I8XX_M1_MIN,           .max = I8XX_M1_MAX },
+        .m2  = { .min = I8XX_M2_MIN,           .max = I8XX_M2_MAX },
+        .p   = { .min = I8XX_P_MIN,            .max = I8XX_P_MAX },
+        .p1  = { .min = I8XX_P1_LVDS_MIN,      .max = I8XX_P1_LVDS_MAX },
+       .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+                .p2_slow = I8XX_P2_LVDS_SLOW,  .p2_fast = I8XX_P2_LVDS_FAST },
+    },
+    { /* INTEL_LIMIT_I9XX_SDVO_DAC */
+        .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
+        .vco = { .min = I9XX_VCO_MIN,          .max = I9XX_VCO_MAX },
+        .n   = { .min = I9XX_N_MIN,            .max = I9XX_N_MAX },
+        .m   = { .min = I9XX_M_MIN,            .max = I9XX_M_MAX },
+        .m1  = { .min = I9XX_M1_MIN,           .max = I9XX_M1_MAX },
+        .m2  = { .min = I9XX_M2_MIN,           .max = I9XX_M2_MAX },
+        .p   = { .min = I9XX_P_SDVO_DAC_MIN,   .max = I9XX_P_SDVO_DAC_MAX },
+        .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
+       .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+                .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+    },
+    { /* INTEL_LIMIT_I9XX_LVDS */
+        .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
+        .vco = { .min = I9XX_VCO_MIN,          .max = I9XX_VCO_MAX },
+        .n   = { .min = I9XX_N_MIN,            .max = I9XX_N_MAX },
+        .m   = { .min = I9XX_M_MIN,            .max = I9XX_M_MAX },
+        .m1  = { .min = I9XX_M1_MIN,           .max = I9XX_M1_MAX },
+        .m2  = { .min = I9XX_M2_MIN,           .max = I9XX_M2_MAX },
+        .p   = { .min = I9XX_P_LVDS_MIN,       .max = I9XX_P_LVDS_MAX },
+        .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
+       /* The single-channel range is 25-112Mhz, and dual-channel
+        * is 80-224Mhz.  Prefer single channel as much as possible.
+        */
+       .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+                .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_FAST },
+    },
+};
+
+static const intel_limit_t *intel_limit (struct drm_crtc *crtc)
+{
+       drm_device_t *dev = crtc->dev;
+       const intel_limit_t *limit;
+       
+       if (IS_I9XX(dev)) {
+               if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS))
+                       limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
+               else
+                       limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+       } else {
+               if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS))
+                       limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
+               else
+                       limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
+       }
+       return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
+{
+       clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+       clock->p = clock->p1 * clock->p2;
+       clock->vco = refclk * clock->m / (clock->n + 2);
+       clock->dot = clock->vco / clock->p;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+       clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+       clock->p = clock->p1 * clock->p2;
+       clock->vco = refclk * clock->m / (clock->n + 2);
+       clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk,
+                       intel_clock_t *clock)
+{
+       if (IS_I9XX(dev))
+               return i9xx_clock (refclk, clock);
+       else
+               return i8xx_clock (refclk, clock);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+{
+    struct drm_device *dev = crtc->dev;
+    struct drm_crtc_config *crtc_config = &dev->crtc_config;
+    struct drm_output *l_entry;
+
+    list_for_each_entry(l_entry, &crtc_config->output_list, head) {
+           if (l_entry->crtc == crtc) {
+                   struct intel_output *intel_output = l_entry->driver_private;
+                   if (intel_output->type == type)
+                           return true;
+           }
+    }
+    return false;
+}
+
+#define INTELPllInvalid(s)   { /* ErrorF (s) */; return false; }
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given outputs.
+ */
+
+static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+{
+       const intel_limit_t *limit = intel_limit (crtc);
+       
+       if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
+               INTELPllInvalid ("p1 out of range\n");
+       if (clock->p   < limit->p.min   || limit->p.max   < clock->p)
+               INTELPllInvalid ("p out of range\n");
+       if (clock->m2  < limit->m2.min  || limit->m2.max  < clock->m2)
+               INTELPllInvalid ("m2 out of range\n");
+       if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
+               INTELPllInvalid ("m1 out of range\n");
+       if (clock->m1 <= clock->m2)
+               INTELPllInvalid ("m1 <= m2\n");
+       if (clock->m   < limit->m.min   || limit->m.max   < clock->m)
+               INTELPllInvalid ("m out of range\n");
+       if (clock->n   < limit->n.min   || limit->n.max   < clock->n)
+               INTELPllInvalid ("n out of range\n");
+       if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+               INTELPllInvalid ("vco out of range\n");
+       /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+        * output, etc., rather than just a single range.
+        */
+       if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+               INTELPllInvalid ("dot out of range\n");
+       
+       return true;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE.  The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
+                               int refclk, intel_clock_t *best_clock)
+{
+       drm_device_t *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       intel_clock_t   clock;
+       const intel_limit_t   *limit = intel_limit (crtc);
+       int err = target;
+
+       if (IS_I9XX(dev)& intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+           (I915_READ(LVDS) & LVDS_PORT_EN) != 0)
+       {
+               /* For LVDS, if the panel is on, just rely on its current settings for
+                * dual-channel.  We haven't figured out how to reliably set up
+                * different single/dual channel state, if we even can.
+        */
+               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
+                       clock.p2 = limit->p2.p2_fast;
+               else
+                       clock.p2 = limit->p2.p2_slow;
+       } else {
+               if (target < limit->p2.dot_limit)
+                       clock.p2 = limit->p2.p2_slow;
+               else
+                       clock.p2 = limit->p2.p2_fast;
+       }
+       
+       memset (best_clock, 0, sizeof (*best_clock));
+       
+       for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) 
+       {
+               for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; clock.m2++) 
+               {
+                       for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) 
+                       {
+                               for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; clock.p1++) 
+                               {
+                                       int this_err;
+                                       
+                                       intel_clock (dev, refclk, &clock);
+                                       
+                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                               continue;
+                                       
+                                       this_err = abs(clock.dot - target);
+                                       if (this_err < err) {
+                                               *best_clock = clock;
+                                               err = this_err;
+                                       }
+                               }
+                       }
+               }
+       }
+       return (err != target);
+}
+
+void
+intel_wait_for_vblank(drm_device_t *dev)
+{
+       /* Wait for 20ms, i.e. one cycle at 50hz. */
+       udelay(20000);
+}
+
+void
+intel_pipe_set_base(struct drm_crtc *crtc, int x, int y)
+{
+       drm_device_t *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       int pipe = intel_crtc->pipe;
+       unsigned long Start, Offset;
+       int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE);
+       int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+       
+       Start = crtc->fb->offset;
+       Offset = ((y * crtc->fb->width + x) * (crtc->fb->bits_per_pixel / 8));
+
+       if (IS_I965G(dev)) {
+               I915_WRITE(dspbase, Offset);
+               I915_READ(dspbase);
+               I915_WRITE(dspsurf, Start);
+               I915_READ(dspsurf);
+       } else {
+               I915_WRITE(dspbase, Start + Offset);
+               I915_READ(dspbase);
+       }
+       
+
+#if 0
+       drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen);
+       
+       if (!sPriv)
+               return;
+               
+       switch (pipe) {
+               case 0:
+                       sPriv->pipeA_x = x;
+                       sPriv->pipeA_y = y;
+                       break;
+       case 1:
+               sPriv->pipeB_x = x;
+               sPriv->pipeB_y = y;
+               break;
+       default:
+               xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                          "Can't update pipe %d in SAREA\n", pipe);
+               break;
+       }
+#endif
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       drm_device_t *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       int pipe = intel_crtc->pipe;
+       int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       u32 temp;
+
+       /* XXX: When our outputs are all unaware of DPMS modes other than off
+        * and on, we should map those modes to DPMSModeOff in the CRTC.
+        */
+       switch (mode) {
+       case DPMSModeOn:
+       case DPMSModeStandby:
+       case DPMSModeSuspend:
+               /* Enable the DPLL */
+               temp = I915_READ(dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) == 0)
+               {
+                       I915_WRITE(dpll_reg, temp);
+                       I915_READ(dpll_reg);
+                       /* Wait for the clocks to stabilize. */
+                       udelay(150);
+                       I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+                       I915_READ(dpll_reg);
+                       /* Wait for the clocks to stabilize. */
+                       udelay(150);
+                       I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+                       I915_READ(dpll_reg);
+                       /* Wait for the clocks to stabilize. */
+                       udelay(150);
+               }
+               
+               /* Enable the pipe */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) == 0)
+                       I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+               
+               /* Enable the plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) == 0)
+               {
+                       I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+               }
+               
+               intel_crtc_load_lut(crtc);
+               
+               /* Give the overlay scaler a chance to enable if it's on this pipe */
+               //intel_crtc_dpms_video(crtc, TRUE); TODO
+       break;
+       case DPMSModeOff:
+               /* Give the overlay scaler a chance to disable if it's on this pipe */
+               //intel_crtc_dpms_video(crtc, FALSE); TODO
+               
+               /* Disable the VGA plane that we never use */
+               I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+               
+               /* Disable display plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) != 0)
+               {
+                       I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+                       I915_READ(dspbase_reg);
+               }
+               
+               if (!IS_I9XX(dev)) {
+                       /* Wait for vblank for the disable to take effect */
+                       intel_wait_for_vblank(dev);
+               }
+               
+               /* Next, disable display pipes */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) != 0) {
+                       I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+                       I915_READ(pipeconf_reg);
+               }
+               
+               /* Wait for vblank for the disable to take effect. */
+               intel_wait_for_vblank(dev);
+               
+               temp = I915_READ(dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) != 0) {
+                       I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+                       I915_READ(dpll_reg);
+               }
+               
+               /* Wait for the clocks to turn off. */
+               udelay(150);
+               break;
+       }
+       
+#if 0 //TODO
+       if (pI830->directRenderingEnabled) {
+               drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen);
+               Bool enabled = crtc->enabled && mode != DPMSModeOff;
+               
+               if (!sPriv)
+                       return;
+               
+               switch (pipe) {
+               case 0:
+                       sPriv->pipeA_w = enabled ? crtc->mode.HDisplay : 0;
+                       sPriv->pipeA_h = enabled ? crtc->mode.VDisplay : 0;
+                       break;
+               case 1:
+                       sPriv->pipeB_w = enabled ? crtc->mode.HDisplay : 0;
+                       sPriv->pipeB_h = enabled ? crtc->mode.VDisplay : 0;
+                       break;
+               default:
+                       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+                                  "Can't update pipe %d in SAREA\n", pipe);
+                       break;
+               }
+       }
+#endif
+}
+
+static bool intel_crtc_lock(struct drm_crtc *crtc)
+{
+   /* Sync the engine before mode switch */
+//   i830WaitSync(crtc->scrn);
+
+#if 0 // TODO def XF86DRI
+    return I830DRILock(crtc->scrn);
+#else
+    return FALSE;
+#endif
+}
+
+static void intel_crtc_unlock (struct drm_crtc *crtc)
+{
+#if 0 // TODO def XF86DRI
+    I830DRIUnlock (crtc->scrn);
+#endif
+}
+
+static void intel_crtc_prepare (struct drm_crtc *crtc)
+{
+    crtc->funcs->dpms (crtc, DPMSModeOff);
+}
+
+static void intel_crtc_commit (struct drm_crtc *crtc)
+{
+       crtc->funcs->dpms (crtc, DPMSModeOn);
+//     if (crtc->scrn->pScreen != NULL)
+//             xf86_reload_cursors (crtc->scrn->pScreen);
+}
+
+void intel_output_prepare (struct drm_output *output)
+{
+       output->funcs->dpms (output, DPMSModeOff);
+}
+
+void intel_output_commit (struct drm_output *output)
+{
+       output->funcs->dpms (output, DPMSModeOn);
+}
+
+static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+
+/** Returns the core display clock speed for i830 - i945 */
+static int intel_get_core_clock_speed(drm_device_t *dev)
+{
+
+       /* Core clock values taken from the published datasheets.
+        * The 830 may go up to 166 Mhz, which we should check.
+        */
+       if (IS_I945G(dev))
+               return 400000;
+       else if (IS_I915G(dev))
+               return 333000;
+       else if (IS_I945GM(dev) || IS_845G(dev))
+               return 200000;
+       else if (IS_I915GM(dev)) {
+#if 0
+               u16 gcfgc = pciReadWord(dev->PciTag, I915_GCFGC);
+               
+               if (gcfgc & I915_LOW_FREQUENCY_ENABLE)
+                       return 133000;
+               else {
+                       switch (gcfgc & I915_DISPLAY_CLOCK_MASK) {
+                       case I915_DISPLAY_CLOCK_333_MHZ:
+                               return 333000;
+                       default:
+                       case I915_DISPLAY_CLOCK_190_200_MHZ:
+                               return 190000;
+                       }
+               }
+#endif
+       } else if (IS_I865G(dev))
+               return 266000;
+       else if (IS_I855(dev)) {
+#if 0
+               PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */
+               u16 hpllcc = pciReadWord(bridge, I855_HPLLCC);
+               
+#endif
+               u16 hpllcc = 0;
+               /* Assume that the hardware is in the high speed state.  This
+                * should be the default.
+                */
+               switch (hpllcc & I855_CLOCK_CONTROL_MASK) {
+               case I855_CLOCK_133_200:
+               case I855_CLOCK_100_200:
+                       return 200000;
+               case I855_CLOCK_166_250:
+                       return 250000;
+               case I855_CLOCK_100_133:
+                       return 133000;
+               }
+       } else /* 852, 830 */
+               return 133000;
+       
+       return 0; /* Silence gcc warning */
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe (drm_device_t *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32  pfit_control;
+    
+       /* i830 doesn't have a panel fitter */
+       if (IS_I830(dev))
+               return -1;
+    
+       pfit_control = I915_READ(PFIT_CONTROL);
+    
+       /* See if the panel fitter is in use */
+       if ((pfit_control & PFIT_ENABLE) == 0)
+               return -1;
+       
+       /* 965 can place panel fitter on either pipe */
+       if (IS_I965G(dev))
+               return (pfit_control >> 29) & 0x3;
+       
+       /* older chips can only use pipe 1 */
+       return 1;
+}
+
+static void intel_crtc_mode_set(struct drm_crtc *crtc,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode,
+                               int x, int y)
+{
+       drm_device_t *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       int pipe = intel_crtc->pipe;
+       int fp_reg = (pipe == 0) ? FPA0 : FPB0;
+       int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+       int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
+       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+       int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+       int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+       int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+       int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+       int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+       int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+       int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+       int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+       int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+       int refclk;
+       intel_clock_t clock;
+       u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+       bool ok, is_sdvo = false, is_dvo = false;
+       bool is_crt = false, is_lvds = false, is_tv = false;
+       struct drm_crtc_config *crtc_config = &dev->crtc_config;
+       struct drm_output *output;
+
+       list_for_each_entry(output, &crtc_config->output_list, head) {
+               struct intel_output *intel_output = output->driver_private;
+
+               if (output->crtc != crtc)
+                       continue;
+
+               switch (intel_output->type) {
+               case INTEL_OUTPUT_LVDS:
+                       is_lvds = TRUE;
+                       break;
+               case INTEL_OUTPUT_SDVO:
+                       is_sdvo = TRUE;
+                       break;
+               case INTEL_OUTPUT_DVO:
+                       is_dvo = TRUE;
+                       break;
+               case INTEL_OUTPUT_TVOUT:
+                       is_tv = TRUE;
+                       break;
+               case INTEL_OUTPUT_ANALOG:
+                       is_crt = TRUE;
+                       break;
+               }
+       }
+       
+       if (IS_I9XX(dev)) {
+               refclk = 96000;
+       } else {
+               refclk = 48000;
+       }
+
+       ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+       if (!ok) {
+               DRM_ERROR("Couldn't find PLL settings for mode!\n");
+               return;
+       }
+
+       fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+       
+       dpll = DPLL_VGA_MODE_DIS;
+       if (IS_I9XX(dev)) {
+               if (is_lvds)
+                       dpll |= DPLLB_MODE_LVDS;
+               else
+                       dpll |= DPLLB_MODE_DAC_SERIAL;
+               if (is_sdvo)
+               {
+                       dpll |= DPLL_DVO_HIGH_SPEED;
+                       if (IS_I945G(dev) || IS_I945GM(dev))
+                       {
+                               int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                               dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+                       }
+               }
+               
+               /* compute bitmask from p1 value */
+               dpll |= (1 << (clock.p1 - 1)) << 16;
+               switch (clock.p2) {
+               case 5:
+                       dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+                       break;
+               case 7:
+                       dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+                       break;
+               case 10:
+                       dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+                       break;
+               case 14:
+                       dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+                       break;
+               }
+               if (IS_I965G(dev))
+                       dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+       } else {
+               if (is_lvds) {
+                       dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+               } else {
+                       if (clock.p1 == 2)
+                               dpll |= PLL_P1_DIVIDE_BY_TWO;
+                       else
+                               dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+                       if (clock.p2 == 4)
+                               dpll |= PLL_P2_DIVIDE_BY_4;
+               }
+       }
+       
+       if (is_tv)
+       {
+               /* XXX: just matching BIOS for now */
+/*     dpll |= PLL_REF_INPUT_TVCLKINBC; */
+               dpll |= 3;
+       }
+#if 0
+       else if (is_lvds)
+               dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
+#endif
+       else
+               dpll |= PLL_REF_INPUT_DREFCLK;
+       
+       /* Set up the display plane register */
+       dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+       switch (crtc->fb->bits_per_pixel) {
+       case 8:
+               dspcntr |= DISPPLANE_8BPP;
+               break;
+       case 16:
+               if (crtc->fb->depth == 15)
+                       dspcntr |= DISPPLANE_15_16BPP;
+               else
+                       dspcntr |= DISPPLANE_16BPP;
+               break;
+       case 32:
+               dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+               break;
+       default:
+               DRM_ERROR("Unknown color depth\n");
+               return;
+       }
+       
+
+       if (pipe == 0)
+               dspcntr |= DISPPLANE_SEL_PIPE_A;
+       else
+               dspcntr |= DISPPLANE_SEL_PIPE_B;
+       
+       pipeconf = I915_READ(pipeconf_reg);
+       if (pipe == 0 && !IS_I965G(dev))
+       {
+               /* Enable pixel doubling when the dot clock is > 90% of the (display)
+                * core speed.
+                *
+                * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
+                * pipe == 0 check?
+                */
+               if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
+                       pipeconf |= PIPEACONF_DOUBLE_WIDE;
+               else
+                       pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+       }
+
+       dspcntr |= DISPLAY_PLANE_ENABLE;
+       pipeconf |= PIPEACONF_ENABLE;
+       dpll |= DPLL_VCO_ENABLE;
+
+       
+       /* Disable the panel fitter if it was on our pipe */
+       if (intel_panel_fitter_pipe(dev) == pipe)
+               I915_WRITE(PFIT_CONTROL, 0);
+
+#if 0
+    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+              "Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+    xf86PrintModeline(pScrn->scrnIndex, mode);
+    if (!xf86ModesEqual(mode, adjusted_mode)) {
+       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+                  "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+       xf86PrintModeline(pScrn->scrnIndex, mode);
+    }
+    i830PrintPll("chosen", &clock);
+#endif
+
+    if (dpll & DPLL_VCO_ENABLE)
+    {
+       I915_WRITE(fp_reg, fp);
+       I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+       I915_READ(dpll_reg);
+       udelay(150);
+    }
+
+    /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+     * This is an exception to the general rule that mode_set doesn't turn
+     * things on.
+     */
+    if (is_lvds)
+    {
+           u32 lvds = I915_READ(LVDS);
+
+           lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+           /* Set the B0-B3 data pairs corresponding to whether we're going to
+            * set the DPLLs for dual-channel mode or not.
+            */
+           if (clock.p2 == 7)
+                   lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+           else
+                   lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+           
+           /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+            * appropriately here, but we need to look more thoroughly into how
+            * panels behave in the two modes.
+            */
+           
+           I915_WRITE(LVDS, lvds);
+           I915_READ(LVDS);
+    }
+    
+    I915_WRITE(fp_reg, fp);
+    I915_WRITE(dpll_reg, dpll);
+    I915_READ(dpll_reg);
+    /* Wait for the clocks to stabilize. */
+    udelay(150);
+    
+    if (IS_I965G(dev)) {
+           int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+           I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+                      ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+    } else {
+           /* write it again -- the BIOS does, after all */
+           I915_WRITE(dpll_reg, dpll);
+    }
+    I915_READ(dpll_reg);
+    /* Wait for the clocks to stabilize. */
+    udelay(150);
+    
+    I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+              ((adjusted_mode->crtc_htotal - 1) << 16));
+    I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+              ((adjusted_mode->crtc_hblank_end - 1) << 16));
+    I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+              ((adjusted_mode->crtc_hsync_end - 1) << 16));
+    I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+              ((adjusted_mode->crtc_vtotal - 1) << 16));
+    I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+              ((adjusted_mode->crtc_vblank_end - 1) << 16));
+    I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+              ((adjusted_mode->crtc_vsync_end - 1) << 16));
+    I915_WRITE(dspstride_reg, crtc->fb->pitch * (crtc->fb->bits_per_pixel / 8));
+    /* pipesrc and dspsize control the size that is scaled from, which should
+     * always be the user's requested size.
+     */
+    I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+    I915_WRITE(dsppos_reg, 0);
+    I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+    I915_WRITE(pipeconf_reg, pipeconf);
+    I915_READ(pipeconf_reg);
+
+    intel_wait_for_vblank(dev);
+
+    I915_WRITE(dspcntr_reg, dspcntr);
+
+    /* Flush the plane changes */
+    intel_pipe_set_base(crtc, x, y);
+
+#ifdef XF86DRI // TODO
+//   I830DRISetVBlankInterrupt (pScrn, TRUE);
+#endif
+
+    intel_wait_for_vblank(dev);    
+
+
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+       drm_device_t *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
+       int i;
+
+       /* The clocks have to be on to load the palette. */
+       if (!crtc->enabled)
+               return;
+
+       for (i = 0; i < 256; i++) {
+               I915_WRITE(palreg + 4 * i,
+                          (intel_crtc->lut_r[i] << 16) |
+                          (intel_crtc->lut_g[i] << 8) |
+                          intel_crtc->lut_b[i]);
+       }
+}
+
+/** Sets the color ramps on behalf of RandR */
+static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                u16 *blue, int size)
+{
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       int i;
+       
+       for (i = 0; i < 256; i++) {
+               intel_crtc->lut_r[i] = red[i] >> 8;
+               intel_crtc->lut_g[i] = green[i] >> 8;
+               intel_crtc->lut_b[i] = blue[i] >> 8;
+       }
+       
+       intel_crtc_load_lut(crtc);
+}
+
+struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev,
+                                            struct drm_crtc *crtc)
+{
+       return NULL;
+}
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+       .dpms = intel_crtc_dpms,
+       .lock = intel_crtc_lock,
+       .unlock = intel_crtc_unlock,
+       .mode_fixup = intel_crtc_mode_fixup,
+       .mode_set = intel_crtc_mode_set,
+       .gamma_set = intel_crtc_gamma_set,
+       .prepare = intel_crtc_prepare,
+       .commit = intel_crtc_commit,
+};
+
+
+void intel_crtc_init(drm_device_t *dev, int pipe)
+{
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+       int i;
+
+       crtc = drm_crtc_create(dev, &intel_crtc_funcs);
+       if (crtc == NULL)
+               return;
+
+       intel_crtc = kmalloc(sizeof(struct intel_crtc), GFP_KERNEL);
+       if (intel_crtc == NULL) {
+               kfree(crtc);
+               return;
+       }
+
+       intel_crtc->pipe = pipe;
+       for (i = 0; i < 256; i++) {
+               intel_crtc->lut_r[i] = i;
+               intel_crtc->lut_g[i] = i;
+               intel_crtc->lut_b[i] = i;
+       }
+
+       crtc->driver_private = intel_crtc;
+}
+
+int intel_output_clones (drm_device_t *dev, int type_mask)
+{
+       int index_mask = 0;
+       struct drm_output *output;
+       int entry = 0;
+
+        list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               struct intel_output *intel_output = output->driver_private;
+               if (type_mask & (1 << intel_output->type))
+                       index_mask |= (1 << entry);
+               entry++;
+       }
+       return index_mask;
+}
+
+
+static void intel_setup_outputs(drm_device_t *dev)
+{
+       struct drm_output *output;
+
+       intel_crt_init(dev);
+
+       /* Set up integrated LVDS */
+       if (IS_MOBILE(dev) && !IS_I830(dev))
+               intel_lvds_init(dev);
+
+       if (IS_I9XX(dev)) {
+               intel_sdvo_init(dev, SDVOB);
+               intel_sdvo_init(dev, SDVOC);
+       }
+
+       list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+               struct intel_output *intel_output = output->driver_private;
+               int crtc_mask = 0, clone_mask = 0;
+               
+               /* valid crtcs */
+               switch(intel_output->type) {
+               case INTEL_OUTPUT_DVO:
+               case INTEL_OUTPUT_SDVO:
+                       crtc_mask = ((1 << 0)|
+                                    (1 << 1));
+                       clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+                                     (1 << INTEL_OUTPUT_DVO) |
+                                     (1 << INTEL_OUTPUT_SDVO));
+                       break;
+               case INTEL_OUTPUT_ANALOG:
+                       crtc_mask = ((1 << 0));
+                       clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+                                     (1 << INTEL_OUTPUT_DVO) |
+                                     (1 << INTEL_OUTPUT_SDVO));
+                       break;
+               case INTEL_OUTPUT_LVDS:
+                       crtc_mask = (1 << 1);
+                       clone_mask = (1 << INTEL_OUTPUT_LVDS);
+                       break;
+               case INTEL_OUTPUT_TVOUT:
+                       crtc_mask = ((1 << 0) |
+                                    (1 << 1));
+                       clone_mask = (1 << INTEL_OUTPUT_TVOUT);
+                       break;
+               }
+               output->possible_crtcs = crtc_mask;
+               output->possible_clones = intel_output_clones(dev, clone_mask);
+       }
+}
+
+void intel_modeset_init(drm_device_t *dev)
+{
+       int num_pipe;
+       int i;
+
+       drm_crtc_config_init(dev);
+
+
+       if (IS_MOBILE(dev) || IS_I9XX(dev))
+               num_pipe = 2;
+       else
+               num_pipe = 1;
+       DRM_DEBUG("%d display pipe%s available.\n",
+                 num_pipe, num_pipe > 1 ? "s" : "");
+
+       for (i = 0; i < num_pipe; i++) {
+               intel_crtc_init(dev, i);
+       }
+
+       intel_setup_outputs(dev);
+
+       drm_initial_config(dev, false);
+}
+
+void intel_modeset_cleanup(drm_device_t *dev)
+{
+       drm_crtc_config_cleanup(dev);
+}
diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h
new file mode 100644 (file)
index 0000000..5b8bef6
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_crtc.h"
+
+/*
+ * Display related stuff
+ */
+
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+
+#define INTEL_I2C_BUS_DVO 1
+#define INTEL_I2C_BUS_SDVO 2
+
+/* these are outputs from the chip - integrated only 
+   external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+
+#define INTEL_DVO_CHIP_NONE 0
+#define INTEL_DVO_CHIP_LVDS 1
+#define INTEL_DVO_CHIP_TMDS 2
+#define INTEL_DVO_CHIP_TVOUT 4
+
+struct intel_i2c_chan {
+       drm_device_t *drm_dev; /* for getting at dev. private (mmio etc.) */
+       u32 reg; /* GPIO reg */
+       struct i2c_adapter adapter;
+       struct i2c_algo_bit_data algo;
+        u8 slave_addr;
+};
+
+struct intel_output {
+       int type;
+       struct intel_i2c_chan *i2c_bus; /* for control functions */
+       struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
+       bool load_detect_tmp;
+       void *dev_priv;
+};
+
+struct intel_crtc {
+       int pipe;
+       u8 lut_r[256], lut_g[256], lut_b[256];
+};
+
+struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg,
+                                       const char *name);
+void intel_i2c_destroy(struct intel_i2c_chan *chan);
+int intel_ddc_get_modes(struct drm_output *output);
+extern bool intel_ddc_probe(struct drm_output *output);
+
+extern void intel_crt_init(drm_device_t *dev);
+extern void intel_sdvo_init(drm_device_t *dev, int output_device);
+extern void intel_lvds_init(drm_device_t *dev);
+
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+extern void intel_output_prepare (struct drm_output *output);
+extern void intel_output_commit (struct drm_output *output);
+#endif /* __INTEL_DRV_H__ */
diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c
new file mode 100644 (file)
index 0000000..acae28a
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright Â© 2006 Intel Corporation
+ *   Eric Anholt <eric@anholt.net>
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/*
+ * Intel GPIO access functions
+ */
+
+#define I2C_RISEFALL_TIME 20
+
+static int get_clock(void *data)
+{
+       struct intel_i2c_chan *chan = data;
+       drm_i915_private_t *dev_priv = chan->drm_dev->dev_private;
+       u32 val;
+
+       val = I915_READ(chan->reg);
+       return ((val & GPIO_CLOCK_VAL_IN) != 0);
+}
+
+static int get_data(void *data)
+{
+       struct intel_i2c_chan *chan = data;
+       drm_i915_private_t *dev_priv = chan->drm_dev->dev_private;
+       u32 val;
+
+       val = I915_READ(chan->reg);
+       return ((val & GPIO_DATA_VAL_IN) != 0);
+}
+
+static void set_clock(void *data, int state_high)
+{
+       struct intel_i2c_chan *chan = data;
+       drm_device_t *dev = chan->drm_dev;
+       drm_i915_private_t *dev_priv = chan->drm_dev->dev_private;
+       u32 reserved = 0, clock_bits;
+
+       /* On most chips, these bits must be preserved in software. */
+       if (!IS_I830(dev) && !IS_845G(dev))
+               reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+                                                  GPIO_CLOCK_PULLUP_DISABLE);
+
+       if (state_high)
+               clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
+       else
+               clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+                       GPIO_CLOCK_VAL_MASK;
+       I915_WRITE(chan->reg, reserved | clock_bits);
+       udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+static void set_data(void *data, int state_high)
+{
+       struct intel_i2c_chan *chan = data;
+       drm_device_t *dev = chan->drm_dev;
+       drm_i915_private_t *dev_priv = chan->drm_dev->dev_private;
+       u32 reserved = 0, data_bits;
+
+       /* On most chips, these bits must be preserved in software. */
+       if (!IS_I830(dev) && !IS_845G(dev))
+               reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+                                                  GPIO_CLOCK_PULLUP_DISABLE);
+
+       if (state_high)
+               data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+       else
+               data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+                       GPIO_DATA_VAL_MASK;
+
+       I915_WRITE(chan->reg, reserved | data_bits);
+       udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+/**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+ * @output: driver specific output device
+ * @reg: GPIO reg to use
+ * @name: name for this bus
+ *
+ * Creates and registers a new i2c bus with the Linux i2c layer, for use
+ * in output probing and control (e.g. DDC or SDVO control functions).
+ *
+ * Possible values for @reg include:
+ *   %GPIOA
+ *   %GPIOB
+ *   %GPIOC
+ *   %GPIOD
+ *   %GPIOE
+ *   %GPIOF
+ *   %GPIOG
+ *   %GPIOH
+ * see PRM for details on how these different busses are used.
+ */
+struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg,
+                                       const char *name)
+{
+       struct intel_i2c_chan *chan;
+
+       chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL);
+       if (!chan)
+               goto out_free;
+
+       chan->drm_dev = dev;
+       chan->reg = reg;
+       snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
+       chan->adapter.owner = THIS_MODULE;
+       chan->adapter.id = I2C_HW_B_INTELFB;
+       chan->adapter.algo_data = &chan->algo;
+       chan->adapter.dev.parent = &dev->pdev->dev;
+       chan->algo.setsda = set_data;
+       chan->algo.setscl = set_clock;
+       chan->algo.getsda = get_data;
+       chan->algo.getscl = get_clock;
+       chan->algo.udelay = 20;
+       chan->algo.timeout = usecs_to_jiffies(2200);
+       chan->algo.data = chan;
+
+       i2c_set_adapdata(&chan->adapter, chan);
+
+       if(i2c_bit_add_bus(&chan->adapter))
+               goto out_free;
+
+       /* JJJ:  raise SCL and SDA? */
+       set_data(chan, 1);
+       set_clock(chan, 1);
+       udelay(20);
+
+       return chan;
+
+out_free:
+       kfree(chan);
+       return NULL;
+}
+
+/**
+ * intel_i2c_destroy - unregister and free i2c bus resources
+ * @output: channel to free
+ *
+ * Unregister the adapter from the i2c layer, then free the structure.
+ */
+void intel_i2c_destroy(struct intel_i2c_chan *chan)
+{
+       if (!chan)
+               return;
+
+       i2c_del_adapter(&chan->adapter);
+       kfree(chan);
+}
+
+       
+       
diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c
new file mode 100644 (file)
index 0000000..c2ac567
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright Â© 2006 Intel Corporation
+ *   Eric Anholt <eric@anholt.net>
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to i830_lvds_get_max_backlight().
+ */
+static void lvds_set_backlight(drm_device_t *dev, u32 level)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       unsigned long blc_pwm_ctl;
+
+       level &= BACKLIGHT_DUTY_CYCLE_MASK;
+       blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+       I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl |
+                  (level << BACKLIGHT_DUTY_CYCLE_SHIFT));
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 lvds_get_max_backlight(drm_device_t *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+    
+       return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+               BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+}
+
+int lvds_backlight(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       unsigned long dvoa_enabled, dvob_enabled, dvoc_enabled, lvds_enabled;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       printk(KERN_ERR "max backlight value: %d\n",
+              lvds_get_max_backlight(dev));
+       dvoa_enabled = I915_READ(DVOA);
+       dvob_enabled = I915_READ(DVOB);
+       dvoc_enabled = I915_READ(DVOC);
+       lvds_enabled = I915_READ(LVDS);
+
+       printk(KERN_ERR "dvoa_enabled: 0x%08lx\n", dvoa_enabled);
+       printk(KERN_ERR "dvob_enabled: 0x%08lx\n", dvob_enabled);
+       printk(KERN_ERR "dvoc_enabled: 0x%08lx\n", dvoc_enabled);
+       printk(KERN_ERR "lvds_enabled: 0x%08lx\n", lvds_enabled);
+       printk(KERN_ERR "BLC_PWM_CTL: 0x%08x\n", I915_READ(BLC_PWM_CTL));
+       
+       return 0;
+}
+
+static const struct drm_output_funcs intel_lvds_output_funcs;
+
+/**
+ * intel_lvds_init - setup LVDS outputs on this device
+ * @dev: drm device
+ *
+ * Create the output, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void intel_lvds_init(drm_device_t *dev)
+{
+       struct drm_output *output;
+       struct intel_output *intel_output;
+       int modes;
+
+       output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS");
+       if (!output)
+               return;
+
+       intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL);
+       if (!intel_output) {
+               drm_output_destroy(output);
+               return;
+       }
+
+       intel_output->type = INTEL_OUTPUT_LVDS;
+       output->driver_private = intel_output;
+       output->subpixel_order = SubPixelHorizontalRGB;
+       output->interlace_allowed = 0;
+       output->doublescan_allowed = 0;
+
+       intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+       if (!intel_output->ddc_bus) {
+               dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+                          "failed.\n");
+               return;
+       }
+
+       modes = intel_ddc_get_modes(output);
+       printk(KERN_ERR "LVDS: added %d modes from EDID.\n", modes);
+       intel_i2c_destroy(intel_output->ddc_bus);
+       drm_output_destroy(output);
+}
+
diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c
new file mode 100644 (file)
index 0000000..0e56147
--- /dev/null
@@ -0,0 +1,49 @@
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/**
+ * intel_ddc_probe
+ *
+ */
+bool intel_ddc_probe(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+       u8 out_buf[] = { 0x0, 0x0};
+       u8 buf[2];
+       int ret;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = 0x50,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = 0x50,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = buf,
+               }
+       };
+
+       ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+       if (ret == 2)
+               return true;
+
+       return false;
+}
+
+/**
+ * intel_ddc_get_modes - get modelist from monitor
+ * @output: DRM output device to use
+ *
+ * Fetch the EDID information from @output using the DDC bus.
+ */
+int intel_ddc_get_modes(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+
+       return drm_add_edid_modes(output, &intel_output->ddc_bus->adapter);
+}
diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c
new file mode 100644 (file)
index 0000000..4094b78
--- /dev/null
@@ -0,0 +1,999 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright Â© 2006 Intel Corporation
+ *   Eric Anholt <eric@anholt.net>
+ *   Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_sdvo_regs.h"
+
+struct intel_sdvo_priv {
+       struct intel_i2c_chan *i2c_bus;
+       int slaveaddr;
+       int output_device;
+
+       u16 active_outputs;
+
+       struct intel_sdvo_caps caps;
+       int pixel_clock_min, pixel_clock_max;
+
+       int save_sdvo_mult;
+       u16 save_active_outputs;
+       struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+       struct intel_sdvo_dtd save_output_dtd[16];
+       u32 save_SDVOX;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+static void intel_sdvo_write_sdvox(struct drm_output *output, u32 val)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv   *sdvo_priv = intel_output->dev_priv;
+       u32 bval = val, cval = val;
+       int i;
+
+       if (sdvo_priv->output_device == SDVOB)
+               cval = I915_READ(SDVOC);
+       else
+               bval = I915_READ(SDVOB);
+       
+       /*
+        * Write the registers twice for luck. Sometimes,
+        * writing them only once doesn't appear to 'stick'.
+        * The BIOS does this too. Yay, magic
+        */
+       for (i = 0; i < 2; i++)
+       {
+               I915_WRITE(SDVOB, bval);
+               I915_READ(SDVOB);
+               I915_WRITE(SDVOC, cval);
+               I915_READ(SDVOC);
+       }
+}
+
+static bool intel_sdvo_read_byte(struct drm_output *output, u8 addr,
+                                u8 *ch)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       u8 out_buf[2];
+       u8 buf[2];
+       int ret;
+
+       struct i2c_msg msgs[] = {
+               { 
+                       .addr = sdvo_priv->i2c_bus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               }, 
+               {
+                       .addr = sdvo_priv->i2c_bus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2)
+       {
+//             DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]); 
+               *ch = buf[0];
+               return true;
+       }
+
+       DRM_DEBUG("i2c transfer returned %d\n", ret);
+       return false;
+}
+
+
+static bool intel_sdvo_read_byte_quiet(struct drm_output *output, int addr,
+                                      u8 *ch)
+{
+       return true;
+
+}
+
+static bool intel_sdvo_write_byte(struct drm_output *output, int addr,
+                                 u8 ch)
+{
+       struct intel_output *intel_output = output->driver_private;
+       u8 out_buf[2];
+       struct i2c_msg msgs[] = {
+               { 
+                       .addr = intel_output->i2c_bus->slave_addr,
+                       .flags = 0,
+                       .len = 2,
+                       .buf = out_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1)
+       {
+               return true;
+       }
+       return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+const static struct _sdvo_cmd_name {
+    u8 cmd;
+    char *name;
+} sdvo_cmd_names[] = {
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+};
+
+#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output)   ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+static void intel_sdvo_write_cmd(struct drm_output *output, u8 cmd,
+                                void *args, int args_len)
+{
+       struct intel_output *intel_output = output->driver_private;
+       int i;
+
+       for (i = 0; i < args_len; i++) {
+               intel_sdvo_write_byte(output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]);
+       }
+       intel_sdvo_write_byte(output, SDVO_I2C_OPCODE, cmd);
+}
+
+static const char *cmd_status_names[] = {
+       "Power on",
+       "Success",
+       "Not supported",
+       "Invalid arg",
+       "Pending",
+       "Target not specified",
+       "Scaling not supported"
+};
+
+static u8 intel_sdvo_read_response(struct drm_output *output, void *response,
+                                  int response_len)
+{
+       int i;
+       u8 status;
+
+       /* Read the command response */
+       for (i = 0; i < response_len; i++) {
+               intel_sdvo_read_byte(output, SDVO_I2C_RETURN_0 + i,
+                                    &((u8 *)response)[i]);
+       }
+
+       /* read the return status */
+       intel_sdvo_read_byte(output, SDVO_I2C_CMD_STATUS, &status);
+
+       return status;
+
+}
+
+int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+       if (mode->clock >= 100000)
+               return 1;
+       else if (mode->clock >= 50000)
+               return 2;
+       else
+               return 4;
+}
+
+static void intel_sdvo_set_control_bus_switch(struct drm_output *output, u8 target)
+{
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+}
+
+static bool intel_sdvo_set_target_input(struct drm_output *output, bool target_0, bool target_1)
+{
+       struct intel_sdvo_set_target_input_args targets = {0};
+       u8 status;
+
+       if (target_0 && target_1)
+               return SDVO_CMD_STATUS_NOTSUPP;
+
+       if (target_1)
+               targets.target_1 = 1;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_INPUT, &targets,
+                            sizeof(targets));
+
+       status = intel_sdvo_read_response(output, NULL, 0);
+
+       return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool intel_sdvo_get_trained_inputs(struct drm_output *output, bool *input_1, bool *input_2)
+{
+       struct intel_sdvo_get_trained_inputs_response response;
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
+       status = intel_sdvo_read_response(output, &response, sizeof(response));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       *input_1 = response.input0_trained;
+       *input_2 = response.input1_trained;
+       return TRUE;
+}
+
+static bool intel_sdvo_get_active_outputs(struct drm_output *output,
+                                         u16 *outputs)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
+       status = intel_sdvo_read_response(output, outputs, sizeof(*outputs));
+
+       return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_active_outputs(struct drm_output *output,
+                                         u16 outputs)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
+                            sizeof(outputs));
+       status = intel_sdvo_read_response(output, NULL, 0);
+       return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_encoder_power_state(struct drm_output *output,
+                                              int mode)
+{
+       u8 status, state = SDVO_ENCODER_STATE_ON;
+
+       switch (mode) {
+       case DPMSModeOn:
+               state = SDVO_ENCODER_STATE_ON;
+               break;
+       case DPMSModeStandby:
+               state = SDVO_ENCODER_STATE_STANDBY;
+               break;
+       case DPMSModeSuspend:
+               state = SDVO_ENCODER_STATE_SUSPEND;
+               break;
+       case DPMSModeOff:
+               state = SDVO_ENCODER_STATE_OFF;
+               break;
+       }
+       
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+                            sizeof(state));
+       status = intel_sdvo_read_response(output, NULL, 0);
+
+       return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_input_pixel_clock_range(struct drm_output *output,
+                                                  int *clock_min,
+                                                  int *clock_max)
+{
+       struct intel_sdvo_pixel_clock_range clocks;
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+                            NULL, 0);
+
+       status = intel_sdvo_read_response(output, &clocks, sizeof(clocks));
+
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       /* Convert the values from units of 10 kHz to kHz. */
+       *clock_min = clocks.min * 10;
+       *clock_max = clocks.max * 10;
+
+       return TRUE;
+}
+
+static bool intel_sdvo_set_target_output(struct drm_output *output,
+                                        u16 outputs)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
+                            sizeof(outputs));
+
+       status = intel_sdvo_read_response(output, NULL, 0);
+       return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_timing(struct drm_output *output, u8 cmd,
+                                 struct intel_sdvo_dtd *dtd)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, cmd, NULL, 0);
+       status = intel_sdvo_read_response(output, &dtd->part1,
+                                         sizeof(dtd->part1));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       intel_sdvo_write_cmd(output, cmd + 1, NULL, 0);
+       status = intel_sdvo_read_response(output, &dtd->part2,
+                                         sizeof(dtd->part2));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+static bool intel_sdvo_get_input_timing(struct drm_output *output,
+                                        struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_get_timing(output,
+                                    SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_get_output_timing(struct drm_output *output,
+                                        struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_get_timing(output,
+                                    SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_timing(struct drm_output *output, u8 cmd,
+                                 struct intel_sdvo_dtd *dtd)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1));
+       status = intel_sdvo_read_response(output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       intel_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+       status = intel_sdvo_read_response(output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+static bool intel_sdvo_set_input_timing(struct drm_output *output,
+                                        struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_set_timing(output,
+                                    SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_output_timing(struct drm_output *output,
+                                        struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_set_timing(output,
+                                    SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+#if 0
+static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output,
+                                                 struct intel_sdvo_dtd *dtd)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
+                            NULL, 0);
+
+       status = intel_sdvo_read_response(output, &dtd->part1,
+                                         sizeof(dtd->part1));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
+                            NULL, 0);
+       status = intel_sdvo_read_response(output, &dtd->part2,
+                                         sizeof(dtd->part2));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+#endif
+
+static int intel_sdvo_get_clock_rate_mult(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+       u8 response, status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
+       status = intel_sdvo_read_response(output, &response, 1);
+
+       if (status != SDVO_CMD_STATUS_SUCCESS) {
+               DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+               return SDVO_CLOCK_RATE_MULT_1X;
+       } else {
+               DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+       }
+
+       return response;
+}
+
+static bool intel_sdvo_set_clock_rate_mult(struct drm_output *output, u8 val)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+       status = intel_sdvo_read_response(output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_output *output,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       /* Make the CRTC code factor in the SDVO pixel multiplier.  The SDVO
+        * device will be told of the multiplier during mode_set.
+        */
+       adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+       return TRUE;
+}
+
+static void intel_sdvo_mode_set(struct drm_output *output,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = output->crtc;
+       struct intel_crtc *intel_crtc = crtc->driver_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       u16 width, height;
+       u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+       u16 h_sync_offset, v_sync_offset;
+       u32 sdvox;
+       struct intel_sdvo_dtd output_dtd;
+       int sdvo_pixel_multiply;
+
+       if (!mode)
+               return;
+
+       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;
+
+       output_dtd.part1.clock = mode->clock / 10;
+       output_dtd.part1.h_active = width & 0xff;
+       output_dtd.part1.h_blank = h_blank_len & 0xff;
+       output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+               ((h_blank_len >> 8) & 0xf);
+       output_dtd.part1.v_active = height & 0xff;
+       output_dtd.part1.v_blank = v_blank_len & 0xff;
+       output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+               ((v_blank_len >> 8) & 0xf);
+       
+       output_dtd.part2.h_sync_off = h_sync_offset;
+       output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+       output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+               (v_sync_len & 0xf);
+       output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+               ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+               ((v_sync_len & 0x30) >> 4);
+       
+       output_dtd.part2.dtd_flags = 0x18;
+       if (mode->flags & V_PHSYNC)
+               output_dtd.part2.dtd_flags |= 0x2;
+       if (mode->flags & V_PVSYNC)
+               output_dtd.part2.dtd_flags |= 0x4;
+
+       output_dtd.part2.sdvo_flags = 0;
+       output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+       output_dtd.part2.reserved = 0;
+
+       /* Set the output timing to the screen */
+       intel_sdvo_set_target_output(output, sdvo_priv->active_outputs);
+       intel_sdvo_set_output_timing(output, &output_dtd);
+
+       /* Set the input timing to the screen. Assume always input 0. */
+       intel_sdvo_set_target_input(output, TRUE, FALSE);
+
+       /* We would like to use i830_sdvo_create_preferred_input_timing() to
+        * provide the device with a timing it can support, if it supports that
+        * feature.  However, presumably we would need to adjust the CRTC to
+        * output the preferred timing, and we don't support that currently.
+        */
+#if 0
+       success = intel_sdvo_create_preferred_input_timing(output, clock,
+                                                          width, height);
+       if (success) {
+               struct intel_sdvo_dtd *input_dtd;
+               
+               intel_sdvo_get_preferred_input_timing(output, &input_dtd);
+               intel_sdvo_set_input_timing(output, &input_dtd);
+       }
+#else
+       intel_sdvo_set_input_timing(output, &output_dtd);
+#endif 
+
+       switch (intel_sdvo_get_pixel_multiplier(mode)) {
+       case 1:
+               intel_sdvo_set_clock_rate_mult(output,
+                                              SDVO_CLOCK_RATE_MULT_1X);
+               break;
+       case 2:
+               intel_sdvo_set_clock_rate_mult(output,
+                                              SDVO_CLOCK_RATE_MULT_2X);
+               break;
+       case 4:
+               intel_sdvo_set_clock_rate_mult(output,
+                                              SDVO_CLOCK_RATE_MULT_4X);
+               break;
+       }       
+
+       /* Set the SDVO control regs. */
+       sdvox = I915_READ(sdvo_priv->output_device);
+       switch (sdvo_priv->output_device) {
+       case SDVOB:
+               sdvox &= SDVOB_PRESERVE_MASK;
+               break;
+       case SDVOC:
+               sdvox &= SDVOC_PRESERVE_MASK;
+               break;
+       }
+       sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+       if (intel_crtc->pipe == 1)
+               sdvox |= SDVO_PIPE_B_SELECT;
+
+       sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
+       if (IS_I965G(dev)) {
+               /* done in crtc_mode_set as the dpll_md reg must be written 
+                  early */
+       } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+               /* done in crtc_mode_set as it lives inside the 
+                  dpll register */
+       } else {
+               sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+       }
+
+       intel_sdvo_write_sdvox(output, sdvox);
+}
+
+static void intel_sdvo_dpms(struct drm_output *output, int mode)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       u32 temp;
+
+       if (mode != DPMSModeOn) {
+               intel_sdvo_set_active_outputs(output, 0);
+               if (0)
+                       intel_sdvo_set_encoder_power_state(output, mode);
+
+               if (mode == DPMSModeOff) {
+                       temp = I915_READ(sdvo_priv->output_device);
+                       if ((temp & SDVO_ENABLE) != 0) {
+                               intel_sdvo_write_sdvox(output, temp & ~SDVO_ENABLE);
+                       }
+               }
+       } else {
+               bool input1, input2;
+               int i;
+               u8 status;
+               
+               temp = I915_READ(sdvo_priv->output_device);
+               if ((temp & SDVO_ENABLE) == 0)
+                       intel_sdvo_write_sdvox(output, temp | SDVO_ENABLE);
+               for (i = 0; i < 2; i++)
+                 intel_wait_for_vblank(dev);
+               
+               status = intel_sdvo_get_trained_inputs(output, &input1,
+                                                      &input2);
+
+               
+               /* Warn if the device reported failure to sync. */
+               if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+                       DRM_ERROR("First %s output reported failure to sync\n",
+                                  SDVO_NAME(sdvo_priv));
+               }
+               
+               if (0)
+                       intel_sdvo_set_encoder_power_state(output, mode);
+               intel_sdvo_set_active_outputs(output, sdvo_priv->active_outputs);
+       }       
+       return;
+}
+
+static void intel_sdvo_save(struct drm_output *output)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       int o;
+
+       sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(output);
+       intel_sdvo_get_active_outputs(output, &sdvo_priv->save_active_outputs);
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+               intel_sdvo_set_target_input(output, TRUE, FALSE);
+               intel_sdvo_get_input_timing(output,
+                                           &sdvo_priv->save_input_dtd_1);
+       }
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+               intel_sdvo_set_target_input(output, FALSE, TRUE);
+               intel_sdvo_get_input_timing(output,
+                                           &sdvo_priv->save_input_dtd_2);
+       }
+
+       for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+       {
+               u16  this_output = (1 << o);
+               if (sdvo_priv->caps.output_flags & this_output)
+               {
+                       intel_sdvo_set_target_output(output, this_output);
+                       intel_sdvo_get_output_timing(output,
+                                                    &sdvo_priv->save_output_dtd[o]);
+               }
+       }
+
+       sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
+}
+
+static void intel_sdvo_restore(struct drm_output *output)
+{
+       drm_device_t *dev = output->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       int o;
+       int i;
+       bool input1, input2;
+       u8 status;
+
+       intel_sdvo_set_active_outputs(output, 0);
+
+       for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+       {
+               u16  this_output = (1 << o);
+               if (sdvo_priv->caps.output_flags & this_output) {
+                       intel_sdvo_set_target_output(output, this_output);
+                       intel_sdvo_set_output_timing(output, &sdvo_priv->save_output_dtd[o]);
+               }
+       }
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+               intel_sdvo_set_target_input(output, TRUE, FALSE);
+               intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_1);
+       }
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+               intel_sdvo_set_target_input(output, FALSE, TRUE);
+               intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_2);
+       }
+       
+       intel_sdvo_set_clock_rate_mult(output, sdvo_priv->save_sdvo_mult);
+       
+       I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+       
+       if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
+       {
+               for (i = 0; i < 2; i++)
+                       intel_wait_for_vblank(dev);
+               status = intel_sdvo_get_trained_inputs(output, &input1, &input2);
+               if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+                       DRM_DEBUG("First %s output reported failure to sync\n",
+                                  SDVO_NAME(sdvo_priv));
+       }
+       
+       intel_sdvo_set_active_outputs(output, sdvo_priv->save_active_outputs);
+}
+
+static int intel_sdvo_mode_valid(struct drm_output *output,
+                                struct drm_display_mode *mode)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+       if (mode->flags & V_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       if (sdvo_priv->pixel_clock_min > mode->clock)
+               return MODE_CLOCK_HIGH;
+
+       if (sdvo_priv->pixel_clock_max < mode->clock)
+               return MODE_CLOCK_LOW;
+
+       return MODE_OK;
+}
+
+static bool intel_sdvo_get_capabilities(struct drm_output *output, struct intel_sdvo_caps *caps)
+{
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
+       status = intel_sdvo_read_response(output, caps, sizeof(*caps));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static void intel_sdvo_dump_cmd(struct drm_output *output, int opcode)
+{
+
+
+}
+
+static void intel_sdvo_dump_device(struct drm_output *output)
+{
+
+}
+
+void intel_sdvo_dump(void)
+{
+
+}
+
+
+static enum drm_output_status intel_sdvo_detect(struct drm_output *output)
+{
+       u8 response[2];
+       u8 status;
+
+       intel_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+       status = intel_sdvo_read_response(output, &response, 2);
+
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return output_status_unknown;
+
+       DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+       if ((response[0] != 0) || (response[1] != 0))
+               return output_status_connected;
+       else
+               return output_status_disconnected;
+}
+
+static int intel_sdvo_get_modes(struct drm_output *output)
+{
+       struct drm_display_mode *modes;
+
+       /* set the bus switch and get the modes */
+       intel_sdvo_set_control_bus_switch(output, SDVO_CONTROL_BUS_DDC2);
+       intel_ddc_get_modes(output);
+
+       if (list_empty(&output->probed_modes))
+               return 0;
+       return 1;
+#if 0
+       /* Mac mini hack.  On this device, I get DDC through the analog, which
+        * load-detects as disconnected.  I fail to DDC through the SDVO DDC,
+        * but it does load-detect as connected.  So, just steal the DDC bits 
+        * from analog when we fail at finding it the right way.
+        */
+       /* TODO */
+       return NULL;
+
+       return NULL;
+#endif
+}
+
+static void intel_sdvo_destroy(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+
+       if (intel_output->i2c_bus)
+               intel_i2c_destroy(intel_output->i2c_bus);
+
+       if (intel_output) {
+               kfree(intel_output);
+               output->driver_private = NULL;
+       }
+}
+
+static const struct drm_output_funcs intel_sdvo_output_funcs = {
+       .dpms = intel_sdvo_dpms,
+       .save = intel_sdvo_save,
+       .restore = intel_sdvo_restore,
+       .mode_valid = intel_sdvo_mode_valid,
+       .mode_fixup = intel_sdvo_mode_fixup,
+       .prepare = intel_output_prepare,
+       .mode_set = intel_sdvo_mode_set,
+       .commit = intel_output_commit,
+       .detect = intel_sdvo_detect,
+       .get_modes = intel_sdvo_get_modes,
+       .cleanup = intel_sdvo_destroy
+};
+
+void intel_sdvo_init(drm_device_t *dev, int output_device)
+{
+       struct drm_output *output;
+       struct intel_output *intel_output;
+       struct intel_sdvo_priv *sdvo_priv;
+       struct intel_i2c_chan *i2cbus = NULL;
+       u8 ch[0x40];
+       int i;
+       char name[DRM_OUTPUT_LEN];
+       char *name_prefix;
+       char *name_suffix;
+       
+
+       output = drm_output_create(dev, &intel_sdvo_output_funcs, NULL);
+       if (!output)
+               return;
+
+       intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
+       if (!intel_output) {
+               drm_output_destroy(output);
+               return;
+       }
+
+       sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
+       intel_output->type = INTEL_OUTPUT_SDVO;
+       output->driver_private = intel_output;
+       output->interlace_allowed = 0;
+       output->doublescan_allowed = 0;
+
+       /* setup the DDC bus. */
+       if (output_device == SDVOB)
+               i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+       else
+               i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+       if (i2cbus == NULL) {
+               drm_output_destroy(output);
+               return;
+       }
+
+       sdvo_priv->i2c_bus = i2cbus;
+
+       if (output_device == SDVOB) {
+               name_suffix = "-1";
+               sdvo_priv->i2c_bus->slave_addr = 0x38;
+       } else {
+               name_suffix = "-2";
+               sdvo_priv->i2c_bus->slave_addr = 0x39;
+       }
+
+       sdvo_priv->output_device = output_device;
+       intel_output->i2c_bus = i2cbus;
+       intel_output->dev_priv = sdvo_priv;
+
+
+       /* Read the regs to test if we can talk to the device */
+       for (i = 0; i < 0x40; i++) {
+               if (!intel_sdvo_read_byte(output, i, &ch[i])) {
+                       DRM_DEBUG("No SDVO device found on SDVO%c\n",
+                                 output_device == SDVOB ? 'B' : 'C');
+                       drm_output_destroy(output);
+                       return;
+               }
+       }
+
+       intel_sdvo_get_capabilities(output, &sdvo_priv->caps);
+
+       memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+
+       if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+       {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+               output->subpixel_order = SubPixelHorizontalRGB;
+               name_prefix="TMDS";
+       }
+       else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+       {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+               output->subpixel_order = SubPixelHorizontalRGB;
+               name_prefix="TMDS";
+       }
+       else
+       {
+               unsigned char   bytes[2];
+               
+               memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
+               DRM_DEBUG("%s: No active TMDS outputs (0x%02x%02x)\n",
+                         SDVO_NAME(sdvo_priv),
+                         bytes[0], bytes[1]);
+       }
+       strcpy (name, name_prefix);
+       strcat (name, name_suffix);
+       if (!drm_output_rename(output, name))
+       {
+               drm_output_destroy(output);
+               return;
+       }
+               
+
+       /* Set the input timing to the screen. Assume always input 0. */
+       intel_sdvo_set_target_input(output, TRUE, FALSE);
+       
+       intel_sdvo_get_input_pixel_clock_range(output,
+                                              &sdvo_priv->pixel_clock_min,
+                                              &sdvo_priv->pixel_clock_max);
+
+
+       DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
+                 "clock range %dMHz - %dMHz, "
+                 "input 1: %c, input 2: %c, "
+                 "output 1: %c, output 2: %c\n",
+                 SDVO_NAME(sdvo_priv),
+                 sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+                 sdvo_priv->caps.device_rev_id,
+                 sdvo_priv->pixel_clock_min / 1000,
+                 sdvo_priv->pixel_clock_max / 1000,
+                 (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+                 (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+                 sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0 ? 'Y' : 'N',
+                 sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1 ? 'Y' : 'N');
+
+       intel_output->ddc_bus = i2cbus; 
+}
diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h
new file mode 100644 (file)
index 0000000..b8cb1dc
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright Â© 2006 Intel Corporation
+ */
+
+/**
+ * @file SDVO command definitions and structures.
+ */
+
+#define SDVO_OUTPUT_FIRST   (0)
+#define SDVO_OUTPUT_TMDS0   (1 << 0)
+#define SDVO_OUTPUT_RGB0    (1 << 1)
+#define SDVO_OUTPUT_CVBS0   (1 << 2)
+#define SDVO_OUTPUT_SVID0   (1 << 3)
+#define SDVO_OUTPUT_YPRPB0  (1 << 4)
+#define SDVO_OUTPUT_SCART0  (1 << 5)
+#define SDVO_OUTPUT_LVDS0   (1 << 6)
+#define SDVO_OUTPUT_TMDS1   (1 << 8)
+#define SDVO_OUTPUT_RGB1    (1 << 13)
+#define SDVO_OUTPUT_LVDS1   (1 << 14)
+#define SDVO_OUTPUT_LAST    (14)
+
+struct intel_sdvo_caps {
+    u8 vendor_id;
+    u8 device_id;
+    u8 device_rev_id;
+    u8 sdvo_version_major;
+    u8 sdvo_version_minor;
+    unsigned int sdvo_inputs_mask:2;
+    unsigned int smooth_scaling:1;
+    unsigned int sharp_scaling:1;
+    unsigned int up_scaling:1;
+    unsigned int down_scaling:1;
+    unsigned int stall_support:1;
+    unsigned int pad:1;
+    u16 output_flags;
+} __attribute__((packed));
+
+/** This matches the EDID DTD structure, more or less */
+struct intel_sdvo_dtd {
+    struct {
+       u16 clock;              /**< pixel clock, in 10kHz units */
+       u8 h_active;            /**< lower 8 bits (pixels) */
+       u8 h_blank;             /**< lower 8 bits (pixels) */
+       u8 h_high;              /**< upper 4 bits each h_active, h_blank */
+       u8 v_active;            /**< lower 8 bits (lines) */
+       u8 v_blank;             /**< lower 8 bits (lines) */
+       u8 v_high;              /**< upper 4 bits each v_active, v_blank */
+    } part1;
+
+    struct {
+       u8 h_sync_off;  /**< lower 8 bits, from hblank start */
+       u8 h_sync_width;        /**< lower 8 bits (pixels) */
+       /** lower 4 bits each vsync offset, vsync width */
+       u8 v_sync_off_width;
+       /**
+        * 2 high bits of hsync offset, 2 high bits of hsync width,
+        * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+        */
+       u8 sync_off_width_high;
+       u8 dtd_flags;
+       u8 sdvo_flags;
+       /** bits 6-7 of vsync offset at bits 6-7 */
+       u8 v_sync_off_high;
+       u8 reserved;
+    } part2;
+} __attribute__((packed));
+
+struct intel_sdvo_pixel_clock_range {
+    u16 min;                   /**< pixel clock, in 10kHz units */
+    u16 max;                   /**< pixel clock, in 10kHz units */
+} __attribute__((packed));
+
+struct intel_sdvo_preferred_input_timing_args {
+    u16 clock;
+    u16 width;
+    u16 height;
+} __attribute__((packed));
+
+/* I2C registers for SDVO */
+#define SDVO_I2C_ARG_0                         0x07
+#define SDVO_I2C_ARG_1                         0x06
+#define SDVO_I2C_ARG_2                         0x05
+#define SDVO_I2C_ARG_3                         0x04
+#define SDVO_I2C_ARG_4                         0x03
+#define SDVO_I2C_ARG_5                         0x02
+#define SDVO_I2C_ARG_6                         0x01
+#define SDVO_I2C_ARG_7                         0x00
+#define SDVO_I2C_OPCODE                                0x08
+#define SDVO_I2C_CMD_STATUS                    0x09
+#define SDVO_I2C_RETURN_0                      0x0a
+#define SDVO_I2C_RETURN_1                      0x0b
+#define SDVO_I2C_RETURN_2                      0x0c
+#define SDVO_I2C_RETURN_3                      0x0d
+#define SDVO_I2C_RETURN_4                      0x0e
+#define SDVO_I2C_RETURN_5                      0x0f
+#define SDVO_I2C_RETURN_6                      0x10
+#define SDVO_I2C_RETURN_7                      0x11
+#define SDVO_I2C_VENDOR_BEGIN                  0x20
+
+/* Status results */
+#define SDVO_CMD_STATUS_POWER_ON               0x0
+#define SDVO_CMD_STATUS_SUCCESS                        0x1
+#define SDVO_CMD_STATUS_NOTSUPP                        0x2
+#define SDVO_CMD_STATUS_INVALID_ARG            0x3
+#define SDVO_CMD_STATUS_PENDING                        0x4
+#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED   0x5
+#define SDVO_CMD_STATUS_SCALING_NOT_SUPP       0x6
+
+/* SDVO commands, argument/result registers */
+
+#define SDVO_CMD_RESET                                 0x01
+
+/** Returns a struct intel_sdvo_caps */
+#define SDVO_CMD_GET_DEVICE_CAPS                       0x02
+
+#define SDVO_CMD_GET_FIRMWARE_REV                      0x86
+# define SDVO_DEVICE_FIRMWARE_MINOR                    SDVO_I2C_RETURN_0
+# define SDVO_DEVICE_FIRMWARE_MAJOR                    SDVO_I2C_RETURN_1
+# define SDVO_DEVICE_FIRMWARE_PATCH                    SDVO_I2C_RETURN_2
+
+/**
+ * Reports which inputs are trained (managed to sync).
+ *
+ * Devices must have trained within 2 vsyncs of a mode change.
+ */
+#define SDVO_CMD_GET_TRAINED_INPUTS                    0x03
+struct intel_sdvo_get_trained_inputs_response {
+    unsigned int input0_trained:1;
+    unsigned int input1_trained:1;
+    unsigned int pad:6;
+} __attribute__((packed));
+
+/** Returns a struct intel_sdvo_output_flags of active outputs. */
+#define SDVO_CMD_GET_ACTIVE_OUTPUTS                    0x04
+
+/**
+ * Sets the current set of active outputs.
+ *
+ * Takes a struct intel_sdvo_output_flags.  Must be preceded by a SET_IN_OUT_MAP
+ * on multi-output devices.
+ */
+#define SDVO_CMD_SET_ACTIVE_OUTPUTS                    0x05
+
+/**
+ * Returns the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Returns two struct intel_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_GET_IN_OUT_MAP                                0x06
+
+/**
+ * Sets the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Takes two struct i380_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_SET_IN_OUT_MAP                                0x07
+
+/**
+ * Returns a struct intel_sdvo_output_flags of attached displays.
+ */
+#define SDVO_CMD_GET_ATTACHED_DISPLAYS                 0x0b
+
+/**
+ * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
+ */
+#define SDVO_CMD_GET_HOT_PLUG_SUPPORT                  0x0c
+
+/**
+ * Takes a struct intel_sdvo_output_flags.
+ */
+#define SDVO_CMD_SET_ACTIVE_HOT_PLUG                   0x0d
+
+/**
+ * Returns a struct intel_sdvo_output_flags of displays with hot plug
+ * interrupts enabled.
+ */
+#define SDVO_CMD_GET_ACTIVE_HOT_PLUG                   0x0e
+
+#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE            0x0f
+struct intel_sdvo_get_interrupt_event_source_response {
+    u16 interrupt_status;
+    unsigned int ambient_light_interrupt:1;
+    unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Selects which input is affected by future input commands.
+ *
+ * Commands affected include SET_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
+ */
+#define SDVO_CMD_SET_TARGET_INPUT                      0x10
+struct intel_sdvo_set_target_input_args {
+    unsigned int target_1:1;
+    unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Takes a struct intel_sdvo_output_flags of which outputs are targetted by
+ * future output commands.
+ *
+ * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
+ * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
+ */
+#define SDVO_CMD_SET_TARGET_OUTPUT                     0x11
+
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART1               0x12
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART2               0x13
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART1               0x14
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART2               0x15
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1              0x16
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2              0x17
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1              0x18
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2              0x19
+/* Part 1 */
+# define SDVO_DTD_CLOCK_LOW                            SDVO_I2C_ARG_0
+# define SDVO_DTD_CLOCK_HIGH                           SDVO_I2C_ARG_1
+# define SDVO_DTD_H_ACTIVE                             SDVO_I2C_ARG_2
+# define SDVO_DTD_H_BLANK                              SDVO_I2C_ARG_3
+# define SDVO_DTD_H_HIGH                               SDVO_I2C_ARG_4
+# define SDVO_DTD_V_ACTIVE                             SDVO_I2C_ARG_5
+# define SDVO_DTD_V_BLANK                              SDVO_I2C_ARG_6
+# define SDVO_DTD_V_HIGH                               SDVO_I2C_ARG_7
+/* Part 2 */
+# define SDVO_DTD_HSYNC_OFF                            SDVO_I2C_ARG_0
+# define SDVO_DTD_HSYNC_WIDTH                          SDVO_I2C_ARG_1
+# define SDVO_DTD_VSYNC_OFF_WIDTH                      SDVO_I2C_ARG_2
+# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH                  SDVO_I2C_ARG_3
+# define SDVO_DTD_DTD_FLAGS                            SDVO_I2C_ARG_4
+# define SDVO_DTD_DTD_FLAG_INTERLACED                          (1 << 7)
+# define SDVO_DTD_DTD_FLAG_STEREO_MASK                         (3 << 5)
+# define SDVO_DTD_DTD_FLAG_INPUT_MASK                          (3 << 3)
+# define SDVO_DTD_DTD_FLAG_SYNC_MASK                           (3 << 1)
+# define SDVO_DTD_SDVO_FLAS                            SDVO_I2C_ARG_5
+# define SDVO_DTD_SDVO_FLAG_STALL                              (1 << 7)
+# define SDVO_DTD_SDVO_FLAG_CENTERED                           (0 << 6)
+# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT                         (1 << 6)
+# define SDVO_DTD_SDVO_FLAG_SCALING_MASK                       (3 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_NONE                       (0 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP                      (1 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH                     (2 << 4)
+# define SDVO_DTD_VSYNC_OFF_HIGH                       SDVO_I2C_ARG_6
+
+/**
+ * Generates a DTD based on the given width, height, and flags.
+ *
+ * This will be supported by any device supporting scaling or interlaced
+ * modes.
+ */
+#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING         0x1a
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW         SDVO_I2C_ARG_0
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH                SDVO_I2C_ARG_1
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW         SDVO_I2C_ARG_2
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH                SDVO_I2C_ARG_3
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW                SDVO_I2C_ARG_4
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH       SDVO_I2C_ARG_5
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS             SDVO_I2C_ARG_6
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED          (1 << 0)
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED              (1 << 1)
+
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1      0x1b
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2      0x1c
+
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE           0x1d
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE          0x1e
+
+/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
+#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS                0x1f
+
+/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_GET_CLOCK_RATE_MULT                   0x20
+/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_SET_CLOCK_RATE_MULT                   0x21
+# define SDVO_CLOCK_RATE_MULT_1X                               (1 << 0)
+# define SDVO_CLOCK_RATE_MULT_2X                               (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_4X                               (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS              0x27
+
+#define SDVO_CMD_GET_TV_FORMAT                         0x28
+
+#define SDVO_CMD_SET_TV_FORMAT                         0x29
+
+#define SDVO_CMD_GET_SUPPORTED_POWER_STATES            0x2a
+#define SDVO_CMD_GET_ENCODER_POWER_STATE               0x2b
+#define SDVO_CMD_SET_ENCODER_POWER_STATE               0x2c
+# define SDVO_ENCODER_STATE_ON                                 (1 << 0)
+# define SDVO_ENCODER_STATE_STANDBY                            (1 << 1)
+# define SDVO_ENCODER_STATE_SUSPEND                            (1 << 2)
+# define SDVO_ENCODER_STATE_OFF                                        (1 << 3)
+
+#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT             0x93
+
+#define SDVO_CMD_SET_CONTROL_BUS_SWITCH                        0x7a
+# define SDVO_CONTROL_BUS_PROM                         0x0
+# define SDVO_CONTROL_BUS_DDC1                         0x1
+# define SDVO_CONTROL_BUS_DDC2                         0x2
+# define SDVO_CONTROL_BUS_DDC3                         0x3
+
index aed3eea..a5f1473 100644 (file)
 #include "i915_drm.h"
 #include "i915_drv.h"
 
-#define IS_I965G(dev)  (dev->pci_device == 0x2972 || \
-                       dev->pci_device == 0x2982 || \
-                       dev->pci_device == 0x2992 || \
-                       dev->pci_device == 0x29A2 || \
-                       dev->pci_device == 0x2A02)
-
-
 /* Really want an OS-independent resettable timer.  Would like to have
  * this loop run for (eg) 3 sec, but have the timer reset every time
  * the head pointer changes, so that EBUSY only happens if the ring
@@ -87,6 +80,7 @@ void i915_kernel_lost_context(drm_device_t * dev)
 
 static int i915_dma_cleanup(drm_device_t * dev)
 {
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        /* Make sure interrupts are disabled here because the uninstall ioctl
         * may not have been called from userspace and after dev_private
         * is freed, it's too late.
@@ -94,25 +88,16 @@ static int i915_dma_cleanup(drm_device_t * dev)
        if (dev->irq)
                drm_irq_uninstall(dev);
 
-       if (dev->dev_private) {
-               drm_i915_private_t *dev_priv =
-                   (drm_i915_private_t *) dev->dev_private;
-
-               if (dev_priv->ring.virtual_start) {
-                       drm_core_ioremapfree(&dev_priv->ring.map, dev);
-               }
-
-               if (dev_priv->status_page_dmah) {
-                       drm_pci_free(dev, dev_priv->status_page_dmah);
-                       /* Need to rewrite hardware status page */
-                       I915_WRITE(0x02080, 0x1ffff000);
-               }
-
-               drm_free(dev->dev_private, sizeof(drm_i915_private_t),
-                        DRM_MEM_DRIVER);
-
-               dev->dev_private = NULL;
+       if (dev_priv->status_page_dmah) {
+               drm_pci_free(dev, dev_priv->status_page_dmah);
+               dev_priv->status_page_dmah = NULL;
+               dev_priv->hw_status_page = NULL;
+               dev_priv->dma_status_page = 0;
+               /* Need to rewrite hardware status page */
+               I915_WRITE(0x02080, 0x1ffff000);
        }
+       
+       dev_priv->sarea_priv = NULL;
 
        return 0;
 }
@@ -121,8 +106,6 @@ static int i915_initialize(drm_device_t * dev,
                           drm_i915_private_t * dev_priv,
                           drm_i915_init_t * init)
 {
-       memset(dev_priv, 0, sizeof(drm_i915_private_t));
-
        DRM_GETSAREA();
        if (!dev_priv->sarea) {
                DRM_ERROR("can not find sarea!\n");
@@ -131,14 +114,6 @@ static int i915_initialize(drm_device_t * dev,
                return DRM_ERR(EINVAL);
        }
 
-       dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);
-       if (!dev_priv->mmio_map) {
-               dev->dev_private = (void *)dev_priv;
-               i915_dma_cleanup(dev);
-               DRM_ERROR("can not find mmio map!\n");
-               return DRM_ERR(EINVAL);
-       }
-
        dev_priv->sarea_priv = (drm_i915_sarea_t *)
            ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);
 
@@ -195,7 +170,11 @@ static int i915_initialize(drm_device_t * dev,
 
        I915_WRITE(0x02080, dev_priv->dma_status_page);
        DRM_DEBUG("Enabled hardware status page\n");
-       dev->dev_private = (void *)dev_priv;
+
+       /* this probably doesn't belong here - TODO */
+       drm_framebuffer_set_object(dev, dev_priv->sarea_priv->front_handle);
+       drm_set_desired_modes(dev);
+
        return 0;
 }
 
@@ -237,7 +216,7 @@ static int i915_dma_resume(drm_device_t * dev)
 static int i915_dma_init(DRM_IOCTL_ARGS)
 {
        DRM_DEVICE;
-       drm_i915_private_t *dev_priv;
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        drm_i915_init_t init;
        int retcode = 0;
 
@@ -246,10 +225,6 @@ static int i915_dma_init(DRM_IOCTL_ARGS)
 
        switch (init.func) {
        case I915_INIT_DMA:
-               dev_priv = drm_alloc(sizeof(drm_i915_private_t),
-                                    DRM_MEM_DRIVER);
-               if (dev_priv == NULL)
-                       return DRM_ERR(ENOMEM);
                retcode = i915_initialize(dev, dev_priv, &init);
                break;
        case I915_CLEANUP_DMA:
@@ -882,6 +857,18 @@ static int i915_mmio(DRM_IOCTL_ARGS)
 
 int i915_driver_load(drm_device_t *dev, unsigned long flags)
 {
+       drm_i915_private_t *dev_priv;
+       int ret;
+       unsigned long mmiobase, mmiolen;
+
+       dev_priv = drm_alloc(sizeof(drm_i915_private_t), DRM_MEM_DRIVER);
+       if (dev_priv == NULL)
+               return DRM_ERR(ENOMEM);
+
+       memset(dev_priv, 0, sizeof(drm_i915_private_t));
+       dev->dev_private = (void *)dev_priv;
+//     dev_priv->flags = flags;
+
        /* i915 has 4 more counters */
        dev->counters += 4;
        dev->types[6] = _DRM_STAT_IRQ;
@@ -889,25 +876,55 @@ int i915_driver_load(drm_device_t *dev, unsigned long flags)
        dev->types[8] = _DRM_STAT_SECONDARY;
        dev->types[9] = _DRM_STAT_DMA;
 
+       if (IS_I9XX(dev)) {
+               dev_priv->mmiobase = drm_get_resource_start(dev, 0);
+               dev_priv->mmiolen = drm_get_resource_len(dev, 0);
+       } else if (drm_get_resource_start(dev, 1)) {
+               dev_priv->mmiobase = drm_get_resource_start(dev, 1);
+               dev_priv->mmiolen = drm_get_resource_len(dev, 1);
+       } else {
+               DRM_ERROR("Unable to find MMIO registers\n");
+               return -ENODEV;
+       }
+
+       ret = drm_addmap(dev, dev_priv->mmiobase, dev_priv->mmiolen,
+                        _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio_map);
+       if (ret != 0) {
+               DRM_ERROR("Cannot add mapping for MMIO registers\n");
+               return ret;
+       }
+       DRM_DEBUG("dev_priv->mmio map is %08X\n", dev_priv->mmio_map);
+       intel_modeset_init(dev);
+       return 0;
+}
+
+int i915_driver_unload(drm_device_t *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       intel_modeset_cleanup(dev);
+       drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+
+       dev->dev_private = NULL;
        return 0;
 }
 
 void i915_driver_lastclose(drm_device_t * dev)
 {
-       if (dev->dev_private) {
-               drm_i915_private_t *dev_priv = dev->dev_private;
-               i915_do_cleanup_pageflip(dev);
-               i915_mem_takedown(&(dev_priv->agp_heap));
-       }
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       
+       i915_mem_takedown(&(dev_priv->agp_heap));
+
        i915_dma_cleanup(dev);
+
+       dev_priv->mmio_map = NULL;
 }
 
 void i915_driver_preclose(drm_device_t * dev, DRMFILE filp)
 {
-       if (dev->dev_private) {
-               drm_i915_private_t *dev_priv = dev->dev_private;
-               i915_mem_release(dev, filp, dev_priv->agp_heap);
-       }
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       i915_mem_release(dev, filp, dev_priv->agp_heap);
 }
 
 drm_ioctl_desc_t i915_ioctls[] = {
@@ -950,8 +967,22 @@ int i915_driver_device_is_agp(drm_device_t * dev)
 
 int i915_driver_firstopen(struct drm_device *dev)
 {
-#ifdef I915_HAVE_BUFFER
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
+       DRM_DEBUG("\n");
        drm_bo_driver_init(dev);
-#endif
+
+       if (!dev_priv->mmio_map) {
+               ret = drm_addmap(dev, dev_priv->mmiobase, dev_priv->mmiolen,
+                                _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio_map);
+               if (ret != 0) {
+                       DRM_ERROR("Cannot add mapping for MMIO registers\n");
+                       return ret;
+               }
+       }
+       DRM_DEBUG("dev_priv->mmio map is %08X\n", dev_priv->mmio_map);
+
        return 0;
 }
+
index e8a7be2..f37f587 100644 (file)
@@ -92,6 +92,9 @@ typedef struct drm_i915_private {
        drm_local_map_t *sarea;
        drm_local_map_t *mmio_map;
 
+       unsigned long mmiobase;
+       unsigned long mmiolen;
+
        drm_i915_sarea_t *sarea_priv;
        drm_i915_ring_buffer_t ring;
 
@@ -145,6 +148,8 @@ extern int i915_max_ioctl;
                                /* i915_dma.c */
 extern void i915_kernel_lost_context(drm_device_t * dev);
 extern int i915_driver_load(struct drm_device *, unsigned long flags);
+extern int i915_driver_unload(drm_device_t *dev);
+extern int i915_driver_firstopen(struct drm_device *dev);
 extern void i915_driver_lastclose(drm_device_t * dev);
 extern void i915_driver_preclose(drm_device_t * dev, DRMFILE filp);
 extern int i915_driver_device_is_agp(drm_device_t * dev);
@@ -206,6 +211,12 @@ extern int i915_move(drm_buffer_object_t *bo, int evict,
 
 #endif
 
+
+/* modesetting */
+extern void intel_modeset_init(drm_device_t *dev);
+extern void intel_modeset_cleanup(drm_device_t *dev);
+
+
 #define I915_READ(reg)          DRM_READ32(dev_priv->mmio_map, (reg))
 #define I915_WRITE(reg,val)     DRM_WRITE32(dev_priv->mmio_map, (reg), (val))
 #define I915_READ16(reg)       DRM_READ16(dev_priv->mmio_map, (reg))
@@ -273,6 +284,30 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
 #define I915_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
 #define I915_VBLANK_CLEAR              (1UL<<1)
 
+#define GPIOA                  0x5010
+#define GPIOB                  0x5014
+#define GPIOC                  0x5018
+#define GPIOD                  0x501c
+#define GPIOE                  0x5020
+#define GPIOF                  0x5024
+#define GPIOG                  0x5028
+#define GPIOH                  0x502c
+# define GPIO_CLOCK_DIR_MASK           (1 << 0)
+# define GPIO_CLOCK_DIR_IN             (0 << 1)
+# define GPIO_CLOCK_DIR_OUT            (1 << 1)
+# define GPIO_CLOCK_VAL_MASK           (1 << 2)
+# define GPIO_CLOCK_VAL_OUT            (1 << 3)
+# define GPIO_CLOCK_VAL_IN             (1 << 4)
+# define GPIO_CLOCK_PULLUP_DISABLE     (1 << 5)
+# define GPIO_DATA_DIR_MASK            (1 << 8)
+# define GPIO_DATA_DIR_IN              (0 << 9)
+# define GPIO_DATA_DIR_OUT             (1 << 9)
+# define GPIO_DATA_VAL_MASK            (1 << 10)
+# define GPIO_DATA_VAL_OUT             (1 << 11)
+# define GPIO_DATA_VAL_IN              (1 << 12)
+# define GPIO_DATA_PULLUP_DISABLE      (1 << 13)
+
+
 #define SRX_INDEX              0x3c4
 #define SRX_DATA               0x3c5
 #define SR01                   1
@@ -281,6 +316,8 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
 #define PPCR                   0x61204
 #define PPCR_ON                        (1<<0)
 
+#define DVOA                   0x61120
+#define DVOA_ON                        (1<<31)
 #define DVOB                   0x61140
 #define DVOB_ON                        (1<<31)
 #define DVOC                   0x61160
@@ -363,4 +400,441 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
 
 #define READ_BREADCRUMB(dev_priv)  (((volatile u32*)(dev_priv->hw_status_page))[5])
 #define READ_HWSP(dev_priv, reg)  (((volatile u32*)(dev_priv->hw_status_page))[reg])
+
+#define BLC_PWM_CTL            0x61254
+#define BACKLIGHT_MODULATION_FREQ_SHIFT                (17)
+/**
+ * This is the most significant 15 bits of the number of backlight cycles in a
+ * complete cycle of the modulated backlight control.
+ *
+ * The actual value is this field multiplied by two.
+ */
+#define BACKLIGHT_MODULATION_FREQ_MASK         (0x7fff << 17)
+#define BLM_LEGACY_MODE                                (1 << 16)
+/**
+ * This is the number of cycles out of the backlight modulation cycle for which
+ * the backlight is on.
+ *
+ * This field must be no greater than the number of cycles in the complete
+ * backlight modulation cycle.
+ */
+#define BACKLIGHT_DUTY_CYCLE_SHIFT             (0)
+#define BACKLIGHT_DUTY_CYCLE_MASK              (0xffff)
+
+#define I915_GCFGC                     0xf0
+#define I915_LOW_FREQUENCY_ENABLE              (1 << 7)
+#define I915_DISPLAY_CLOCK_190_200_MHZ         (0 << 4)
+#define I915_DISPLAY_CLOCK_333_MHZ             (4 << 4)
+#define I915_DISPLAY_CLOCK_MASK                        (7 << 4)
+
+#define I855_HPLLCC                    0xc0
+#define I855_CLOCK_CONTROL_MASK                        (3 << 0)
+#define I855_CLOCK_133_200                     (0 << 0)
+#define I855_CLOCK_100_200                     (1 << 0)
+#define I855_CLOCK_100_133                     (2 << 0)
+#define I855_CLOCK_166_250                     (3 << 0)
+
+/* I830 CRTC registers */
+#define HTOTAL_A       0x60000
+#define HBLANK_A       0x60004
+#define HSYNC_A        0x60008
+#define VTOTAL_A       0x6000c
+#define VBLANK_A       0x60010
+#define VSYNC_A        0x60014
+#define PIPEASRC       0x6001c
+#define BCLRPAT_A      0x60020
+#define VSYNCSHIFT_A   0x60028
+
+#define HTOTAL_B       0x61000
+#define HBLANK_B       0x61004
+#define HSYNC_B        0x61008
+#define VTOTAL_B       0x6100c
+#define VBLANK_B       0x61010
+#define VSYNC_B        0x61014
+#define PIPEBSRC       0x6101c
+#define BCLRPAT_B      0x61020
+#define VSYNCSHIFT_B   0x61028
+
+#define PP_STATUS      0x61200
+# define PP_ON                                 (1 << 31)
+/**
+ * Indicates that all dependencies of the panel are on:
+ *
+ * - PLL enabled
+ * - pipe enabled
+ * - LVDS/DVOB/DVOC on
+ */
+# define PP_READY                              (1 << 30)
+# define PP_SEQUENCE_NONE                      (0 << 28)
+# define PP_SEQUENCE_ON                                (1 << 28)
+# define PP_SEQUENCE_OFF                       (2 << 28)
+# define PP_SEQUENCE_MASK                      0x30000000
+#define PP_CONTROL     0x61204
+# define POWER_TARGET_ON                       (1 << 0)
+
+#define LVDSPP_ON       0x61208
+#define LVDSPP_OFF      0x6120c
+#define PP_CYCLE        0x61210
+
+#define PFIT_CONTROL   0x61230
+# define PFIT_ENABLE                           (1 << 31)
+# define VERT_INTERP_DISABLE                   (0 << 10)
+# define VERT_INTERP_BILINEAR                  (1 << 10)
+# define VERT_INTERP_MASK                      (3 << 10)
+# define VERT_AUTO_SCALE                       (1 << 9)
+# define HORIZ_INTERP_DISABLE                  (0 << 6)
+# define HORIZ_INTERP_BILINEAR                 (1 << 6)
+# define HORIZ_INTERP_MASK                     (3 << 6)
+# define HORIZ_AUTO_SCALE                      (1 << 5)
+# define PANEL_8TO6_DITHER_ENABLE              (1 << 3)
+
+#define PFIT_PGM_RATIOS        0x61234
+# define PFIT_VERT_SCALE_MASK                  0xfff00000
+# define PFIT_HORIZ_SCALE_MASK                 0x0000fff0
+
+#define PFIT_AUTO_RATIOS       0x61238
+
+
+#define DPLL_A         0x06014
+#define DPLL_B         0x06018
+# define DPLL_VCO_ENABLE                       (1 << 31)
+# define DPLL_DVO_HIGH_SPEED                   (1 << 30)
+# define DPLL_SYNCLOCK_ENABLE                  (1 << 29)
+# define DPLL_VGA_MODE_DIS                     (1 << 28)
+# define DPLLB_MODE_DAC_SERIAL                 (1 << 26) /* i915 */
+# define DPLLB_MODE_LVDS                       (2 << 26) /* i915 */
+# define DPLL_MODE_MASK                                (3 << 26)
+# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10       (0 << 24) /* i915 */
+# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5                (1 << 24) /* i915 */
+# define DPLLB_LVDS_P2_CLOCK_DIV_14            (0 << 24) /* i915 */
+# define DPLLB_LVDS_P2_CLOCK_DIV_7             (1 << 24) /* i915 */
+# define DPLL_P2_CLOCK_DIV_MASK                        0x03000000 /* i915 */
+# define DPLL_FPA01_P1_POST_DIV_MASK           0x00ff0000 /* i915 */
+/**
+ *  The i830 generation, in DAC/serial mode, defines p1 as two plus this
+ * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set.
+ */
+# define DPLL_FPA01_P1_POST_DIV_MASK_I830      0x001f0000
+/**
+ * The i830 generation, in LVDS mode, defines P1 as the bit number set within
+ * this field (only one bit may be set).
+ */
+# define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000
+# define DPLL_FPA01_P1_POST_DIV_SHIFT          16
+# define PLL_P2_DIVIDE_BY_4                    (1 << 23) /* i830, required in DVO non-gang */
+# define PLL_P1_DIVIDE_BY_TWO                  (1 << 21) /* i830 */
+# define PLL_REF_INPUT_DREFCLK                 (0 << 13)
+# define PLL_REF_INPUT_TVCLKINA                        (1 << 13) /* i830 */
+# define PLL_REF_INPUT_TVCLKINBC               (2 << 13) /* SDVO TVCLKIN */
+# define PLLB_REF_INPUT_SPREADSPECTRUMIN       (3 << 13)
+# define PLL_REF_INPUT_MASK                    (3 << 13)
+# define PLL_LOAD_PULSE_PHASE_SHIFT            9
+/*
+ * Parallel to Serial Load Pulse phase selection.
+ * Selects the phase for the 10X DPLL clock for the PCIe
+ * digital display port. The range is 4 to 13; 10 or more
+ * is just a flip delay. The default is 6
+ */
+# define PLL_LOAD_PULSE_PHASE_MASK             (0xf << PLL_LOAD_PULSE_PHASE_SHIFT)
+# define DISPLAY_RATE_SELECT_FPA1              (1 << 8)
+
+/**
+ * SDVO multiplier for 945G/GM. Not used on 965.
+ *
+ * \sa DPLL_MD_UDI_MULTIPLIER_MASK
+ */
+# define SDVO_MULTIPLIER_MASK                  0x000000ff
+# define SDVO_MULTIPLIER_SHIFT_HIRES           4
+# define SDVO_MULTIPLIER_SHIFT_VGA             0
+
+/** @defgroup DPLL_MD
+ * @{
+ */
+/** Pipe A SDVO/UDI clock multiplier/divider register for G965. */
+#define DPLL_A_MD              0x0601c
+/** Pipe B SDVO/UDI clock multiplier/divider register for G965. */
+#define DPLL_B_MD              0x06020
+/**
+ * UDI pixel divider, controlling how many pixels are stuffed into a packet.
+ *
+ * Value is pixels minus 1.  Must be set to 1 pixel for SDVO.
+ */
+# define DPLL_MD_UDI_DIVIDER_MASK              0x3f000000
+# define DPLL_MD_UDI_DIVIDER_SHIFT             24
+/** UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */
+# define DPLL_MD_VGA_UDI_DIVIDER_MASK          0x003f0000
+# define DPLL_MD_VGA_UDI_DIVIDER_SHIFT         16
+/**
+ * SDVO/UDI pixel multiplier.
+ *
+ * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus
+ * clock rate is 10 times the DPLL clock.  At low resolution/refresh rate
+ * modes, the bus rate would be below the limits, so SDVO allows for stuffing
+ * dummy bytes in the datastream at an increased clock rate, with both sides of
+ * the link knowing how many bytes are fill.
+ *
+ * So, for a mode with a dotclock of 65Mhz, we would want to double the clock
+ * rate to 130Mhz to get a bus rate of 1.30Ghz.  The DPLL clock rate would be
+ * set to 130Mhz, and the SDVO multiplier set to 2x in this register and
+ * through an SDVO command.
+ *
+ * This register field has values of multiplication factor minus 1, with
+ * a maximum multiplier of 5 for SDVO.
+ */
+# define DPLL_MD_UDI_MULTIPLIER_MASK           0x00003f00
+# define DPLL_MD_UDI_MULTIPLIER_SHIFT          8
+/** SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK. 
+ * This best be set to the default value (3) or the CRT won't work. No,
+ * I don't entirely understand what this does...
+ */
+# define DPLL_MD_VGA_UDI_MULTIPLIER_MASK       0x0000003f
+# define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT      0
+/** @} */
+
+#define DPLL_TEST              0x606c
+# define DPLLB_TEST_SDVO_DIV_1                 (0 << 22)
+# define DPLLB_TEST_SDVO_DIV_2                 (1 << 22)
+# define DPLLB_TEST_SDVO_DIV_4                 (2 << 22)
+# define DPLLB_TEST_SDVO_DIV_MASK              (3 << 22)
+# define DPLLB_TEST_N_BYPASS                   (1 << 19)
+# define DPLLB_TEST_M_BYPASS                   (1 << 18)
+# define DPLLB_INPUT_BUFFER_ENABLE             (1 << 16)
+# define DPLLA_TEST_N_BYPASS                   (1 << 3)
+# define DPLLA_TEST_M_BYPASS                   (1 << 2)
+# define DPLLA_INPUT_BUFFER_ENABLE             (1 << 0)
+
+#define ADPA                   0x61100
+#define ADPA_DAC_ENABLE        (1<<31)
+#define ADPA_DAC_DISABLE       0
+#define ADPA_PIPE_SELECT_MASK  (1<<30)
+#define ADPA_PIPE_A_SELECT     0
+#define ADPA_PIPE_B_SELECT     (1<<30)
+#define ADPA_USE_VGA_HVPOLARITY (1<<15)
+#define ADPA_SETS_HVPOLARITY   0
+#define ADPA_VSYNC_CNTL_DISABLE (1<<11)
+#define ADPA_VSYNC_CNTL_ENABLE 0
+#define ADPA_HSYNC_CNTL_DISABLE (1<<10)
+#define ADPA_HSYNC_CNTL_ENABLE 0
+#define ADPA_VSYNC_ACTIVE_HIGH (1<<4)
+#define ADPA_VSYNC_ACTIVE_LOW  0
+#define ADPA_HSYNC_ACTIVE_HIGH (1<<3)
+#define ADPA_HSYNC_ACTIVE_LOW  0
+
+#define FPA0           0x06040
+#define FPA1           0x06044
+#define FPB0           0x06048
+#define FPB1           0x0604c
+# define FP_N_DIV_MASK                         0x003f0000
+# define FP_N_DIV_SHIFT                                16
+# define FP_M1_DIV_MASK                                0x00003f00
+# define FP_M1_DIV_SHIFT                       8
+# define FP_M2_DIV_MASK                                0x0000003f
+# define FP_M2_DIV_SHIFT                       0
+
+
+#define PORT_HOTPLUG_EN                0x61110
+# define SDVOB_HOTPLUG_INT_EN                  (1 << 26)
+# define SDVOC_HOTPLUG_INT_EN                  (1 << 25)
+# define TV_HOTPLUG_INT_EN                     (1 << 18)
+# define CRT_HOTPLUG_INT_EN                    (1 << 9)
+# define CRT_HOTPLUG_FORCE_DETECT              (1 << 3)
+
+#define PORT_HOTPLUG_STAT      0x61114
+# define CRT_HOTPLUG_INT_STATUS                        (1 << 11)
+# define TV_HOTPLUG_INT_STATUS                 (1 << 10)
+# define CRT_HOTPLUG_MONITOR_MASK              (3 << 8)
+# define CRT_HOTPLUG_MONITOR_COLOR             (3 << 8)
+# define CRT_HOTPLUG_MONITOR_MONO              (2 << 8)
+# define CRT_HOTPLUG_MONITOR_NONE              (0 << 8)
+# define SDVOC_HOTPLUG_INT_STATUS              (1 << 7)
+# define SDVOB_HOTPLUG_INT_STATUS              (1 << 6)
+
+#define SDVOB                  0x61140
+#define SDVOC                  0x61160
+#define SDVO_ENABLE                            (1 << 31)
+#define SDVO_PIPE_B_SELECT                     (1 << 30)
+#define SDVO_STALL_SELECT                      (1 << 29)
+#define SDVO_INTERRUPT_ENABLE                  (1 << 26)
+/**
+ * 915G/GM SDVO pixel multiplier.
+ *
+ * Programmed value is multiplier - 1, up to 5x.
+ *
+ * \sa DPLL_MD_UDI_MULTIPLIER_MASK
+ */
+#define SDVO_PORT_MULTIPLY_MASK                        (7 << 23)
+#define SDVO_PORT_MULTIPLY_SHIFT               23
+#define SDVO_PHASE_SELECT_MASK                 (15 << 19)
+#define SDVO_PHASE_SELECT_DEFAULT              (6 << 19)
+#define SDVO_CLOCK_OUTPUT_INVERT               (1 << 18)
+#define SDVOC_GANG_MODE                                (1 << 16)
+#define SDVO_BORDER_ENABLE                     (1 << 7)
+#define SDVOB_PCIE_CONCURRENCY                 (1 << 3)
+#define SDVO_DETECTED                          (1 << 2)
+/* Bits to be preserved when writing */
+#define SDVOB_PRESERVE_MASK                    ((1 << 17) | (1 << 16) | (1 << 14))
+#define SDVOC_PRESERVE_MASK                    (1 << 17)
+
+/** @defgroup LVDS
+ * @{
+ */
+/**
+ * This register controls the LVDS output enable, pipe selection, and data
+ * format selection.
+ *
+ * All of the clock/data pairs are force powered down by power sequencing.
+ */
+#define LVDS                   0x61180
+/**
+ * Enables the LVDS port.  This bit must be set before DPLLs are enabled, as
+ * the DPLL semantics change when the LVDS is assigned to that pipe.
+ */
+# define LVDS_PORT_EN                  (1 << 31)
+/** Selects pipe B for LVDS data.  Must be set on pre-965. */
+# define LVDS_PIPEB_SELECT             (1 << 30)
+
+/**
+ * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per
+ * pixel.
+ */
+# define LVDS_A0A2_CLKA_POWER_MASK     (3 << 8)
+# define LVDS_A0A2_CLKA_POWER_DOWN     (0 << 8)
+# define LVDS_A0A2_CLKA_POWER_UP       (3 << 8)
+/**
+ * Controls the A3 data pair, which contains the additional LSBs for 24 bit
+ * mode.  Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be
+ * on.
+ */
+# define LVDS_A3_POWER_MASK            (3 << 6)
+# define LVDS_A3_POWER_DOWN            (0 << 6)
+# define LVDS_A3_POWER_UP              (3 << 6)
+/**
+ * Controls the CLKB pair.  This should only be set when LVDS_B0B3_POWER_UP
+ * is set.
+ */
+# define LVDS_CLKB_POWER_MASK          (3 << 4)
+# define LVDS_CLKB_POWER_DOWN          (0 << 4)
+# define LVDS_CLKB_POWER_UP            (3 << 4)
+
+/**
+ * Controls the B0-B3 data pairs.  This must be set to match the DPLL p2
+ * setting for whether we are in dual-channel mode.  The B3 pair will
+ * additionally only be powered up when LVDS_A3_POWER_UP is set.
+ */
+# define LVDS_B0B3_POWER_MASK          (3 << 2)
+# define LVDS_B0B3_POWER_DOWN          (0 << 2)
+# define LVDS_B0B3_POWER_UP            (3 << 2)
+
+#define PIPEACONF 0x70008
+#define PIPEACONF_ENABLE       (1<<31)
+#define PIPEACONF_DISABLE      0
+#define PIPEACONF_DOUBLE_WIDE  (1<<30)
+#define I965_PIPECONF_ACTIVE   (1<<30)
+#define PIPEACONF_SINGLE_WIDE  0
+#define PIPEACONF_PIPE_UNLOCKED 0
+#define PIPEACONF_PIPE_LOCKED  (1<<25)
+#define PIPEACONF_PALETTE      0
+#define PIPEACONF_GAMMA        (1<<24)
+#define PIPECONF_FORCE_BORDER  (1<<25)
+#define PIPECONF_PROGRESSIVE   (0 << 21)
+#define PIPECONF_INTERLACE_W_FIELD_INDICATION  (6 << 21)
+#define PIPECONF_INTERLACE_FIELD_0_ONLY                (7 << 21)
+
+#define PIPEBCONF 0x71008
+#define PIPEBCONF_ENABLE       (1<<31)
+#define PIPEBCONF_DISABLE      0
+#define PIPEBCONF_DOUBLE_WIDE  (1<<30)
+#define PIPEBCONF_DISABLE      0
+#define PIPEBCONF_GAMMA        (1<<24)
+#define PIPEBCONF_PALETTE      0
+
+#define PIPEBGCMAXRED          0x71010
+#define PIPEBGCMAXGREEN                0x71014
+#define PIPEBGCMAXBLUE         0x71018
+#define PIPEBSTAT              0x71024
+#define PIPEBFRAMEHIGH         0x71040
+#define PIPEBFRAMEPIXEL                0x71044
+
+#define DSPACNTR               0x70180
+#define DSPBCNTR               0x71180
+#define DISPLAY_PLANE_ENABLE                   (1<<31)
+#define DISPLAY_PLANE_DISABLE                  0
+#define DISPPLANE_GAMMA_ENABLE                 (1<<30)
+#define DISPPLANE_GAMMA_DISABLE                        0
+#define DISPPLANE_PIXFORMAT_MASK               (0xf<<26)
+#define DISPPLANE_8BPP                         (0x2<<26)
+#define DISPPLANE_15_16BPP                     (0x4<<26)
+#define DISPPLANE_16BPP                                (0x5<<26)
+#define DISPPLANE_32BPP_NO_ALPHA               (0x6<<26)
+#define DISPPLANE_32BPP                                (0x7<<26)
+#define DISPPLANE_STEREO_ENABLE                        (1<<25)
+#define DISPPLANE_STEREO_DISABLE               0
+#define DISPPLANE_SEL_PIPE_MASK                        (1<<24)
+#define DISPPLANE_SEL_PIPE_A                   0
+#define DISPPLANE_SEL_PIPE_B                   (1<<24)
+#define DISPPLANE_SRC_KEY_ENABLE               (1<<22)
+#define DISPPLANE_SRC_KEY_DISABLE              0
+#define DISPPLANE_LINE_DOUBLE                  (1<<20)
+#define DISPPLANE_NO_LINE_DOUBLE               0
+#define DISPPLANE_STEREO_POLARITY_FIRST                0
+#define DISPPLANE_STEREO_POLARITY_SECOND       (1<<18)
+/* plane B only */
+#define DISPPLANE_ALPHA_TRANS_ENABLE           (1<<15)
+#define DISPPLANE_ALPHA_TRANS_DISABLE          0
+#define DISPPLANE_SPRITE_ABOVE_DISPLAYA                0
+#define DISPPLANE_SPRITE_ABOVE_OVERLAY         (1)
+
+#define DSPABASE               0x70184
+#define DSPASTRIDE             0x70188
+
+#define DSPBBASE               0x71184
+#define DSPBADDR               DSPBBASE
+#define DSPBSTRIDE             0x71188
+
+#define DSPAKEYVAL             0x70194
+#define DSPAKEYMASK            0x70198
+
+#define DSPAPOS                        0x7018C /* reserved */
+#define DSPASIZE               0x70190
+#define DSPBPOS                        0x7118C
+#define DSPBSIZE               0x71190
+
+#define DSPASURF               0x7019C
+#define DSPATILEOFF            0x701A4
+
+#define DSPBSURF               0x7119C
+#define DSPBTILEOFF            0x711A4
+
+#define VGACNTRL               0x71400
+# define VGA_DISP_DISABLE                      (1 << 31)
+# define VGA_2X_MODE                           (1 << 30)
+# define VGA_PIPE_B_SELECT                     (1 << 29)
+
+/*
+ * Palette registers
+ */
+#define PALETTE_A              0x0a000
+#define PALETTE_B              0x0a800
+
+#define IS_I830(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82830_CGC)
+#define IS_845G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82845G_IG)
+#define IS_I85X(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82855GM_IG)
+#define IS_I855(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82855GM_IG)
+#define IS_I865G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82865_IG)
+
+#define IS_I915G(pI810) (dev->pci_device == PCI_DEVICE_ID_INTEL_82915G_IG)/* || dev->pci_device == PCI_DEVICE_ID_INTELPCI_CHIP_E7221_G)*/
+#define IS_I915GM(pI810) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82915GM_IG)
+#define IS_I945G(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82945G_IG)
+#define IS_I945GM(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82945GM_IG)
+
+#define IS_I965G(dev) ((dev)->pci_device == 0x2972 || \
+                      (dev)->pci_device == 0x2982 || \
+                      (dev)->pci_device == 0x2992 || \
+                      (dev)->pci_device == 0x29A2)
+
+
+#define IS_I9XX(pI810) (IS_I915G(pI810) || IS_I915GM(pI810) || IS_I945G(pI810) || IS_I945GM(pI810) || IS_I965G(pI810))
+
+#define IS_MOBILE(pI810) (IS_I830(pI810) || IS_I85X(pI810) || IS_I915GM(pI810) || IS_I945GM(pI810))
+
 #endif