18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2006-2011 Intel Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *	Eric Anholt <eric@anholt.net>
78c2ecf20Sopenharmony_ci *	Dave Airlie <airlied@linux.ie>
88c2ecf20Sopenharmony_ci *	Jesse Barnes <jesse.barnes@intel.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/dmi.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "cdv_device.h"
188c2ecf20Sopenharmony_ci#include "intel_bios.h"
198c2ecf20Sopenharmony_ci#include "power.h"
208c2ecf20Sopenharmony_ci#include "psb_drv.h"
218c2ecf20Sopenharmony_ci#include "psb_intel_drv.h"
228c2ecf20Sopenharmony_ci#include "psb_intel_reg.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/**
258c2ecf20Sopenharmony_ci * LVDS I2C backlight control macros
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#define BRIGHTNESS_MAX_LEVEL 100
288c2ecf20Sopenharmony_ci#define BRIGHTNESS_MASK 0xFF
298c2ecf20Sopenharmony_ci#define BLC_I2C_TYPE	0x01
308c2ecf20Sopenharmony_ci#define BLC_PWM_TYPT	0x02
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define BLC_POLARITY_NORMAL 0
338c2ecf20Sopenharmony_ci#define BLC_POLARITY_INVERSE 1
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define PSB_BLC_MAX_PWM_REG_FREQ       (0xFFFE)
368c2ecf20Sopenharmony_ci#define PSB_BLC_MIN_PWM_REG_FREQ	(0x2)
378c2ecf20Sopenharmony_ci#define PSB_BLC_PWM_PRECISION_FACTOR	(10)
388c2ecf20Sopenharmony_ci#define PSB_BACKLIGHT_PWM_CTL_SHIFT	(16)
398c2ecf20Sopenharmony_ci#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct cdv_intel_lvds_priv {
428c2ecf20Sopenharmony_ci	/**
438c2ecf20Sopenharmony_ci	 * Saved LVDO output states
448c2ecf20Sopenharmony_ci	 */
458c2ecf20Sopenharmony_ci	uint32_t savePP_ON;
468c2ecf20Sopenharmony_ci	uint32_t savePP_OFF;
478c2ecf20Sopenharmony_ci	uint32_t saveLVDS;
488c2ecf20Sopenharmony_ci	uint32_t savePP_CONTROL;
498c2ecf20Sopenharmony_ci	uint32_t savePP_CYCLE;
508c2ecf20Sopenharmony_ci	uint32_t savePFIT_CONTROL;
518c2ecf20Sopenharmony_ci	uint32_t savePFIT_PGM_RATIOS;
528c2ecf20Sopenharmony_ci	uint32_t saveBLC_PWM_CTL;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Returns the maximum level of the backlight duty cycle field.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_cistatic u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
618c2ecf20Sopenharmony_ci	u32 retval;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (gma_power_begin(dev, false)) {
648c2ecf20Sopenharmony_ci		retval = ((REG_READ(BLC_PWM_CTL) &
658c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_MASK) >>
668c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		gma_power_end(dev);
698c2ecf20Sopenharmony_ci	} else
708c2ecf20Sopenharmony_ci		retval = ((dev_priv->regs.saveBLC_PWM_CTL &
718c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_MASK) >>
728c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return retval;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/**
788c2ecf20Sopenharmony_ci * Sets the backlight level.
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * level backlight level, from 0 to cdv_intel_lvds_get_max_backlight().
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
858c2ecf20Sopenharmony_ci	u32 blc_pwm_ctl;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (gma_power_begin(dev, false)) {
888c2ecf20Sopenharmony_ci		blc_pwm_ctl =
898c2ecf20Sopenharmony_ci			REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
908c2ecf20Sopenharmony_ci		REG_WRITE(BLC_PWM_CTL,
918c2ecf20Sopenharmony_ci				(blc_pwm_ctl |
928c2ecf20Sopenharmony_ci				(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
938c2ecf20Sopenharmony_ci		gma_power_end(dev);
948c2ecf20Sopenharmony_ci	} else {
958c2ecf20Sopenharmony_ci		blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL &
968c2ecf20Sopenharmony_ci				~BACKLIGHT_DUTY_CYCLE_MASK;
978c2ecf20Sopenharmony_ci		dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl |
988c2ecf20Sopenharmony_ci					(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/**
1038c2ecf20Sopenharmony_ci * Sets the power state for the panel.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_set_power(struct drm_device *dev,
1068c2ecf20Sopenharmony_ci				     struct drm_encoder *encoder, bool on)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1098c2ecf20Sopenharmony_ci	u32 pp_status;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (!gma_power_begin(dev, true))
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (on) {
1158c2ecf20Sopenharmony_ci		REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
1168c2ecf20Sopenharmony_ci			  POWER_TARGET_ON);
1178c2ecf20Sopenharmony_ci		do {
1188c2ecf20Sopenharmony_ci			pp_status = REG_READ(PP_STATUS);
1198c2ecf20Sopenharmony_ci		} while ((pp_status & PP_ON) == 0);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		cdv_intel_lvds_set_backlight(dev,
1228c2ecf20Sopenharmony_ci				dev_priv->mode_dev.backlight_duty_cycle);
1238c2ecf20Sopenharmony_ci	} else {
1248c2ecf20Sopenharmony_ci		cdv_intel_lvds_set_backlight(dev, 0);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
1278c2ecf20Sopenharmony_ci			  ~POWER_TARGET_ON);
1288c2ecf20Sopenharmony_ci		do {
1298c2ecf20Sopenharmony_ci			pp_status = REG_READ(PP_STATUS);
1308c2ecf20Sopenharmony_ci		} while (pp_status & PP_ON);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	gma_power_end(dev);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
1388c2ecf20Sopenharmony_ci	if (mode == DRM_MODE_DPMS_ON)
1398c2ecf20Sopenharmony_ci		cdv_intel_lvds_set_power(dev, encoder, true);
1408c2ecf20Sopenharmony_ci	else
1418c2ecf20Sopenharmony_ci		cdv_intel_lvds_set_power(dev, encoder, false);
1428c2ecf20Sopenharmony_ci	/* XXX: We never power down the LVDS pairs. */
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_save(struct drm_connector *connector)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_restore(struct drm_connector *connector)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic enum drm_mode_status cdv_intel_lvds_mode_valid(struct drm_connector *connector,
1548c2ecf20Sopenharmony_ci			      struct drm_display_mode *mode)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
1578c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1588c2ecf20Sopenharmony_ci	struct drm_display_mode *fixed_mode =
1598c2ecf20Sopenharmony_ci					dev_priv->mode_dev.panel_fixed_mode;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* just in case */
1628c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
1638c2ecf20Sopenharmony_ci		return MODE_NO_DBLESCAN;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* just in case */
1668c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1678c2ecf20Sopenharmony_ci		return MODE_NO_INTERLACE;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (fixed_mode) {
1708c2ecf20Sopenharmony_ci		if (mode->hdisplay > fixed_mode->hdisplay)
1718c2ecf20Sopenharmony_ci			return MODE_PANEL;
1728c2ecf20Sopenharmony_ci		if (mode->vdisplay > fixed_mode->vdisplay)
1738c2ecf20Sopenharmony_ci			return MODE_PANEL;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	return MODE_OK;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder,
1798c2ecf20Sopenharmony_ci				  const struct drm_display_mode *mode,
1808c2ecf20Sopenharmony_ci				  struct drm_display_mode *adjusted_mode)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
1838c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1848c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
1858c2ecf20Sopenharmony_ci	struct drm_encoder *tmp_encoder;
1868c2ecf20Sopenharmony_ci	struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Should never happen!! */
1898c2ecf20Sopenharmony_ci	list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list,
1908c2ecf20Sopenharmony_ci			    head) {
1918c2ecf20Sopenharmony_ci		if (tmp_encoder != encoder
1928c2ecf20Sopenharmony_ci		    && tmp_encoder->crtc == encoder->crtc) {
1938c2ecf20Sopenharmony_ci			pr_err("Can't enable LVDS and another encoder on the same pipe\n");
1948c2ecf20Sopenharmony_ci			return false;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/*
1998c2ecf20Sopenharmony_ci	 * If we have timings from the BIOS for the panel, put them in
2008c2ecf20Sopenharmony_ci	 * to the adjusted mode.  The CRTC will be set up for this mode,
2018c2ecf20Sopenharmony_ci	 * with the panel scaling set up to source from the H/VDisplay
2028c2ecf20Sopenharmony_ci	 * of the original mode.
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	if (panel_fixed_mode != NULL) {
2058c2ecf20Sopenharmony_ci		adjusted_mode->hdisplay = panel_fixed_mode->hdisplay;
2068c2ecf20Sopenharmony_ci		adjusted_mode->hsync_start = panel_fixed_mode->hsync_start;
2078c2ecf20Sopenharmony_ci		adjusted_mode->hsync_end = panel_fixed_mode->hsync_end;
2088c2ecf20Sopenharmony_ci		adjusted_mode->htotal = panel_fixed_mode->htotal;
2098c2ecf20Sopenharmony_ci		adjusted_mode->vdisplay = panel_fixed_mode->vdisplay;
2108c2ecf20Sopenharmony_ci		adjusted_mode->vsync_start = panel_fixed_mode->vsync_start;
2118c2ecf20Sopenharmony_ci		adjusted_mode->vsync_end = panel_fixed_mode->vsync_end;
2128c2ecf20Sopenharmony_ci		adjusted_mode->vtotal = panel_fixed_mode->vtotal;
2138c2ecf20Sopenharmony_ci		adjusted_mode->clock = panel_fixed_mode->clock;
2148c2ecf20Sopenharmony_ci		drm_mode_set_crtcinfo(adjusted_mode,
2158c2ecf20Sopenharmony_ci				      CRTC_INTERLACE_HALVE_V);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/*
2198c2ecf20Sopenharmony_ci	 * XXX: It would be nice to support lower refresh rates on the
2208c2ecf20Sopenharmony_ci	 * panels to reduce power consumption, and perhaps match the
2218c2ecf20Sopenharmony_ci	 * user's requested refresh rate.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return true;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_prepare(struct drm_encoder *encoder)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2308c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2318c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (!gma_power_begin(dev, true))
2348c2ecf20Sopenharmony_ci		return;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
2378c2ecf20Sopenharmony_ci	mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
2388c2ecf20Sopenharmony_ci					  BACKLIGHT_DUTY_CYCLE_MASK);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	cdv_intel_lvds_set_power(dev, encoder, false);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	gma_power_end(dev);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_commit(struct drm_encoder *encoder)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2488c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2498c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (mode_dev->backlight_duty_cycle == 0)
2528c2ecf20Sopenharmony_ci		mode_dev->backlight_duty_cycle =
2538c2ecf20Sopenharmony_ci		    cdv_intel_lvds_get_max_backlight(dev);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	cdv_intel_lvds_set_power(dev, encoder, true);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_mode_set(struct drm_encoder *encoder,
2598c2ecf20Sopenharmony_ci				struct drm_display_mode *mode,
2608c2ecf20Sopenharmony_ci				struct drm_display_mode *adjusted_mode)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2638c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2648c2ecf20Sopenharmony_ci	struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc);
2658c2ecf20Sopenharmony_ci	u32 pfit_control;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/*
2688c2ecf20Sopenharmony_ci	 * The LVDS pin pair will already have been turned on in the
2698c2ecf20Sopenharmony_ci	 * cdv_intel_crtc_mode_set since it has a large impact on the DPLL
2708c2ecf20Sopenharmony_ci	 * settings.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * Enable automatic panel scaling so that non-native modes fill the
2758c2ecf20Sopenharmony_ci	 * screen.  Should be enabled before the pipe is enabled, according to
2768c2ecf20Sopenharmony_ci	 * register description and PRM.
2778c2ecf20Sopenharmony_ci	 */
2788c2ecf20Sopenharmony_ci	if (mode->hdisplay != adjusted_mode->hdisplay ||
2798c2ecf20Sopenharmony_ci	    mode->vdisplay != adjusted_mode->vdisplay)
2808c2ecf20Sopenharmony_ci		pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
2818c2ecf20Sopenharmony_ci				HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
2828c2ecf20Sopenharmony_ci				HORIZ_INTERP_BILINEAR);
2838c2ecf20Sopenharmony_ci	else
2848c2ecf20Sopenharmony_ci		pfit_control = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	pfit_control |= gma_crtc->pipe << PFIT_PIPE_SHIFT;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (dev_priv->lvds_dither)
2898c2ecf20Sopenharmony_ci		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	REG_WRITE(PFIT_CONTROL, pfit_control);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/**
2958c2ecf20Sopenharmony_ci * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
2968c2ecf20Sopenharmony_ci */
2978c2ecf20Sopenharmony_cistatic int cdv_intel_lvds_get_modes(struct drm_connector *connector)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct drm_device *dev = connector->dev;
3008c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
3018c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
3028c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
3038c2ecf20Sopenharmony_ci	int ret;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = psb_intel_ddc_get_modes(connector, &gma_encoder->i2c_bus->adapter);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (ret)
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (mode_dev->panel_fixed_mode != NULL) {
3118c2ecf20Sopenharmony_ci		struct drm_display_mode *mode =
3128c2ecf20Sopenharmony_ci		    drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
3138c2ecf20Sopenharmony_ci		if (!mode)
3148c2ecf20Sopenharmony_ci			return 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		drm_mode_probed_add(connector, mode);
3178c2ecf20Sopenharmony_ci		return 1;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/**
3248c2ecf20Sopenharmony_ci * cdv_intel_lvds_destroy - unregister and free LVDS structures
3258c2ecf20Sopenharmony_ci * @connector: connector to free
3268c2ecf20Sopenharmony_ci *
3278c2ecf20Sopenharmony_ci * Unregister the DDC bus for this connector then free the driver private
3288c2ecf20Sopenharmony_ci * structure.
3298c2ecf20Sopenharmony_ci */
3308c2ecf20Sopenharmony_cistatic void cdv_intel_lvds_destroy(struct drm_connector *connector)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	psb_intel_i2c_destroy(gma_encoder->i2c_bus);
3358c2ecf20Sopenharmony_ci	drm_connector_unregister(connector);
3368c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
3378c2ecf20Sopenharmony_ci	kfree(connector);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int cdv_intel_lvds_set_property(struct drm_connector *connector,
3418c2ecf20Sopenharmony_ci				       struct drm_property *property,
3428c2ecf20Sopenharmony_ci				       uint64_t value)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = connector->encoder;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (!strcmp(property->name, "scaling mode") && encoder) {
3478c2ecf20Sopenharmony_ci		struct gma_crtc *crtc = to_gma_crtc(encoder->crtc);
3488c2ecf20Sopenharmony_ci		uint64_t curValue;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		if (!crtc)
3518c2ecf20Sopenharmony_ci			return -1;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		switch (value) {
3548c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_FULLSCREEN:
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_NO_SCALE:
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci		case DRM_MODE_SCALE_ASPECT:
3598c2ecf20Sopenharmony_ci			break;
3608c2ecf20Sopenharmony_ci		default:
3618c2ecf20Sopenharmony_ci			return -1;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		if (drm_object_property_get_value(&connector->base,
3658c2ecf20Sopenharmony_ci						     property,
3668c2ecf20Sopenharmony_ci						     &curValue))
3678c2ecf20Sopenharmony_ci			return -1;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		if (curValue == value)
3708c2ecf20Sopenharmony_ci			return 0;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		if (drm_object_property_set_value(&connector->base,
3738c2ecf20Sopenharmony_ci							property,
3748c2ecf20Sopenharmony_ci							value))
3758c2ecf20Sopenharmony_ci			return -1;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		if (crtc->saved_mode.hdisplay != 0 &&
3788c2ecf20Sopenharmony_ci		    crtc->saved_mode.vdisplay != 0) {
3798c2ecf20Sopenharmony_ci			if (!drm_crtc_helper_set_mode(encoder->crtc,
3808c2ecf20Sopenharmony_ci						      &crtc->saved_mode,
3818c2ecf20Sopenharmony_ci						      encoder->crtc->x,
3828c2ecf20Sopenharmony_ci						      encoder->crtc->y,
3838c2ecf20Sopenharmony_ci						      encoder->crtc->primary->fb))
3848c2ecf20Sopenharmony_ci				return -1;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci	} else if (!strcmp(property->name, "backlight") && encoder) {
3878c2ecf20Sopenharmony_ci		if (drm_object_property_set_value(&connector->base,
3888c2ecf20Sopenharmony_ci							property,
3898c2ecf20Sopenharmony_ci							value))
3908c2ecf20Sopenharmony_ci			return -1;
3918c2ecf20Sopenharmony_ci		else
3928c2ecf20Sopenharmony_ci                        gma_backlight_set(encoder->dev, value);
3938c2ecf20Sopenharmony_ci	} else if (!strcmp(property->name, "DPMS") && encoder) {
3948c2ecf20Sopenharmony_ci		const struct drm_encoder_helper_funcs *helpers =
3958c2ecf20Sopenharmony_ci					encoder->helper_private;
3968c2ecf20Sopenharmony_ci		helpers->dpms(encoder, value);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs
4028c2ecf20Sopenharmony_ci					cdv_intel_lvds_helper_funcs = {
4038c2ecf20Sopenharmony_ci	.dpms = cdv_intel_lvds_encoder_dpms,
4048c2ecf20Sopenharmony_ci	.mode_fixup = cdv_intel_lvds_mode_fixup,
4058c2ecf20Sopenharmony_ci	.prepare = cdv_intel_lvds_prepare,
4068c2ecf20Sopenharmony_ci	.mode_set = cdv_intel_lvds_mode_set,
4078c2ecf20Sopenharmony_ci	.commit = cdv_intel_lvds_commit,
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs
4118c2ecf20Sopenharmony_ci				cdv_intel_lvds_connector_helper_funcs = {
4128c2ecf20Sopenharmony_ci	.get_modes = cdv_intel_lvds_get_modes,
4138c2ecf20Sopenharmony_ci	.mode_valid = cdv_intel_lvds_mode_valid,
4148c2ecf20Sopenharmony_ci	.best_encoder = gma_best_encoder,
4158c2ecf20Sopenharmony_ci};
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = {
4188c2ecf20Sopenharmony_ci	.dpms = drm_helper_connector_dpms,
4198c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
4208c2ecf20Sopenharmony_ci	.set_property = cdv_intel_lvds_set_property,
4218c2ecf20Sopenharmony_ci	.destroy = cdv_intel_lvds_destroy,
4228c2ecf20Sopenharmony_ci};
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/*
4258c2ecf20Sopenharmony_ci * Enumerate the child dev array parsed from VBT to check whether
4268c2ecf20Sopenharmony_ci * the LVDS is present.
4278c2ecf20Sopenharmony_ci * If it is present, return 1.
4288c2ecf20Sopenharmony_ci * If it is not present, return false.
4298c2ecf20Sopenharmony_ci * If no child dev is parsed from VBT, it assumes that the LVDS is present.
4308c2ecf20Sopenharmony_ci */
4318c2ecf20Sopenharmony_cistatic bool lvds_is_present_in_vbt(struct drm_device *dev,
4328c2ecf20Sopenharmony_ci				   u8 *i2c_pin)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
4358c2ecf20Sopenharmony_ci	int i;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (!dev_priv->child_dev_num)
4388c2ecf20Sopenharmony_ci		return true;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	for (i = 0; i < dev_priv->child_dev_num; i++) {
4418c2ecf20Sopenharmony_ci		struct child_device_config *child = dev_priv->child_dev + i;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		/* If the device type is not LFP, continue.
4448c2ecf20Sopenharmony_ci		 * We have to check both the new identifiers as well as the
4458c2ecf20Sopenharmony_ci		 * old for compatibility with some BIOSes.
4468c2ecf20Sopenharmony_ci		 */
4478c2ecf20Sopenharmony_ci		if (child->device_type != DEVICE_TYPE_INT_LFP &&
4488c2ecf20Sopenharmony_ci		    child->device_type != DEVICE_TYPE_LFP)
4498c2ecf20Sopenharmony_ci			continue;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		if (child->i2c_pin)
4528c2ecf20Sopenharmony_ci		    *i2c_pin = child->i2c_pin;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		/* However, we cannot trust the BIOS writers to populate
4558c2ecf20Sopenharmony_ci		 * the VBT correctly.  Since LVDS requires additional
4568c2ecf20Sopenharmony_ci		 * information from AIM blocks, a non-zero addin offset is
4578c2ecf20Sopenharmony_ci		 * a good indicator that the LVDS is actually present.
4588c2ecf20Sopenharmony_ci		 */
4598c2ecf20Sopenharmony_ci		if (child->addin_offset)
4608c2ecf20Sopenharmony_ci			return true;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		/* But even then some BIOS writers perform some black magic
4638c2ecf20Sopenharmony_ci		 * and instantiate the device without reference to any
4648c2ecf20Sopenharmony_ci		 * additional data.  Trust that if the VBT was written into
4658c2ecf20Sopenharmony_ci		 * the OpRegion then they have validated the LVDS's existence.
4668c2ecf20Sopenharmony_ci		 */
4678c2ecf20Sopenharmony_ci		if (dev_priv->opregion.vbt)
4688c2ecf20Sopenharmony_ci			return true;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return false;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/**
4758c2ecf20Sopenharmony_ci * cdv_intel_lvds_init - setup LVDS connectors on this device
4768c2ecf20Sopenharmony_ci * @dev: drm device
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * Create the connector, register the LVDS DDC bus, and try to figure out what
4798c2ecf20Sopenharmony_ci * modes we can display on the LVDS panel (if present).
4808c2ecf20Sopenharmony_ci */
4818c2ecf20Sopenharmony_civoid cdv_intel_lvds_init(struct drm_device *dev,
4828c2ecf20Sopenharmony_ci		     struct psb_intel_mode_device *mode_dev)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder;
4858c2ecf20Sopenharmony_ci	struct gma_connector *gma_connector;
4868c2ecf20Sopenharmony_ci	struct cdv_intel_lvds_priv *lvds_priv;
4878c2ecf20Sopenharmony_ci	struct drm_connector *connector;
4888c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
4898c2ecf20Sopenharmony_ci	struct drm_display_mode *scan;
4908c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
4918c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
4928c2ecf20Sopenharmony_ci	u32 lvds;
4938c2ecf20Sopenharmony_ci	int pipe;
4948c2ecf20Sopenharmony_ci	u8 pin;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!dev_priv->lvds_enabled_in_vbt)
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	pin = GMBUS_PORT_PANEL;
5008c2ecf20Sopenharmony_ci	if (!lvds_is_present_in_vbt(dev, &pin)) {
5018c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("LVDS is not present in VBT\n");
5028c2ecf20Sopenharmony_ci		return;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	gma_encoder = kzalloc(sizeof(struct gma_encoder),
5068c2ecf20Sopenharmony_ci				    GFP_KERNEL);
5078c2ecf20Sopenharmony_ci	if (!gma_encoder)
5088c2ecf20Sopenharmony_ci		return;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	gma_connector = kzalloc(sizeof(struct gma_connector),
5118c2ecf20Sopenharmony_ci				      GFP_KERNEL);
5128c2ecf20Sopenharmony_ci	if (!gma_connector)
5138c2ecf20Sopenharmony_ci		goto failed_connector;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL);
5168c2ecf20Sopenharmony_ci	if (!lvds_priv)
5178c2ecf20Sopenharmony_ci		goto failed_lvds_priv;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	gma_encoder->dev_priv = lvds_priv;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	connector = &gma_connector->base;
5228c2ecf20Sopenharmony_ci	gma_connector->save = cdv_intel_lvds_save;
5238c2ecf20Sopenharmony_ci	gma_connector->restore = cdv_intel_lvds_restore;
5248c2ecf20Sopenharmony_ci	encoder = &gma_encoder->base;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	drm_connector_init(dev, connector,
5288c2ecf20Sopenharmony_ci			   &cdv_intel_lvds_connector_funcs,
5298c2ecf20Sopenharmony_ci			   DRM_MODE_CONNECTOR_LVDS);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	gma_connector_attach_encoder(gma_connector, gma_encoder);
5348c2ecf20Sopenharmony_ci	gma_encoder->type = INTEL_OUTPUT_LVDS;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs);
5378c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector,
5388c2ecf20Sopenharmony_ci				 &cdv_intel_lvds_connector_helper_funcs);
5398c2ecf20Sopenharmony_ci	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
5408c2ecf20Sopenharmony_ci	connector->interlace_allowed = false;
5418c2ecf20Sopenharmony_ci	connector->doublescan_allowed = false;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/*Attach connector properties*/
5448c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
5458c2ecf20Sopenharmony_ci				      dev->mode_config.scaling_mode_property,
5468c2ecf20Sopenharmony_ci				      DRM_MODE_SCALE_FULLSCREEN);
5478c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
5488c2ecf20Sopenharmony_ci				      dev_priv->backlight_property,
5498c2ecf20Sopenharmony_ci				      BRIGHTNESS_MAX_LEVEL);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/**
5528c2ecf20Sopenharmony_ci	 * Set up I2C bus
5538c2ecf20Sopenharmony_ci	 * FIXME: distroy i2c_bus when exit
5548c2ecf20Sopenharmony_ci	 */
5558c2ecf20Sopenharmony_ci	gma_encoder->i2c_bus = psb_intel_i2c_create(dev,
5568c2ecf20Sopenharmony_ci							 GPIOB,
5578c2ecf20Sopenharmony_ci							 "LVDSBLC_B");
5588c2ecf20Sopenharmony_ci	if (!gma_encoder->i2c_bus) {
5598c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR,
5608c2ecf20Sopenharmony_ci			&dev->pdev->dev, "I2C bus registration failed.\n");
5618c2ecf20Sopenharmony_ci		goto failed_blc_i2c;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	gma_encoder->i2c_bus->slave_addr = 0x2C;
5648c2ecf20Sopenharmony_ci	dev_priv->lvds_i2c_bus = gma_encoder->i2c_bus;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/*
5678c2ecf20Sopenharmony_ci	 * LVDS discovery:
5688c2ecf20Sopenharmony_ci	 * 1) check for EDID on DDC
5698c2ecf20Sopenharmony_ci	 * 2) check for VBT data
5708c2ecf20Sopenharmony_ci	 * 3) check to see if LVDS is already on
5718c2ecf20Sopenharmony_ci	 *    if none of the above, no panel
5728c2ecf20Sopenharmony_ci	 * 4) make sure lid is open
5738c2ecf20Sopenharmony_ci	 *    if closed, act like it's not there for now
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* Set up the DDC bus. */
5778c2ecf20Sopenharmony_ci	gma_encoder->ddc_bus = psb_intel_i2c_create(dev,
5788c2ecf20Sopenharmony_ci							 GPIOC,
5798c2ecf20Sopenharmony_ci							 "LVDSDDC_C");
5808c2ecf20Sopenharmony_ci	if (!gma_encoder->ddc_bus) {
5818c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &dev->pdev->dev,
5828c2ecf20Sopenharmony_ci			   "DDC bus registration " "failed.\n");
5838c2ecf20Sopenharmony_ci		goto failed_ddc;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/*
5878c2ecf20Sopenharmony_ci	 * Attempt to get the fixed panel mode from DDC.  Assume that the
5888c2ecf20Sopenharmony_ci	 * preferred mode is the right one.
5898c2ecf20Sopenharmony_ci	 */
5908c2ecf20Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
5918c2ecf20Sopenharmony_ci	psb_intel_ddc_get_modes(connector,
5928c2ecf20Sopenharmony_ci				&gma_encoder->ddc_bus->adapter);
5938c2ecf20Sopenharmony_ci	list_for_each_entry(scan, &connector->probed_modes, head) {
5948c2ecf20Sopenharmony_ci		if (scan->type & DRM_MODE_TYPE_PREFERRED) {
5958c2ecf20Sopenharmony_ci			mode_dev->panel_fixed_mode =
5968c2ecf20Sopenharmony_ci			    drm_mode_duplicate(dev, scan);
5978c2ecf20Sopenharmony_ci			goto out;	/* FIXME: check for quirks */
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Failed to get EDID, what about VBT? do we need this?*/
6028c2ecf20Sopenharmony_ci	if (dev_priv->lfp_lvds_vbt_mode) {
6038c2ecf20Sopenharmony_ci		mode_dev->panel_fixed_mode =
6048c2ecf20Sopenharmony_ci			drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
6058c2ecf20Sopenharmony_ci		if (mode_dev->panel_fixed_mode) {
6068c2ecf20Sopenharmony_ci			mode_dev->panel_fixed_mode->type |=
6078c2ecf20Sopenharmony_ci				DRM_MODE_TYPE_PREFERRED;
6088c2ecf20Sopenharmony_ci			goto out;	/* FIXME: check for quirks */
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci	/*
6128c2ecf20Sopenharmony_ci	 * If we didn't get EDID, try checking if the panel is already turned
6138c2ecf20Sopenharmony_ci	 * on.	If so, assume that whatever is currently programmed is the
6148c2ecf20Sopenharmony_ci	 * correct mode.
6158c2ecf20Sopenharmony_ci	 */
6168c2ecf20Sopenharmony_ci	lvds = REG_READ(LVDS);
6178c2ecf20Sopenharmony_ci	pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
6188c2ecf20Sopenharmony_ci	crtc = psb_intel_get_crtc_from_pipe(dev, pipe);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (crtc && (lvds & LVDS_PORT_EN)) {
6218c2ecf20Sopenharmony_ci		mode_dev->panel_fixed_mode =
6228c2ecf20Sopenharmony_ci		    cdv_intel_crtc_mode_get(dev, crtc);
6238c2ecf20Sopenharmony_ci		if (mode_dev->panel_fixed_mode) {
6248c2ecf20Sopenharmony_ci			mode_dev->panel_fixed_mode->type |=
6258c2ecf20Sopenharmony_ci			    DRM_MODE_TYPE_PREFERRED;
6268c2ecf20Sopenharmony_ci			goto out;	/* FIXME: check for quirks */
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* If we still don't have a mode after all that, give up. */
6318c2ecf20Sopenharmony_ci	if (!mode_dev->panel_fixed_mode) {
6328c2ecf20Sopenharmony_ci		DRM_DEBUG
6338c2ecf20Sopenharmony_ci			("Found no modes on the lvds, ignoring the LVDS\n");
6348c2ecf20Sopenharmony_ci		goto failed_find;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* setup PWM */
6388c2ecf20Sopenharmony_ci	{
6398c2ecf20Sopenharmony_ci		u32 pwm;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci		pwm = REG_READ(BLC_PWM_CTL2);
6428c2ecf20Sopenharmony_ci		if (pipe == 1)
6438c2ecf20Sopenharmony_ci			pwm |= PWM_PIPE_B;
6448c2ecf20Sopenharmony_ci		else
6458c2ecf20Sopenharmony_ci			pwm &= ~PWM_PIPE_B;
6468c2ecf20Sopenharmony_ci		pwm |= PWM_ENABLE;
6478c2ecf20Sopenharmony_ci		REG_WRITE(BLC_PWM_CTL2, pwm);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ciout:
6518c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
6528c2ecf20Sopenharmony_ci	drm_connector_register(connector);
6538c2ecf20Sopenharmony_ci	return;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cifailed_find:
6568c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
6578c2ecf20Sopenharmony_ci	pr_err("Failed find\n");
6588c2ecf20Sopenharmony_ci	psb_intel_i2c_destroy(gma_encoder->ddc_bus);
6598c2ecf20Sopenharmony_cifailed_ddc:
6608c2ecf20Sopenharmony_ci	pr_err("Failed DDC\n");
6618c2ecf20Sopenharmony_ci	psb_intel_i2c_destroy(gma_encoder->i2c_bus);
6628c2ecf20Sopenharmony_cifailed_blc_i2c:
6638c2ecf20Sopenharmony_ci	pr_err("Failed BLC\n");
6648c2ecf20Sopenharmony_ci	drm_encoder_cleanup(encoder);
6658c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
6668c2ecf20Sopenharmony_ci	kfree(lvds_priv);
6678c2ecf20Sopenharmony_cifailed_lvds_priv:
6688c2ecf20Sopenharmony_ci	kfree(gma_connector);
6698c2ecf20Sopenharmony_cifailed_connector:
6708c2ecf20Sopenharmony_ci	kfree(gma_encoder);
6718c2ecf20Sopenharmony_ci}
672