BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
}
-int lvds_backlight(DRM_IOCTL_ARGS)
+/**
+ * Sets the power state for the panel.
+ */
+static void intel_lvds_set_power(struct drm_device *dev, bool on)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 pp_status;
+
+ if (on) {
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_output *output, int mode)
+{
+ struct drm_device *dev = output->dev;
+
+ if (mode == DPMSModeOn)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ dev_priv->savePP_ON = I915_READ(LVDSPP_ON);
+ dev_priv->savePP_OFF = I915_READ(LVDSPP_OFF);
+ dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
+ dev_priv->savePP_CYCLE = I915_READ(PP_CYCLE);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+}
+
+static void intel_lvds_restore(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(LVDSPP_ON, dev_priv->savePP_ON);
+ I915_WRITE(LVDSPP_OFF, dev_priv->savePP_OFF);
+ I915_WRITE(PP_CYCLE, dev_priv->savePP_CYCLE);
+ I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+}
+
+static int intel_lvds_mode_valid(struct drm_output *output,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+
+ if (fixed_mode) {
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static bool intel_lvds_mode_fixup(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = output->crtc->driver_private;
+ struct drm_output *tmp_output;
+
- spin_lock(&dev->crtc_config.config_lock);
- list_for_each_entry(tmp_output, &dev->crtc_config.output_list, head) {
++ spin_lock(&dev->mode_config.config_lock);
++ list_for_each_entry(tmp_output, &dev->mode_config.output_list, head) {
+ if (tmp_output != output && tmp_output->crtc == output->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "output on the same pipe\n");
+ return false;
+ }
+ }
- spin_lock(&dev->crtc_config.config_lock);
++ spin_lock(&dev->mode_config.config_lock);
+
+ if (intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+
+ /*
+ * If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dev_priv->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ dev_priv->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ dev_priv->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ dev_priv->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ dev_priv->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
+// xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V);
+ }
+
+ /*
+ * XXX: It would be nice to support lower refresh rates on the
+ * panels to reduce power consumption, and perhaps match the
+ * user's requested refresh rate.
+ */
+
+ return true;
+}
+
+static void intel_lvds_mode_set(struct drm_output *output,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = output->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = output->crtc->driver_private;
+ u32 pfit_control;
+
+ /*
+ * The LVDS pin pair will already have been turned on in the
+ * intel_crtc_mode_set since it has a large impact on the DPLL
+ * settings.
+ */
+
+ /*
+ * Enable automatic panel scaling so that non-native modes fill the
+ * screen. Should be enabled before the pipe is enabled, according to
+ * register description and PRM.
+ */
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+ VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+
+ if (!IS_I965G(dev)) {
+ if (dev_priv->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ }
+ else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ I915_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns OUTPUT_STATUS_CONNECTED. This output should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_output_status intel_lvds_detect(struct drm_output *output)
+{
+ return output_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_output *output)
{
- DRM_DEVICE;
- unsigned long dvoa_enabled, dvob_enabled, dvoc_enabled, lvds_enabled;
+ struct intel_output *intel_output = output->driver_private;
+ struct drm_device *dev = output->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct edid *edid_info;
+ int ret = 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 0;
+ }
+ intel_i2c_destroy(intel_output->ddc_bus);
+
+ ret = intel_ddc_get_modes(output);
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID */
+ if (!output->monitor_info) {
+ struct detailed_data_monitor_range *edid_range;
+ edid_info = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL);
+ if (!edid_info)
+ goto out;
+
+ edid_info->detailed_timings[0].data.other_data.type =
+ EDID_DETAIL_MONITOR_RANGE;
+ edid_range = &edid_info->detailed_timings[0].data.other_data.data.range;
+
+ /* Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ edid_range->min_vfreq = 0;
+ edid_range->max_vfreq = 200;
+ edid_range->min_hfreq_khz = 0;
+ edid_range->max_hfreq_khz = 200;
+ output->monitor_info = edid_info;
+ }
+
+out:
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode =
+ drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(output, mode);
+ return 1;
+ }
- 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;
}
intel_output->type = INTEL_OUTPUT_LVDS;
output->driver_private = intel_output;
output->subpixel_order = SubPixelHorizontalRGB;
- output->interlace_allowed = 0;
- output->doublescan_allowed = 0;
+ output->interlace_allowed = FALSE;
+ output->doublescan_allowed = FALSE;
- 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;
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(output);
+ list_for_each_entry(scan, &output->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED)
+ break;
}
- modes = intel_ddc_get_modes(output);
- intel_i2c_destroy(intel_output->ddc_bus);
- drm_output_destroy(output);
-}
+ if (scan)
+ dev_priv->panel_fixed_mode = scan;
+
+ /*
+ * If we didn't get EDID, try checking if the panel is already turned
+ * on. If so, assume that whatever is currently programmed is the
+ * correct mode.
+ */
+ if (!dev_priv->panel_fixed_mode) {
+ u32 lvds = I915_READ(LVDS);
+ int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
- struct drm_crtc_config *crtc_config = &dev->crtc_config;
++ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_crtc *crtc;
+ /* FIXME: need drm_crtc_from_pipe */
- //crtc = drm_crtc_from_pipe(crtc_config, pipe);
++ //crtc = drm_crtc_from_pipe(mode_config, pipe);
+
+ if (lvds & LVDS_PORT_EN && 0) {
+ dev_priv->panel_fixed_mode =
+ intel_crtc_mode_get(dev, crtc);
+ if (dev_priv->panel_fixed_mode)
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ }
+ }
+
+/* No BIOS poking yet... */
+#if 0
+ /* Get the LVDS fixed mode out of the BIOS. We should support LVDS
+ * with the BIOS being unavailable or broken, but lack the
+ * configuration options for now.
+ */
+ bios_mode = intel_bios_get_panel_mode(pScrn);
+ if (bios_mode != NULL) {
+ if (dev_priv->panel_fixed_mode != NULL) {
+ if (dev_priv->debug_modes &&
+ !xf86ModesEqual(dev_priv->panel_fixed_mode,
+ bios_mode))
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "BIOS panel mode data doesn't match probed data, "
+ "continuing with probed.\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, bios_mode);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "probed mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, dev_priv->panel_fixed_mode);
+ xfree(bios_mode->name);
+ xfree(bios_mode);
+ }
+ } else {
+ dev_priv->panel_fixed_mode = bios_mode;
+ }
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Couldn't detect panel mode. Disabling panel\n");
+ goto disable_exit;
+ }
+
+ /* Blacklist machines with BIOSes that list an LVDS panel without actually
+ * having one.
+ */
+ if (dev_priv->PciInfo->chipType == PCI_CHIP_I945_GM) {
+ if (dev_priv->PciInfo->subsysVendor == 0xa0a0) /* aopen mini pc */
+ goto disable_exit;
+
+ if ((dev_priv->PciInfo->subsysVendor == 0x8086) &&
+ (dev_priv->PciInfo->subsysCard == 0x7270)) {
+ /* It's a Mac Mini or Macbook Pro.
+ *
+ * Apple hardware is out to get us. The macbook pro has a real
+ * LVDS panel, but the mac mini does not, and they have the same
+ * device IDs. We'll distinguish by panel size, on the assumption
+ * that Apple isn't about to make any machines with an 800x600
+ * display.
+ */
+ if (dev_priv->panel_fixed_mode != NULL &&
+ dev_priv->panel_fixed_mode->HDisplay == 800 &&
+ dev_priv->panel_fixed_mode->VDisplay == 600)
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Suspected Mac Mini, ignoring the LVDS\n");
+ goto disable_exit;
+ }
+ }
+ }
+
+#endif
+ return;
+}
--- /dev/null
- drm_framebuffer_set_object(dev, dev_priv->sarea_priv->front_handle);
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+int i915_driver_load(drm_device_t *dev, unsigned long flags)
+{
+ drm_i915_private_t *dev_priv;
+ drm_i915_init_t init;
+ int ret;
+
+ 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;
+ dev->types[7] = _DRM_STAT_PRIMARY;
+ 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);
++ dev_priv->baseaddr = drm_get_resource_start(dev, 2) & 0xff000000;
+ } 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);
++ dev_priv->baseaddr = drm_get_resource_start(dev, 0) & 0xff000000;
+ } 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;
+ }
+
+
+ ret = drm_setup(dev);
+ if (ret) {
+ DRM_ERROR("drm_setup failed\n");
+ return ret;
+ }
+
+ DRM_GETSAREA();
+ if (!dev_priv->sarea) {
+ DRM_ERROR("can not find sarea!\n");
+ dev->dev_private = (void *)dev_priv;
+ i915_dma_cleanup(dev);
+ return DRM_ERR(EINVAL);
+ }
+
+ /* FIXME: where does the sarea_priv really go? */
+ dev_priv->sarea_priv = kmalloc(sizeof(drm_i915_sarea_t), GFP_KERNEL);
+
+ /* FIXME: need real front buffer offset */
+ dev_priv->sarea_priv->front_handle = 0xa0000000 + 1024*1024;
+
+ drm_bo_driver_init(dev);
+ /* this probably doesn't belong here - TODO */
++ //drm_framebuffer_set_object(dev, dev_priv->sarea_priv->front_handle);
+ intel_modeset_init(dev);
+ drm_set_desired_modes(dev);
+
+ /* FIXME: command ring needs AGP space, do we own it at this point? */
+ dev_priv->ring.Start = 0xa0000000;
+ dev_priv->ring.End = 128*1024;
+ dev_priv->ring.Size = 128*1024;
+ dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+
+ dev_priv->ring.map.offset = dev_priv->ring.Start;
+ dev_priv->ring.map.size = dev_priv->ring.Size;
+ dev_priv->ring.map.type = 0;
+ dev_priv->ring.map.flags = 0;
+ dev_priv->ring.map.mtrr = 0;
+
+ drm_core_ioremap(&dev_priv->ring.map, dev);
+
+ if (dev_priv->ring.map.handle == NULL) {
+ dev->dev_private = (void *)dev_priv;
+ i915_dma_cleanup(dev);
+ DRM_ERROR("can not ioremap virtual address for"
+ " ring buffer\n");
+ return DRM_ERR(ENOMEM);
+ }
+
+ dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
+ dev_priv->cpp = 4;
+ dev_priv->sarea_priv->pf_current_page = 0;
+
+ /* We are using separate values as placeholders for mechanisms for
+ * private backbuffer/depthbuffer usage.
+ */
+ dev_priv->use_mi_batchbuffer_start = 0;
+
+ /* Allow hardware batchbuffers unless told otherwise.
+ */
+ dev_priv->allow_batchbuffer = 1;
+
+ /* Program Hardware Status Page */
+ dev_priv->status_page_dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE,
+ 0xffffffff);
+
+ if (!dev_priv->status_page_dmah) {
+ dev->dev_private = (void *)dev_priv;
+ i915_dma_cleanup(dev);
+ DRM_ERROR("Can not allocate hardware status page\n");
+ return DRM_ERR(ENOMEM);
+ }
+ dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
+ dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;
+
+ memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
+ DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
+
+ I915_WRITE(0x02080, dev_priv->dma_status_page);
+ DRM_DEBUG("Enabled hardware status page\n");
+
+ 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)
+{
+ 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)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ i915_mem_release(dev, filp, dev_priv->agp_heap);
+}
+