[RFC 10/21] DRM: Add VIA DRM driver

James Simmons jsimmons at infradead.org
Sat Jun 8 09:50:42 PDT 2013


commit 86edf1f84ab36213c3bd3a6deb9c0811c7458b2c
Author: James Simmons <jsimmons at infradead.org>
Date:   Sat Jun 8 09:57:49 2013 -0400

    via: Our implementation of KMS outputs and crtc
    
    The header file defines via_crtc, via_connector, and via_encoder. Our
    via_connector extents drm_connector by including the i2c_adaptor if
    the connector supports it and a flags field to tell if a connector specific
    feature is enabled or not. The flags field in via_encoder is used the same
    way. The via_encoder needs to know what diport it needs to use. The via_crtc
    is the most complex. Settings like the display fifo vary depending on the
    hardware platform so we cache the value in via_crtc and can use the same
    routine to set the registers on all platforms. The same is for the register
    sets which not only varies per platform but also per each crtc on board.
    
    Signed-Off-by: James Simmons <jsimmons at infradead.org>

diff --git a/drivers/gpu/drm/via/via_display.c b/drivers/gpu/drm/via/via_display.c
new file mode 100644
index 0000000..4a7223d6
--- /dev/null
+++ b/drivers/gpu/drm/via/via_display.c
@@ -0,0 +1,583 @@
+/*
+ * 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.
+ *
+ * Authors:
+ *	James Simmons <jsimmons at infradead.org>
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "via_drv.h"
+
+/*
+ * Shared encoder routines.
+ */
+void
+via_encoder_commit(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct via_crtc *iga = NULL;
+	u8 value = 0;
+
+	if (encoder->crtc == NULL)
+		return;
+
+	iga = container_of(encoder->crtc, struct via_crtc, base);
+	if (iga->index)
+		value = BIT(4);
+
+	/* Set IGA source and turn on DI port clock */
+	switch (enc->diPort) {
+	case DISP_DI_DVP0:
+		/* DVP0 Data Source Selection. */
+		svga_wcrt_mask(VGABASE, 0x96, value, BIT(4));
+		/* enable DVP0 under CX700 */
+		if (encoder->dev->pdev->device == PCI_DEVICE_ID_VIA_VT3157)
+			svga_wcrt_mask(VGABASE, 0x91, BIT(5), BIT(5));
+		/* Turn on DVP0 clk */
+		svga_wseq_mask(VGABASE, 0x1E, 0xC0, BIT(7) | BIT(6));
+		break;
+
+	case DISP_DI_DVP1:
+		svga_wcrt_mask(VGABASE, 0x9B, value, BIT(4));
+		/* enable DVP1 under these chipset. Does DVI exist
+		 * for pre CX700 hardware */
+		if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT3157) ||
+		    (dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+		    (dev->pdev->device == PCI_DEVICE_ID_VIA_VX875) ||
+		    (dev->pdev->device == PCI_DEVICE_ID_VIA_VX900))
+			svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(5));
+		/* Turn on DVP1 clk */
+		svga_wseq_mask(VGABASE, 0x1E, 0x30, BIT(5) | BIT(4));
+		break;
+
+	case DISP_DI_DFPH:
+		/* Port 96 is used on older hardware for the DVP0 */
+		if ((dev->pdev->device != PCI_DEVICE_ID_VIA_VT3157) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VT1122) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VX875) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VX900))
+			svga_wcrt_mask(VGABASE, 0x96, value, BIT(4));
+
+		svga_wcrt_mask(VGABASE, 0x97, value, BIT(4));
+		/* Turn on DFPH clock */
+		svga_wseq_mask(VGABASE, 0x2A, 0x0C, BIT(3) | BIT(2));
+		break;
+
+	case DISP_DI_DFPL:
+		/* Port 9B is used on older hardware for the DVP1 */
+		if ((dev->pdev->device != PCI_DEVICE_ID_VIA_VT3157) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VT1122) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VX875) &&
+		    (dev->pdev->device != PCI_DEVICE_ID_VIA_VX900))
+			svga_wcrt_mask(VGABASE, 0x9B, value, BIT(4));
+
+		svga_wcrt_mask(VGABASE, 0x99, value, BIT(4));
+		/* Turn on DFPL clock */
+		svga_wseq_mask(VGABASE, 0x2A, 0x03, BIT(1) | BIT(0));
+		break;
+
+	case DISP_DI_DFP:
+		if ((dev->pdev->device == PCI_DEVICE_ID_VIA_K8M890) ||
+		    (dev->pdev->device == PCI_DEVICE_ID_VIA_VT3343))
+			svga_wcrt_mask(VGABASE, 0x97, 0x84,
+					BIT(7) | BIT(2) | BIT(1) | BIT(0));
+
+		svga_wcrt_mask(VGABASE, 0x97, value, BIT(4));
+		svga_wcrt_mask(VGABASE, 0x99, value, BIT(4));
+		/* Turn on DFP clk */
+		svga_wseq_mask(VGABASE, 0x2A, 0x0F, BIT(3) | BIT(2) | BIT(1) | BIT(0));
+		break;
+
+	/* For TTL Type LCD */
+	case (DISP_DI_DFPL + DISP_DI_DVP1):
+		svga_wcrt_mask(VGABASE, 0x99, value, BIT(4));
+		svga_wcrt_mask(VGABASE, 0x9B, value, BIT(4));
+
+		/* Turn on DFPL, DVP1 clk */
+		svga_wseq_mask(VGABASE, 0x2A, 0x03, BIT(1) | BIT(0));
+		svga_wseq_mask(VGABASE, 0x1E, 0x30, BIT(5) | BIT(4));
+		break;
+
+	/* For 409 TTL Type LCD */
+	case (DISP_DI_DFPH + DISP_DI_DFPL + DISP_DI_DVP1):
+		svga_wcrt_mask(VGABASE, 0x97, value, BIT(4));
+		svga_wcrt_mask(VGABASE, 0x99, value, BIT(4));
+		svga_wcrt_mask(VGABASE, 0x9B, value, BIT(4));
+
+		/* Turn on DFPHL, DVP1 clk */
+		svga_wseq_mask(VGABASE, 0x2A, 0x0F, BIT(3) | BIT(2) | BIT(1) | BIT(0));
+		svga_wseq_mask(VGABASE, 0x1E, 0x30, BIT(5) | BIT(4));
+		break;
+
+	case DISP_DI_DAC:
+		if (iga->index) value = BIT(6);
+
+		svga_wseq_mask(VGABASE, 0x16, value, BIT(6));
+		break;
+
+	default:
+		DRM_ERROR("Unsupported DIPort.\n");
+	case DISP_DI_NONE:
+		break;
+	}
+
+	/* Older chipsets only used CR91 to control all DI ports.
+	 * For newer chipsets (CX700 and above) CR91 and CRD3 are
+	 * used to control DVP0 and DVP1 seperately */
+	if (iga->index && dev->pdev->device != PCI_DEVICE_ID_VIA_VT3157)
+		svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(5));
+
+	/* Now turn on the display */
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+void
+via_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+
+	/* First turn off the display */
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	switch (enc->diPort) {
+	case DISP_DI_DVP0:
+		svga_wseq_mask(VGABASE, 0x1E, 0x00, BIT(7) | BIT(6));
+		break;
+
+	case DISP_DI_DVP1:
+		svga_wseq_mask(VGABASE, 0x1E, 0x00, BIT(5) | BIT(4));
+		break;
+
+	case DISP_DI_DFPH:
+		svga_wseq_mask(VGABASE, 0x2A, 0x00, BIT(3) | BIT(2));
+		break;
+
+	case DISP_DI_DFPL:
+		svga_wseq_mask(VGABASE, 0x2A, 0x00, BIT(1) | BIT(0));
+		break;
+
+	case DISP_DI_DFP:
+		svga_wseq_mask(VGABASE, 0x2A, 0x00,
+				BIT(3) | BIT(2) | BIT(1) | BIT(0));
+		break;
+
+	/* TTL LCD, Quanta case */
+	case DISP_DI_DFPL + DISP_DI_DVP1:
+		svga_wseq_mask(VGABASE, 0x1E, 0x00, BIT(5) | BIT(4));
+		svga_wseq_mask(VGABASE, 0x2A, 0x00, BIT(1) | BIT(0));
+		break;
+
+	case DISP_DI_DFPH + DISP_DI_DFPL + DISP_DI_DVP1:
+		svga_wseq_mask(VGABASE, 0x1E, 0x00, BIT(5) | BIT(4));
+		svga_wseq_mask(VGABASE, 0x2A, 0x00,
+				BIT(3) | BIT(2) | BIT(1) | BIT(0));
+		break;
+
+	case DISP_DI_DAC:
+		svga_wseq_mask(VGABASE, 0x16, 0x00, BIT(6));
+		break;
+
+	default:
+		DRM_ERROR("Unsupported DIPort.\n");
+	case DISP_DI_NONE:
+		break;
+	}
+}
+
+void
+via_set_sync_polarity(struct drm_encoder *encoder, struct drm_display_mode *mode,
+			struct drm_display_mode *adjusted_mode)
+{
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+	struct drm_via_private *dev_priv = encoder->dev->dev_private;
+	u8 syncreg = 0;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+		syncreg |= BIT(6);
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+		syncreg |= BIT(5);
+
+	switch (enc->diPort) {
+	case DISP_DI_DAC:
+		svga_wmisc_mask(VGABASE, (syncreg << 1), BIT(7) | BIT(6));
+		break;
+
+	case DISP_DI_DVP0:
+		svga_wcrt_mask(VGABASE, 0x96, syncreg, BIT(6) | BIT(5));
+		break;
+
+	case DISP_DI_DVP1:
+		svga_wcrt_mask(VGABASE, 0x9B, syncreg, BIT(6) | BIT(5));
+		break;
+
+	case DISP_DI_DFPH:
+		svga_wcrt_mask(VGABASE, 0x97, syncreg, BIT(6) | BIT(5));
+		break;
+
+	case DISP_DI_DFPL:
+		svga_wcrt_mask(VGABASE, 0x99, syncreg, BIT(6) | BIT(5));
+		break;
+
+	/* For TTL Type LCD */
+	case (DISP_DI_DFPL + DISP_DI_DVP1):
+		svga_wcrt_mask(VGABASE, 0x99, syncreg, BIT(6) | BIT(5));
+		svga_wcrt_mask(VGABASE, 0x9B, syncreg, BIT(6) | BIT(5));
+		break;
+
+	default:
+		DRM_ERROR("No DIPort.\n");
+	case DISP_DI_NONE:
+		break;
+	}
+}
+
+void
+via_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+struct drm_encoder *
+via_best_encoder(struct drm_connector *connector)
+{
+	int enc_id = connector->encoder_ids[0];
+	struct drm_encoder *encoder = NULL;
+	struct drm_mode_object *obj;
+
+	/* pick the encoder ids */
+	if (enc_id) {
+		obj = drm_mode_object_find(connector->dev, enc_id,
+						DRM_MODE_OBJECT_ENCODER);
+		if (obj)
+			encoder = obj_to_encoder(obj);
+	}
+	return encoder;
+}
+
+void via_encoder_cleanup(struct drm_encoder *encoder)
+{
+	struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+
+	drm_encoder_cleanup(encoder);
+	kfree(enc);
+}
+
+/*
+ * Shared connector routines.
+ */
+int
+via_get_edid_modes(struct drm_connector *connector)
+{
+	struct via_connector *con = container_of(connector, struct via_connector, base);
+	struct edid *edid = drm_get_edid(&con->base, con->ddc_bus);
+
+	return drm_add_edid_modes(connector, edid);
+}
+
+int
+via_connector_mode_valid(struct drm_connector *connector,
+			struct drm_display_mode *mode)
+{
+	if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+	    !connector->interlace_allowed)
+		return MODE_NO_INTERLACE;
+
+	if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
+	    !connector->doublescan_allowed)
+		return MODE_NO_DBLESCAN;
+
+	/* Check Clock Range */
+	if (mode->clock > 400000)
+		return MODE_CLOCK_HIGH;
+
+	if (mode->clock < 25000)
+		return MODE_CLOCK_LOW;
+
+	return MODE_OK;
+}
+
+int
+via_connector_set_property(struct drm_connector *connector,
+				struct drm_property *property, uint64_t value)
+{
+	struct drm_encoder *encoder = connector->encoder;
+	struct drm_encoder_helper_funcs *encoder_funcs;
+	struct drm_device *dev = connector->dev;
+
+	if (encoder) {
+		encoder_funcs = encoder->helper_private;
+
+		if (property == dev->mode_config.dpms_property)
+			encoder_funcs->dpms(encoder, (uint32_t)(value & 0xf));
+	}
+	return 0;
+}
+
+void
+via_connector_destroy(struct drm_connector *connector)
+{
+	drm_mode_connector_update_edid_property(connector, NULL);
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+/* 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 drm_via_private *dev_priv = dev->dev_private;
+	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 (dev->pci_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 void
+via_i2c_reg_init(struct drm_via_private *dev_priv)
+{
+	vga_wseq(VGABASE, 0x31, 0x01);
+	svga_wseq_mask(VGABASE, 0x31, 0x30, 0x30);
+	vga_wseq(VGABASE, 0x26, 0x01);
+	svga_wseq_mask(VGABASE, 0x26, 0x30, 0x30);
+	vga_wseq(VGABASE, 0x2C, 0xc2);
+	vga_wseq(VGABASE, 0x3D, 0xc0);
+	svga_wseq_mask(VGABASE, 0x2C, 0x30, 0x30);
+	svga_wseq_mask(VGABASE, 0x3D, 0x30, 0x30);
+}
+
+static void
+via_hwcursor_init(struct drm_via_private *dev_priv)
+{
+	/* set 0 as transparent color key */
+	VIA_WRITE(PRIM_HI_TRANSCOLOR, 0);
+	VIA_WRITE(PRIM_HI_FIFO, 0x0D000D0F);
+	VIA_WRITE(PRIM_HI_INVTCOLOR, 0X00FFFFFF);
+	VIA_WRITE(V327_HI_INVTCOLOR, 0X00FFFFFF);
+
+	/* set 0 as transparent color key */
+	VIA_WRITE(HI_TRANSPARENT_COLOR, 0);
+	VIA_WRITE(HI_INVTCOLOR, 0X00FFFFFF);
+	VIA_WRITE(ALPHA_V3_PREFIFO_CONTROL, 0xE0000);
+	VIA_WRITE(ALPHA_V3_FIFO_CONTROL, 0xE0F0000);
+
+	/* Turn cursor off. */
+	VIA_WRITE(HI_CONTROL, VIA_READ(HI_CONTROL) & 0xFFFFFFFA);
+}
+
+static void
+via_init_crtc_regs(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+
+	via_unlock_crtc(VGABASE, dev->pdev->device);
+
+	/* always set to 1 */
+	svga_wcrt_mask(VGABASE, 0x03, BIT(7), BIT(7));
+	/* bits 0 to 7 of line compare */
+	vga_wcrt(VGABASE, 0x18, 0xFF);
+	/* bit 8 of line compare */
+	svga_wcrt_mask(VGABASE, 0x07, BIT(4), BIT(4));
+	/* bit 9 of line compare */
+	svga_wcrt_mask(VGABASE, 0x09, BIT(6), BIT(6));
+	/* bit 10 of line compare */
+	svga_wcrt_mask(VGABASE, 0x35, BIT(4), BIT(4));
+	/* adjust hsync by one character - value 011 */
+	svga_wcrt_mask(VGABASE, 0x33, 0x06, 0x07);
+	/* extend mode always set to e3h */
+	vga_wcrt(VGABASE, 0x17, 0xE3);
+	/* extend mode always set to 0h */
+	vga_wcrt(VGABASE, 0x08, 0x00);
+	/* extend mode always set to 0h */
+	vga_wcrt(VGABASE, 0x14, 0x00);
+
+	/* If K8M800, enable Prefetch Mode. */
+	if ((dev->pdev->device == PCI_DEVICE_ID_VIA_K8M800) ||
+	    (dev->pdev->device == PCI_DEVICE_ID_VIA_K8M890))
+		svga_wcrt_mask(VGABASE, 0x33, 0x00, BIT(3));
+
+	if ((dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266) &&
+	    (dev_priv->revision == CLE266_REVISION_AX))
+		svga_wseq_mask(VGABASE, 0x1A, BIT(1), BIT(1));
+
+	via_lock_crtc(VGABASE);
+}
+
+static void
+via_display_init(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	u8 index = 0x3D, value;
+
+	/* Check if spread spectrum is enabled */
+	if (dev->pci_device == PCI_DEVICE_ID_VIA_VX900)
+		index = 0x2C;
+
+	value = vga_rseq(VGABASE, 0x1E);
+	if (value & BIT(3)) {
+		value = vga_rseq(VGABASE, index);
+		vga_wseq(VGABASE, index, value & 0xFE);
+
+		value = vga_rseq(VGABASE, 0x1E);
+		vga_wseq(VGABASE, 0x1E, value & 0xF7);
+
+		dev_priv->spread_spectrum = true;
+	} else
+		dev_priv->spread_spectrum = false;
+
+	/* Load fixed CRTC timing registers */
+	via_init_crtc_regs(dev);
+
+	/* Init TD timing register (power sequence) */
+	via_init_td_timing_regs(dev);
+
+	/* I/O address bit to be 1. Enable access */
+	/* to frame buffer at A0000-BFFFFh */
+	svga_wmisc_mask(VGABASE, BIT(0), BIT(0));
+}
+
+int
+via_modeset_init(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	int i;
+
+	drm_mode_config_init(dev);
+
+	/* What is the max ? */
+	dev->mode_config.min_width = 320;
+	dev->mode_config.min_height = 200;
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+
+	via_display_init(dev);
+	via_i2c_reg_init(dev_priv);
+	via_i2c_init(dev);
+	via_hwcursor_init(dev_priv);
+
+	for (i = 0; i < 2; i++)
+		via_crtc_init(dev, i);
+
+	via_analog_init(dev);
+
+	via_lvds_init(dev);
+
+	switch (dev->pdev->device) {
+	/* CX700 can support HDMI and non HDMI based DVI ports */
+	case PCI_DEVICE_ID_VIA_VT3157:
+		if (!via_tmds_init(dev))
+			break;
+
+	/* Newer platforms use HDMI encoder */
+	case PCI_DEVICE_ID_VIA_VT1122:
+	case PCI_DEVICE_ID_VIA_VX875:
+	case PCI_DEVICE_ID_VIA_VX900:
+		via_hdmi_init(dev, DISP_DI_NONE);
+		break;
+
+	default:
+		via_tmds_init(dev);
+		break;
+	}
+
+	/*
+	 * Set up the framebuffer device
+	 */
+	return via_framebuffer_init(dev, &dev_priv->helper);
+}
+
+void via_modeset_fini(struct drm_device *dev)
+{
+	struct drm_connector *connector, *ot;
+	struct drm_encoder *encoder, *enct;
+
+	drm_kms_helper_poll_fini(dev);
+	via_framebuffer_fini(dev);
+
+	/* drm_mode_config_init has not been called yet */
+	if (!dev->mode_config.dpms_property)
+		return;
+
+	/* We need to cleanup the connectors before the encoders */
+	list_for_each_entry_safe(connector, ot,
+				&dev->mode_config.connector_list, head)
+		connector->funcs->destroy(connector);
+
+	list_for_each_entry_safe(encoder, enct,
+				&dev->mode_config.encoder_list, head)
+		encoder->funcs->destroy(encoder);
+
+	drm_mode_config_cleanup(dev);
+
+	via_i2c_exit();
+}
diff --git a/drivers/gpu/drm/via/via_display.h b/drivers/gpu/drm/via/via_display.h
new file mode 100644
index 0000000..a4da6c8
--- /dev/null
+++ b/drivers/gpu/drm/via/via_display.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2012 James Simmons <jsimmons at infradead.org> 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.
+ */
+#ifndef _VIA_DISPLAY_H_
+#define _VIA_DISPLAY_H_
+
+#include <video/vga.h>
+#include "crtc_hw.h"
+
+#include "drm_edid.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
+
+/* IGA Scaling disable */
+#define VIA_NO_SCALING	0
+
+/* IGA Scaling down */
+#define VIA_HOR_SHRINK	BIT(0)
+#define VIA_VER_SHRINK	BIT(1)
+#define VIA_SHRINK	(BIT(0) | BIT(1))
+
+/* IGA Scaling up */
+#define VIA_HOR_EXPAND	BIT(2)
+#define VIA_VER_EXPAND	BIT(3)
+#define VIA_EXPAND	(BIT(2) | BIT(3))
+
+/* Define IGA Scaling up/down status :  Horizontal or Vertical  */
+/* Is IGA Hor scaling up/down status */
+#define	HOR_SCALE	BIT(0)
+/* Is IGA Ver scaling up/down status */
+#define	VER_SCALE	BIT(1)
+/* Is IGA Hor and Ver scaling up/down status */
+#define	HOR_VER_SCALE	(BIT(0) | BIT(1))
+
+struct via_crtc {
+	struct drm_crtc base;
+	struct ttm_bo_kmap_obj cursor_kmap;
+	struct crtc_timings pixel_timings;
+	struct crtc_timings timings;
+	unsigned int display_queue_expire_num;
+	unsigned int fifo_high_threshold;
+	unsigned int fifo_threshold;
+	unsigned int fifo_max_depth;
+	struct vga_registers display_queue;
+	struct vga_registers high_threshold;
+	struct vga_registers threshold;
+	struct vga_registers fifo_depth;
+	struct vga_registers offset;
+	struct vga_registers fetch;
+	int scaling_mode;
+	uint8_t index;
+};
+
+struct via_connector {
+	struct drm_connector base;
+	struct i2c_adapter *ddc_bus;
+	uint32_t flags;
+};
+
+#define DISP_DI_NONE		0x00
+#define DISP_DI_DVP0		BIT(0)
+#define DISP_DI_DVP1		BIT(1)
+#define DISP_DI_DFPL		BIT(2)
+#define DISP_DI_DFPH		BIT(3)
+#define DISP_DI_DFP		BIT(4)
+#define DISP_DI_DAC		BIT(5)
+
+struct via_encoder {
+	struct drm_encoder base;
+	uint32_t flags;
+	int diPort;
+	struct via_connector cons[];
+};
+
+static inline void
+via_lock_crtc(void __iomem *regs)
+{
+	svga_wcrt_mask(regs, 0x11, BIT(7), BIT(7));
+}
+
+static inline void
+via_unlock_crtc(void __iomem *regs, int pci_id)
+{
+	u8 mask = BIT(0);
+
+	svga_wcrt_mask(regs, 0x11, 0, BIT(7));
+	if ((pci_id == PCI_DEVICE_ID_VIA_VX875) ||
+	    (pci_id == PCI_DEVICE_ID_VIA_VX900))
+		mask = BIT(4);
+	svga_wcrt_mask(regs, 0x47, 0, mask);
+}
+
+static inline void
+enable_second_display_channel(void __iomem *regs)
+{
+	svga_wcrt_mask(regs, 0x6A, BIT(7), BIT(7));
+}
+
+static inline void
+disable_second_display_channel(void __iomem *regs)
+{
+	svga_wcrt_mask(regs, 0x6A, 0x00, BIT(7));
+}
+
+/* display */
+extern int via_modeset_init(struct drm_device *dev);
+extern void via_modeset_fini(struct drm_device *dev);
+
+/* i2c */
+extern struct i2c_adapter *via_find_ddc_bus(int port);
+extern int via_i2c_init(struct drm_device *dev);
+extern void via_i2c_exit(void);
+
+/* clock */
+extern u32 via_get_clk_value(struct drm_device *dev, u32 clk);
+extern void via_set_vclock(struct drm_crtc *crtc, u32 clk);
+
+/* framebuffers */
+extern int via_framebuffer_init(struct drm_device *dev, struct drm_fb_helper **ptr);
+extern void via_framebuffer_fini(struct drm_device *dev);
+
+/* crtc */
+extern void via_load_crtc_pixel_timing(struct drm_crtc *crtc,
+					struct drm_display_mode *mode);
+extern void via_crtc_init(struct drm_device *dev, int index);
+
+/* encoders */
+extern void via_set_sync_polarity(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode);
+extern struct drm_encoder* via_best_encoder(struct drm_connector *connector);
+extern void via_encoder_cleanup(struct drm_encoder *encoder);
+extern void via_encoder_prepare(struct drm_encoder *encoder);
+extern void via_encoder_disable(struct drm_encoder *encoder);
+extern void via_encoder_commit(struct drm_encoder *encoder);
+
+/* connectors */
+extern int via_connector_set_property(struct drm_connector *connector,
+					struct drm_property *property,
+					uint64_t value);
+extern int via_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode);
+extern void via_connector_destroy(struct drm_connector *connector);
+extern int via_get_edid_modes(struct drm_connector *connector);
+
+extern void via_hdmi_init(struct drm_device *dev, int diPort);
+extern void via_analog_init(struct drm_device *dev);
+extern void via_lvds_init(struct drm_device *dev);
+extern int via_tmds_init(struct drm_device *dev);
+
+#endif


More information about the dri-devel mailing list