18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2006-2009 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/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/intel-mid.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
178c2ecf20Sopenharmony_ci
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/* The max/min PWM frequency in BPCR[31:17] - */
258c2ecf20Sopenharmony_ci/* The smallest number is 1 (not 0) that can fit in the
268c2ecf20Sopenharmony_ci * 15-bit field of the and then*/
278c2ecf20Sopenharmony_ci/* shifts to the left by one bit to get the actual 16-bit
288c2ecf20Sopenharmony_ci * value that the 15-bits correspond to.*/
298c2ecf20Sopenharmony_ci#define MRST_BLC_MAX_PWM_REG_FREQ	    0xFFFF
308c2ecf20Sopenharmony_ci#define BRIGHTNESS_MAX_LEVEL 100
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/**
338c2ecf20Sopenharmony_ci * Sets the power state for the panel.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic void oaktrail_lvds_set_power(struct drm_device *dev,
368c2ecf20Sopenharmony_ci				struct gma_encoder *gma_encoder,
378c2ecf20Sopenharmony_ci				bool on)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	u32 pp_status;
408c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!gma_power_begin(dev, true))
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (on) {
468c2ecf20Sopenharmony_ci		REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
478c2ecf20Sopenharmony_ci			  POWER_TARGET_ON);
488c2ecf20Sopenharmony_ci		do {
498c2ecf20Sopenharmony_ci			pp_status = REG_READ(PP_STATUS);
508c2ecf20Sopenharmony_ci		} while ((pp_status & (PP_ON | PP_READY)) == PP_READY);
518c2ecf20Sopenharmony_ci		dev_priv->is_lvds_on = true;
528c2ecf20Sopenharmony_ci		if (dev_priv->ops->lvds_bl_power)
538c2ecf20Sopenharmony_ci			dev_priv->ops->lvds_bl_power(dev, true);
548c2ecf20Sopenharmony_ci	} else {
558c2ecf20Sopenharmony_ci		if (dev_priv->ops->lvds_bl_power)
568c2ecf20Sopenharmony_ci			dev_priv->ops->lvds_bl_power(dev, false);
578c2ecf20Sopenharmony_ci		REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
588c2ecf20Sopenharmony_ci			  ~POWER_TARGET_ON);
598c2ecf20Sopenharmony_ci		do {
608c2ecf20Sopenharmony_ci			pp_status = REG_READ(PP_STATUS);
618c2ecf20Sopenharmony_ci		} while (pp_status & PP_ON);
628c2ecf20Sopenharmony_ci		dev_priv->is_lvds_on = false;
638c2ecf20Sopenharmony_ci		pm_request_idle(&dev->pdev->dev);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	gma_power_end(dev);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
718c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder = to_gma_encoder(encoder);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (mode == DRM_MODE_DPMS_ON)
748c2ecf20Sopenharmony_ci		oaktrail_lvds_set_power(dev, gma_encoder, true);
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		oaktrail_lvds_set_power(dev, gma_encoder, false);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* XXX: We never power down the LVDS pairs. */
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic void oaktrail_lvds_mode_set(struct drm_encoder *encoder,
828c2ecf20Sopenharmony_ci			       struct drm_display_mode *mode,
838c2ecf20Sopenharmony_ci			       struct drm_display_mode *adjusted_mode)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
868c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
878c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
888c2ecf20Sopenharmony_ci	struct drm_mode_config *mode_config = &dev->mode_config;
898c2ecf20Sopenharmony_ci	struct drm_connector *connector = NULL;
908c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = encoder->crtc;
918c2ecf20Sopenharmony_ci	u32 lvds_port;
928c2ecf20Sopenharmony_ci	uint64_t v = DRM_MODE_SCALE_FULLSCREEN;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (!gma_power_begin(dev, true))
958c2ecf20Sopenharmony_ci		return;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/*
988c2ecf20Sopenharmony_ci	 * The LVDS pin pair will already have been turned on in the
998c2ecf20Sopenharmony_ci	 * psb_intel_crtc_mode_set since it has a large impact on the DPLL
1008c2ecf20Sopenharmony_ci	 * settings.
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci	lvds_port = (REG_READ(LVDS) &
1038c2ecf20Sopenharmony_ci		    (~LVDS_PIPEB_SELECT)) |
1048c2ecf20Sopenharmony_ci		    LVDS_PORT_EN |
1058c2ecf20Sopenharmony_ci		    LVDS_BORDER_EN;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* If the firmware says dither on Moorestown, or the BIOS does
1088c2ecf20Sopenharmony_ci	   on Oaktrail then enable dithering */
1098c2ecf20Sopenharmony_ci	if (mode_dev->panel_wants_dither || dev_priv->lvds_dither)
1108c2ecf20Sopenharmony_ci		lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	REG_WRITE(LVDS, lvds_port);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Find the connector we're trying to set up */
1158c2ecf20Sopenharmony_ci	list_for_each_entry(connector, &mode_config->connector_list, head) {
1168c2ecf20Sopenharmony_ci		if (!connector->encoder || connector->encoder->crtc != crtc)
1178c2ecf20Sopenharmony_ci			continue;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (list_entry_is_head(connector, &mode_config->connector_list, head)) {
1218c2ecf20Sopenharmony_ci		DRM_ERROR("Couldn't find connector when setting mode");
1228c2ecf20Sopenharmony_ci		gma_power_end(dev);
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	drm_object_property_get_value(
1278c2ecf20Sopenharmony_ci		&connector->base,
1288c2ecf20Sopenharmony_ci		dev->mode_config.scaling_mode_property,
1298c2ecf20Sopenharmony_ci		&v);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (v == DRM_MODE_SCALE_NO_SCALE)
1328c2ecf20Sopenharmony_ci		REG_WRITE(PFIT_CONTROL, 0);
1338c2ecf20Sopenharmony_ci	else if (v == DRM_MODE_SCALE_ASPECT) {
1348c2ecf20Sopenharmony_ci		if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) ||
1358c2ecf20Sopenharmony_ci		    (mode->hdisplay != adjusted_mode->crtc_hdisplay)) {
1368c2ecf20Sopenharmony_ci			if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) ==
1378c2ecf20Sopenharmony_ci			    (mode->hdisplay * adjusted_mode->crtc_vdisplay))
1388c2ecf20Sopenharmony_ci				REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
1398c2ecf20Sopenharmony_ci			else if ((adjusted_mode->crtc_hdisplay *
1408c2ecf20Sopenharmony_ci				mode->vdisplay) > (mode->hdisplay *
1418c2ecf20Sopenharmony_ci				adjusted_mode->crtc_vdisplay))
1428c2ecf20Sopenharmony_ci				REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
1438c2ecf20Sopenharmony_ci					  PFIT_SCALING_MODE_PILLARBOX);
1448c2ecf20Sopenharmony_ci			else
1458c2ecf20Sopenharmony_ci				REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
1468c2ecf20Sopenharmony_ci					  PFIT_SCALING_MODE_LETTERBOX);
1478c2ecf20Sopenharmony_ci		} else
1488c2ecf20Sopenharmony_ci			REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
1498c2ecf20Sopenharmony_ci	} else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/
1508c2ecf20Sopenharmony_ci		REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	gma_power_end(dev);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void oaktrail_lvds_prepare(struct drm_encoder *encoder)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
1588c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1598c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder = to_gma_encoder(encoder);
1608c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (!gma_power_begin(dev, true))
1638c2ecf20Sopenharmony_ci		return;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
1668c2ecf20Sopenharmony_ci	mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
1678c2ecf20Sopenharmony_ci					  BACKLIGHT_DUTY_CYCLE_MASK);
1688c2ecf20Sopenharmony_ci	oaktrail_lvds_set_power(dev, gma_encoder, false);
1698c2ecf20Sopenharmony_ci	gma_power_end(dev);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1758c2ecf20Sopenharmony_ci	u32 ret;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (gma_power_begin(dev, false)) {
1788c2ecf20Sopenharmony_ci		ret = ((REG_READ(BLC_PWM_CTL) &
1798c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_MASK) >>
1808c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		gma_power_end(dev);
1838c2ecf20Sopenharmony_ci	} else
1848c2ecf20Sopenharmony_ci		ret = ((dev_priv->regs.saveBLC_PWM_CTL &
1858c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_MASK) >>
1868c2ecf20Sopenharmony_ci			  BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return ret;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void oaktrail_lvds_commit(struct drm_encoder *encoder)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
1948c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
1958c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder = to_gma_encoder(encoder);
1968c2ecf20Sopenharmony_ci	struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (mode_dev->backlight_duty_cycle == 0)
1998c2ecf20Sopenharmony_ci		mode_dev->backlight_duty_cycle =
2008c2ecf20Sopenharmony_ci					oaktrail_lvds_get_max_backlight(dev);
2018c2ecf20Sopenharmony_ci	oaktrail_lvds_set_power(dev, gma_encoder, true);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
2058c2ecf20Sopenharmony_ci	.dpms = oaktrail_lvds_dpms,
2068c2ecf20Sopenharmony_ci	.mode_fixup = psb_intel_lvds_mode_fixup,
2078c2ecf20Sopenharmony_ci	.prepare = oaktrail_lvds_prepare,
2088c2ecf20Sopenharmony_ci	.mode_set = oaktrail_lvds_mode_set,
2098c2ecf20Sopenharmony_ci	.commit = oaktrail_lvds_commit,
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/* Returns the panel fixed mode from configuration. */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void oaktrail_lvds_get_configuration_mode(struct drm_device *dev,
2158c2ecf20Sopenharmony_ci					struct psb_intel_mode_device *mode_dev)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = NULL;
2188c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2198c2ecf20Sopenharmony_ci	struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mode_dev->panel_fixed_mode = NULL;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Use the firmware provided data on Moorestown */
2248c2ecf20Sopenharmony_ci	if (dev_priv->has_gct) {
2258c2ecf20Sopenharmony_ci		mode = kzalloc(sizeof(*mode), GFP_KERNEL);
2268c2ecf20Sopenharmony_ci		if (!mode)
2278c2ecf20Sopenharmony_ci			return;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
2308c2ecf20Sopenharmony_ci		mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
2318c2ecf20Sopenharmony_ci		mode->hsync_start = mode->hdisplay + \
2328c2ecf20Sopenharmony_ci				((ti->hsync_offset_hi << 8) | \
2338c2ecf20Sopenharmony_ci				ti->hsync_offset_lo);
2348c2ecf20Sopenharmony_ci		mode->hsync_end = mode->hsync_start + \
2358c2ecf20Sopenharmony_ci				((ti->hsync_pulse_width_hi << 8) | \
2368c2ecf20Sopenharmony_ci				ti->hsync_pulse_width_lo);
2378c2ecf20Sopenharmony_ci		mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \
2388c2ecf20Sopenharmony_ci							ti->hblank_lo);
2398c2ecf20Sopenharmony_ci		mode->vsync_start = \
2408c2ecf20Sopenharmony_ci			mode->vdisplay + ((ti->vsync_offset_hi << 4) | \
2418c2ecf20Sopenharmony_ci						ti->vsync_offset_lo);
2428c2ecf20Sopenharmony_ci		mode->vsync_end = \
2438c2ecf20Sopenharmony_ci			mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \
2448c2ecf20Sopenharmony_ci						ti->vsync_pulse_width_lo);
2458c2ecf20Sopenharmony_ci		mode->vtotal = mode->vdisplay + \
2468c2ecf20Sopenharmony_ci				((ti->vblank_hi << 8) | ti->vblank_lo);
2478c2ecf20Sopenharmony_ci		mode->clock = ti->pixel_clock * 10;
2488c2ecf20Sopenharmony_ci#if 0
2498c2ecf20Sopenharmony_ci		pr_info("hdisplay is %d\n", mode->hdisplay);
2508c2ecf20Sopenharmony_ci		pr_info("vdisplay is %d\n", mode->vdisplay);
2518c2ecf20Sopenharmony_ci		pr_info("HSS is %d\n", mode->hsync_start);
2528c2ecf20Sopenharmony_ci		pr_info("HSE is %d\n", mode->hsync_end);
2538c2ecf20Sopenharmony_ci		pr_info("htotal is %d\n", mode->htotal);
2548c2ecf20Sopenharmony_ci		pr_info("VSS is %d\n", mode->vsync_start);
2558c2ecf20Sopenharmony_ci		pr_info("VSE is %d\n", mode->vsync_end);
2568c2ecf20Sopenharmony_ci		pr_info("vtotal is %d\n", mode->vtotal);
2578c2ecf20Sopenharmony_ci		pr_info("clock is %d\n", mode->clock);
2588c2ecf20Sopenharmony_ci#endif
2598c2ecf20Sopenharmony_ci		mode_dev->panel_fixed_mode = mode;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* Use the BIOS VBT mode if available */
2638c2ecf20Sopenharmony_ci	if (mode_dev->panel_fixed_mode == NULL && mode_dev->vbt_mode)
2648c2ecf20Sopenharmony_ci		mode_dev->panel_fixed_mode = drm_mode_duplicate(dev,
2658c2ecf20Sopenharmony_ci						mode_dev->vbt_mode);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* Then try the LVDS VBT mode */
2688c2ecf20Sopenharmony_ci	if (mode_dev->panel_fixed_mode == NULL)
2698c2ecf20Sopenharmony_ci		if (dev_priv->lfp_lvds_vbt_mode)
2708c2ecf20Sopenharmony_ci			mode_dev->panel_fixed_mode =
2718c2ecf20Sopenharmony_ci				drm_mode_duplicate(dev,
2728c2ecf20Sopenharmony_ci					dev_priv->lfp_lvds_vbt_mode);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* If we still got no mode then bail */
2758c2ecf20Sopenharmony_ci	if (mode_dev->panel_fixed_mode == NULL)
2768c2ecf20Sopenharmony_ci		return;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	drm_mode_set_name(mode_dev->panel_fixed_mode);
2798c2ecf20Sopenharmony_ci	drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/**
2838c2ecf20Sopenharmony_ci * oaktrail_lvds_init - setup LVDS connectors on this device
2848c2ecf20Sopenharmony_ci * @dev: drm device
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci * Create the connector, register the LVDS DDC bus, and try to figure out what
2878c2ecf20Sopenharmony_ci * modes we can display on the LVDS panel (if present).
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_civoid oaktrail_lvds_init(struct drm_device *dev,
2908c2ecf20Sopenharmony_ci		    struct psb_intel_mode_device *mode_dev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct gma_encoder *gma_encoder;
2938c2ecf20Sopenharmony_ci	struct gma_connector *gma_connector;
2948c2ecf20Sopenharmony_ci	struct drm_connector *connector;
2958c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
2968c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
2978c2ecf20Sopenharmony_ci	struct edid *edid;
2988c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c_adap;
2998c2ecf20Sopenharmony_ci	struct drm_display_mode *scan;	/* *modes, *bios_mode; */
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL);
3028c2ecf20Sopenharmony_ci	if (!gma_encoder)
3038c2ecf20Sopenharmony_ci		return;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL);
3068c2ecf20Sopenharmony_ci	if (!gma_connector)
3078c2ecf20Sopenharmony_ci		goto failed_connector;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	connector = &gma_connector->base;
3108c2ecf20Sopenharmony_ci	encoder = &gma_encoder->base;
3118c2ecf20Sopenharmony_ci	dev_priv->is_lvds_on = true;
3128c2ecf20Sopenharmony_ci	drm_connector_init(dev, connector,
3138c2ecf20Sopenharmony_ci			   &psb_intel_lvds_connector_funcs,
3148c2ecf20Sopenharmony_ci			   DRM_MODE_CONNECTOR_LVDS);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	gma_connector_attach_encoder(gma_connector, gma_encoder);
3198c2ecf20Sopenharmony_ci	gma_encoder->type = INTEL_OUTPUT_LVDS;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs);
3228c2ecf20Sopenharmony_ci	drm_connector_helper_add(connector,
3238c2ecf20Sopenharmony_ci				 &psb_intel_lvds_connector_helper_funcs);
3248c2ecf20Sopenharmony_ci	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
3258c2ecf20Sopenharmony_ci	connector->interlace_allowed = false;
3268c2ecf20Sopenharmony_ci	connector->doublescan_allowed = false;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
3298c2ecf20Sopenharmony_ci					dev->mode_config.scaling_mode_property,
3308c2ecf20Sopenharmony_ci					DRM_MODE_SCALE_FULLSCREEN);
3318c2ecf20Sopenharmony_ci	drm_object_attach_property(&connector->base,
3328c2ecf20Sopenharmony_ci					dev_priv->backlight_property,
3338c2ecf20Sopenharmony_ci					BRIGHTNESS_MAX_LEVEL);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	mode_dev->panel_wants_dither = false;
3368c2ecf20Sopenharmony_ci	if (dev_priv->has_gct)
3378c2ecf20Sopenharmony_ci		mode_dev->panel_wants_dither = (dev_priv->gct_data.
3388c2ecf20Sopenharmony_ci			Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE);
3398c2ecf20Sopenharmony_ci        if (dev_priv->lvds_dither)
3408c2ecf20Sopenharmony_ci                mode_dev->panel_wants_dither = 1;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/*
3438c2ecf20Sopenharmony_ci	 * LVDS discovery:
3448c2ecf20Sopenharmony_ci	 * 1) check for EDID on DDC
3458c2ecf20Sopenharmony_ci	 * 2) check for VBT data
3468c2ecf20Sopenharmony_ci	 * 3) check to see if LVDS is already on
3478c2ecf20Sopenharmony_ci	 *    if none of the above, no panel
3488c2ecf20Sopenharmony_ci	 * 4) make sure lid is open
3498c2ecf20Sopenharmony_ci	 *    if closed, act like it's not there for now
3508c2ecf20Sopenharmony_ci	 */
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	edid = NULL;
3538c2ecf20Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
3548c2ecf20Sopenharmony_ci	i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
3558c2ecf20Sopenharmony_ci	if (i2c_adap)
3568c2ecf20Sopenharmony_ci		edid = drm_get_edid(connector, i2c_adap);
3578c2ecf20Sopenharmony_ci	if (edid == NULL && dev_priv->lpc_gpio_base) {
3588c2ecf20Sopenharmony_ci		oaktrail_lvds_i2c_init(encoder);
3598c2ecf20Sopenharmony_ci		if (gma_encoder->ddc_bus != NULL) {
3608c2ecf20Sopenharmony_ci			i2c_adap = &gma_encoder->ddc_bus->adapter;
3618c2ecf20Sopenharmony_ci			edid = drm_get_edid(connector, i2c_adap);
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Attempt to get the fixed panel mode from DDC.  Assume that the
3668c2ecf20Sopenharmony_ci	 * preferred mode is the right one.
3678c2ecf20Sopenharmony_ci	 */
3688c2ecf20Sopenharmony_ci	if (edid) {
3698c2ecf20Sopenharmony_ci		drm_connector_update_edid_property(connector, edid);
3708c2ecf20Sopenharmony_ci		drm_add_edid_modes(connector, edid);
3718c2ecf20Sopenharmony_ci		kfree(edid);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		list_for_each_entry(scan, &connector->probed_modes, head) {
3748c2ecf20Sopenharmony_ci			if (scan->type & DRM_MODE_TYPE_PREFERRED) {
3758c2ecf20Sopenharmony_ci				mode_dev->panel_fixed_mode =
3768c2ecf20Sopenharmony_ci				    drm_mode_duplicate(dev, scan);
3778c2ecf20Sopenharmony_ci				goto out;	/* FIXME: check for quirks */
3788c2ecf20Sopenharmony_ci			}
3798c2ecf20Sopenharmony_ci		}
3808c2ecf20Sopenharmony_ci	} else
3818c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No ddc adapter available!\n");
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	 * If we didn't get EDID, try geting panel timing
3848c2ecf20Sopenharmony_ci	 * from configuration data
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	oaktrail_lvds_get_configuration_mode(dev, mode_dev);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (mode_dev->panel_fixed_mode) {
3898c2ecf20Sopenharmony_ci		mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
3908c2ecf20Sopenharmony_ci		goto out;	/* FIXME: check for quirks */
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* If we still don't have a mode after all that, give up. */
3948c2ecf20Sopenharmony_ci	if (!mode_dev->panel_fixed_mode) {
3958c2ecf20Sopenharmony_ci		dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
3968c2ecf20Sopenharmony_ci		goto failed_find;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciout:
4008c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	drm_connector_register(connector);
4038c2ecf20Sopenharmony_ci	return;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cifailed_find:
4068c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
4098c2ecf20Sopenharmony_ci	if (gma_encoder->ddc_bus) {
4108c2ecf20Sopenharmony_ci		psb_intel_i2c_destroy(gma_encoder->ddc_bus);
4118c2ecf20Sopenharmony_ci		gma_encoder->ddc_bus = NULL;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/* failed_ddc: */
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	drm_encoder_cleanup(encoder);
4178c2ecf20Sopenharmony_ci	drm_connector_cleanup(connector);
4188c2ecf20Sopenharmony_ci	kfree(gma_connector);
4198c2ecf20Sopenharmony_cifailed_connector:
4208c2ecf20Sopenharmony_ci	kfree(gma_encoder);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
423