Merge branch 'linux-4.20' of git://github.com/skeggsb/linux into drm-next
authorDave Airlie <airlied@redhat.com>
Thu, 11 Oct 2018 00:23:11 +0000 (10:23 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 11 Oct 2018 00:24:28 +0000 (10:24 +1000)
Just initial HDMI 2.0 support, and a bunch of other cleanups.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Ben Skeggs <bskeggs@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CABDvA=mgEm9JxP7AX7Sff-AEs7a75M4SqwFHmLPZhJojm4k=OA@mail.gmail.com
17 files changed:
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/include/nvif/cl5070.h
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_display.h
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c

index 1dbd1dcdcf153bc2d14ef9cb7edff4e32b2774ac..5f163a025e8906b671f653df46c14b4387fe6b3f 100644 (file)
@@ -36,6 +36,7 @@
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_scdc_helper.h>
 #include <drm/drm_edid.h>
 
 #include <nvif/class.h>
@@ -531,6 +532,7 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
 static void
 nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
 {
+       struct nouveau_drm *drm = nouveau_drm(encoder->dev);
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
        struct nv50_disp *disp = nv50_disp(encoder->dev);
@@ -548,9 +550,12 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
                .pwr.rekey = 56, /* binary driver, and tegra, constant */
        };
        struct nouveau_connector *nv_connector;
+       struct drm_hdmi_info *hdmi;
        u32 max_ac_packet;
        union hdmi_infoframe avi_frame;
        union hdmi_infoframe vendor_frame;
+       bool scdc_supported, high_tmds_clock_ratio = false, scrambling = false;
+       u8 config;
        int ret;
        int size;
 
@@ -558,8 +563,11 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
        if (!drm_detect_hdmi_monitor(nv_connector->edid))
                return;
 
+       hdmi = &nv_connector->base.display_info.hdmi;
+       scdc_supported = hdmi->scdc.supported;
+
        ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode,
-                                                      false);
+                                                      scdc_supported);
        if (!ret) {
                /* We have an AVI InfoFrame, populate it to the display */
                args.pwr.avi_infoframe_length
@@ -582,12 +590,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
        max_ac_packet -= 18; /* constant from tegra */
        args.pwr.max_ac_packet = max_ac_packet / 32;
 
+       if (hdmi->scdc.scrambling.supported) {
+               high_tmds_clock_ratio = mode->clock > 340000;
+               scrambling = high_tmds_clock_ratio ||
+                       hdmi->scdc.scrambling.low_rates;
+       }
+
+       args.pwr.scdc =
+               NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling |
+               NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio;
+
        size = sizeof(args.base)
                + sizeof(args.pwr)
                + args.pwr.avi_infoframe_length
                + args.pwr.vendor_infoframe_length;
        nvif_mthd(&disp->disp->object, 0, &args, size);
+
        nv50_audio_enable(encoder, mode);
+
+       /* If SCDC is supported by the downstream monitor, update
+        * divider / scrambling settings to what we programmed above.
+        */
+       if (!hdmi->scdc.scrambling.supported)
+               return;
+
+       ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config);
+       if (ret < 0) {
+               NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret);
+               return;
+       }
+       config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE);
+       config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio;
+       config |= SCDC_SCRAMBLING_ENABLE * scrambling;
+       ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config);
+       if (ret < 0)
+               NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n",
+                        config, ret);
 }
 
 /******************************************************************************
index 7cdf53615d7bb817494e81359dbb5007952d7683..bced8198726912ae4c8b7cf17a776adeddbc27c2 100644 (file)
@@ -69,7 +69,10 @@ struct nv50_disp_sor_hdmi_pwr_v0 {
        __u8  rekey;
        __u8  avi_infoframe_length;
        __u8  vendor_infoframe_length;
-       __u8  pad06[2];
+#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE (1 << 0)
+#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 (1 << 1)
+       __u8  scdc;
+       __u8  pad07[1];
 };
 
 struct nv50_disp_sor_lvds_script_v0 {
index 408b955e5c39a6b41043c18fb37ae8dc9de42c04..5f5be6368aed815d5c61bb26f39263695d822089 100644 (file)
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
 #include "nouveau_encoder.h"
+#include "nouveau_connector.h"
 
 static struct ida bl_ida;
 #define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
 
-struct backlight_connector {
-       struct list_head head;
+struct nouveau_backlight {
+       struct backlight_device *dev;
        int id;
 };
 
 static bool
-nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector
-               *connector)
+nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
+                          struct nouveau_backlight *bl)
 {
        const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
        if (nb < 0 || nb >= 100)
@@ -57,17 +58,18 @@ nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_c
                snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
        else
                snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
-       connector->id = nb;
+       bl->id = nb;
        return true;
 }
 
 static int
 nv40_get_intensity(struct backlight_device *bd)
 {
-       struct nouveau_drm *drm = bl_get_data(bd);
+       struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+       struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
        struct nvif_object *device = &drm->client.device.object;
        int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
-                                  NV40_PMC_BACKLIGHT_MASK) >> 16;
+                  NV40_PMC_BACKLIGHT_MASK) >> 16;
 
        return val;
 }
@@ -75,13 +77,14 @@ nv40_get_intensity(struct backlight_device *bd)
 static int
 nv40_set_intensity(struct backlight_device *bd)
 {
-       struct nouveau_drm *drm = bl_get_data(bd);
+       struct nouveau_encoder *nv_encoder = bl_get_data(bd);
+       struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
        struct nvif_object *device = &drm->client.device.object;
        int val = bd->props.brightness;
        int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
 
        nvif_wr32(device, NV40_PMC_BACKLIGHT,
-                (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
+                 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
 
        return 0;
 }
@@ -93,38 +96,19 @@ static const struct backlight_ops nv40_bl_ops = {
 };
 
 static int
-nv40_backlight_init(struct drm_connector *connector)
+nv40_backlight_init(struct nouveau_encoder *encoder,
+                   struct backlight_properties *props,
+                   const struct backlight_ops **ops)
 {
-       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);
        struct nvif_object *device = &drm->client.device.object;
-       struct backlight_properties props;
-       struct backlight_device *bd;
-       struct backlight_connector bl_connector;
-       char backlight_name[BL_NAME_SIZE];
 
        if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
-               return 0;
-
-       memset(&props, 0, sizeof(struct backlight_properties));
-       props.type = BACKLIGHT_RAW;
-       props.max_brightness = 31;
-       if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
-               NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
-               return 0;
-       }
-       bd = backlight_device_register(backlight_name , connector->kdev, drm,
-                                      &nv40_bl_ops, &props);
-
-       if (IS_ERR(bd)) {
-               if (bl_connector.id > 0)
-                       ida_simple_remove(&bl_ida, bl_connector.id);
-               return PTR_ERR(bd);
-       }
-       list_add(&bl_connector.head, &drm->bl_connectors);
-       drm->backlight = bd;
-       bd->props.brightness = nv40_get_intensity(bd);
-       backlight_update_status(bd);
+               return -ENODEV;
 
+       props->type = BACKLIGHT_RAW;
+       props->max_brightness = 31;
+       *ops = &nv40_bl_ops;
        return 0;
 }
 
@@ -154,7 +138,7 @@ nv50_set_intensity(struct backlight_device *bd)
        u32 val = (bd->props.brightness * div) / 100;
 
        nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
-                       NV50_PDISP_SOR_PWM_CTL_NEW | val);
+                 NV50_PDISP_SOR_PWM_CTL_NEW | val);
        return 0;
 }
 
@@ -194,9 +178,10 @@ nva3_set_intensity(struct backlight_device *bd)
        div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
        val = (bd->props.brightness * div) / 100;
        if (div) {
-               nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
-                               NV50_PDISP_SOR_PWM_CTL_NEW |
-                               NVA3_PDISP_SOR_PWM_CTL_UNK);
+               nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
+                         val |
+                         NV50_PDISP_SOR_PWM_CTL_NEW |
+                         NVA3_PDISP_SOR_PWM_CTL_UNK);
                return 0;
        }
 
@@ -210,110 +195,119 @@ static const struct backlight_ops nva3_bl_ops = {
 };
 
 static int
-nv50_backlight_init(struct drm_connector *connector)
+nv50_backlight_init(struct nouveau_encoder *nv_encoder,
+                   struct backlight_properties *props,
+                   const struct backlight_ops **ops)
 {
-       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
        struct nvif_object *device = &drm->client.device.object;
-       struct nouveau_encoder *nv_encoder;
-       struct backlight_properties props;
-       struct backlight_device *bd;
-       const struct backlight_ops *ops;
-       struct backlight_connector bl_connector;
-       char backlight_name[BL_NAME_SIZE];
-
-       nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
-       if (!nv_encoder) {
-               nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
-               if (!nv_encoder)
-                       return -ENODEV;
-       }
 
        if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)))
-               return 0;
+               return -ENODEV;
 
        if (drm->client.device.info.chipset <= 0xa0 ||
            drm->client.device.info.chipset == 0xaa ||
            drm->client.device.info.chipset == 0xac)
-               ops = &nv50_bl_ops;
+               *ops = &nv50_bl_ops;
        else
-               ops = &nva3_bl_ops;
-
-       memset(&props, 0, sizeof(struct backlight_properties));
-       props.type = BACKLIGHT_RAW;
-       props.max_brightness = 100;
-       if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
-               NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
-               return 0;
-       }
-       bd = backlight_device_register(backlight_name , connector->kdev,
-                                      nv_encoder, ops, &props);
+               *ops = &nva3_bl_ops;
 
-       if (IS_ERR(bd)) {
-               if (bl_connector.id > 0)
-                       ida_simple_remove(&bl_ida, bl_connector.id);
-               return PTR_ERR(bd);
-       }
+       props->type = BACKLIGHT_RAW;
+       props->max_brightness = 100;
 
-       list_add(&bl_connector.head, &drm->bl_connectors);
-       drm->backlight = bd;
-       bd->props.brightness = bd->ops->get_brightness(bd);
-       backlight_update_status(bd);
        return 0;
 }
 
 int
-nouveau_backlight_init(struct drm_device *dev)
+nouveau_backlight_init(struct drm_connector *connector)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       struct nouveau_backlight *bl;
+       struct nouveau_encoder *nv_encoder = NULL;
        struct nvif_device *device = &drm->client.device;
-       struct drm_connector *connector;
-       struct drm_connector_list_iter conn_iter;
-
-       INIT_LIST_HEAD(&drm->bl_connectors);
+       char backlight_name[BL_NAME_SIZE];
+       struct backlight_properties props = {0};
+       const struct backlight_ops *ops;
+       int ret;
 
        if (apple_gmux_present()) {
-               NV_INFO(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
+               NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
                return 0;
        }
 
-       drm_connector_list_iter_begin(dev, &conn_iter);
-       drm_for_each_connector_iter(connector, &conn_iter) {
-               if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
-                   connector->connector_type != DRM_MODE_CONNECTOR_eDP)
-                       continue;
-
-               switch (device->info.family) {
-               case NV_DEVICE_INFO_V0_CURIE:
-                       return nv40_backlight_init(connector);
-               case NV_DEVICE_INFO_V0_TESLA:
-               case NV_DEVICE_INFO_V0_FERMI:
-               case NV_DEVICE_INFO_V0_KEPLER:
-               case NV_DEVICE_INFO_V0_MAXWELL:
-                       return nv50_backlight_init(connector);
-               default:
-                       break;
-               }
+       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+               nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
+       else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+               nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
+       else
+               return 0;
+
+       if (!nv_encoder)
+               return 0;
+
+       switch (device->info.family) {
+       case NV_DEVICE_INFO_V0_CURIE:
+               ret = nv40_backlight_init(nv_encoder, &props, &ops);
+               break;
+       case NV_DEVICE_INFO_V0_TESLA:
+       case NV_DEVICE_INFO_V0_FERMI:
+       case NV_DEVICE_INFO_V0_KEPLER:
+       case NV_DEVICE_INFO_V0_MAXWELL:
+               ret = nv50_backlight_init(nv_encoder, &props, &ops);
+               break;
+       default:
+               return 0;
        }
-       drm_connector_list_iter_end(&conn_iter);
+
+       if (ret == -ENODEV)
+               return 0;
+       else if (ret)
+               return ret;
+
+       bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+       if (!bl)
+               return -ENOMEM;
+
+       if (!nouveau_get_backlight_name(backlight_name, bl)) {
+               NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
+               goto fail_alloc;
+       }
+
+       bl->dev = backlight_device_register(backlight_name, connector->kdev,
+                                           nv_encoder, ops, &props);
+       if (IS_ERR(bl->dev)) {
+               if (bl->id >= 0)
+                       ida_simple_remove(&bl_ida, bl->id);
+               ret = PTR_ERR(bl->dev);
+               goto fail_alloc;
+       }
+
+       nouveau_connector(connector)->backlight = bl;
+       bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev);
+       backlight_update_status(bl->dev);
 
        return 0;
+
+fail_alloc:
+       kfree(bl);
+       return ret;
 }
 
 void
-nouveau_backlight_exit(struct drm_device *dev)
+nouveau_backlight_fini(struct drm_connector *connector)
 {
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct backlight_connector *connector;
+       struct nouveau_connector *nv_conn = nouveau_connector(connector);
+       struct nouveau_backlight *bl = nv_conn->backlight;
 
-       list_for_each_entry(connector, &drm->bl_connectors, head) {
-               if (connector->id >= 0)
-                       ida_simple_remove(&bl_ida, connector->id);
-       }
+       if (!bl)
+               return;
 
-       if (drm->backlight) {
-               backlight_device_unregister(drm->backlight);
-               drm->backlight = NULL;
-       }
+       if (bl->id >= 0)
+               ida_simple_remove(&bl_ida, bl->id);
+
+       backlight_device_unregister(bl->dev);
+       nv_conn->backlight = NULL;
+       kfree(bl);
 }
 
 void
index dde8724aa8f563621756dc4664670ac27399542b..fd80661dff92691f94c9b91c3799712d04cb9c17 100644 (file)
@@ -885,6 +885,22 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
                connector->display_info.bpc = 8;
 }
 
+static int
+nouveau_connector_late_register(struct drm_connector *connector)
+{
+       int ret;
+
+       ret = nouveau_backlight_init(connector);
+
+       return ret;
+}
+
+static void
+nouveau_connector_early_unregister(struct drm_connector *connector)
+{
+       nouveau_backlight_fini(connector);
+}
+
 static int
 nouveau_connector_get_modes(struct drm_connector *connector)
 {
@@ -953,18 +969,33 @@ nouveau_connector_get_modes(struct drm_connector *connector)
 }
 
 static unsigned
-get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
+get_tmds_link_bandwidth(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
+       struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
        struct nouveau_drm *drm = nouveau_drm(connector->dev);
        struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
+       struct drm_display_info *info = NULL;
+       const unsigned duallink_scale =
+               nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1;
+
+       if (drm_detect_hdmi_monitor(nv_connector->edid))
+               info = &nv_connector->base.display_info;
 
-       if (hdmi) {
+       if (info) {
                if (nouveau_hdmimhz > 0)
                        return nouveau_hdmimhz * 1000;
                /* Note: these limits are conservative, some Fermi's
                 * can do 297 MHz. Unclear how this can be determined.
                 */
+               if (drm->client.device.info.chipset >= 0x120) {
+                       const int max_tmds_clock =
+                               info->hdmi.scdc.scrambling.supported ?
+                               594000 : 340000;
+                       return info->max_tmds_clock ?
+                               min(info->max_tmds_clock, max_tmds_clock) :
+                               max_tmds_clock;
+               }
                if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
                        return 297000;
                if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
@@ -972,13 +1003,13 @@ get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
        }
        if (dcb->location != DCB_LOC_ON_CHIP ||
            drm->client.device.info.chipset >= 0x46)
-               return 165000;
+               return 165000 * duallink_scale;
        else if (drm->client.device.info.chipset >= 0x40)
-               return 155000;
+               return 155000 * duallink_scale;
        else if (drm->client.device.info.chipset >= 0x18)
-               return 135000;
+               return 135000 * duallink_scale;
        else
-               return 112000;
+               return 112000 * duallink_scale;
 }
 
 static enum drm_mode_status
@@ -990,7 +1021,6 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
        struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
        unsigned min_clock = 25000, max_clock = min_clock;
        unsigned clock = mode->clock;
-       bool hdmi;
 
        switch (nv_encoder->dcb->type) {
        case DCB_OUTPUT_LVDS:
@@ -1003,11 +1033,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
                max_clock = 400000;
                break;
        case DCB_OUTPUT_TMDS:
-               hdmi = drm_detect_hdmi_monitor(nv_connector->edid);
-               max_clock = get_tmds_link_bandwidth(connector, hdmi);
-               if (!hdmi && nouveau_duallink &&
-                   nv_encoder->dcb->duallink_possible)
-                       max_clock *= 2;
+               max_clock = get_tmds_link_bandwidth(connector);
                break;
        case DCB_OUTPUT_ANALOG:
                max_clock = nv_encoder->dcb->crtconf.maxfreq;
@@ -1069,6 +1095,8 @@ nouveau_connector_funcs = {
        .atomic_destroy_state = nouveau_conn_atomic_destroy_state,
        .atomic_set_property = nouveau_conn_atomic_set_property,
        .atomic_get_property = nouveau_conn_atomic_get_property,
+       .late_register = nouveau_connector_late_register,
+       .early_unregister = nouveau_connector_early_unregister,
 };
 
 static const struct drm_connector_funcs
@@ -1084,6 +1112,8 @@ nouveau_connector_funcs_lvds = {
        .atomic_destroy_state = nouveau_conn_atomic_destroy_state,
        .atomic_set_property = nouveau_conn_atomic_set_property,
        .atomic_get_property = nouveau_conn_atomic_get_property,
+       .late_register = nouveau_connector_late_register,
+       .early_unregister = nouveau_connector_early_unregister,
 };
 
 static int
index 0acc07555bcd53da7a07e4c2b93dc943edda9c0a..f57ef35b1e5e6b6b5ce222bfc1f16fa9c757fa31 100644 (file)
 
 struct nvkm_i2c_port;
 
+#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+struct nouveau_backlight;
+#endif
+
 struct nouveau_connector {
        struct drm_connector base;
        enum dcb_connector_type type;
@@ -55,6 +59,9 @@ struct nouveau_connector {
        struct nouveau_encoder *detected_encoder;
        struct edid *edid;
        struct drm_display_mode *native_mode;
+#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+       struct nouveau_backlight *backlight;
+#endif
 };
 
 static inline struct nouveau_connector *nouveau_connector(
@@ -181,4 +188,30 @@ int nouveau_conn_atomic_get_property(struct drm_connector *,
                                     const struct drm_connector_state *,
                                     struct drm_property *, u64 *);
 struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *);
+
+#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+extern int nouveau_backlight_init(struct drm_connector *);
+extern void nouveau_backlight_fini(struct drm_connector *);
+extern void nouveau_backlight_ctor(void);
+extern void nouveau_backlight_dtor(void);
+#else
+static inline int
+nouveau_backlight_init(struct drm_connector *connector)
+{
+       return 0;
+}
+
+static inline void
+nouveau_backlight_fini(struct drm_connector *connector) {
+}
+
+static inline void
+nouveau_backlight_ctor(void) {
+}
+
+static inline void
+nouveau_backlight_dtor(void) {
+}
+#endif
+
 #endif /* __NOUVEAU_CONNECTOR_H__ */
index 540c0cbbfcee41bf3fdae2fa339d71373e58a133..f326ffd867664cd98dfc9dea789f91917c21856a 100644 (file)
@@ -582,7 +582,6 @@ nouveau_display_create(struct drm_device *dev)
                        goto vblank_err;
        }
 
-       nouveau_backlight_init(dev);
        INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
 #ifdef CONFIG_ACPI
        drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
@@ -607,7 +606,6 @@ nouveau_display_destroy(struct drm_device *dev)
 #ifdef CONFIG_ACPI
        unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
 #endif
-       nouveau_backlight_exit(dev);
        nouveau_display_vblank_fini(dev);
 
        drm_kms_helper_poll_fini(dev);
index ff92b54ce448467b290a0c61f59d3e259fed19df..eb77e41c2d4e21823d768d4073529570811d11ee 100644 (file)
@@ -85,31 +85,6 @@ int  nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
 
 void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
 
-#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
-extern int nouveau_backlight_init(struct drm_device *);
-extern void nouveau_backlight_exit(struct drm_device *);
-extern void nouveau_backlight_ctor(void);
-extern void nouveau_backlight_dtor(void);
-#else
-static inline int
-nouveau_backlight_init(struct drm_device *dev)
-{
-       return 0;
-}
-
-static inline void
-nouveau_backlight_exit(struct drm_device *dev) {
-}
-
-static inline void
-nouveau_backlight_ctor(void) {
-}
-
-static inline void
-nouveau_backlight_dtor(void) {
-}
-#endif
-
 struct drm_framebuffer *
 nouveau_user_framebuffer_create(struct drm_device *, struct drm_file *,
                                const struct drm_mode_fb_cmd2 *);
index 74d2283f2c28e7bb06f97856f93b610d12522a04..2b2baf6e0e0d6bbde2aadefd0a16e55aaadf3736 100644 (file)
@@ -458,75 +458,8 @@ nouveau_accel_init(struct nouveau_drm *drm)
        nouveau_bo_move_init(drm);
 }
 
-static int nouveau_drm_probe(struct pci_dev *pdev,
-                            const struct pci_device_id *pent)
-{
-       struct nvkm_device *device;
-       struct apertures_struct *aper;
-       bool boot = false;
-       int ret;
-
-       if (vga_switcheroo_client_probe_defer(pdev))
-               return -EPROBE_DEFER;
-
-       /* We need to check that the chipset is supported before booting
-        * fbdev off the hardware, as there's no way to put it back.
-        */
-       ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device);
-       if (ret)
-               return ret;
-
-       nvkm_device_del(&device);
-
-       /* Remove conflicting drivers (vesafb, efifb etc). */
-       aper = alloc_apertures(3);
-       if (!aper)
-               return -ENOMEM;
-
-       aper->ranges[0].base = pci_resource_start(pdev, 1);
-       aper->ranges[0].size = pci_resource_len(pdev, 1);
-       aper->count = 1;
-
-       if (pci_resource_len(pdev, 2)) {
-               aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
-               aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
-               aper->count++;
-       }
-
-       if (pci_resource_len(pdev, 3)) {
-               aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
-               aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
-               aper->count++;
-       }
-
-#ifdef CONFIG_X86
-       boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
-#endif
-       if (nouveau_modeset != 2)
-               drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot);
-       kfree(aper);
-
-       ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug,
-                                 true, true, ~0ULL, &device);
-       if (ret)
-               return ret;
-
-       pci_set_master(pdev);
-
-       if (nouveau_atomic)
-               driver_pci.driver_features |= DRIVER_ATOMIC;
-
-       ret = drm_get_pci_dev(pdev, pent, &driver_pci);
-       if (ret) {
-               nvkm_device_del(&device);
-               return ret;
-       }
-
-       return 0;
-}
-
 static int
-nouveau_drm_load(struct drm_device *dev, unsigned long flags)
+nouveau_drm_device_init(struct drm_device *dev)
 {
        struct nouveau_drm *drm;
        int ret;
@@ -538,11 +471,11 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 
        ret = nouveau_cli_init(drm, "DRM-master", &drm->master);
        if (ret)
-               return ret;
+               goto fail_alloc;
 
        ret = nouveau_cli_init(drm, "DRM", &drm->client);
        if (ret)
-               return ret;
+               goto fail_master;
 
        dev->irq_enabled = true;
 
@@ -605,13 +538,15 @@ fail_bios:
 fail_ttm:
        nouveau_vga_fini(drm);
        nouveau_cli_fini(&drm->client);
+fail_master:
        nouveau_cli_fini(&drm->master);
+fail_alloc:
        kfree(drm);
        return ret;
 }
 
 static void
-nouveau_drm_unload(struct drm_device *dev)
+nouveau_drm_device_fini(struct drm_device *dev)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
 
@@ -640,18 +575,116 @@ nouveau_drm_unload(struct drm_device *dev)
        kfree(drm);
 }
 
+static int nouveau_drm_probe(struct pci_dev *pdev,
+                            const struct pci_device_id *pent)
+{
+       struct nvkm_device *device;
+       struct drm_device *drm_dev;
+       struct apertures_struct *aper;
+       bool boot = false;
+       int ret;
+
+       if (vga_switcheroo_client_probe_defer(pdev))
+               return -EPROBE_DEFER;
+
+       /* We need to check that the chipset is supported before booting
+        * fbdev off the hardware, as there's no way to put it back.
+        */
+       ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device);
+       if (ret)
+               return ret;
+
+       nvkm_device_del(&device);
+
+       /* Remove conflicting drivers (vesafb, efifb etc). */
+       aper = alloc_apertures(3);
+       if (!aper)
+               return -ENOMEM;
+
+       aper->ranges[0].base = pci_resource_start(pdev, 1);
+       aper->ranges[0].size = pci_resource_len(pdev, 1);
+       aper->count = 1;
+
+       if (pci_resource_len(pdev, 2)) {
+               aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
+               aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
+               aper->count++;
+       }
+
+       if (pci_resource_len(pdev, 3)) {
+               aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
+               aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
+               aper->count++;
+       }
+
+#ifdef CONFIG_X86
+       boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
+#endif
+       if (nouveau_modeset != 2)
+               drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot);
+       kfree(aper);
+
+       ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug,
+                                 true, true, ~0ULL, &device);
+       if (ret)
+               return ret;
+
+       pci_set_master(pdev);
+
+       if (nouveau_atomic)
+               driver_pci.driver_features |= DRIVER_ATOMIC;
+
+       drm_dev = drm_dev_alloc(&driver_pci, &pdev->dev);
+       if (IS_ERR(drm_dev)) {
+               ret = PTR_ERR(drm_dev);
+               goto fail_nvkm;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret)
+               goto fail_drm;
+
+       drm_dev->pdev = pdev;
+       pci_set_drvdata(pdev, drm_dev);
+
+       ret = nouveau_drm_device_init(drm_dev);
+       if (ret)
+               goto fail_pci;
+
+       ret = drm_dev_register(drm_dev, pent->driver_data);
+       if (ret)
+               goto fail_drm_dev_init;
+
+       return 0;
+
+fail_drm_dev_init:
+       nouveau_drm_device_fini(drm_dev);
+fail_pci:
+       pci_disable_device(pdev);
+fail_drm:
+       drm_dev_put(drm_dev);
+fail_nvkm:
+       nvkm_device_del(&device);
+       return ret;
+}
+
 void
 nouveau_drm_device_remove(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nvkm_client *client;
        struct nvkm_device *device;
 
+       drm_dev_unregister(dev);
+
        dev->irq_enabled = false;
        client = nvxx_client(&drm->client.base);
        device = nvkm_device_find(client->device);
-       drm_put_dev(dev);
 
+       nouveau_drm_device_fini(dev);
+       pci_disable_device(pdev);
+       drm_dev_put(dev);
        nvkm_device_del(&device);
 }
 
@@ -1018,8 +1051,6 @@ driver_stub = {
                DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER |
                DRIVER_KMS_LEGACY_CONTEXT,
 
-       .load = nouveau_drm_load,
-       .unload = nouveau_drm_unload,
        .open = nouveau_drm_open,
        .postclose = nouveau_drm_postclose,
        .lastclose = nouveau_vga_lastclose,
index 6e1acaec34003de38a2cbc2d4531da0f63e55664..0b2191fa96f7bc288e28e2cb6df141422882ef1a 100644 (file)
@@ -194,8 +194,6 @@ struct nouveau_drm {
        /* modesetting */
        struct nvbios vbios;
        struct nouveau_display *display;
-       struct backlight_device *backlight;
-       struct list_head bl_connectors;
        struct work_struct hpd_work;
        struct work_struct fbcon_work;
        int fbcon_new_state;
@@ -244,10 +242,12 @@ void nouveau_drm_device_remove(struct drm_device *dev);
        struct nouveau_cli *_cli = (c);                                        \
        dev_##l(_cli->drm->dev->dev, "%s: "f, _cli->name, ##a);                \
 } while(0)
+
 #define NV_FATAL(drm,f,a...) NV_PRINTK(crit, &(drm)->client, f, ##a)
 #define NV_ERROR(drm,f,a...) NV_PRINTK(err, &(drm)->client, f, ##a)
 #define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a)
 #define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a)
+
 #define NV_DEBUG(drm,f,a...) do {                                              \
        if (unlikely(drm_debug & DRM_UT_DRIVER))                               \
                NV_PRINTK(info, &(drm)->client, f, ##a);                       \
@@ -257,6 +257,12 @@ void nouveau_drm_device_remove(struct drm_device *dev);
                NV_PRINTK(info, &(drm)->client, f, ##a);                       \
 } while(0)
 
+#define NV_PRINTK_ONCE(l,c,f,a...) NV_PRINTK(l##_once,c,f, ##a)
+
+#define NV_ERROR_ONCE(drm,f,a...) NV_PRINTK_ONCE(err, &(drm)->client, f, ##a)
+#define NV_WARN_ONCE(drm,f,a...) NV_PRINTK_ONCE(warn, &(drm)->client, f, ##a)
+#define NV_INFO_ONCE(drm,f,a...) NV_PRINTK_ONCE(info, &(drm)->client, f, ##a)
+
 extern int nouveau_modeset;
 
 #endif
index 3d485dbf310a3be8a24c7e48dd238ca18a378cd1..8089ac9a12e2673b252b6377eef4f30afc934dce 100644 (file)
@@ -50,6 +50,7 @@ nvkm-y += nvkm/engine/disp/hdmig84.o
 nvkm-y += nvkm/engine/disp/hdmigt215.o
 nvkm-y += nvkm/engine/disp/hdmigf119.o
 nvkm-y += nvkm/engine/disp/hdmigk104.o
+nvkm-y += nvkm/engine/disp/hdmigm200.o
 nvkm-y += nvkm/engine/disp/hdmigv100.o
 
 nvkm-y += nvkm/engine/disp/conn.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c
new file mode 100644 (file)
index 0000000..9b16a08
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Ilia Mirkin
+ *
+ * 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, sublicense,
+ * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ilia Mirkin
+ */
+#include "hdmi.h"
+
+void
+gm200_hdmi_scdc(struct nvkm_ior *ior, int head, u8 scdc)
+{
+       struct nvkm_device *device = ior->disp->engine.subdev.device;
+       const u32 hoff = head * 0x800;
+       const u32 ctrl = scdc & 0x3;
+
+       nvkm_mask(device, 0x61c5bc + hoff, 0x00000003, ctrl);
+
+       ior->tmds.high_speed = !!(scdc & 0x2);
+}
index 19911211a12aa55a200ac75bc2f88958598d96bd..0f0c86c32ec3affd8472948a7232cc17647dd028 100644 (file)
@@ -41,6 +41,11 @@ struct nvkm_ior {
                u8 nr;
                u8 bw;
        } dp;
+
+       /* Armed TMDS state. */
+       struct {
+               bool high_speed;
+       } tmds;
 };
 
 struct nvkm_ior_func {
@@ -61,6 +66,7 @@ struct nvkm_ior_func {
                void (*ctrl)(struct nvkm_ior *, int head, bool enable,
                             u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size,
                             u8 *vendor, u8 vendor_size);
+               void (*scdc)(struct nvkm_ior *, int head, u8 scdc);
        } hdmi;
 
        struct {
@@ -144,6 +150,8 @@ void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
 void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
 void gv100_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
 
+void gm200_hdmi_scdc(struct nvkm_ior *, int, u8);
+
 void gt215_hda_hpd(struct nvkm_ior *, int, bool);
 void gt215_hda_eld(struct nvkm_ior *, u8 *, u8);
 
index 3aa5a287923931a9b9c8d86666d14022665e957e..5f758948d6e1e8954ed762b012f25e70dca2b210 100644 (file)
@@ -176,9 +176,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
                nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
                        nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
-                                          "max_ac_packet %d rekey %d\n",
+                                          "max_ac_packet %d rekey %d scdc %d\n",
                                   args->v0.version, args->v0.state,
-                                  args->v0.max_ac_packet, args->v0.rekey);
+                                  args->v0.max_ac_packet, args->v0.rekey,
+                                  args->v0.scdc);
                        if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
                                return -EINVAL;
                        if ((args->v0.avi_infoframe_length
@@ -202,6 +203,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
                                           args->v0.max_ac_packet,
                                           args->v0.rekey, avi, avi_size,
                                           vendor, vendor_size);
+
+               if (outp->ior->func->hdmi.scdc)
+                       outp->ior->func->hdmi.scdc(
+                                       outp->ior, hidx, args->v0.scdc);
+
                return 0;
        }
                break;
index e6e6dfbb1283d41f5937c48bc23c054c73f55066..456a5a143522ae19cb2cd1011a08da4811429fe3 100644 (file)
@@ -120,13 +120,16 @@ void
 gf119_sor_clock(struct nvkm_ior *sor)
 {
        struct nvkm_device *device = sor->disp->engine.subdev.device;
-       const int  div = sor->asy.link == 3;
        const u32 soff = nv50_ior_base(sor);
+       u32 div1 = sor->asy.link == 3;
+       u32 div2 = sor->asy.link == 3;
        if (sor->asy.proto == TMDS) {
-               /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
-               nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
+               const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a;
+               nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18);
+               if (sor->tmds.high_speed)
+                       div2 = 1;
        }
-       nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
+       nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1);
 }
 
 void
index d892bdf0403484ad36193c322eae19fd88a13f64..384f82652bec1180de2d2c82a219263e44184b71 100644 (file)
@@ -99,6 +99,7 @@ gm200_sor = {
        .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gk104_hdmi_ctrl,
+               .scdc = gm200_hdmi_scdc,
        },
        .dp = {
                .lanes = { 0, 1, 2, 3 },
index 040db8a338de216f4430c80a79c3d17f4d17538e..8ba881a729eecd6c89dda224313615bab3f17e1f 100644 (file)
@@ -88,6 +88,7 @@ gv100_sor = {
        .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gv100_hdmi_ctrl,
+               .scdc = gm200_hdmi_scdc,
        },
        .dp = {
                .lanes = { 0, 1, 2, 3 },
index d02e183717dc4575bd667116ab90caaaa8289968..5c14d6ac855d2c96b1622f6bc83392cc02cd52f9 100644 (file)
@@ -801,6 +801,7 @@ acr_r352_load(struct nvkm_acr *_acr, struct nvkm_falcon *falcon,
                bl = acr->hsbl_unload_blob;
        } else {
                nvkm_error(_acr->subdev, "invalid secure boot blob!\n");
+               kfree(bl_desc);
                return -EINVAL;
        }