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 <kevinbr...@gmx.com> Date: Mon Dec 5 02:23:23 2016 -0800 Replacing via_lvds.c with via_fp.c Signed-off-by: Kevin Brace <kevinbr...@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); -} _______________________________________________ Openchrome-devel mailing list Openchrome-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/openchrome-devel