summaryrefslogtreecommitdiff
path: root/drivers/staging/psb/intel_lvds.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/psb/intel_lvds.c')
-rw-r--r--drivers/staging/psb/intel_lvds.c1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/drivers/staging/psb/intel_lvds.c b/drivers/staging/psb/intel_lvds.c
new file mode 100644
index 000000000000..bbef2fe88cac
--- /dev/null
+++ b/drivers/staging/psb/intel_lvds.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+/* MRST defines start */
+uint8_t blc_type;
+uint8_t blc_pol;
+uint8_t blc_freq;
+uint8_t blc_minbrightness;
+uint8_t blc_i2caddr;
+uint8_t blc_brightnesscmd;
+int lvds_backlight; /* restore backlight to this value */
+
+u32 CoreClock;
+u32 PWMControlRegFreq;
+/* MRST defines end */
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ */
+static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+ u32 blc_pwm_ctl;
+
+ blc_pwm_ctl = REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ REG_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+ (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+{
+ return ((REG_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+static void intel_lvds_set_power(struct drm_device *dev,
+ struct intel_output *output, bool on)
+{
+ u32 pp_status;
+
+ if (on) {
+ REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = REG_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev,
+ output->
+ mode_dev->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = REG_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct intel_output *output = enc_to_intel_output(encoder);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ intel_lvds_set_power(dev, output, true);
+ else
+ intel_lvds_set_power(dev, output, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_connector *connector)
+{
+#if 0 /* JB: Disable for drop */
+ struct drm_device *dev = connector->dev;
+
+ dev_priv->savePP_ON = REG_READ(PP_ON_DELAYS);
+ dev_priv->savePP_OFF = REG_READ(PP_OFF_DELAYS);
+ dev_priv->savePP_CONTROL = REG_READ(PP_CONTROL);
+ dev_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);
+ dev_priv->saveBLC_PWM_CTL = REG_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);
+#endif
+}
+
+static void intel_lvds_restore(struct drm_connector *connector)
+{
+#if 0 /* JB: Disable for drop */
+ struct drm_device *dev = connector->dev;
+
+ REG_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ REG_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
+ REG_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
+ REG_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
+ REG_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);
+#endif
+}
+
+static int intel_lvds_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_display_mode *fixed_mode =
+ intel_output->mode_dev->panel_fixed_mode;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter intel_lvds_mode_valid \n");
+#endif /* PRINT_JLIU7 */
+
+ 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_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_mode_device *mode_dev =
+ enc_to_intel_output(encoder)->mode_dev;
+ struct drm_device *dev = encoder->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct drm_encoder *tmp_encoder;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter intel_lvds_mode_fixup \n");
+#endif /* PRINT_JLIU7 */
+
+ /* Should never happen!! */
+ if (IS_MRST(dev) && intel_crtc->pipe != 0) {
+ printk(KERN_ERR
+ "Can't support LVDS/MIPI on pipe B on MRST\n");
+ return false;
+ } else if (!IS_MRST(dev) && !IS_I965G(dev)
+ && intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+ /* Should never happen!! */
+ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list,
+ head) {
+ if (tmp_encoder != encoder
+ && tmp_encoder->crtc == encoder->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "encoder on the same pipe\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 (mode_dev->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay =
+ mode_dev->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ mode_dev->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ mode_dev->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = mode_dev->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay =
+ mode_dev->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ mode_dev->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ mode_dev->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = mode_dev->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = mode_dev->panel_fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode,
+ CRTC_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_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct intel_output *output = enc_to_intel_output(encoder);
+ struct intel_mode_device *mode_dev = output->mode_dev;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter intel_lvds_prepare \n");
+#endif /* PRINT_JLIU7 */
+
+ mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
+ mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ intel_lvds_set_power(dev, output, false);
+}
+
+static void intel_lvds_commit(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct intel_output *output = enc_to_intel_output(encoder);
+ struct intel_mode_device *mode_dev = output->mode_dev;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter intel_lvds_commit \n");
+#endif /* PRINT_JLIU7 */
+
+ if (mode_dev->backlight_duty_cycle == 0)
+ mode_dev->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+
+ intel_lvds_set_power(dev, output, true);
+}
+
+static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_mode_device *mode_dev =
+ enc_to_intel_output(encoder)->mode_dev;
+ struct drm_device *dev = encoder->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ 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.
+ */
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay)
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
+ HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
+ HORIZ_INTERP_BILINEAR);
+ else
+ pfit_control = 0;
+
+ if (!IS_I965G(dev)) {
+ if (mode_dev->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ } else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ REG_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns CONNECTOR_STATUS_CONNECTED.
+ * This connector should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_connector_status intel_lvds_detect(struct drm_connector
+ *connector)
+{
+ return connector_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_mode_device *mode_dev = intel_output->mode_dev;
+ int ret = 0;
+
+ if (!IS_MRST(dev))
+ ret = intel_ddc_get_modes(intel_output);
+
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID, so
+ * Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+
+ if (mode_dev->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode =
+ drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_lvds_destroy - unregister and free LVDS structures
+ * @connector: connector to free
+ *
+ * Unregister the DDC bus for this connector then free the driver private
+ * structure.
+ */
+static void intel_lvds_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
+ .dpms = intel_lvds_dpms,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = intel_lvds_mode_set,
+ .commit = intel_lvds_commit,
+};
+
+static const struct drm_connector_helper_funcs
+ intel_lvds_connector_helper_funcs = {
+ .get_modes = intel_lvds_get_modes,
+ .mode_valid = intel_lvds_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static const struct drm_connector_funcs intel_lvds_connector_funcs = {
+ .save = intel_lvds_save,
+ .restore = intel_lvds_restore,
+ .detect = intel_lvds_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_lvds_destroy,
+};
+
+
+static void intel_lvds_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
+ .destroy = intel_lvds_enc_destroy,
+};
+
+
+
+/**
+ * intel_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, 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(struct drm_device *dev,
+ struct intel_mode_device *mode_dev)
+{
+ struct intel_output *intel_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ struct drm_crtc *crtc;
+ u32 lvds;
+ int pipe;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ intel_output->mode_dev = mode_dev;
+ connector = &intel_output->base;
+ encoder = &intel_output->enc;
+ drm_connector_init(dev, &intel_output->base,
+ &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+ intel_output->type = INTEL_OUTPUT_LVDS;
+
+ drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
+ drm_connector_helper_add(connector,
+ &intel_lvds_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+
+ /*
+ * LVDS discovery:
+ * 1) check for EDID on DDC
+ * 2) check for VBT data
+ * 3) check to see if LVDS is already on
+ * if none of the above, no panel
+ * 4) make sure lid is open
+ * if closed, act like it's not there for now
+ */
+
+ /* Set up the DDC bus. */
+ 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");
+ goto failed_ddc;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(intel_output);
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ mode_dev->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* Failed to get EDID, what about VBT? */
+ if (mode_dev->vbt_mode)
+ mode_dev->panel_fixed_mode =
+ drm_mode_duplicate(dev, mode_dev->vbt_mode);
+
+ /*
+ * 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.
+ */
+ lvds = REG_READ(LVDS);
+ pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+
+ if (crtc && (lvds & LVDS_PORT_EN)) {
+ mode_dev->panel_fixed_mode =
+ intel_crtc_mode_get(dev, crtc);
+ if (mode_dev->panel_fixed_mode) {
+ mode_dev->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!mode_dev->panel_fixed_mode) {
+ DRM_DEBUG
+ ("Found no modes on the lvds, ignoring the LVDS\n");
+ goto failed_find;
+ }
+
+ /* FIXME: detect aopen & mac mini type stuff automatically? */
+ /*
+ * Blacklist machines with BIOSes that list an LVDS panel without
+ * actually having one.
+ */
+ if (IS_I945GM(dev)) {
+ /* aopen mini pc */
+ if (dev->pdev->subsystem_vendor == 0xa0a0) {
+ DRM_DEBUG
+ ("Suspected AOpen Mini PC, ignoring the LVDS\n");
+ goto failed_find;
+ }
+
+ if ((dev->pdev->subsystem_vendor == 0x8086) &&
+ (dev->pdev->subsystem_device == 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 (mode_dev->panel_fixed_mode != NULL &&
+ mode_dev->panel_fixed_mode->hdisplay == 800 &&
+ mode_dev->panel_fixed_mode->vdisplay == 600) {
+ DRM_DEBUG
+ ("Suspected Mac Mini, ignoring the LVDS\n");
+ goto failed_find;
+ }
+ }
+ }
+
+out:
+ drm_sysfs_connector_add(connector);
+
+#if PRINT_JLIU7
+ DRM_INFO("PRINT_JLIU7 hdisplay = %d\n",
+ mode_dev->panel_fixed_mode->hdisplay);
+ DRM_INFO("PRINT_JLIU7 vdisplay = %d\n",
+ mode_dev->panel_fixed_mode->vdisplay);
+ DRM_INFO("PRINT_JLIU7 hsync_start = %d\n",
+ mode_dev->panel_fixed_mode->hsync_start);
+ DRM_INFO("PRINT_JLIU7 hsync_end = %d\n",
+ mode_dev->panel_fixed_mode->hsync_end);
+ DRM_INFO("PRINT_JLIU7 htotal = %d\n",
+ mode_dev->panel_fixed_mode->htotal);
+ DRM_INFO("PRINT_JLIU7 vsync_start = %d\n",
+ mode_dev->panel_fixed_mode->vsync_start);
+ DRM_INFO("PRINT_JLIU7 vsync_end = %d\n",
+ mode_dev->panel_fixed_mode->vsync_end);
+ DRM_INFO("PRINT_JLIU7 vtotal = %d\n",
+ mode_dev->panel_fixed_mode->vtotal);
+ DRM_INFO("PRINT_JLIU7 clock = %d\n",
+ mode_dev->panel_fixed_mode->clock);
+#endif /* PRINT_JLIU7 */
+ return;
+
+failed_find:
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+failed_ddc:
+ drm_encoder_cleanup(encoder);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+/* MRST platform start */
+
+/*
+ * FIXME need to move to register define head file
+ */
+#define MRST_BACKLIGHT_MODULATION_FREQ_SHIFT (16)
+#define MRST_BACKLIGHT_MODULATION_FREQ_MASK (0xffff << 16)
+
+/* The max/min PWM frequency in BPCR[31:17] - */
+/* The smallest number is 1 (not 0) that can fit in the
+ * 15-bit field of the and then*/
+/* shifts to the left by one bit to get the actual 16-bit
+ * value that the 15-bits correspond to.*/
+#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
+
+#define BRIGHTNESS_MAX_LEVEL 100
+#define BLC_PWM_PRECISION_FACTOR 10 /* 10000000 */
+#define BLC_PWM_FREQ_CALC_CONSTANT 32
+#define MHz 1000000
+#define BLC_POLARITY_NORMAL 0
+#define BLC_POLARITY_INVERSE 1
+
+/**
+ * Calculate PWM control register value.
+ */
+static bool mrstLVDSCalculatePWMCtrlRegFreq(struct drm_device *dev)
+{
+ unsigned long value = 0;
+ if (blc_freq == 0) {
+ /* DRM_ERROR(KERN_ERR "mrstLVDSCalculatePWMCtrlRegFreq:
+ * Frequency Requested is 0.\n"); */
+ return false;
+ }
+
+ value = (CoreClock * MHz);
+ value = (value / BLC_PWM_FREQ_CALC_CONSTANT);
+ value = (value * BLC_PWM_PRECISION_FACTOR);
+ value = (value / blc_freq);
+ value = (value / BLC_PWM_PRECISION_FACTOR);
+
+ if (value > (unsigned long) MRST_BLC_MAX_PWM_REG_FREQ) {
+ return 0;
+ } else {
+ PWMControlRegFreq = (u32) value;
+ return 1;
+ }
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 mrst_lvds_get_PWM_ctrl_freq(struct drm_device *dev)
+{
+ u32 max_pwm_blc = 0;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_get_PWM_ctrl_freq \n");
+#endif /* PRINT_JLIU7 */
+
+/*FIXME JLIU7 get the PWM frequency from configuration */
+
+ max_pwm_blc =
+ (REG_READ(BLC_PWM_CTL) & MRST_BACKLIGHT_MODULATION_FREQ_MASK)
+ >> MRST_BACKLIGHT_MODULATION_FREQ_SHIFT;
+
+
+ if (!max_pwm_blc) {
+ if (mrstLVDSCalculatePWMCtrlRegFreq(dev))
+ max_pwm_blc = PWMControlRegFreq;
+ }
+
+ return max_pwm_blc;
+}
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ */
+static void mrst_lvds_set_backlight(struct drm_device *dev, int level)
+{
+ u32 blc_pwm_ctl;
+ u32 max_pwm_blc;
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_set_backlight \n");
+#endif /* PRINT_JLIU7 */
+
+#if 1 /* FIXME JLIU7 */
+ return;
+#endif /* FIXME JLIU7 */
+
+ /* Provent LVDS going to total black */
+ if (level < 20)
+ level = 20;
+
+ max_pwm_blc = mrst_lvds_get_PWM_ctrl_freq(dev);
+
+ if (max_pwm_blc == 0)
+ return;
+
+ blc_pwm_ctl = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL;
+
+ if (blc_pol == BLC_POLARITY_INVERSE)
+ blc_pwm_ctl = max_pwm_blc - blc_pwm_ctl;
+
+ REG_WRITE(BLC_PWM_CTL,
+ (max_pwm_blc << MRST_BACKLIGHT_MODULATION_FREQ_SHIFT) |
+ blc_pwm_ctl);
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+static void mrst_lvds_set_power(struct drm_device *dev,
+ struct intel_output *output, bool on)
+{
+ u32 pp_status;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_set_power \n");
+#endif /* PRINT_JLIU7 */
+
+ if (on) {
+ REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = REG_READ(PP_STATUS);
+ } while ((pp_status & (PP_ON | PP_READY)) == PP_READY);
+
+ mrst_lvds_set_backlight(dev, lvds_backlight);
+ } else {
+ mrst_lvds_set_backlight(dev, 0);
+
+ REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = REG_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void mrst_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct intel_output *output = enc_to_intel_output(encoder);
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_dpms \n");
+#endif /* PRINT_JLIU7 */
+
+ if (mode == DRM_MODE_DPMS_ON)
+ mrst_lvds_set_power(dev, output, true);
+ else
+ mrst_lvds_set_power(dev, output, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void mrst_lvds_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_mode_device *mode_dev =
+ enc_to_intel_output(encoder)->mode_dev;
+ struct drm_device *dev = encoder->dev;
+ u32 pfit_control;
+ u32 lvds_port;
+
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_mode_set \n");
+#endif /* PRINT_JLIU7 */
+
+ /*
+ * 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.
+ */
+ /*FIXME JLIU7 Get panel power delay parameters from config data */
+ REG_WRITE(0x61208, 0x25807d0);
+ REG_WRITE(0x6120c, 0x1f407d0);
+ REG_WRITE(0x61210, 0x270f04);
+
+ lvds_port = (REG_READ(LVDS) & (~LVDS_PIPEB_SELECT)) | LVDS_PORT_EN;
+
+ if (mode_dev->panel_wants_dither)
+ lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE;
+
+ REG_WRITE(LVDS, lvds_port);
+
+ /*
+ * 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.
+ */
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay)
+ pfit_control = PFIT_ENABLE;
+ else
+ pfit_control = 0;
+
+ REG_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+
+static const struct drm_encoder_helper_funcs mrst_lvds_helper_funcs = {
+ .dpms = mrst_lvds_dpms,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = mrst_lvds_mode_set,
+ .commit = intel_lvds_commit,
+};
+
+/** Returns the panel fixed mode from configuration. */
+/** FIXME JLIU7 need to revist it. */
+struct drm_display_mode *mrst_lvds_get_configuration_mode(struct drm_device
+ *dev)
+{
+ struct drm_display_mode *mode;
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+#if 0 /*FIXME jliu7 remove it later */
+ /* hard coded fixed mode for TPO LTPS LPJ040K001A */
+ mode->hdisplay = 800;
+ mode->vdisplay = 480;
+ mode->hsync_start = 836;
+ mode->hsync_end = 846;
+ mode->htotal = 1056;
+ mode->vsync_start = 489;
+ mode->vsync_end = 491;
+ mode->vtotal = 525;
+ mode->clock = 33264;
+#endif /*FIXME jliu7 remove it later */
+
+#if 0 /*FIXME jliu7 remove it later */
+ /* hard coded fixed mode for LVDS 800x480 */
+ mode->hdisplay = 800;
+ mode->vdisplay = 480;
+ mode->hsync_start = 801;
+ mode->hsync_end = 802;
+ mode->htotal = 1024;
+ mode->vsync_start = 481;
+ mode->vsync_end = 482;
+ mode->vtotal = 525;
+ mode->clock = 30994;
+#endif /*FIXME jliu7 remove it later */
+
+#if 1 /*FIXME jliu7 remove it later, jliu7 modify it according to the spec */
+ /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
+ mode->hdisplay = 1024;
+ mode->vdisplay = 600;
+ mode->hsync_start = 1072;
+ mode->hsync_end = 1104;
+ mode->htotal = 1184;
+ mode->vsync_start = 603;
+ mode->vsync_end = 604;
+ mode->vtotal = 608;
+ mode->clock = 53990;
+#endif /*FIXME jliu7 remove it later */
+
+#if 0 /*FIXME jliu7 remove it, it is copied from SBIOS */
+ /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
+ mode->hdisplay = 1024;
+ mode->vdisplay = 600;
+ mode->hsync_start = 1104;
+ mode->hsync_end = 1136;
+ mode->htotal = 1184;
+ mode->vsync_start = 603;
+ mode->vsync_end = 604;
+ mode->vtotal = 608;
+ mode->clock = 53990;
+#endif /*FIXME jliu7 remove it later */
+
+#if 0 /*FIXME jliu7 remove it later */
+ /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */
+ mode->hdisplay = 1024;
+ mode->vdisplay = 600;
+ mode->hsync_start = 1124;
+ mode->hsync_end = 1204;
+ mode->htotal = 1312;
+ mode->vsync_start = 607;
+ mode->vsync_end = 610;
+ mode->vtotal = 621;
+ mode->clock = 48885;
+#endif /*FIXME jliu7 remove it later */
+
+#if 0 /*FIXME jliu7 remove it later */
+ /* hard coded fixed mode for LVDS 1024x768 */
+ mode->hdisplay = 1024;
+ mode->vdisplay = 768;
+ mode->hsync_start = 1048;
+ mode->hsync_end = 1184;
+ mode->htotal = 1344;
+ mode->vsync_start = 771;
+ mode->vsync_end = 777;
+ mode->vtotal = 806;
+ mode->clock = 65000;
+#endif /*FIXME jliu7 remove it later */
+
+#if 0 /*FIXME jliu7 remove it later */
+ /* hard coded fixed mode for LVDS 1366x768 */
+ mode->hdisplay = 1366;
+ mode->vdisplay = 768;
+ mode->hsync_start = 1430;
+ mode->hsync_end = 1558;
+ mode->htotal = 1664;
+ mode->vsync_start = 769;
+ mode->vsync_end = 770;
+ mode->vtotal = 776;
+ mode->clock = 77500;
+#endif /*FIXME jliu7 remove it later */
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ return mode;
+}
+
+/**
+ * mrst_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void mrst_lvds_init(struct drm_device *dev,
+ struct intel_mode_device *mode_dev)
+{
+ struct intel_output *intel_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+#if MRST_I2C
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+#endif
+#if PRINT_JLIU7
+ DRM_INFO("JLIU7 enter mrst_lvds_init \n");
+#endif /* PRINT_JLIU7 */
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ intel_output->mode_dev = mode_dev;
+ connector = &intel_output->base;
+ encoder = &intel_output->enc;
+ drm_connector_init(dev, &intel_output->base,
+ &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+ intel_output->type = INTEL_OUTPUT_LVDS;
+
+ drm_encoder_helper_add(encoder, &mrst_lvds_helper_funcs);
+ drm_connector_helper_add(connector,
+ &intel_lvds_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ lvds_backlight = BRIGHTNESS_MAX_LEVEL;
+
+ /*
+ * LVDS discovery:
+ * 1) check for EDID on DDC
+ * 2) check for VBT data
+ * 3) check to see if LVDS is already on
+ * if none of the above, no panel
+ * 4) make sure lid is open
+ * if closed, act like it's not there for now
+ */
+
+#if MRST_I2C
+ /* Set up the DDC bus. */
+ 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");
+ goto failed_ddc;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(intel_output);
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ mode_dev->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+#endif /* MRST_I2C */
+
+ /*
+ * If we didn't get EDID, try geting panel timing
+ * from configuration data
+ */
+ mode_dev->panel_fixed_mode = mrst_lvds_get_configuration_mode(dev);
+
+ if (mode_dev->panel_fixed_mode) {
+ mode_dev->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!mode_dev->panel_fixed_mode) {
+ DRM_DEBUG
+ ("Found no modes on the lvds, ignoring the LVDS\n");
+ goto failed_find;
+ }
+
+out:
+ drm_sysfs_connector_add(connector);
+ return;
+
+failed_find:
+ DRM_DEBUG("No LVDS modes found, disabling.\n");
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+#if MRST_I2C
+failed_ddc:
+#endif
+ drm_encoder_cleanup(encoder);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+/* MRST platform end */