[Openchrome-devel] drm-openchrome: drivers/gpu/drm
Kevin Brace
kevinbrace at kemper.freedesktop.org
Mon Dec 5 10:25:17 UTC 2016
drivers/gpu/drm/via/Makefile | 2
drivers/gpu/drm/via/via_fp.c | 800 +++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/via/via_lvds.c | 800 -----------------------------------------
3 files changed, 801 insertions(+), 801 deletions(-)
New commits:
commit 731df0a882bbdabc0b690cb2332d2ef1265cadc6
Author: Kevin Brace <kevinbrace at gmx.com>
Date: Mon Dec 5 02:23:23 2016 -0800
Replacing via_lvds.c with via_fp.c
Signed-off-by: Kevin Brace <kevinbrace at gmx.com>
diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile
index a0479bb..0da93af 100644
--- a/drivers/gpu/drm/via/Makefile
+++ b/drivers/gpu/drm/via/Makefile
@@ -7,6 +7,6 @@ via-y := via_drv.o via_pm.o via_i2c.o via_irq.o via_verifier.o via_ioc32.o \
init_ttm.o ttm_gem.o via_ttm.o via_fence.o via_sgdma.o \
via_h1_dma.o via_h1_cmdbuf.o \
via_display.o via_crtc.o via_fb.o crtc_hw.o via_clocks.o \
- via_analog.o via_lvds.o via_tmds.o via_hdmi.o
+ via_analog.o via_fp.o via_tmds.o via_hdmi.o
obj-$(CONFIG_DRM_VIA) += via.o
diff --git a/drivers/gpu/drm/via/via_fp.c b/drivers/gpu/drm/via/via_fp.c
new file mode 100644
index 0000000..c85c708
--- /dev/null
+++ b/drivers/gpu/drm/via/via_fp.c
@@ -0,0 +1,800 @@
+/*
+ * 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.
+ */
+#include <linux/dmi.h>
+#include <asm/olpc.h>
+
+#include "via_drv.h"
+
+/* Encoder flags for LVDS */
+#define LVDS_DUAL_CHANNEL 1
+
+/* 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_enable_internal_lvds(struct drm_encoder *encoder)
+{
+ struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+ struct drm_via_private *dev_priv = encoder->dev->dev_private;
+ struct drm_device *dev = encoder->dev;
+
+ /* Turn on LCD panel */
+ if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
+ if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
+ /* Software control power sequence ON */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7));
+ svga_wcrt_mask(VGABASE, 0x91, BIT(0), BIT(0));
+ /* Delay td0 msec. */
+ mdelay(200);
+ /* VDD ON */
+ svga_wcrt_mask(VGABASE, 0x91, BIT(4), BIT(4));
+ /* Delay td1 msec. */
+ mdelay(25);
+ /* DATA ON */
+ svga_wcrt_mask(VGABASE, 0x91, BIT(3), BIT(3));
+ /* VEE ON (unused on vt3353) */
+ svga_wcrt_mask(VGABASE, 0x91, BIT(2), BIT(2));
+ /* Delay td3 msec. */
+ mdelay(250);
+ /* Back-Light ON */
+ svga_wcrt_mask(VGABASE, 0x91, BIT(1), BIT(1));
+ } else {
+ /* Use first power sequence control: *
+ * Use hardware control power sequence. */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(0));
+ /* Turn on back light and panel path. */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7) | BIT(6));
+ /* Turn on hardware power sequence. */
+ svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
+ }
+ }
+
+ if (enc->diPort & DISP_DI_DFPH) {
+ if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
+ /* Software control power sequence ON */
+ svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
+ svga_wcrt_mask(VGABASE, 0xD3, BIT(0), BIT(0));
+ /* Delay td0 msec. */
+ mdelay(200);
+ /* VDD ON */
+ svga_wcrt_mask(VGABASE, 0xD3, BIT(4), BIT(4));
+ /* Delay td1 msec. */
+ mdelay(25);
+ /* DATA ON */
+ svga_wcrt_mask(VGABASE, 0xD3, BIT(3), BIT(3));
+ /* VEE ON (unused on vt3353) */
+ svga_wcrt_mask(VGABASE, 0xD3, BIT(2), BIT(2));
+ /* Delay td3 msec. */
+ mdelay(250);
+ /* Back-Light ON */
+ svga_wcrt_mask(VGABASE, 0xD3, BIT(1), BIT(1));
+ } else {
+ /* Use hardware control power sequence. */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(0));
+ /* Turn on back light and panel path. */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(7) | BIT(6));
+ /* Turn on hardware power sequence. */
+ svga_wcrt_mask(VGABASE, 0xD4, BIT(1), BIT(1));
+ }
+ }
+
+ /* Power on LVDS channel. */
+ if (enc->flags & LVDS_DUAL_CHANNEL) {
+ /* For high resolution LCD (internal),
+ * power on both LVDS0 and LVDS1 */
+ svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7) | BIT(6));
+ } else {
+ if (enc->diPort & DISP_DI_DFPL)
+ svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7));
+ else if (enc->diPort & DISP_DI_DFPH)
+ svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(6));
+ }
+}
+
+static void
+via_disable_internal_lvds(struct drm_encoder *encoder)
+{
+ struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
+ struct drm_via_private *dev_priv = encoder->dev->dev_private;
+ struct drm_device *dev = encoder->dev;
+
+ /* Turn off LCD panel */
+ if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
+ /* Set LCD software power sequence off */
+ if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
+ /* Back-Light OFF */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(1));
+ /* Delay td3 msec. */
+ mdelay(250);
+ /* VEE OFF (unused on vt3353) */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(2));
+ /* DATA OFF */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(3));
+ /* Delay td1 msec. */
+ mdelay(25);
+ /* VDD OFF */
+ svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(4));
+ } else {
+ /* Use first power sequence control: *
+ * Turn off power sequence. */
+ svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(3));
+
+ /* Turn off back light and panel path. */
+ svga_wcrt_mask(VGABASE, 0x91, 0xC0, BIT(7) | BIT(6));
+ }
+ }
+
+ if (enc->diPort & DISP_DI_DFPH) {
+ /* Set LCD software power sequence off */
+ if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
+ /* Back-Light OFF */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(1));
+ /* Delay td3 msec. */
+ mdelay(250);
+ /* VEE OFF */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(2));
+ /* DATA OFF */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(3));
+ /* Delay td1 msec. */
+ mdelay(25);
+ /* VDD OFF */
+ svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(4));
+ } else {
+ /* Use second power sequence control: *
+ * Turn off power sequence. */
+ svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
+ /* Turn off back light and panel path. */
+ svga_wcrt_mask(VGABASE, 0xD3, 0xC0, BIT(7) | BIT(6));
+ }
+ }
+
+ /* Power off LVDS channel. */
+ if (enc->flags & LVDS_DUAL_CHANNEL) {
+ /* For high resolution LCD (internal) we
+ * power off both LVDS0 and LVDS1 */
+ svga_wcrt_mask(VGABASE, 0xD2, 0xC0, BIT(7) | BIT(6));
+ } else {
+ if (enc->diPort & DISP_DI_DFPL)
+ svga_wcrt_mask(VGABASE, 0xD2, BIT(7), BIT(7));
+ else if (enc->diPort & DISP_DI_DFPH)
+ svga_wcrt_mask(VGABASE, 0xD2, BIT(6), BIT(6));
+ }
+}
+
+static void
+via_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_via_private *dev_priv = encoder->dev->dev_private;
+ struct drm_device *dev = encoder->dev;
+ struct via_crtc *iga = NULL;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (encoder->crtc == NULL)
+ return;
+ iga = container_of(encoder->crtc, struct via_crtc, base);
+
+ /* when using the EPIA-EX board, if we do not set this bit,
+ * light LCD will failed in nonRandR structure,
+ * So, when light LCD this bit is always setted */
+ svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
+
+ if (dev_priv->spread_spectrum) {
+ if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_VX875) ||
+ (dev->pdev->device == PCI_DEVICE_ID_VIA_VX900_VGA)) {
+ /* GPIO-4/5 are used for spread spectrum,
+ * we must clear SR3D[7:6] to disable
+ * GPIO-4/5 output */
+ svga_wseq_mask(VGABASE, 0x3D, BIT(0), 0xC1);
+ } else {
+ svga_wseq_mask(VGABASE, 0x2C, BIT(0), BIT(0));
+ }
+ svga_wseq_mask(VGABASE, 0x1E, BIT(3), BIT(3));
+ }
+ via_enable_internal_lvds(encoder);
+ break;
+
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ via_disable_internal_lvds(encoder);
+ break;
+ }
+}
+
+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;
+}
+
+const struct drm_encoder_helper_funcs via_lvds_helper_funcs = {
+ .dpms = via_lvds_dpms,
+ .mode_fixup = via_lvds_mode_fixup,
+ .mode_set = via_set_sync_polarity,
+ .prepare = via_encoder_prepare,
+ .commit = via_encoder_commit,
+ .disable = via_encoder_disable,
+};
+
+const struct drm_encoder_funcs via_lvds_enc_funcs = {
+ .destroy = via_encoder_cleanup,
+};
+
+/* detect this connector connect status */
+static enum drm_connector_status
+via_lcd_detect(struct drm_connector *connector, bool force)
+{
+ struct via_connector *con = container_of(connector, struct via_connector, base);
+ enum drm_connector_status ret = connector_status_disconnected;
+ struct edid *edid = drm_get_edid(&con->base, con->ddc_bus);
+
+ if (edid) {
+ drm_mode_connector_update_edid_property(&con->base, edid);
+ kfree(edid);
+ ret = connector_status_connected;
+ } else {
+ struct drm_via_private *dev_priv = connector->dev->dev_private;
+ u8 mask = BIT(1);
+
+ if (connector->dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)
+ mask = BIT(3);
+
+ if (vga_rcrt(VGABASE, 0x3B) & mask)
+ ret = connector_status_connected;
+
+ if (machine_is_olpc())
+ ret = connector_status_connected;
+ }
+ return ret;
+}
+
+static const struct drm_prop_enum_list dithering_enum_list[] =
+{
+ { DRM_MODE_DITHERING_OFF, "Off" },
+ { DRM_MODE_DITHERING_ON, "On" },
+ { DRM_MODE_DITHERING_AUTO, "Automatic" },
+};
+
+static int
+via_lcd_set_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t value)
+{
+ struct via_connector *con = container_of(connector, struct via_connector, base);
+ struct drm_via_private *dev_priv = connector->dev->dev_private;
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop;
+ uint64_t orig;
+ int ret;
+
+ ret = drm_object_property_get_value(&connector->base, property, &orig);
+ if (!ret && (orig != value)) {
+ if (property == dev->mode_config.scaling_mode_property) {
+ switch (value) {
+ 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:
+ return -EINVAL;
+ }
+ }
+
+ list_for_each_entry(prop, &con->props, head) {
+ if (property == prop) {
+ u8 reg_value;
+
+ switch (value) {
+ case DRM_MODE_DITHERING_AUTO:
+ case DRM_MODE_DITHERING_ON:
+ reg_value = BIT(0);
+ break;
+
+ case DRM_MODE_DITHERING_OFF:
+ reg_value = 0x00;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ svga_wcrt_mask(VGABASE, 0x88, reg_value, BIT(0));
+ }
+ }
+ }
+ return 0;
+}
+
+struct drm_connector_funcs via_lcd_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = via_lcd_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = via_lcd_set_property,
+ .destroy = via_connector_destroy,
+};
+
+static int
+via_lcd_get_modes(struct drm_connector *connector)
+{
+ int count = via_get_edid_modes(connector);
+
+ /* If no edid then we detect the mode using
+ * the scratch pad registers. */
+ if (!count) {
+ struct drm_display_mode *native_mode = NULL;
+ struct drm_device *dev = connector->dev;
+
+ /* 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->vrefresh = 50;
+ native_mode->hsync = 0;
+ } else {
+ struct drm_via_private *dev_priv = dev->dev_private;
+ u8 reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F);
+ int hdisplay = 0, vdisplay = 0;
+
+ switch (reg_value) {
+ case 0x00:
+ hdisplay = 640;
+ vdisplay = 480;
+ break;
+
+ case 0x01:
+ hdisplay = 800;
+ vdisplay = 600;
+ break;
+
+ case 0x02:
+ hdisplay = 1024;
+ vdisplay = 768;
+ break;
+
+ case 0x03:
+ hdisplay = 1280;
+ vdisplay = 768;
+ break;
+
+ case 0x04:
+ hdisplay = 1280;
+ vdisplay = 1024;
+ break;
+
+ case 0x05:
+ hdisplay = 1400;
+ vdisplay = 1050;
+ break;
+
+ case 0x06:
+ hdisplay = 1440;
+ vdisplay = 900;
+ break;
+
+ case 0x07:
+ hdisplay = 1280;
+ vdisplay = 800;
+ break;
+
+ case 0x08:
+ hdisplay = 800;
+ vdisplay = 480;
+ break;
+
+ case 0x09:
+ hdisplay = 1024;
+ vdisplay = 600;
+ break;
+
+ case 0x0A:
+ hdisplay = 1366;
+ vdisplay = 768;
+ break;
+
+ case 0x0B:
+ hdisplay = 1600;
+ vdisplay = 1200;
+ break;
+
+ case 0x0C:
+ hdisplay = 1280;
+ vdisplay = 768;
+ break;
+
+ case 0x0D:
+ hdisplay = 1280;
+ vdisplay = 1024;
+ break;
+
+ case 0x0E:
+ hdisplay = 1600;
+ vdisplay = 1200;
+ break;
+
+ case 0x0F:
+ hdisplay = 480;
+ vdisplay = 640;
+ break;
+
+ default:
+ break;
+ }
+
+ 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;
+ }
+ }
+ return count;
+}
+
+static int
+via_lcd_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;
+ 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 (dev->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_lcd_connector_helper_funcs = {
+ .get_modes = via_lcd_get_modes,
+ .mode_valid = via_lcd_mode_valid,
+ .best_encoder = via_best_encoder,
+};
+
+static int __init via_ttl_lvds_dmi_callback(const struct dmi_system_id *id)
+{
+ DRM_INFO("LVDS is TTL type for %s\n", id->ident);
+ return 1;
+}
+
+static const struct dmi_system_id via_ttl_lvds[] = {
+ {
+ .callback = via_ttl_lvds_dmi_callback,
+ .ident = "VIA Quanta Netbook",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "QCI"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "VT6413A"),
+ },
+ }, {
+ .callback = via_ttl_lvds_dmi_callback,
+ .ident = "Amilo Pro V2030",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
+ },
+ },
+
+ { }
+};
+
+void
+via_lvds_init(struct drm_device *dev)
+{
+ struct drm_via_private *dev_priv = dev->dev_private;
+ bool dual_channel = false, is_msb = false;
+ uint64_t dither = DRM_MODE_DITHERING_OFF;
+ struct drm_property *dithering;
+ struct via_connector *con;
+ struct via_encoder *enc;
+ struct edid *edid;
+ u8 reg_value;
+
+ enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL);
+ if (!enc) {
+ DRM_INFO("Failed to allocate LVDS output\n");
+ return;
+ }
+ con = &enc->cons[0];
+ INIT_LIST_HEAD(&con->props);
+
+ drm_connector_init(dev, &con->base, &via_lcd_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ drm_connector_helper_add(&con->base, &via_lcd_connector_helper_funcs);
+ drm_connector_register(&con->base);
+
+ switch (dev->pdev->device) {
+ case PCI_DEVICE_ID_VIA_VX875:
+ case PCI_DEVICE_ID_VIA_VX900_VGA:
+ con->ddc_bus = via_find_ddc_bus(0x2C);
+ break;
+ default:
+ con->ddc_bus = via_find_ddc_bus(0x31);
+ break;
+ }
+
+ edid = drm_get_edid(&con->base, con->ddc_bus);
+ if (!edid) {
+ if (!machine_is_olpc()) {
+ u8 mask = BIT(1);
+
+ if (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)
+ mask = BIT(3);
+
+ /* First we have to make sure a LVDS is present */
+ reg_value = (vga_rcrt(VGABASE, 0x3B) & mask);
+ if (!reg_value)
+ goto no_device;
+
+ /* If no edid then we detect the mode using
+ * the scratch pad registers. */
+ reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F);
+
+ switch (reg_value) {
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x09:
+ case 0x0B:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ dual_channel = true;
+ break;
+
+ default:
+ break;
+ }
+
+ DRM_DEBUG("panel index %x detected\n", reg_value);
+
+ if (reg_value < 0x0A)
+ dither = DRM_MODE_DITHERING_ON;
+ }
+ } else {
+ /* 00 LVDS1 + LVDS2 10 = Dual channel. Other are reserved */
+ if ((vga_rseq(VGABASE, 0x13) >> 6) == 2)
+ dual_channel = true;
+
+ kfree(edid);
+ }
+ con->base.doublescan_allowed = false;
+ con->base.interlace_allowed = false;
+
+ drm_mode_create_scaling_mode_property(dev);
+ drm_object_attach_property(&con->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_CENTER);
+
+ dithering = drm_property_create_enum(dev, 0, "dithering",
+ dithering_enum_list,
+ ARRAY_SIZE(dithering_enum_list));
+ list_add(&dithering->head, &con->props);
+
+ drm_object_attach_property(&con->base.base, dithering, dither);
+ via_lcd_set_property(&con->base, dithering, dither);
+
+ /* Now setup the encoder */
+ drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+ drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs);
+
+ enc->base.possible_crtcs = BIT(1);
+
+ switch (dev->pdev->device) {
+ case PCI_DEVICE_ID_VIA_CLE266:
+ enc->diPort = DISP_DI_DVP1;
+ break;
+
+ case PCI_DEVICE_ID_VIA_VX875:
+ case PCI_DEVICE_ID_VIA_VX900_VGA:
+ enc->diPort = DISP_DI_DFPL;
+ break;
+
+ default:
+ enc->diPort = DISP_DI_DFPH;
+ break;
+ }
+
+ /* There has to be a way to detect TTL LVDS
+ * For now we use the DMI to handle this */
+ if (dmi_check_system(via_ttl_lvds))
+ enc->diPort = DISP_DI_DFPL | DISP_DI_DVP1;
+
+ reg_value = 0x00;
+ if (enc->diPort == DISP_DI_DFPH) {
+ if (!is_msb)
+ reg_value = BIT(0);
+ svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(0));
+ } else if (enc->diPort == DISP_DI_DFPL) {
+ if (!is_msb)
+ reg_value = BIT(1);
+ svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(1));
+ }
+
+ if (dual_channel)
+ enc->flags |= LVDS_DUAL_CHANNEL;
+
+ /* Put it all together */
+ drm_mode_connector_attach_encoder(&con->base, &enc->base);
+ return;
+
+no_device:
+ drm_connector_unregister(&con->base);
+ drm_connector_cleanup(&con->base);
+ kfree(enc);
+}
diff --git a/drivers/gpu/drm/via/via_lvds.c b/drivers/gpu/drm/via/via_lvds.c
deleted file mode 100644
index c85c708..0000000
--- a/drivers/gpu/drm/via/via_lvds.c
+++ /dev/null
@@ -1,800 +0,0 @@
-/*
- * 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.
- */
-#include <linux/dmi.h>
-#include <asm/olpc.h>
-
-#include "via_drv.h"
-
-/* Encoder flags for LVDS */
-#define LVDS_DUAL_CHANNEL 1
-
-/* 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_enable_internal_lvds(struct drm_encoder *encoder)
-{
- struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
- struct drm_via_private *dev_priv = encoder->dev->dev_private;
- struct drm_device *dev = encoder->dev;
-
- /* Turn on LCD panel */
- if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
- if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
- /* Software control power sequence ON */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7));
- svga_wcrt_mask(VGABASE, 0x91, BIT(0), BIT(0));
- /* Delay td0 msec. */
- mdelay(200);
- /* VDD ON */
- svga_wcrt_mask(VGABASE, 0x91, BIT(4), BIT(4));
- /* Delay td1 msec. */
- mdelay(25);
- /* DATA ON */
- svga_wcrt_mask(VGABASE, 0x91, BIT(3), BIT(3));
- /* VEE ON (unused on vt3353) */
- svga_wcrt_mask(VGABASE, 0x91, BIT(2), BIT(2));
- /* Delay td3 msec. */
- mdelay(250);
- /* Back-Light ON */
- svga_wcrt_mask(VGABASE, 0x91, BIT(1), BIT(1));
- } else {
- /* Use first power sequence control: *
- * Use hardware control power sequence. */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(0));
- /* Turn on back light and panel path. */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7) | BIT(6));
- /* Turn on hardware power sequence. */
- svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
- }
- }
-
- if (enc->diPort & DISP_DI_DFPH) {
- if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
- /* Software control power sequence ON */
- svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
- svga_wcrt_mask(VGABASE, 0xD3, BIT(0), BIT(0));
- /* Delay td0 msec. */
- mdelay(200);
- /* VDD ON */
- svga_wcrt_mask(VGABASE, 0xD3, BIT(4), BIT(4));
- /* Delay td1 msec. */
- mdelay(25);
- /* DATA ON */
- svga_wcrt_mask(VGABASE, 0xD3, BIT(3), BIT(3));
- /* VEE ON (unused on vt3353) */
- svga_wcrt_mask(VGABASE, 0xD3, BIT(2), BIT(2));
- /* Delay td3 msec. */
- mdelay(250);
- /* Back-Light ON */
- svga_wcrt_mask(VGABASE, 0xD3, BIT(1), BIT(1));
- } else {
- /* Use hardware control power sequence. */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(0));
- /* Turn on back light and panel path. */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(7) | BIT(6));
- /* Turn on hardware power sequence. */
- svga_wcrt_mask(VGABASE, 0xD4, BIT(1), BIT(1));
- }
- }
-
- /* Power on LVDS channel. */
- if (enc->flags & LVDS_DUAL_CHANNEL) {
- /* For high resolution LCD (internal),
- * power on both LVDS0 and LVDS1 */
- svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7) | BIT(6));
- } else {
- if (enc->diPort & DISP_DI_DFPL)
- svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7));
- else if (enc->diPort & DISP_DI_DFPH)
- svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(6));
- }
-}
-
-static void
-via_disable_internal_lvds(struct drm_encoder *encoder)
-{
- struct via_encoder *enc = container_of(encoder, struct via_encoder, base);
- struct drm_via_private *dev_priv = encoder->dev->dev_private;
- struct drm_device *dev = encoder->dev;
-
- /* Turn off LCD panel */
- if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) {
- /* Set LCD software power sequence off */
- if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
- /* Back-Light OFF */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(1));
- /* Delay td3 msec. */
- mdelay(250);
- /* VEE OFF (unused on vt3353) */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(2));
- /* DATA OFF */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(3));
- /* Delay td1 msec. */
- mdelay(25);
- /* VDD OFF */
- svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(4));
- } else {
- /* Use first power sequence control: *
- * Turn off power sequence. */
- svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(3));
-
- /* Turn off back light and panel path. */
- svga_wcrt_mask(VGABASE, 0x91, 0xC0, BIT(7) | BIT(6));
- }
- }
-
- if (enc->diPort & DISP_DI_DFPH) {
- /* Set LCD software power sequence off */
- if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)) {
- /* Back-Light OFF */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(1));
- /* Delay td3 msec. */
- mdelay(250);
- /* VEE OFF */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(2));
- /* DATA OFF */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(3));
- /* Delay td1 msec. */
- mdelay(25);
- /* VDD OFF */
- svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(4));
- } else {
- /* Use second power sequence control: *
- * Turn off power sequence. */
- svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1));
- /* Turn off back light and panel path. */
- svga_wcrt_mask(VGABASE, 0xD3, 0xC0, BIT(7) | BIT(6));
- }
- }
-
- /* Power off LVDS channel. */
- if (enc->flags & LVDS_DUAL_CHANNEL) {
- /* For high resolution LCD (internal) we
- * power off both LVDS0 and LVDS1 */
- svga_wcrt_mask(VGABASE, 0xD2, 0xC0, BIT(7) | BIT(6));
- } else {
- if (enc->diPort & DISP_DI_DFPL)
- svga_wcrt_mask(VGABASE, 0xD2, BIT(7), BIT(7));
- else if (enc->diPort & DISP_DI_DFPH)
- svga_wcrt_mask(VGABASE, 0xD2, BIT(6), BIT(6));
- }
-}
-
-static void
-via_lvds_dpms(struct drm_encoder *encoder, int mode)
-{
- struct drm_via_private *dev_priv = encoder->dev->dev_private;
- struct drm_device *dev = encoder->dev;
- struct via_crtc *iga = NULL;
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- if (encoder->crtc == NULL)
- return;
- iga = container_of(encoder->crtc, struct via_crtc, base);
-
- /* when using the EPIA-EX board, if we do not set this bit,
- * light LCD will failed in nonRandR structure,
- * So, when light LCD this bit is always setted */
- svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3));
-
- if (dev_priv->spread_spectrum) {
- if ((dev->pdev->device == PCI_DEVICE_ID_VIA_VT1122) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_VX875) ||
- (dev->pdev->device == PCI_DEVICE_ID_VIA_VX900_VGA)) {
- /* GPIO-4/5 are used for spread spectrum,
- * we must clear SR3D[7:6] to disable
- * GPIO-4/5 output */
- svga_wseq_mask(VGABASE, 0x3D, BIT(0), 0xC1);
- } else {
- svga_wseq_mask(VGABASE, 0x2C, BIT(0), BIT(0));
- }
- svga_wseq_mask(VGABASE, 0x1E, BIT(3), BIT(3));
- }
- via_enable_internal_lvds(encoder);
- break;
-
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- via_disable_internal_lvds(encoder);
- break;
- }
-}
-
-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;
-}
-
-const struct drm_encoder_helper_funcs via_lvds_helper_funcs = {
- .dpms = via_lvds_dpms,
- .mode_fixup = via_lvds_mode_fixup,
- .mode_set = via_set_sync_polarity,
- .prepare = via_encoder_prepare,
- .commit = via_encoder_commit,
- .disable = via_encoder_disable,
-};
-
-const struct drm_encoder_funcs via_lvds_enc_funcs = {
- .destroy = via_encoder_cleanup,
-};
-
-/* detect this connector connect status */
-static enum drm_connector_status
-via_lcd_detect(struct drm_connector *connector, bool force)
-{
- struct via_connector *con = container_of(connector, struct via_connector, base);
- enum drm_connector_status ret = connector_status_disconnected;
- struct edid *edid = drm_get_edid(&con->base, con->ddc_bus);
-
- if (edid) {
- drm_mode_connector_update_edid_property(&con->base, edid);
- kfree(edid);
- ret = connector_status_connected;
- } else {
- struct drm_via_private *dev_priv = connector->dev->dev_private;
- u8 mask = BIT(1);
-
- if (connector->dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)
- mask = BIT(3);
-
- if (vga_rcrt(VGABASE, 0x3B) & mask)
- ret = connector_status_connected;
-
- if (machine_is_olpc())
- ret = connector_status_connected;
- }
- return ret;
-}
-
-static const struct drm_prop_enum_list dithering_enum_list[] =
-{
- { DRM_MODE_DITHERING_OFF, "Off" },
- { DRM_MODE_DITHERING_ON, "On" },
- { DRM_MODE_DITHERING_AUTO, "Automatic" },
-};
-
-static int
-via_lcd_set_property(struct drm_connector *connector,
- struct drm_property *property, uint64_t value)
-{
- struct via_connector *con = container_of(connector, struct via_connector, base);
- struct drm_via_private *dev_priv = connector->dev->dev_private;
- struct drm_device *dev = connector->dev;
- struct drm_property *prop;
- uint64_t orig;
- int ret;
-
- ret = drm_object_property_get_value(&connector->base, property, &orig);
- if (!ret && (orig != value)) {
- if (property == dev->mode_config.scaling_mode_property) {
- switch (value) {
- 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:
- return -EINVAL;
- }
- }
-
- list_for_each_entry(prop, &con->props, head) {
- if (property == prop) {
- u8 reg_value;
-
- switch (value) {
- case DRM_MODE_DITHERING_AUTO:
- case DRM_MODE_DITHERING_ON:
- reg_value = BIT(0);
- break;
-
- case DRM_MODE_DITHERING_OFF:
- reg_value = 0x00;
- break;
-
- default:
- return -EINVAL;
- }
- svga_wcrt_mask(VGABASE, 0x88, reg_value, BIT(0));
- }
- }
- }
- return 0;
-}
-
-struct drm_connector_funcs via_lcd_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .detect = via_lcd_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .set_property = via_lcd_set_property,
- .destroy = via_connector_destroy,
-};
-
-static int
-via_lcd_get_modes(struct drm_connector *connector)
-{
- int count = via_get_edid_modes(connector);
-
- /* If no edid then we detect the mode using
- * the scratch pad registers. */
- if (!count) {
- struct drm_display_mode *native_mode = NULL;
- struct drm_device *dev = connector->dev;
-
- /* 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->vrefresh = 50;
- native_mode->hsync = 0;
- } else {
- struct drm_via_private *dev_priv = dev->dev_private;
- u8 reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F);
- int hdisplay = 0, vdisplay = 0;
-
- switch (reg_value) {
- case 0x00:
- hdisplay = 640;
- vdisplay = 480;
- break;
-
- case 0x01:
- hdisplay = 800;
- vdisplay = 600;
- break;
-
- case 0x02:
- hdisplay = 1024;
- vdisplay = 768;
- break;
-
- case 0x03:
- hdisplay = 1280;
- vdisplay = 768;
- break;
-
- case 0x04:
- hdisplay = 1280;
- vdisplay = 1024;
- break;
-
- case 0x05:
- hdisplay = 1400;
- vdisplay = 1050;
- break;
-
- case 0x06:
- hdisplay = 1440;
- vdisplay = 900;
- break;
-
- case 0x07:
- hdisplay = 1280;
- vdisplay = 800;
- break;
-
- case 0x08:
- hdisplay = 800;
- vdisplay = 480;
- break;
-
- case 0x09:
- hdisplay = 1024;
- vdisplay = 600;
- break;
-
- case 0x0A:
- hdisplay = 1366;
- vdisplay = 768;
- break;
-
- case 0x0B:
- hdisplay = 1600;
- vdisplay = 1200;
- break;
-
- case 0x0C:
- hdisplay = 1280;
- vdisplay = 768;
- break;
-
- case 0x0D:
- hdisplay = 1280;
- vdisplay = 1024;
- break;
-
- case 0x0E:
- hdisplay = 1600;
- vdisplay = 1200;
- break;
-
- case 0x0F:
- hdisplay = 480;
- vdisplay = 640;
- break;
-
- default:
- break;
- }
-
- 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;
- }
- }
- return count;
-}
-
-static int
-via_lcd_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;
- 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 (dev->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_lcd_connector_helper_funcs = {
- .get_modes = via_lcd_get_modes,
- .mode_valid = via_lcd_mode_valid,
- .best_encoder = via_best_encoder,
-};
-
-static int __init via_ttl_lvds_dmi_callback(const struct dmi_system_id *id)
-{
- DRM_INFO("LVDS is TTL type for %s\n", id->ident);
- return 1;
-}
-
-static const struct dmi_system_id via_ttl_lvds[] = {
- {
- .callback = via_ttl_lvds_dmi_callback,
- .ident = "VIA Quanta Netbook",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "QCI"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "VT6413A"),
- },
- }, {
- .callback = via_ttl_lvds_dmi_callback,
- .ident = "Amilo Pro V2030",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
- },
- },
-
- { }
-};
-
-void
-via_lvds_init(struct drm_device *dev)
-{
- struct drm_via_private *dev_priv = dev->dev_private;
- bool dual_channel = false, is_msb = false;
- uint64_t dither = DRM_MODE_DITHERING_OFF;
- struct drm_property *dithering;
- struct via_connector *con;
- struct via_encoder *enc;
- struct edid *edid;
- u8 reg_value;
-
- enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL);
- if (!enc) {
- DRM_INFO("Failed to allocate LVDS output\n");
- return;
- }
- con = &enc->cons[0];
- INIT_LIST_HEAD(&con->props);
-
- drm_connector_init(dev, &con->base, &via_lcd_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
- drm_connector_helper_add(&con->base, &via_lcd_connector_helper_funcs);
- drm_connector_register(&con->base);
-
- switch (dev->pdev->device) {
- case PCI_DEVICE_ID_VIA_VX875:
- case PCI_DEVICE_ID_VIA_VX900_VGA:
- con->ddc_bus = via_find_ddc_bus(0x2C);
- break;
- default:
- con->ddc_bus = via_find_ddc_bus(0x31);
- break;
- }
-
- edid = drm_get_edid(&con->base, con->ddc_bus);
- if (!edid) {
- if (!machine_is_olpc()) {
- u8 mask = BIT(1);
-
- if (dev->pdev->device == PCI_DEVICE_ID_VIA_CLE266)
- mask = BIT(3);
-
- /* First we have to make sure a LVDS is present */
- reg_value = (vga_rcrt(VGABASE, 0x3B) & mask);
- if (!reg_value)
- goto no_device;
-
- /* If no edid then we detect the mode using
- * the scratch pad registers. */
- reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F);
-
- switch (reg_value) {
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x09:
- case 0x0B:
- case 0x0D:
- case 0x0E:
- case 0x0F:
- dual_channel = true;
- break;
-
- default:
- break;
- }
-
- DRM_DEBUG("panel index %x detected\n", reg_value);
-
- if (reg_value < 0x0A)
- dither = DRM_MODE_DITHERING_ON;
- }
- } else {
- /* 00 LVDS1 + LVDS2 10 = Dual channel. Other are reserved */
- if ((vga_rseq(VGABASE, 0x13) >> 6) == 2)
- dual_channel = true;
-
- kfree(edid);
- }
- con->base.doublescan_allowed = false;
- con->base.interlace_allowed = false;
-
- drm_mode_create_scaling_mode_property(dev);
- drm_object_attach_property(&con->base.base,
- dev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_CENTER);
-
- dithering = drm_property_create_enum(dev, 0, "dithering",
- dithering_enum_list,
- ARRAY_SIZE(dithering_enum_list));
- list_add(&dithering->head, &con->props);
-
- drm_object_attach_property(&con->base.base, dithering, dither);
- via_lcd_set_property(&con->base, dithering, dither);
-
- /* Now setup the encoder */
- drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs,
- DRM_MODE_ENCODER_LVDS);
- drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs);
-
- enc->base.possible_crtcs = BIT(1);
-
- switch (dev->pdev->device) {
- case PCI_DEVICE_ID_VIA_CLE266:
- enc->diPort = DISP_DI_DVP1;
- break;
-
- case PCI_DEVICE_ID_VIA_VX875:
- case PCI_DEVICE_ID_VIA_VX900_VGA:
- enc->diPort = DISP_DI_DFPL;
- break;
-
- default:
- enc->diPort = DISP_DI_DFPH;
- break;
- }
-
- /* There has to be a way to detect TTL LVDS
- * For now we use the DMI to handle this */
- if (dmi_check_system(via_ttl_lvds))
- enc->diPort = DISP_DI_DFPL | DISP_DI_DVP1;
-
- reg_value = 0x00;
- if (enc->diPort == DISP_DI_DFPH) {
- if (!is_msb)
- reg_value = BIT(0);
- svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(0));
- } else if (enc->diPort == DISP_DI_DFPL) {
- if (!is_msb)
- reg_value = BIT(1);
- svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(1));
- }
-
- if (dual_channel)
- enc->flags |= LVDS_DUAL_CHANNEL;
-
- /* Put it all together */
- drm_mode_connector_attach_encoder(&con->base, &enc->base);
- return;
-
-no_device:
- drm_connector_unregister(&con->base);
- drm_connector_cleanup(&con->base);
- kfree(enc);
-}
More information about the Openchrome-devel
mailing list