diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_connector.c')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 858 |
1 files changed, 584 insertions, 274 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 390d82c3c4b..1fa222e8f00 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -26,21 +26,40 @@ #include <acpi/button.h> -#include "drmP.h" -#include "drm_edid.h" -#include "drm_crtc_helper.h" +#include <linux/pm_runtime.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> #include "nouveau_reg.h" -#include "nouveau_drv.h" +#include "nouveau_drm.h" +#include "dispnv04/hw.h" +#include "nouveau_acpi.h" + +#include "nouveau_display.h" +#include "nouveau_connector.h" #include "nouveau_encoder.h" #include "nouveau_crtc.h" -#include "nouveau_connector.h" -#include "nouveau_hw.h" -static void nouveau_connector_hotplug(void *, int); +#include <subdev/i2c.h> +#include <subdev/gpio.h> +#include <engine/disp.h> -static struct nouveau_encoder * -find_encoder_by_type(struct drm_connector *connector, int type) +MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); +static int nouveau_tv_disable = 0; +module_param_named(tv_disable, nouveau_tv_disable, int, 0400); + +MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status"); +static int nouveau_ignorelid = 0; +module_param_named(ignorelid, nouveau_ignorelid, int, 0400); + +MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); +static int nouveau_duallink = 1; +module_param_named(duallink, nouveau_duallink, int, 0400); + +struct nouveau_encoder * +find_encoder(struct drm_connector *connector, int type) { struct drm_device *dev = connector->dev; struct nouveau_encoder *nv_encoder; @@ -57,7 +76,8 @@ find_encoder_by_type(struct drm_connector *connector, int type) continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - if (type == OUTPUT_ANY || nv_encoder->dcb->type == type) + if (type == DCB_OUTPUT_ANY || + (nv_encoder->dcb && nv_encoder->dcb->type == type)) return nv_encoder; } @@ -78,65 +98,45 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder) return NULL; } -/*TODO: This could use improvement, and learn to handle the fixed - * BIOS tables etc. It's fine currently, for its only user. - */ -int -nouveau_connector_bpp(struct drm_connector *connector) -{ - struct nouveau_connector *nv_connector = nouveau_connector(connector); - - if (nv_connector->edid && nv_connector->edid->revision >= 4) { - u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4; - if (bpc > 4) - return bpc; - } - - return 18; -} - static void nouveau_connector_destroy(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - struct drm_nouveau_private *dev_priv; - struct nouveau_gpio_engine *pgpio; - struct drm_device *dev; - - if (!nv_connector) - return; - - dev = nv_connector->base.dev; - dev_priv = dev->dev_private; - NV_DEBUG_KMS(dev, "\n"); - - pgpio = &dev_priv->engine.gpio; - if (pgpio->irq_unregister) { - pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag, - nouveau_connector_hotplug, connector); - } - + nouveau_event_ref(NULL, &nv_connector->hpd); kfree(nv_connector->edid); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); + if (nv_connector->aux.transfer) + drm_dp_aux_unregister(&nv_connector->aux); kfree(connector); } -static struct nouveau_i2c_chan * -nouveau_connector_ddc_detect(struct drm_connector *connector, - struct nouveau_encoder **pnv_encoder) +static struct nouveau_encoder * +nouveau_connector_ddc_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - int i; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); + struct nouveau_encoder *nv_encoder; + struct drm_mode_object *obj; + int i, panel = -ENODEV; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct nouveau_i2c_chan *i2c = NULL; - struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; - int id; + /* eDP panels need powering on by us (if the VBIOS doesn't default it + * to on) before doing any AUX channel transactions. LVDS panel power + * is handled by the SOR itself, and not required for LVDS DDC. + */ + if (nv_connector->type == DCB_CONNECTOR_eDP) { + panel = gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); + if (panel == 0) { + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); + msleep(300); + } + } - id = connector->encoder_ids[i]; - if (!id) + for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) { + int id = connector->encoder_ids[i]; + if (id == 0) break; obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); @@ -144,16 +144,24 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, continue; nv_encoder = nouveau_encoder(obj_to_encoder(obj)); - if (nv_encoder->dcb->i2c_index < 0xf) - i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); - - if (i2c && nouveau_probe_i2c_addr(i2c, 0x50)) { - *pnv_encoder = nv_encoder; - return i2c; + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + int ret = nouveau_dp_detect(nv_encoder); + if (ret == 0) + break; + } else + if (nv_encoder->i2c) { + if (nv_probe_i2c(nv_encoder->i2c, 0x50)) + break; } } - return NULL; + /* eDP panel not detected, restore panel power GPIO to previous + * state to avoid confusing the SOR for other output types. + */ + if (!nv_encoder && panel == 0) + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); + + return nv_encoder; } static struct nouveau_encoder * @@ -166,8 +174,8 @@ nouveau_connector_of_detect(struct drm_connector *connector) struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev); if (!dn || - !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) || - (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG)))) + !((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) || + (nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG)))) return NULL; for_each_child_of_node(dn, cn) { @@ -191,33 +199,38 @@ nouveau_connector_set_encoder(struct drm_connector *connector, struct nouveau_encoder *nv_encoder) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - struct drm_nouveau_private *dev_priv = connector->dev->dev_private; + struct nouveau_drm *drm = nouveau_drm(connector->dev); struct drm_device *dev = connector->dev; if (nv_connector->detected_encoder == nv_encoder) return; nv_connector->detected_encoder = nv_encoder; - if (nv_encoder->dcb->type == OUTPUT_LVDS || - nv_encoder->dcb->type == OUTPUT_TMDS) { + if (nv_device(drm->device)->card_type >= NV_50) { + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + } else + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || + nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { connector->doublescan_allowed = false; connector->interlace_allowed = false; } else { connector->doublescan_allowed = true; - if (dev_priv->card_type == NV_20 || - (dev_priv->card_type == NV_10 && - (dev->pci_device & 0x0ff0) != 0x0100 && - (dev->pci_device & 0x0ff0) != 0x0150)) + if (nv_device(drm->device)->card_type == NV_20 || + ((nv_device(drm->device)->card_type == NV_10 || + nv_device(drm->device)->card_type == NV_11) && + (dev->pdev->device & 0x0ff0) != 0x0100 && + (dev->pdev->device & 0x0ff0) != 0x0150)) /* HW is broken */ connector->interlace_allowed = false; else connector->interlace_allowed = true; } - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { - drm_connector_property_set_value(connector, + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { + drm_object_property_set_value(&connector->base, dev->mode_config.dvi_i_subconnector_property, - nv_encoder->dcb->type == OUTPUT_TMDS ? + nv_encoder->dcb->type == DCB_OUTPUT_TMDS ? DRM_MODE_SUBCONNECTOR_DVID : DRM_MODE_SUBCONNECTOR_DVIA); } @@ -227,10 +240,14 @@ static enum drm_connector_status nouveau_connector_detect(struct drm_connector *connector, bool force) { struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; - struct nouveau_i2c_chan *i2c; + struct nouveau_encoder *nv_partner; + struct nouveau_i2c_port *i2c; int type; + int ret; + enum drm_connector_status conn_status = connector_status_disconnected; /* Cleanup the previous EDID block. */ if (nv_connector->edid) { @@ -239,58 +256,60 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) nv_connector->edid = NULL; } - i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); - if (i2c) { + ret = pm_runtime_get_sync(connector->dev->dev); + if (ret < 0 && ret != -EACCES) + return conn_status; + + nv_encoder = nouveau_connector_ddc_detect(connector); + if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { nv_connector->edid = drm_get_edid(connector, &i2c->adapter); drm_mode_connector_update_edid_property(connector, nv_connector->edid); if (!nv_connector->edid) { - NV_ERROR(dev, "DDC responded, but no EDID for %s\n", - drm_get_connector_name(connector)); + NV_ERROR(drm, "DDC responded, but no EDID for %s\n", + connector->name); goto detect_analog; } - if (nv_encoder->dcb->type == OUTPUT_DP && - !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { - NV_ERROR(dev, "Detected %s, but failed init\n", - drm_get_connector_name(connector)); - return connector_status_disconnected; - } - /* Override encoder type for DVI-I based on whether EDID * says the display is digital or analog, both use the * same i2c channel so the value returned from ddc_detect * isn't necessarily correct. */ - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + nv_partner = NULL; + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) + nv_partner = find_encoder(connector, DCB_OUTPUT_ANALOG); + if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG) + nv_partner = find_encoder(connector, DCB_OUTPUT_TMDS); + + if (nv_partner && ((nv_encoder->dcb->type == DCB_OUTPUT_ANALOG && + nv_partner->dcb->type == DCB_OUTPUT_TMDS) || + (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && + nv_partner->dcb->type == DCB_OUTPUT_ANALOG))) { if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) - type = OUTPUT_TMDS; + type = DCB_OUTPUT_TMDS; else - type = OUTPUT_ANALOG; - - nv_encoder = find_encoder_by_type(connector, type); - if (!nv_encoder) { - NV_ERROR(dev, "Detected %d encoder on %s, " - "but no object!\n", type, - drm_get_connector_name(connector)); - return connector_status_disconnected; - } + type = DCB_OUTPUT_ANALOG; + + nv_encoder = find_encoder(connector, type); } nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } nv_encoder = nouveau_connector_of_detect(connector); if (nv_encoder) { nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } detect_analog: - nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG); + nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG); if (!nv_encoder && !nouveau_tv_disable) - nv_encoder = find_encoder_by_type(connector, OUTPUT_TV); + nv_encoder = find_encoder(connector, DCB_OUTPUT_TV); if (nv_encoder && force) { struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_encoder_helper_funcs *helper = @@ -299,19 +318,25 @@ detect_analog: if (helper->detect(encoder, connector) == connector_status_connected) { nouveau_connector_set_encoder(connector, nv_encoder); - return connector_status_connected; + conn_status = connector_status_connected; + goto out; } } - return connector_status_disconnected; + out: + + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + + return conn_status; } static enum drm_connector_status nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) { struct drm_device *dev = connector->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; enum drm_connector_status status = connector_status_disconnected; @@ -323,12 +348,12 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) nv_connector->edid = NULL; } - nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); + nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); if (!nv_encoder) return connector_status_disconnected; /* Try retrieving EDID via DDC */ - if (!dev_priv->vbios.fp_no_ddc) { + if (!drm->vbios.fp_no_ddc) { status = nouveau_connector_detect(connector, force); if (status == connector_status_connected) goto out; @@ -344,7 +369,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) * valid - it's not (rh#613284) */ if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) { - if (!nouveau_acpi_edid(dev, connector)) { + if ((nv_connector->edid = nouveau_acpi_edid(dev, connector))) { status = connector_status_connected; goto out; } @@ -354,7 +379,7 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) * modeline is avalilable for the panel, set it as the panel's * native mode and exit. */ - if (nouveau_bios_fp_mode(dev, NULL) && (dev_priv->vbios.fp_no_ddc || + if (nouveau_bios_fp_mode(dev, NULL) && (drm->vbios.fp_no_ddc || nv_encoder->dcb->lvdsconf.use_straps_for_mode)) { status = connector_status_connected; goto out; @@ -363,13 +388,14 @@ nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) /* Still nothing, some VBIOS images have a hardcoded EDID block * stored for the panel stored in them. */ - if (!dev_priv->vbios.fp_no_ddc) { + if (!drm->vbios.fp_no_ddc) { struct edid *edid = (struct edid *)nouveau_bios_embedded_edid(dev); if (edid) { - nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL); - *(nv_connector->edid) = *edid; - status = connector_status_connected; + nv_connector->edid = + kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + if (nv_connector->edid) + status = connector_status_connected; } } @@ -389,22 +415,23 @@ out: static void nouveau_connector_force(struct drm_connector *connector) { + struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder; int type; - if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) { + if (nv_connector->type == DCB_CONNECTOR_DVI_I) { if (connector->force == DRM_FORCE_ON_DIGITAL) - type = OUTPUT_TMDS; + type = DCB_OUTPUT_TMDS; else - type = OUTPUT_ANALOG; + type = DCB_OUTPUT_ANALOG; } else - type = OUTPUT_ANY; + type = DCB_OUTPUT_ANY; - nv_encoder = find_encoder_by_type(connector, type); + nv_encoder = find_encoder(connector, type); if (!nv_encoder) { - NV_ERROR(connector->dev, "can't find encoder to force %s on!\n", - drm_get_connector_name(connector)); + NV_ERROR(drm, "can't find encoder to force %s on!\n", + connector->name); connector->status = connector_status_disconnected; return; } @@ -416,15 +443,20 @@ static int nouveau_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + struct nouveau_display *disp = nouveau_display(connector->dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); struct drm_device *dev = connector->dev; + struct nouveau_crtc *nv_crtc; int ret; + nv_crtc = NULL; + if (connector->encoder && connector->encoder->crtc) + nv_crtc = nouveau_crtc(connector->encoder->crtc); + /* Scaling mode */ if (property == dev->mode_config.scaling_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; bool modeset = false; switch (value) { @@ -438,7 +470,7 @@ nouveau_connector_set_property(struct drm_connector *connector, } /* LVDS always needs gpu scaling */ - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS && + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && value == DRM_MODE_SCALE_NONE) return -EINVAL; @@ -450,8 +482,6 @@ nouveau_connector_set_property(struct drm_connector *connector, modeset = true; nv_connector->scaling_mode = value; - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); if (!nv_crtc) return 0; @@ -463,7 +493,7 @@ nouveau_connector_set_property(struct drm_connector *connector, if (!ret) return -EINVAL; } else { - ret = nv_crtc->set_scale(nv_crtc, value, true); + ret = nv_crtc->set_scale(nv_crtc, true); if (ret) return ret; } @@ -471,26 +501,74 @@ nouveau_connector_set_property(struct drm_connector *connector, return 0; } - /* Dithering */ - if (property == dev->mode_config.dithering_mode_property) { - struct nouveau_crtc *nv_crtc = NULL; + /* Underscan */ + if (property == disp->underscan_property) { + if (nv_connector->underscan != value) { + nv_connector->underscan = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; - if (value == DRM_MODE_DITHERING_ON) - nv_connector->use_dithering = true; - else - nv_connector->use_dithering = false; + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } - if (connector->encoder && connector->encoder->crtc) - nv_crtc = nouveau_crtc(connector->encoder->crtc); + if (property == disp->underscan_hborder_property) { + if (nv_connector->underscan_hborder != value) { + nv_connector->underscan_hborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + if (property == disp->underscan_vborder_property) { + if (nv_connector->underscan_vborder != value) { + nv_connector->underscan_vborder = value; + if (!nv_crtc || !nv_crtc->set_scale) + return 0; + + return nv_crtc->set_scale(nv_crtc, true); + } + + return 0; + } + + /* Dithering */ + if (property == disp->dithering_mode) { + nv_connector->dithering_mode = value; if (!nv_crtc || !nv_crtc->set_dither) return 0; - return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, - true); + return nv_crtc->set_dither(nv_crtc, true); } - if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) + if (property == disp->dithering_depth) { + nv_connector->dithering_depth = value; + if (!nv_crtc || !nv_crtc->set_dither) + return 0; + + return nv_crtc->set_dither(nv_crtc, true); + } + + if (nv_crtc && nv_crtc->set_color_vibrance) { + /* Hue */ + if (property == disp->vibrant_hue_property) { + nv_crtc->vibrant_hue = value - 90; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + /* Saturation */ + if (property == disp->color_vibrance_property) { + nv_crtc->color_vibrance = value - 100; + return nv_crtc->set_color_vibrance(nv_crtc, true); + } + } + + if (nv_encoder && nv_encoder->dcb->type == DCB_OUTPUT_TV) return get_slave_funcs(encoder)->set_property( encoder, connector, property, value); @@ -501,6 +579,7 @@ static struct drm_display_mode * nouveau_connector_native_mode(struct drm_connector *connector) { struct drm_connector_helper_funcs *helper = connector->helper_private; + struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *largest = NULL; @@ -514,7 +593,7 @@ nouveau_connector_native_mode(struct drm_connector *connector) /* Use preferred mode if there is one.. */ if (mode->type & DRM_MODE_TYPE_PREFERRED) { - NV_DEBUG_KMS(dev, "native mode from preferred\n"); + NV_DEBUG(drm, "native mode from preferred\n"); return drm_mode_duplicate(dev, mode); } @@ -537,7 +616,7 @@ nouveau_connector_native_mode(struct drm_connector *connector) largest = mode; } - NV_DEBUG_KMS(dev, "native mode from largest: %dx%d@%d\n", + NV_DEBUG(drm, "native mode from largest: %dx%d@%d\n", high_w, high_h, high_v); return largest ? drm_mode_duplicate(dev, largest) : NULL; } @@ -598,11 +677,60 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector) return modes; } +static void +nouveau_connector_detect_depth(struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nvbios *bios = &drm->vbios; + struct drm_display_mode *mode = nv_connector->native_mode; + bool duallink; + + /* if the edid is feeling nice enough to provide this info, use it */ + if (nv_connector->edid && connector->display_info.bpc) + return; + + /* EDID 1.4 is *supposed* to be supported on eDP, but, Apple... */ + if (nv_connector->type == DCB_CONNECTOR_eDP) { + connector->display_info.bpc = 6; + return; + } + + /* we're out of options unless we're LVDS, default to 8bpc */ + if (nv_encoder->dcb->type != DCB_OUTPUT_LVDS) { + connector->display_info.bpc = 8; + return; + } + + connector->display_info.bpc = 6; + + /* LVDS: panel straps */ + if (bios->fp_no_ddc) { + if (bios->fp.if_is_24bit) + connector->display_info.bpc = 8; + return; + } + + /* LVDS: DDC panel, need to first determine the number of links to + * know which if_is_24bit flag to check... + */ + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) + duallink = ((u8 *)nv_connector->edid)[121] == 2; + else + duallink = mode->clock >= bios->fp.duallink_transition_clk; + + if ((!duallink && (bios->fp.strapless_is_24bit & 1)) || + ( duallink && (bios->fp.strapless_is_24bit & 2))) + connector->display_info.bpc = 8; +} + static int nouveau_connector_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); @@ -618,15 +746,21 @@ nouveau_connector_get_modes(struct drm_connector *connector) if (nv_connector->edid) ret = drm_add_edid_modes(connector, nv_connector->edid); else - if (nv_encoder->dcb->type == OUTPUT_LVDS && + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && (nv_encoder->dcb->lvdsconf.use_straps_for_mode || - dev_priv->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { + drm->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { struct drm_display_mode mode; nouveau_bios_fp_mode(dev, &mode); nv_connector->native_mode = drm_mode_duplicate(dev, &mode); } + /* Determine display colour depth for everything except LVDS now, + * DP requires this before mode_valid() is called. + */ + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + /* Find the native mode if this is a digital panel, if we didn't * find any modes through DDC previously add the native mode to * the list of modes. @@ -642,11 +776,19 @@ nouveau_connector_get_modes(struct drm_connector *connector) ret = 1; } - if (nv_encoder->dcb->type == OUTPUT_TV) + /* Determine LVDS colour depth, must happen after determining + * "native" mode as some VBIOS tables require us to use the + * pixel clock as part of the lookup... + */ + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + nouveau_connector_detect_depth(connector); + + if (nv_encoder->dcb->type == DCB_OUTPUT_TV) ret = get_slave_funcs(encoder)->get_modes(encoder, connector); - if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || - nv_connector->dcb->type == DCB_CONNECTOR_eDP) + if (nv_connector->type == DCB_CONNECTOR_LVDS || + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG || + nv_connector->type == DCB_CONNECTOR_eDP) ret += nouveau_connector_scaler_modes_add(connector); return ret; @@ -656,15 +798,15 @@ static unsigned get_tmds_link_bandwidth(struct drm_connector *connector) { struct nouveau_connector *nv_connector = nouveau_connector(connector); - struct drm_nouveau_private *dev_priv = connector->dev->dev_private; - struct dcb_entry *dcb = nv_connector->detected_encoder->dcb; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct dcb_output *dcb = nv_connector->detected_encoder->dcb; if (dcb->location != DCB_LOC_ON_CHIP || - dev_priv->chipset >= 0x46) + nv_device(drm->device)->chipset >= 0x46) return 165000; - else if (dev_priv->chipset >= 0x40) + else if (nv_device(drm->device)->chipset >= 0x40) return 155000; - else if (dev_priv->chipset >= 0x18) + else if (nv_device(drm->device)->chipset >= 0x18) return 135000; else return 112000; @@ -681,7 +823,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, unsigned clock = mode->clock; switch (nv_encoder->dcb->type) { - case OUTPUT_LVDS: + case DCB_OUTPUT_LVDS: if (nv_connector->native_mode && (mode->hdisplay > nv_connector->native_mode->hdisplay || mode->vdisplay > nv_connector->native_mode->vdisplay)) @@ -690,25 +832,22 @@ nouveau_connector_mode_valid(struct drm_connector *connector, min_clock = 0; max_clock = 400000; break; - case OUTPUT_TMDS: + case DCB_OUTPUT_TMDS: max_clock = get_tmds_link_bandwidth(connector); if (nouveau_duallink && nv_encoder->dcb->duallink_possible) max_clock *= 2; break; - case OUTPUT_ANALOG: + case DCB_OUTPUT_ANALOG: max_clock = nv_encoder->dcb->crtconf.maxfreq; if (!max_clock) max_clock = 350000; break; - case OUTPUT_TV: + case DCB_OUTPUT_TV: return get_slave_funcs(encoder)->mode_valid(encoder, mode); - case OUTPUT_DP: - if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) - max_clock = nv_encoder->dp.link_nr * 270000; - else - max_clock = nv_encoder->dp.link_nr * 162000; - - clock = clock * nouveau_connector_bpp(connector) / 8; + case DCB_OUTPUT_DP: + max_clock = nv_encoder->dp.link_nr; + max_clock *= nv_encoder->dp.link_bw; + clock = clock * (connector->display_info.bpc * 3) / 10; break; default: BUG_ON(1); @@ -766,66 +905,255 @@ nouveau_connector_funcs_lvds = { .force = nouveau_connector_force }; +static void +nouveau_connector_dp_dpms(struct drm_connector *connector, int mode) +{ + struct nouveau_encoder *nv_encoder = NULL; + + if (connector->encoder) + nv_encoder = nouveau_encoder(connector->encoder); + if (nv_encoder && nv_encoder->dcb && + nv_encoder->dcb->type == DCB_OUTPUT_DP) { + if (mode == DRM_MODE_DPMS_ON) { + u8 data = DP_SET_POWER_D0; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + usleep_range(1000, 2000); + } else { + u8 data = DP_SET_POWER_D3; + nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1); + } + } + + drm_helper_connector_dpms(connector, mode); +} + +static const struct drm_connector_funcs +nouveau_connector_funcs_dp = { + .dpms = nouveau_connector_dp_dpms, + .save = NULL, + .restore = NULL, + .detect = nouveau_connector_detect, + .destroy = nouveau_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = nouveau_connector_set_property, + .force = nouveau_connector_force +}; + +static void +nouveau_connector_hotplug_work(struct work_struct *work) +{ + struct nouveau_connector *nv_connector = + container_of(work, typeof(*nv_connector), work); + struct drm_connector *connector = &nv_connector->base; + struct nouveau_drm *drm = nouveau_drm(connector->dev); + const char *name = connector->name; + + if (nv_connector->status & NVKM_HPD_IRQ) { + } else { + bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG); + + NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); + + if (plugged) + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + else + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + drm_helper_hpd_irq_event(connector->dev); + } + + nouveau_event_get(nv_connector->hpd); +} + +static int +nouveau_connector_hotplug(void *data, u32 type, int index) +{ + struct nouveau_connector *nv_connector = data; + nv_connector->status = type; + schedule_work(&nv_connector->work); + return NVKM_EVENT_DROP; +} + +static ssize_t +nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct nouveau_connector *nv_connector = + container_of(aux, typeof(*nv_connector), aux); + struct nouveau_encoder *nv_encoder; + struct nouveau_i2c_port *port; + int ret; + + nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + if (!nv_encoder || !(port = nv_encoder->i2c)) + return -ENODEV; + if (WARN_ON(msg->size > 16)) + return -E2BIG; + if (msg->size == 0) + return msg->size; + + ret = nouveau_i2c(port)->acquire(port, 0); + if (ret) + return ret; + + ret = port->func->aux(port, false, msg->request, msg->address, + msg->buffer, msg->size); + nouveau_i2c(port)->release(port); + if (ret >= 0) { + msg->reply = ret; + return msg->size; + } + + return ret; +} + +static int +drm_conntype_from_dcb(enum dcb_connector_type dcb) +{ + switch (dcb) { + case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV; + case DCB_CONNECTOR_DMS59_0 : + case DCB_CONNECTOR_DMS59_1 : + case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII; + case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID; + case DCB_CONNECTOR_LVDS : + case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: + case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort; + case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : + case DCB_CONNECTOR_HDMI_C : return DRM_MODE_CONNECTOR_HDMIA; + default: + break; + } + + return DRM_MODE_CONNECTOR_Unknown; +} + struct drm_connector * nouveau_connector_create(struct drm_device *dev, int index) { const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_display *disp = nouveau_display(dev); struct nouveau_connector *nv_connector = NULL; - struct dcb_connector_table_entry *dcb = NULL; + struct nouveau_disp *pdisp = nouveau_disp(drm->device); struct drm_connector *connector; int type, ret = 0; + bool dummy; - NV_DEBUG_KMS(dev, "\n"); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + nv_connector = nouveau_connector(connector); + if (nv_connector->index == index) + return connector; + } - if (index >= dev_priv->vbios.dcb.connector.entries) - return ERR_PTR(-EINVAL); + nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); + if (!nv_connector) + return ERR_PTR(-ENOMEM); - dcb = &dev_priv->vbios.dcb.connector.entry[index]; - if (dcb->drm) - return dcb->drm; + connector = &nv_connector->base; + nv_connector->index = index; + + /* attempt to parse vbios connector type and hotplug gpio */ + nv_connector->dcb = olddcb_conn(dev, index); + if (nv_connector->dcb) { + u32 entry = ROM16(nv_connector->dcb[0]); + if (olddcb_conntab(dev)[3] >= 4) + entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; + + nv_connector->type = nv_connector->dcb[0]; + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(drm, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } + + /* Gigabyte NX85T */ + if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + + /* Gigabyte GV-NX86T512H */ + if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { + if (nv_connector->type == DCB_CONNECTOR_HDMI_1) + nv_connector->type = DCB_CONNECTOR_DVI_I; + } + } else { + nv_connector->type = DCB_CONNECTOR_NONE; + } + + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE) { + struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_table *dcbt = &drm->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } + + if (encoders & (1 << DCB_OUTPUT_DP)) { + if (encoders & (1 << DCB_OUTPUT_TMDS)) + nv_connector->type = DCB_CONNECTOR_DP; + else + nv_connector->type = DCB_CONNECTOR_eDP; + } else + if (encoders & (1 << DCB_OUTPUT_TMDS)) { + if (encoders & (1 << DCB_OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << DCB_OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << DCB_OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << DCB_OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } + } + + switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + case DRM_MODE_CONNECTOR_LVDS: + ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); + if (ret) { + NV_ERROR(drm, "Error parsing LVDS table, disabling\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } - switch (dcb->type) { - case DCB_CONNECTOR_VGA: - type = DRM_MODE_CONNECTOR_VGA; - break; - case DCB_CONNECTOR_TV_0: - case DCB_CONNECTOR_TV_1: - case DCB_CONNECTOR_TV_3: - type = DRM_MODE_CONNECTOR_TV; - break; - case DCB_CONNECTOR_DVI_I: - type = DRM_MODE_CONNECTOR_DVII; - break; - case DCB_CONNECTOR_DVI_D: - type = DRM_MODE_CONNECTOR_DVID; - break; - case DCB_CONNECTOR_HDMI_0: - case DCB_CONNECTOR_HDMI_1: - type = DRM_MODE_CONNECTOR_HDMIA; - break; - case DCB_CONNECTOR_LVDS: - type = DRM_MODE_CONNECTOR_LVDS; funcs = &nouveau_connector_funcs_lvds; break; - case DCB_CONNECTOR_DP: - type = DRM_MODE_CONNECTOR_DisplayPort; - break; - case DCB_CONNECTOR_eDP: - type = DRM_MODE_CONNECTOR_eDP; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + nv_connector->aux.dev = dev->dev; + nv_connector->aux.transfer = nouveau_connector_aux_xfer; + ret = drm_dp_aux_register(&nv_connector->aux); + if (ret) { + NV_ERROR(drm, "failed to register aux channel\n"); + kfree(nv_connector); + return ERR_PTR(ret); + } + + funcs = &nouveau_connector_funcs_dp; break; default: - NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type); - return ERR_PTR(-EINVAL); + funcs = &nouveau_connector_funcs; + break; } - nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); - if (!nv_connector) - return ERR_PTR(-ENOMEM); - nv_connector->dcb = dcb; - connector = &nv_connector->base; - /* defaults, will get overridden in detect() */ connector->interlace_allowed = false; connector->doublescan_allowed = false; @@ -833,35 +1161,44 @@ nouveau_connector_create(struct drm_device *dev, int index) drm_connector_init(dev, connector, funcs, type); drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); - /* Check if we need dithering enabled */ - if (dcb->type == DCB_CONNECTOR_LVDS) { - bool dummy, is_24bit = false; - - ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit); - if (ret) { - NV_ERROR(dev, "Error parsing LVDS table, disabling " - "LVDS\n"); - goto fail; - } - - nv_connector->use_dithering = !is_24bit; - } - /* Init DVI-I specific properties */ - if (dcb->type == DCB_CONNECTOR_DVI_I) { - drm_mode_create_dvi_i_properties(dev); - drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0); - drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); + if (nv_connector->type == DCB_CONNECTOR_DVI_I) + drm_object_attach_property(&connector->base, dev->mode_config.dvi_i_subconnector_property, 0); + + /* Add overscan compensation options to digital outputs */ + if (disp->underscan_property && + (type == DRM_MODE_CONNECTOR_DVID || + type == DRM_MODE_CONNECTOR_DVII || + type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_DisplayPort)) { + drm_object_attach_property(&connector->base, + disp->underscan_property, + UNDERSCAN_OFF); + drm_object_attach_property(&connector->base, + disp->underscan_hborder_property, + 0); + drm_object_attach_property(&connector->base, + disp->underscan_vborder_property, + 0); } - switch (dcb->type) { + /* Add hue and saturation options */ + if (disp->vibrant_hue_property) + drm_object_attach_property(&connector->base, + disp->vibrant_hue_property, + 90); + if (disp->color_vibrance_property) + drm_object_attach_property(&connector->base, + disp->color_vibrance_property, + 150); + + switch (nv_connector->type) { case DCB_CONNECTOR_VGA: - if (dev_priv->card_type >= NV_50) { - drm_connector_attach_property(connector, + if (nv_device(drm->device)->card_type >= NV_50) { + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); } - connector->polled = DRM_CONNECTOR_POLL_CONNECT; /* fall-through */ case DCB_CONNECTOR_TV_0: case DCB_CONNECTOR_TV_1: @@ -871,61 +1208,34 @@ nouveau_connector_create(struct drm_device *dev, int index) default: nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, nv_connector->scaling_mode); - drm_connector_attach_property(connector, - dev->mode_config.dithering_mode_property, - nv_connector->use_dithering ? - DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); - - if (dcb->type != DCB_CONNECTOR_LVDS) { - if (dev_priv->card_type >= NV_50) - connector->polled = DRM_CONNECTOR_POLL_HPD; - else - connector->polled = DRM_CONNECTOR_POLL_CONNECT; + if (disp->dithering_mode) { + nv_connector->dithering_mode = DITHERING_MODE_AUTO; + drm_object_attach_property(&connector->base, + disp->dithering_mode, + nv_connector->dithering_mode); + } + if (disp->dithering_depth) { + nv_connector->dithering_depth = DITHERING_DEPTH_AUTO; + drm_object_attach_property(&connector->base, + disp->dithering_depth, + nv_connector->dithering_depth); } break; } - if (pgpio->irq_register) { - pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, - nouveau_connector_hotplug, connector); - } - - drm_sysfs_connector_add(connector); - dcb->drm = connector; - return dcb->drm; - -fail: - drm_connector_cleanup(connector); - kfree(connector); - return ERR_PTR(ret); - -} - -static void -nouveau_connector_hotplug(void *data, int plugged) -{ - struct drm_connector *connector = data; - struct drm_device *dev = connector->dev; - - NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un", - drm_get_connector_name(connector)); - - if (connector->encoder && connector->encoder->crtc && - connector->encoder->crtc->enabled) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder); - struct drm_encoder_helper_funcs *helper = - connector->encoder->helper_private; + ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index, + nouveau_connector_hotplug, + nv_connector, &nv_connector->hpd); + if (ret) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + else + connector->polled = DRM_CONNECTOR_POLL_HPD; - if (nv_encoder->dcb->type == OUTPUT_DP) { - if (plugged) - helper->dpms(connector->encoder, DRM_MODE_DPMS_ON); - else - helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); - } - } + INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work); - drm_helper_hpd_irq_event(dev); + drm_sysfs_connector_add(connector); + return connector; } |
