From: Kevin Brace <kevinbr...@bracecomputerlab.com>

Signed-off-by: Kevin Brace <kevinbr...@bracecomputerlab.com>
---
 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 <kevinbr...@bracecomputerlab.com>
+ * James Simmons <jsimm...@infradead.org>
+ */
+
+#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

Reply via email to