[PATCH v2 18/32] drm/via: Add via_lvds.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Kevin Brace <kevinbrace@xxxxxxxxxxxxxxxxxxxx>

Signed-off-by: Kevin Brace <kevinbrace@xxxxxxxxxxxxxxxxxxxx>
---
 drivers/gpu/drm/via/via_lvds.c | 1420 ++++++++++++++++++++++++++++++++
 1 file changed, 1420 insertions(+)
 create mode 100644 drivers/gpu/drm/via/via_lvds.c

diff --git a/drivers/gpu/drm/via/via_lvds.c b/drivers/gpu/drm/via/via_lvds.c
new file mode 100644
index 000000000000..44f80cc0d6ef
--- /dev/null
+++ b/drivers/gpu/drm/via/via_lvds.c
@@ -0,0 +1,1420 @@
+/*
+ * Copyright © 2016-2018 Kevin Brace.
+ * Copyright 2012 James Simmons. All Rights Reserved.
+ * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved.
+ * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) 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.
+ *
+ * Author(s):
+ * Kevin Brace <kevinbrace@xxxxxxxxxxxxxxxxxxxx>
+ * James Simmons <jsimmons@xxxxxxxxxxxxx>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <asm/olpc.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_probe_helper.h>
+
+#include "via_drv.h"
+
+#define TD0 200
+#define TD1 25
+#define TD2 0
+#define TD3 25
+
+/* Non-I2C bus FP native screen resolution information table.*/
+static via_lvds_info via_lvds_info_table[] = {
+	{ 640,  480},
+	{ 800,  600},
+	{1024,  768},
+	{1280,  768},
+	{1280, 1024},
+	{1400, 1050},
+	{1600, 1200},
+	{1280,  800},
+	{ 800,  480},
+	{1024,  768},
+	{1366,  768},
+	{1024,  768},
+	{1280,  768},
+	{1280, 1024},
+	{1400, 1050},
+	{1600, 1200}
+};
+
+/* Power sequence relations */
+struct td_timer {
+	struct vga_regset tdRegs[2];
+};
+
+static struct td_timer td_timer_regs[] = {
+	/* td_timer0 */
+	{ { { VGA_CRT_IC, 0x8B, 0, 7 }, { VGA_CRT_IC, 0x8F, 0, 3 } } },
+	/* td_timer1 */
+	{ { { VGA_CRT_IC, 0x8C, 0, 7 }, { VGA_CRT_IC, 0x8F, 4, 7 } } },
+	/* td_timer2 */
+	{ { { VGA_CRT_IC, 0x8D, 0, 7 }, { VGA_CRT_IC, 0x90, 0, 3 } } },
+	/* td_timer3 */
+	{ { { VGA_CRT_IC, 0x8E, 0, 7 }, { VGA_CRT_IC, 0x90, 4, 7 } } }
+};
+
+/*
+ * Function Name:  via_init_td_timing_regs
+ * Description: Init TD timing register (power sequence)
+ */
+static void via_init_td_timing_regs(struct drm_device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+	unsigned int td_timer[4] = { 500, 50, 0, 510 }, i;
+	struct vga_registers timings;
+	u32 reg_value;
+
+	/* Fill primary power sequence */
+	for (i = 0; i < 4; i++) {
+		/* Calculate TD Timer, every step is 572.1uSec */
+		reg_value = td_timer[i] * 10000 / 5721;
+
+		timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs);
+		timings.regs = td_timer_regs[i].tdRegs;
+		load_value_to_registers(VGABASE, &timings, reg_value);
+	}
+
+	/* Note: VT3353 have two hardware power sequences
+	 * other chips only have one hardware power sequence */
+	if (pdev->device == PCI_DEVICE_ID_VIA_VT1122) {
+		/* set CRD4[0] to "1" to select 2nd LCD power sequence. */
+		svga_wcrt_mask(VGABASE, 0xD4, BIT(0), BIT(0));
+		/* Fill secondary power sequence */
+		for (i = 0; i < 4; i++) {
+			/* Calculate TD Timer, every step is 572.1uSec */
+			reg_value = td_timer[i] * 10000 / 5721;
+
+			timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs);
+			timings.regs = td_timer_regs[i].tdRegs;
+			load_value_to_registers(VGABASE, &timings, reg_value);
+		}
+	}
+}
+
+static bool via_fp_probe_edid(struct i2c_adapter *i2c_bus)
+{
+	u8 out = 0x0;
+	u8 buf[8];
+	struct i2c_msg msgs[] = {
+		{
+			.addr = DDC_ADDR,
+			.flags = 0,
+			.len = 1,
+			.buf = &out,
+		},
+		{
+			.addr = DDC_ADDR,
+			.flags = I2C_M_RD,
+			.len = 8,
+			.buf = buf,
+		}
+	};
+	int i2c_ret;
+	bool ret = false;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	i2c_ret = i2c_transfer(i2c_bus, msgs, 2);
+	if (i2c_ret != 2) {
+		goto exit;
+	}
+
+	if (drm_edid_header_is_valid(buf) < 6) {
+		goto exit;
+	}
+
+	ret = true;
+exit:
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+	return ret;
+}
+
+/* caculate the cetering timing using mode and adjusted_mode */
+static void via_centering_timing(const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	int panel_hsync_time = adjusted_mode->hsync_end -
+		adjusted_mode->hsync_start;
+	int panel_vsync_time = adjusted_mode->vsync_end -
+		adjusted_mode->vsync_start;
+	int panel_hblank_start = adjusted_mode->hdisplay;
+	int panel_vbank_start = adjusted_mode->vdisplay;
+	int hborder = (adjusted_mode->hdisplay - mode->hdisplay) / 2;
+	int vborder = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
+	int new_hblank_start = hborder + mode->hdisplay;
+	int new_vblank_start = vborder + mode->vdisplay;
+
+	adjusted_mode->hdisplay = mode->hdisplay;
+	adjusted_mode->hsync_start = (adjusted_mode->hsync_start -
+		panel_hblank_start) + new_hblank_start;
+	adjusted_mode->hsync_end = adjusted_mode->hsync_start +
+		panel_hsync_time;
+	adjusted_mode->vdisplay = mode->vdisplay;
+	adjusted_mode->vsync_start = (adjusted_mode->vsync_start -
+		panel_vbank_start) + new_vblank_start;
+	adjusted_mode->vsync_end = adjusted_mode->vsync_start +
+		panel_vsync_time;
+	/* Adjust Crtc H and V */
+	adjusted_mode->crtc_hdisplay = adjusted_mode->hdisplay;
+	adjusted_mode->crtc_hblank_start = new_hblank_start;
+	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal - hborder;
+	adjusted_mode->crtc_hsync_start = adjusted_mode->hsync_start;
+	adjusted_mode->crtc_hsync_end = adjusted_mode->hsync_end;
+	adjusted_mode->crtc_vdisplay = adjusted_mode->vdisplay;
+	adjusted_mode->crtc_vblank_start = new_vblank_start;
+	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal - vborder;
+	adjusted_mode->crtc_vsync_start = adjusted_mode->vsync_start;
+	adjusted_mode->crtc_vsync_end = adjusted_mode->vsync_end;
+}
+
+static void via_lvds_cle266_soft_power_seq(struct via_drm_priv *dev_priv,
+						bool power_state)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	if (power_state) {
+		/* Wait for 25 ms. */
+		mdelay(25);
+
+		/* Turn on FP VDD rail. */
+		via_lvds_set_primary_soft_vdd(VGABASE, true);
+
+		/* Wait for 510 ms. */
+		mdelay(510);
+
+		/* Turn on FP data transmission. */
+		via_lvds_set_primary_soft_data(VGABASE, true);
+
+		/* Wait for 1 ms. */
+		mdelay(1);
+
+		/* Turn on FP VEE rail. */
+		via_lvds_set_primary_soft_vee(VGABASE, true);
+
+		/* Turn on FP back light. */
+		via_lvds_set_primary_soft_back_light(VGABASE, true);
+	} else {
+		/* Wait for 1 ms. */
+		mdelay(1);
+
+		/* Turn off FP back light. */
+		via_lvds_set_primary_soft_back_light(VGABASE, false);
+
+		/* Turn off FP VEE rail. */
+		via_lvds_set_primary_soft_vee(VGABASE, false);
+
+		/* Wait for 510 ms. */
+		mdelay(510);
+
+		/* Turn off FP data transmission. */
+		via_lvds_set_primary_soft_data(VGABASE, false);
+
+		/* Wait for 25 ms. */
+		mdelay(25);
+
+		/* Turn off FP VDD rail. */
+		via_lvds_set_primary_soft_vdd(VGABASE, false);
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_primary_soft_power_seq(struct via_drm_priv *dev_priv,
+						bool power_state)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	/* Turn off FP hardware power sequence. */
+	via_lvds_set_primary_hard_power(VGABASE, false);
+
+	/* Use software FP power sequence control. */
+	via_lvds_set_primary_power_seq_type(VGABASE, false);
+
+	if (power_state) {
+		/* Turn on FP display period. */
+		via_lvds_set_primary_direct_display_period(VGABASE, true);
+
+		/* Wait for TD0 ms. */
+		mdelay(TD0);
+
+		/* Turn on FP VDD rail. */
+		via_lvds_set_primary_soft_vdd(VGABASE, true);
+
+		/* Wait for TD1 ms. */
+		mdelay(TD1);
+
+		/* Turn on FP data transmission. */
+		via_lvds_set_primary_soft_data(VGABASE, true);
+
+		/* Wait for TD2 ms. */
+		mdelay(TD2);
+
+		/* Turn on FP VEE rail. */
+		via_lvds_set_primary_soft_vee(VGABASE, true);
+
+		/* Wait for TD3 ms. */
+		mdelay(TD3);
+
+		/* Turn on FP back light. */
+		via_lvds_set_primary_soft_back_light(VGABASE, true);
+	} else {
+		/* Turn off FP back light. */
+		via_lvds_set_primary_soft_back_light(VGABASE, false);
+
+		/* Wait for TD3 ms. */
+		mdelay(TD3);
+
+		/* Turn off FP VEE rail. */
+		via_lvds_set_primary_soft_vee(VGABASE, false);
+
+		/* Wait for TD2 ms. */
+		mdelay(TD2);
+
+		/* Turn off FP data transmission. */
+		via_lvds_set_primary_soft_data(VGABASE, false);
+
+		/* Wait for TD1 ms. */
+		mdelay(TD1);
+
+		/* Turn off FP VDD rail. */
+		via_lvds_set_primary_soft_vdd(VGABASE, false);
+
+		/* Turn off FP display period. */
+		via_lvds_set_primary_direct_display_period(VGABASE, false);
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_secondary_soft_power_seq(struct via_drm_priv *dev_priv,
+						bool power_state)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	/* Turn off FP hardware power sequence. */
+	via_lvds_set_secondary_hard_power(VGABASE, false);
+
+	/* Use software FP power sequence control. */
+	via_lvds_set_secondary_power_seq_type(VGABASE, false);
+
+	if (power_state) {
+		/* Turn on FP display period. */
+		via_lvds_set_secondary_direct_display_period(VGABASE, true);
+
+		/* Wait for TD0 ms. */
+		mdelay(TD0);
+
+		/* Turn on FP VDD rail. */
+		via_lvds_set_secondary_soft_vdd(VGABASE, true);
+
+		/* Wait for TD1 ms. */
+		mdelay(TD1);
+
+		/* Turn on FP data transmission. */
+		via_lvds_set_secondary_soft_data(VGABASE, true);
+
+		/* Wait for TD2 ms. */
+		mdelay(TD2);
+
+		/* Turn on FP VEE rail. */
+		via_lvds_set_secondary_soft_vee(VGABASE, true);
+
+		/* Wait for TD3 ms. */
+		mdelay(TD3);
+
+		/* Turn on FP back light. */
+		via_lvds_set_secondary_soft_back_light(VGABASE, true);
+	} else {
+		/* Turn off FP back light. */
+		via_lvds_set_secondary_soft_back_light(VGABASE, false);
+
+		/* Wait for TD3 ms. */
+		mdelay(TD3);
+
+		/* Turn off FP VEE rail. */
+		via_lvds_set_secondary_soft_vee(VGABASE, false);
+
+		/* Wait for TD2 ms. */
+		mdelay(TD2);
+
+		/* Turn off FP data transmission. */
+		via_lvds_set_secondary_soft_data(VGABASE, false);
+
+		/* Wait for TD1 ms. */
+		mdelay(TD1);
+
+		/* Turn off FP VDD rail. */
+		via_lvds_set_secondary_soft_vdd(VGABASE, false);
+
+		/* Turn off FP display period. */
+		via_lvds_set_secondary_direct_display_period(VGABASE, false);
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_primary_hard_power_seq(struct via_drm_priv *dev_priv,
+						bool power_state)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	/* Use hardware FP power sequence control. */
+	via_lvds_set_primary_power_seq_type(VGABASE, true);
+
+	if (power_state) {
+		/* Turn on FP display period. */
+		via_lvds_set_primary_direct_display_period(VGABASE, true);
+
+		/* Turn on FP hardware power sequence. */
+		via_lvds_set_primary_hard_power(VGABASE, true);
+
+		/* Turn on FP back light. */
+		via_lvds_set_primary_direct_back_light_ctrl(VGABASE, true);
+	} else {
+		/* Turn off FP back light. */
+		via_lvds_set_primary_direct_back_light_ctrl(VGABASE, false);
+
+		/* Turn off FP hardware power sequence. */
+		via_lvds_set_primary_hard_power(VGABASE, false);
+
+		/* Turn on FP display period. */
+		via_lvds_set_primary_direct_display_period(VGABASE, false);
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_power(struct via_drm_priv *dev_priv,
+				unsigned short device,
+				u32 di_port, bool power_state)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch (device) {
+	case PCI_DEVICE_ID_VIA_CLE266:
+		via_lvds_cle266_soft_power_seq(dev_priv, power_state);
+		break;
+	case PCI_DEVICE_ID_VIA_KM400:
+	case PCI_DEVICE_ID_VIA_CN700:
+	case PCI_DEVICE_ID_VIA_PM800:
+	case PCI_DEVICE_ID_VIA_K8M800:
+	case PCI_DEVICE_ID_VIA_VT3343:
+	case PCI_DEVICE_ID_VIA_K8M890:
+	case PCI_DEVICE_ID_VIA_P4M900:
+		via_lvds_primary_hard_power_seq(dev_priv, power_state);
+		break;
+	case PCI_DEVICE_ID_VIA_VT3157:
+	case PCI_DEVICE_ID_VIA_VT1122:
+		if (di_port & VIA_DI_PORT_LVDS1) {
+			via_lvds_primary_soft_power_seq(dev_priv, power_state);
+			via_lvds1_set_power(VGABASE, power_state);
+		}
+
+		if (di_port & VIA_DI_PORT_LVDS2) {
+			via_lvds_secondary_soft_power_seq(dev_priv, power_state);
+			via_lvds2_set_power(VGABASE, power_state);
+		}
+
+		break;
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900_VGA:
+		via_lvds_primary_hard_power_seq(dev_priv, power_state);
+		via_lvds1_set_power(VGABASE, power_state);
+		break;
+	default:
+		DRM_DEBUG_KMS("VIA Technologies Chrome IGP "
+				"FP Power: Unrecognized "
+				"PCI Device ID.\n");
+		break;
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+/*
+ * Sets flat panel I/O pad state.
+ */
+static void via_lvds_io_pad_setting(struct via_drm_priv *dev_priv,
+					u32 di_port, bool io_pad_on)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch(di_port) {
+	case VIA_DI_PORT_DVP0:
+		via_dvp0_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case VIA_DI_PORT_DVP1:
+		via_dvp1_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case VIA_DI_PORT_FPDPLOW:
+		via_fpdp_low_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case VIA_DI_PORT_FPDPHIGH:
+		via_fpdp_high_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case (VIA_DI_PORT_FPDPLOW |
+		VIA_DI_PORT_FPDPHIGH):
+		via_fpdp_low_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		via_fpdp_high_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case VIA_DI_PORT_LVDS1:
+		via_lvds1_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case VIA_DI_PORT_LVDS2:
+		via_lvds2_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	case (VIA_DI_PORT_LVDS1 |
+		VIA_DI_PORT_LVDS2):
+		via_lvds1_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00);
+		via_lvds2_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("FP I/O Pad: %s\n", io_pad_on ? "On": "Off");
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_format(struct via_drm_priv *dev_priv,
+				u32 di_port, u8 format)
+{
+	u8 temp = format & 0x01;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch(di_port) {
+	case VIA_DI_PORT_LVDS1:
+		via_lvds1_set_format(VGABASE, temp);
+		break;
+	case VIA_DI_PORT_LVDS2:
+		via_lvds2_set_format(VGABASE, temp);
+		break;
+	case (VIA_DI_PORT_LVDS1 |
+		VIA_DI_PORT_LVDS2):
+		via_lvds1_set_format(VGABASE, temp);
+		via_lvds2_set_format(VGABASE, temp);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_output_format(struct via_drm_priv *dev_priv,
+					u32 di_port, u8 output_format)
+{
+	u8 temp = output_format & 0x01;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch(di_port) {
+	case VIA_DI_PORT_LVDS1:
+		via_lvds1_set_output_format(VGABASE, temp);
+		break;
+	case VIA_DI_PORT_LVDS2:
+		via_lvds2_set_output_format(VGABASE, temp);
+		break;
+	case (VIA_DI_PORT_LVDS1 |
+		VIA_DI_PORT_LVDS2):
+		via_lvds1_set_output_format(VGABASE, temp);
+		via_lvds2_set_output_format(VGABASE, temp);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_dithering(struct via_drm_priv *dev_priv,
+				u32 di_port, bool dithering)
+{
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch(di_port) {
+	case VIA_DI_PORT_LVDS1:
+		via_lvds1_set_dithering(VGABASE, dithering);
+		break;
+	case VIA_DI_PORT_LVDS2:
+		via_lvds2_set_dithering(VGABASE, dithering);
+		break;
+	case (VIA_DI_PORT_LVDS1 |
+		VIA_DI_PORT_LVDS2):
+		via_lvds1_set_dithering(VGABASE, dithering);
+		via_lvds2_set_dithering(VGABASE, dithering);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+/*
+ * Sets flat panel display source.
+ */
+static void via_lvds_display_source(struct via_drm_priv *dev_priv,
+					u32 di_port, int index)
+{
+	u8 display_source = index & 0x01;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch(di_port) {
+	case VIA_DI_PORT_DVP0:
+		via_dvp0_set_display_source(VGABASE, display_source);
+		break;
+	case VIA_DI_PORT_DVP1:
+		via_dvp1_set_display_source(VGABASE, display_source);
+		break;
+	case VIA_DI_PORT_FPDPLOW:
+		via_fpdp_low_set_display_source(VGABASE, display_source);
+		via_dvp1_set_display_source(VGABASE, display_source);
+		break;
+	case VIA_DI_PORT_FPDPHIGH:
+		via_fpdp_high_set_display_source(VGABASE, display_source);
+		via_dvp0_set_display_source(VGABASE, display_source);
+		break;
+	case (VIA_DI_PORT_FPDPLOW |
+		VIA_DI_PORT_FPDPHIGH):
+		via_fpdp_low_set_display_source(VGABASE, display_source);
+		via_fpdp_high_set_display_source(VGABASE, display_source);
+		break;
+	case VIA_DI_PORT_LVDS1:
+		via_lvds1_set_display_source(VGABASE, display_source);
+		break;
+	case VIA_DI_PORT_LVDS2:
+		via_lvds2_set_display_source(VGABASE, display_source);
+		break;
+	case (VIA_DI_PORT_LVDS1 |
+		VIA_DI_PORT_LVDS2):
+		via_lvds1_set_display_source(VGABASE, display_source);
+		via_lvds2_set_display_source(VGABASE, display_source);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("FP Display Source: IGA%d\n",
+			display_source + 1);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct via_encoder *enc = container_of(encoder,
+					struct via_encoder, base);
+	struct drm_device *dev = encoder->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+
+	/* PCI Device ID */
+	u16 chipset = pdev->device;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		via_lvds_power(dev_priv, chipset, enc->di_port, true);
+		via_lvds_io_pad_setting(dev_priv, enc->di_port, true);
+		break;
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_OFF:
+		via_lvds_power(dev_priv, chipset, enc->di_port, false);
+		via_lvds_io_pad_setting(dev_priv, enc->di_port, false);
+		break;
+	default:
+		break;
+	}
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static bool via_lvds_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct drm_property *prop = encoder->dev->mode_config.scaling_mode_property;
+	struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base);
+	struct drm_display_mode *native_mode = NULL, *tmp, *t;
+	struct drm_connector *connector = NULL, *con;
+	u64 scale_mode = DRM_MODE_SCALE_CENTER;
+	struct drm_device *dev = encoder->dev;
+
+	list_for_each_entry(con, &dev->mode_config.connector_list, head) {
+		if (encoder == con->encoder) {
+			connector = con;
+			break;
+		}
+	}
+
+	if (!connector) {
+		DRM_INFO("LVDS encoder is not used by any connector\n");
+		return false;
+	}
+
+	list_for_each_entry_safe(tmp, t, &connector->modes, head) {
+		if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) {
+			native_mode = tmp;
+			break;
+		}
+	}
+
+	if (!native_mode) {
+		DRM_INFO("No native mode for LVDS\n");
+		return false;
+	}
+
+	drm_object_property_get_value(&connector->base, prop, &scale_mode);
+
+	if ((mode->hdisplay != native_mode->hdisplay) ||
+		(mode->vdisplay != native_mode->vdisplay)) {
+		if (scale_mode == DRM_MODE_SCALE_NONE)
+			return false;
+		drm_mode_copy(adjusted_mode, native_mode);
+	}
+	drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+	iga->scaling_mode = VIA_NO_SCALING;
+	/* Take care of 410 downscaling */
+	if ((mode->hdisplay > native_mode->hdisplay) ||
+		(mode->vdisplay > native_mode->vdisplay)) {
+		iga->scaling_mode = VIA_SHRINK;
+	} else {
+		if (!iga->index || scale_mode == DRM_MODE_SCALE_CENTER) {
+			/* Do centering according to mode and adjusted_mode */
+			via_centering_timing(mode, adjusted_mode);
+		} else {
+			if (mode->hdisplay < native_mode->hdisplay)
+				iga->scaling_mode |= VIA_HOR_EXPAND;
+			if (mode->vdisplay < native_mode->vdisplay)
+				iga->scaling_mode |= VIA_VER_EXPAND;
+		}
+	}
+	return true;
+}
+
+static void via_lvds_prepare(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder,
+					struct via_encoder, base);
+	struct drm_device *dev = encoder->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+
+	/* PCI Device ID */
+	u16 chipset = pdev->device;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	via_lvds_power(dev_priv, chipset, enc->di_port, false);
+	via_lvds_io_pad_setting(dev_priv, enc->di_port, false);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_commit(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder,
+					struct via_encoder, base);
+	struct drm_device *dev = encoder->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+
+	/* PCI Device ID */
+	u16 chipset = pdev->device;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	via_lvds_power(dev_priv, chipset, enc->di_port, true);
+	via_lvds_io_pad_setting(dev_priv, enc->di_port, true);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void
+via_lvds_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+		struct drm_display_mode *adjusted_mode)
+{
+	struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base);
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_device *dev = encoder->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+
+	/* PCI Device ID */
+	u16 chipset = pdev->device;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	/* Temporary implementation.*/
+	switch (chipset) {
+	case PCI_DEVICE_ID_VIA_P4M900:
+		via_fpdp_low_set_adjustment(VGABASE, 0x08);
+		break;
+	default:
+		break;
+	}
+
+	switch (chipset) {
+	case PCI_DEVICE_ID_VIA_VT3157:
+	case PCI_DEVICE_ID_VIA_VT1122:
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900_VGA:
+		/* OPENLDI Mode */
+		via_lvds_format(dev_priv, enc->di_port, 0x01);
+
+		/* Sequential Mode */
+		via_lvds_output_format(dev_priv, enc->di_port, 0x01);
+
+		/* Turn on dithering. */
+		via_lvds_dithering(dev_priv, enc->di_port, true);
+		break;
+	default:
+		break;
+	}
+
+	via_lvds_display_source(dev_priv, enc->di_port, iga->index);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+static void via_lvds_disable(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder,
+					struct via_encoder, base);
+	struct drm_device *dev = encoder->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+
+	/* PCI Device ID */
+	u16 chipset = pdev->device;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	via_lvds_power(dev_priv, chipset, enc->di_port, false);
+	via_lvds_io_pad_setting(dev_priv, enc->di_port, false);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+const struct drm_encoder_helper_funcs via_lvds_helper_funcs = {
+	.dpms = via_lvds_dpms,
+	.prepare = via_lvds_prepare,
+	.commit = via_lvds_commit,
+	.mode_set = via_lvds_mode_set,
+	.disable = via_lvds_disable,
+};
+
+const struct drm_encoder_funcs via_lvds_enc_funcs = {
+	.destroy = via_encoder_cleanup,
+};
+
+/* Detect FP presence. */
+static enum drm_connector_status
+via_lvds_detect(struct drm_connector *connector, bool force)
+{
+	struct drm_device *dev = connector->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_connector *con = container_of(connector,
+					struct via_connector, base);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+	enum drm_connector_status ret = connector_status_disconnected;
+	struct i2c_adapter *i2c_bus;
+	struct edid *edid = NULL;
+	u8 mask;
+	uint32_t i, i2c_bus_bit;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	if (machine_is_olpc()) {
+		ret = connector_status_connected;
+		goto exit;
+	}
+
+	i2c_bus_bit = VIA_I2C_BUS2;
+	for (i = 0; i < 2; i++) {
+		if (con->i2c_bus & i2c_bus_bit) {
+			if (i2c_bus_bit & VIA_I2C_BUS2) {
+				i2c_bus = via_find_ddc_bus(0x31);
+			} else if (i2c_bus_bit & VIA_I2C_BUS3) {
+				i2c_bus = via_find_ddc_bus(0x2c);
+			} else {
+				i2c_bus = NULL;
+				i2c_bus_bit = i2c_bus_bit << 1;
+				continue;
+			}
+		} else {
+			i2c_bus = NULL;
+			i2c_bus_bit = i2c_bus_bit << 1;
+			continue;
+		}
+
+		if (!via_fp_probe_edid(i2c_bus)) {
+			i2c_bus_bit = i2c_bus_bit << 1;
+			continue;
+		}
+
+		edid = drm_get_edid(&con->base, i2c_bus);
+		if (edid) {
+			if (edid->input & DRM_EDID_INPUT_DIGITAL) {
+				ret = connector_status_connected;
+				kfree(edid);
+				DRM_DEBUG_KMS("FP detected.\n");
+				DRM_DEBUG_KMS("i2c_bus_bit: %x\n", i2c_bus_bit);
+				goto exit;
+			} else {
+				kfree(edid);
+			}
+		}
+
+		i2c_bus_bit = i2c_bus_bit << 1;
+	}
+
+	if (pdev->device == PCI_DEVICE_ID_VIA_CLE266) {
+		mask = BIT(3);
+	} else {
+		mask = BIT(1);
+	}
+
+	if (vga_rcrt(VGABASE, 0x3B) & mask) {
+		ret = connector_status_connected;
+	}
+
+exit:
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+	return ret;
+}
+
+static int via_lvds_set_property(struct drm_connector *connector,
+				struct drm_property *property,
+				uint64_t val)
+{
+	struct drm_device *dev = connector->dev;
+	uint64_t orig;
+	int ret;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	ret = drm_object_property_get_value(&connector->base, property, &orig);
+	if (ret) {
+		DRM_ERROR("FP Property not found!\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (orig != val) {
+		if (property == dev->mode_config.scaling_mode_property) {
+			switch (val) {
+			case DRM_MODE_SCALE_NONE:
+				break;
+			case DRM_MODE_SCALE_CENTER:
+				break;
+			case DRM_MODE_SCALE_ASPECT:
+				break;
+			case DRM_MODE_SCALE_FULLSCREEN:
+				break;
+			default:
+				DRM_ERROR("Invalid FP property!\n");
+				ret = -EINVAL;
+				break;
+			}
+		}
+	}
+
+exit:
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+	return ret;
+}
+
+struct drm_connector_funcs via_lvds_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = via_lvds_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = via_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state =
+			drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state =
+			drm_atomic_helper_connector_destroy_state,
+};
+
+static int
+via_lvds_get_modes(struct drm_connector *connector)
+{
+	struct via_connector *con = container_of(connector, struct via_connector, base);
+	struct drm_device *dev = connector->dev;
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+	struct i2c_adapter *i2c_bus;
+	struct edid *edid = NULL;
+	struct drm_display_mode *native_mode = NULL;
+	u8 reg_value;
+	int hdisplay, vdisplay;
+	int count = 0;
+	uint32_t i, i2c_bus_bit;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	/* OLPC is very special */
+	if (machine_is_olpc()) {
+		native_mode = drm_mode_create(dev);
+
+		native_mode->clock = 56519;
+		native_mode->hdisplay = 1200;
+		native_mode->hsync_start = 1211;
+		native_mode->hsync_end = 1243;
+		native_mode->htotal = 1264;
+		native_mode->hskew = 0;
+		native_mode->vdisplay = 900;
+		native_mode->vsync_start = 901;
+		native_mode->vsync_end = 911;
+		native_mode->vtotal = 912;
+		native_mode->vscan = 0;
+
+		native_mode->type = DRM_MODE_TYPE_PREFERRED |
+					DRM_MODE_TYPE_DRIVER;
+		drm_mode_set_name(native_mode);
+		drm_mode_probed_add(connector, native_mode);
+		count = 1;
+		goto exit;
+	}
+
+	i2c_bus_bit = VIA_I2C_BUS2;
+	for (i = 0; i < 2; i++) {
+		if (con->i2c_bus & i2c_bus_bit) {
+			if (i2c_bus_bit & VIA_I2C_BUS2) {
+				i2c_bus = via_find_ddc_bus(0x31);
+			} else if (i2c_bus_bit & VIA_I2C_BUS3) {
+				i2c_bus = via_find_ddc_bus(0x2c);
+			} else {
+				i2c_bus = NULL;
+				i2c_bus_bit = i2c_bus_bit << 1;
+				continue;
+			}
+		} else {
+			i2c_bus = NULL;
+			i2c_bus_bit = i2c_bus_bit << 1;
+			continue;
+		}
+
+		edid = drm_get_edid(&con->base, i2c_bus);
+		if (edid) {
+			if (edid->input & DRM_EDID_INPUT_DIGITAL) {
+				drm_connector_update_edid_property(connector, edid);
+				count = drm_add_edid_modes(connector, edid);
+				kfree(edid);
+				DRM_DEBUG_KMS("FP EDID information was obtained.\n");
+				DRM_DEBUG_KMS("i2c_bus_bit: %x\n", i2c_bus_bit);
+				break;
+			} else {
+				kfree(edid);
+			}
+		}
+
+		i2c_bus_bit = i2c_bus_bit << 1;
+	}
+
+	reg_value = (vga_rcrt(VGABASE, 0x3f) & 0x0f);
+	hdisplay = vdisplay = 0;
+	hdisplay = via_lvds_info_table[reg_value].x;
+	vdisplay = via_lvds_info_table[reg_value].y;
+
+	if (hdisplay && vdisplay) {
+		native_mode = drm_cvt_mode(dev, hdisplay, vdisplay,
+					60, false, false, false);
+	}
+
+	if (native_mode) {
+		native_mode->type = DRM_MODE_TYPE_PREFERRED |
+						DRM_MODE_TYPE_DRIVER;
+		drm_mode_set_name(native_mode);
+		drm_mode_probed_add(connector, native_mode);
+		count = 1;
+	}
+
+exit:
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+	return count;
+}
+
+static int via_lvds_mode_valid(struct drm_connector *connector,
+				struct drm_display_mode *mode)
+{
+	struct drm_property *prop = connector->dev->mode_config.scaling_mode_property;
+	struct drm_display_mode *native_mode = NULL, *tmp, *t;
+	struct drm_device *dev = connector->dev;
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	u64 scale_mode = DRM_MODE_SCALE_CENTER;
+
+	list_for_each_entry_safe(tmp, t, &connector->modes, head) {
+		if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) {
+			native_mode = tmp;
+			break;
+		}
+	}
+
+	drm_object_property_get_value(&connector->base, prop, &scale_mode);
+
+	if ((scale_mode == DRM_MODE_SCALE_NONE) &&
+		((mode->hdisplay != native_mode->hdisplay) ||
+		(mode->vdisplay != native_mode->vdisplay)))
+		return MODE_PANEL;
+
+	/* Don't support mode larger than physical size */
+	if (pdev->device != PCI_DEVICE_ID_VIA_VX900_VGA) {
+		if (mode->hdisplay > native_mode->hdisplay)
+			return MODE_PANEL;
+		if (mode->vdisplay > native_mode->vdisplay)
+			return MODE_PANEL;
+	} else {
+		/* HW limitation 410 only can
+		 * do <= 1.33 scaling */
+		if (mode->hdisplay * 100 > native_mode->hdisplay * 133)
+			return MODE_PANEL;
+		if (mode->vdisplay * 100 > native_mode->vdisplay * 133)
+			return MODE_PANEL;
+
+		/* Now we can not support H V different scale */
+		if ((mode->hdisplay > native_mode->hdisplay) &&
+			(mode->vdisplay < native_mode->vdisplay))
+			return MODE_PANEL;
+
+		if ((mode->hdisplay < native_mode->hdisplay) &&
+			(mode->vdisplay > native_mode->vdisplay))
+			return MODE_PANEL;
+	}
+	return MODE_OK;
+}
+
+struct drm_connector_helper_funcs via_lvds_connector_helper_funcs = {
+	.get_modes = via_lvds_get_modes,
+};
+
+/*
+ * Probe (pre-initialization detection) FP.
+ */
+void via_lvds_probe(struct drm_device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev->dev);
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+	struct drm_connector connector;
+	struct i2c_adapter *i2c_bus;
+	struct edid *edid;
+	u16 chipset = pdev->device;
+	u8 sr12, sr13, sr5a;
+	u8 cr3b;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	sr12 = vga_rseq(VGABASE, 0x12);
+	sr13 = vga_rseq(VGABASE, 0x13);
+	cr3b = vga_rcrt(VGABASE, 0x3b);
+
+	DRM_DEBUG_KMS("chipset: 0x%04x\n", chipset);
+	DRM_DEBUG_KMS("sr12: 0x%02x\n", sr12);
+	DRM_DEBUG_KMS("sr13: 0x%02x\n", sr13);
+	DRM_DEBUG_KMS("cr3b: 0x%02x\n", cr3b);
+
+	/* Detect the presence of FPs. */
+	switch (chipset) {
+	case PCI_DEVICE_ID_VIA_CLE266:
+		if ((sr12 & BIT(4)) || (cr3b & BIT(3))) {
+			dev_priv->int_fp1_presence = true;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_DIP0;
+		} else {
+			dev_priv->int_fp1_presence = false;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+		}
+
+		dev_priv->int_fp2_presence = false;
+		dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+		break;
+	case PCI_DEVICE_ID_VIA_KM400:
+	case PCI_DEVICE_ID_VIA_CN700:
+	case PCI_DEVICE_ID_VIA_PM800:
+	case PCI_DEVICE_ID_VIA_K8M800:
+		/* 3C5.13[3] - DVP0D8 pin strapping
+		 *             0: AGP pins are used for AGP
+		 *             1: AGP pins are used by FPDP
+		 *             (Flat Panel Display Port) */
+		if ((sr13 & BIT(3)) && (cr3b & BIT(1))) {
+			dev_priv->int_fp1_presence = true;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_FPDPHIGH |
+							VIA_DI_PORT_FPDPLOW;
+		} else {
+			dev_priv->int_fp1_presence = false;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+		}
+
+		dev_priv->int_fp2_presence = false;
+		dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+		break;
+	case PCI_DEVICE_ID_VIA_VT3343:
+	case PCI_DEVICE_ID_VIA_K8M890:
+	case PCI_DEVICE_ID_VIA_P4M900:
+		if (cr3b & BIT(1)) {
+			/* 3C5.12[4] - DVP0D4 pin strapping
+			 *             0: 12-bit FPDP (Flat Panel
+			 *                Display Port)
+			 *             1: 24-bit FPDP (Flat Panel
+			 *                Display Port) */
+			if (sr12 & BIT(4)) {
+				dev_priv->int_fp1_presence = true;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_FPDPLOW |
+							VIA_DI_PORT_FPDPHIGH;
+			} else {
+				dev_priv->int_fp1_presence = true;
+				dev_priv->int_fp1_di_port =
+						VIA_DI_PORT_FPDPLOW;
+			}
+		} else {
+			dev_priv->int_fp1_presence = false;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+		}
+
+		dev_priv->int_fp2_presence = false;
+		dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+		break;
+	case PCI_DEVICE_ID_VIA_VT3157:
+	case PCI_DEVICE_ID_VIA_VT1122:
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900_VGA:
+		/* Save SR5A. */
+		sr5a = vga_rseq(VGABASE, 0x5a);
+
+		DRM_DEBUG_KMS("sr5a: 0x%02x\n", sr5a);
+
+		/* Set SR5A[0] to 1.
+		 * This allows the read out of the alternative
+		 * pin strapping settings from SR12 and SR13. */
+		svga_wseq_mask(VGABASE, 0x5a, BIT(0), BIT(0));
+
+		sr13 = vga_rseq(VGABASE, 0x13);
+		if (cr3b & BIT(1)) {
+			if (dev_priv->is_via_nanobook) {
+				dev_priv->int_fp1_presence = false;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+				dev_priv->int_fp2_presence = true;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2;
+			} else if (dev_priv->is_quanta_il1) {
+				/* From the Quanta IL1 schematic. */
+				dev_priv->int_fp1_presence = true;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_DVP1;
+				dev_priv->int_fp2_presence = false;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+
+			} else if (dev_priv->is_samsung_nc20) {
+				dev_priv->int_fp1_presence = false;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+				dev_priv->int_fp2_presence = true;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2;
+
+			/* 3C5.13[7:6] - Integrated LVDS / DVI
+			 *               Mode Select (DVP1D15-14 pin
+			 *               strapping)
+			 *               00: LVDS1 + LVDS2
+			 *               01: DVI + LVDS2
+			 *               10: Dual LVDS Channel
+			 *                   (High Resolution Panel)
+			 *               11: One DVI only (decrease
+			 *                   the clock jitter) */
+			} else if ((!(sr13 & BIT(7))) &&
+					(!(sr13 & BIT(6)))) {
+				dev_priv->int_fp1_presence = true;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_LVDS1;
+
+				/*
+				 * For now, don't support the second
+				 * FP.
+				 */
+				dev_priv->int_fp2_presence = false;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+			} else if ((!(sr13 & BIT(7))) &&
+					(sr13 & BIT(6))) {
+				dev_priv->int_fp1_presence = false;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+				dev_priv->int_fp2_presence = true;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2;
+			} else if ((sr13 & BIT(7)) &&
+					(!(sr13 & BIT(6)))) {
+				dev_priv->int_fp1_presence = true;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_LVDS1 |
+							VIA_DI_PORT_LVDS2;
+				dev_priv->int_fp2_presence = false;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+			} else {
+				dev_priv->int_fp1_presence = false;
+				dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+				dev_priv->int_fp2_presence = false;
+				dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+			}
+		} else {
+			dev_priv->int_fp1_presence = false;
+			dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+			dev_priv->int_fp2_presence = false;
+			dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+		}
+
+		/* Restore SR5A. */
+		vga_wseq(VGABASE, 0x5a, sr5a);
+		break;
+	default:
+		dev_priv->int_fp1_presence = false;
+		dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE;
+		dev_priv->int_fp2_presence = false;
+		dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE;
+		break;
+	}
+
+	dev_priv->int_fp1_i2c_bus = VIA_I2C_NONE;
+	dev_priv->int_fp2_i2c_bus = VIA_I2C_NONE;
+
+	/* Zero clear connector struct.
+	 * Not doing so leads to a crash. */
+	memset(&connector, 0, sizeof(connector));
+
+	/* Register a connector only for I2C bus probing. */
+	drm_connector_init(dev, &connector, &via_lvds_connector_funcs,
+				DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&connector,
+					&via_lvds_connector_helper_funcs);
+	drm_connector_register(&connector);
+
+	if ((dev_priv->int_fp1_presence)
+		&& (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS2))) {
+		i2c_bus = via_find_ddc_bus(0x31);
+		edid = drm_get_edid(&connector, i2c_bus);
+		if (edid) {
+			dev_priv->int_fp1_i2c_bus = VIA_I2C_BUS2;
+			dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2;
+			kfree(edid);
+		}
+	}
+
+	if ((dev_priv->int_fp2_presence)
+		&& (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS2))) {
+		i2c_bus = via_find_ddc_bus(0x31);
+		edid = drm_get_edid(&connector, i2c_bus);
+		if (edid) {
+			dev_priv->int_fp2_i2c_bus = VIA_I2C_BUS2;
+			dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2;
+			kfree(edid);
+		}
+	}
+
+	/* Release the connector resource. */
+	drm_connector_unregister(&connector);
+	drm_connector_cleanup(&connector);
+
+	DRM_DEBUG_KMS("int_fp1_presence: %x\n",
+			dev_priv->int_fp1_presence);
+	DRM_DEBUG_KMS("int_fp1_di_port: 0x%08x\n",
+			dev_priv->int_fp1_di_port);
+	DRM_DEBUG_KMS("int_fp1_i2c_bus: 0x%08x\n",
+			dev_priv->int_fp1_i2c_bus);
+	DRM_DEBUG_KMS("int_fp2_presence: %x\n",
+			dev_priv->int_fp2_presence);
+	DRM_DEBUG_KMS("int_fp2_di_port: 0x%08x\n",
+			dev_priv->int_fp2_di_port);
+	DRM_DEBUG_KMS("int_fp2_i2c_bus: 0x%08x\n",
+			dev_priv->int_fp2_i2c_bus);
+	DRM_DEBUG_KMS("mapped_i2c_bus: 0x%08x\n",
+			dev_priv->mapped_i2c_bus);
+
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+}
+
+void via_lvds_init(struct drm_device *dev)
+{
+	struct via_drm_priv *dev_priv = to_via_drm_priv(dev);
+	struct via_connector *con;
+	struct via_encoder *enc;
+
+	DRM_DEBUG_KMS("Entered %s.\n", __func__);
+
+	if ((!(dev_priv->int_fp1_presence)) &&
+		(!(dev_priv->int_fp2_presence))) {
+		goto exit;
+	}
+
+	enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL);
+	if (!enc) {
+		DRM_ERROR("Failed to allocate FP.\n");
+		goto exit;
+	}
+
+	con = &enc->cons[0];
+	INIT_LIST_HEAD(&con->props);
+
+	drm_connector_init(dev, &con->base, &via_lvds_connector_funcs,
+				DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&con->base, &via_lvds_connector_helper_funcs);
+	drm_connector_register(&con->base);
+
+	if (dev_priv->int_fp1_presence) {
+		con->i2c_bus = dev_priv->int_fp1_i2c_bus;
+	} else if (dev_priv->int_fp2_presence) {
+		con->i2c_bus = dev_priv->int_fp2_i2c_bus;
+	} else {
+		con->i2c_bus = VIA_I2C_NONE;
+	}
+
+	con->base.doublescan_allowed = false;
+	con->base.interlace_allowed = false;
+
+	/* Now setup the encoder */
+	drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs,
+						DRM_MODE_ENCODER_LVDS, NULL);
+	drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs);
+
+	enc->base.possible_crtcs = BIT(1) | BIT(0);
+
+	if (dev_priv->int_fp1_presence) {
+		enc->di_port = dev_priv->int_fp1_di_port;
+	} else if (dev_priv->int_fp2_presence) {
+		enc->di_port = dev_priv->int_fp2_di_port;
+	} else {
+		enc->di_port = VIA_DI_PORT_NONE;
+	}
+
+	/* Put it all together */
+	drm_connector_attach_encoder(&con->base, &enc->base);
+
+	/* Init TD timing register (power sequence) */
+	via_init_td_timing_regs(dev);
+exit:
+	DRM_DEBUG_KMS("Exiting %s.\n", __func__);
+	return;
+}
--
2.35.1





[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux