diff options
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 9 | ||||
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_encoder.h | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 554 |
4 files changed, 571 insertions, 2 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 5df390809f92..947c200655b4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
| @@ -1118,12 +1118,21 @@ nouveau_connector_hotplug(struct nvif_notify *notify) | |||
| 1118 | struct nouveau_drm *drm = nouveau_drm(connector->dev); | 1118 | struct nouveau_drm *drm = nouveau_drm(connector->dev); |
| 1119 | const struct nvif_notify_conn_rep_v0 *rep = notify->data; | 1119 | const struct nvif_notify_conn_rep_v0 *rep = notify->data; |
| 1120 | const char *name = connector->name; | 1120 | const char *name = connector->name; |
| 1121 | struct nouveau_encoder *nv_encoder; | ||
| 1121 | 1122 | ||
| 1122 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { | 1123 | if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { |
| 1124 | NV_DEBUG(drm, "service %s\n", name); | ||
| 1125 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) | ||
| 1126 | nv50_mstm_service(nv_encoder->dp.mstm); | ||
| 1123 | } else { | 1127 | } else { |
| 1124 | bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); | 1128 | bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); |
| 1125 | 1129 | ||
| 1126 | NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); | 1130 | NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); |
| 1131 | if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) { | ||
| 1132 | if (!plugged) | ||
| 1133 | nv50_mstm_remove(nv_encoder->dp.mstm); | ||
| 1134 | } | ||
| 1135 | |||
| 1127 | drm_helper_hpd_irq_event(connector->dev); | 1136 | drm_helper_hpd_irq_event(connector->dev); |
| 1128 | } | 1137 | } |
| 1129 | 1138 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index ddffb5cde673..0d052e1660f8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
| @@ -33,6 +33,10 @@ | |||
| 33 | #include <nvif/class.h> | 33 | #include <nvif/class.h> |
| 34 | #include <nvif/cl5070.h> | 34 | #include <nvif/cl5070.h> |
| 35 | 35 | ||
| 36 | MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)"); | ||
| 37 | static int nouveau_mst = 1; | ||
| 38 | module_param_named(mst, nouveau_mst, int, 0400); | ||
| 39 | |||
| 36 | static void | 40 | static void |
| 37 | nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_aux *aux, u8 *dpcd) | 41 | nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_aux *aux, u8 *dpcd) |
| 38 | { | 42 | { |
| @@ -88,7 +92,9 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) | |||
| 88 | 92 | ||
| 89 | nouveau_dp_probe_oui(dev, aux, dpcd); | 93 | nouveau_dp_probe_oui(dev, aux, dpcd); |
| 90 | 94 | ||
| 91 | ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, 0); | 95 | ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst); |
| 96 | if (ret == 1) | ||
| 97 | return NOUVEAU_DP_MST; | ||
| 92 | if (ret == 0) | 98 | if (ret == 0) |
| 93 | return NOUVEAU_DP_SST; | 99 | return NOUVEAU_DP_SST; |
| 94 | return ret; | 100 | return ret; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 848982bd18e9..198e5f27682f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h | |||
| @@ -103,4 +103,6 @@ struct nouveau_connector * | |||
| 103 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder); | 103 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder); |
| 104 | 104 | ||
| 105 | int nv50_mstm_detect(struct nv50_mstm *, u8 dpcd[8], int allow); | 105 | int nv50_mstm_detect(struct nv50_mstm *, u8 dpcd[8], int allow); |
| 106 | void nv50_mstm_remove(struct nv50_mstm *); | ||
| 107 | void nv50_mstm_service(struct nv50_mstm *); | ||
| 106 | #endif /* __NOUVEAU_ENCODER_H__ */ | 108 | #endif /* __NOUVEAU_ENCODER_H__ */ |
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 854fa31eddb6..a9855a4ec532 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
| @@ -2804,12 +2804,502 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) | |||
| 2804 | /****************************************************************************** | 2804 | /****************************************************************************** |
| 2805 | * MST | 2805 | * MST |
| 2806 | *****************************************************************************/ | 2806 | *****************************************************************************/ |
| 2807 | #define nv50_mstm(p) container_of((p), struct nv50_mstm, mgr) | ||
| 2808 | #define nv50_mstc(p) container_of((p), struct nv50_mstc, connector) | ||
| 2809 | #define nv50_msto(p) container_of((p), struct nv50_msto, encoder) | ||
| 2810 | |||
| 2807 | struct nv50_mstm { | 2811 | struct nv50_mstm { |
| 2808 | struct nouveau_encoder *outp; | 2812 | struct nouveau_encoder *outp; |
| 2809 | 2813 | ||
| 2810 | struct drm_dp_mst_topology_mgr mgr; | 2814 | struct drm_dp_mst_topology_mgr mgr; |
| 2815 | struct nv50_msto *msto[4]; | ||
| 2816 | |||
| 2817 | bool modified; | ||
| 2818 | }; | ||
| 2819 | |||
| 2820 | struct nv50_mstc { | ||
| 2821 | struct nv50_mstm *mstm; | ||
| 2822 | struct drm_dp_mst_port *port; | ||
| 2823 | struct drm_connector connector; | ||
| 2824 | |||
| 2825 | struct drm_display_mode *native; | ||
| 2826 | struct edid *edid; | ||
| 2827 | |||
| 2828 | int pbn; | ||
| 2811 | }; | 2829 | }; |
| 2812 | 2830 | ||
| 2831 | struct nv50_msto { | ||
| 2832 | struct drm_encoder encoder; | ||
| 2833 | |||
| 2834 | struct nv50_head *head; | ||
| 2835 | struct nv50_mstc *mstc; | ||
| 2836 | bool disabled; | ||
| 2837 | }; | ||
| 2838 | |||
| 2839 | static struct drm_dp_payload * | ||
| 2840 | nv50_msto_payload(struct nv50_msto *msto) | ||
| 2841 | { | ||
| 2842 | struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); | ||
| 2843 | struct nv50_mstc *mstc = msto->mstc; | ||
| 2844 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 2845 | int vcpi = mstc->port->vcpi.vcpi, i; | ||
| 2846 | |||
| 2847 | NV_ATOMIC(drm, "%s: vcpi %d\n", msto->encoder.name, vcpi); | ||
| 2848 | for (i = 0; i < mstm->mgr.max_payloads; i++) { | ||
| 2849 | struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; | ||
| 2850 | NV_ATOMIC(drm, "%s: %d: vcpi %d start 0x%02x slots 0x%02x\n", | ||
| 2851 | mstm->outp->base.base.name, i, payload->vcpi, | ||
| 2852 | payload->start_slot, payload->num_slots); | ||
| 2853 | } | ||
| 2854 | |||
| 2855 | for (i = 0; i < mstm->mgr.max_payloads; i++) { | ||
| 2856 | struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; | ||
| 2857 | if (payload->vcpi == vcpi) | ||
| 2858 | return payload; | ||
| 2859 | } | ||
| 2860 | |||
| 2861 | return NULL; | ||
| 2862 | } | ||
| 2863 | |||
| 2864 | static void | ||
| 2865 | nv50_msto_cleanup(struct nv50_msto *msto) | ||
| 2866 | { | ||
| 2867 | struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); | ||
| 2868 | struct nv50_mstc *mstc = msto->mstc; | ||
| 2869 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 2870 | |||
| 2871 | NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name); | ||
| 2872 | if (mstc->port && mstc->port->vcpi.vcpi > 0 && !nv50_msto_payload(msto)) | ||
| 2873 | drm_dp_mst_deallocate_vcpi(&mstm->mgr, mstc->port); | ||
| 2874 | if (msto->disabled) { | ||
| 2875 | msto->mstc = NULL; | ||
| 2876 | msto->head = NULL; | ||
| 2877 | msto->disabled = false; | ||
| 2878 | } | ||
| 2879 | } | ||
| 2880 | |||
| 2881 | static void | ||
| 2882 | nv50_msto_prepare(struct nv50_msto *msto) | ||
| 2883 | { | ||
| 2884 | struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); | ||
| 2885 | struct nv50_mstc *mstc = msto->mstc; | ||
| 2886 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 2887 | struct { | ||
| 2888 | struct nv50_disp_mthd_v1 base; | ||
| 2889 | struct nv50_disp_sor_dp_mst_vcpi_v0 vcpi; | ||
| 2890 | } args = { | ||
| 2891 | .base.version = 1, | ||
| 2892 | .base.method = NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI, | ||
| 2893 | .base.hasht = mstm->outp->dcb->hasht, | ||
| 2894 | .base.hashm = (0xf0ff & mstm->outp->dcb->hashm) | | ||
| 2895 | (0x0100 << msto->head->base.index), | ||
| 2896 | }; | ||
| 2897 | |||
| 2898 | NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); | ||
| 2899 | if (mstc->port && mstc->port->vcpi.vcpi > 0) { | ||
| 2900 | struct drm_dp_payload *payload = nv50_msto_payload(msto); | ||
| 2901 | if (payload) { | ||
| 2902 | args.vcpi.start_slot = payload->start_slot; | ||
| 2903 | args.vcpi.num_slots = payload->num_slots; | ||
| 2904 | args.vcpi.pbn = mstc->port->vcpi.pbn; | ||
| 2905 | args.vcpi.aligned_pbn = mstc->port->vcpi.aligned_pbn; | ||
| 2906 | } | ||
| 2907 | } | ||
| 2908 | |||
| 2909 | NV_ATOMIC(drm, "%s: %s: %02x %02x %04x %04x\n", | ||
| 2910 | msto->encoder.name, msto->head->base.base.name, | ||
| 2911 | args.vcpi.start_slot, args.vcpi.num_slots, | ||
| 2912 | args.vcpi.pbn, args.vcpi.aligned_pbn); | ||
| 2913 | nvif_mthd(&drm->display->disp, 0, &args, sizeof(args)); | ||
| 2914 | } | ||
| 2915 | |||
| 2916 | static int | ||
| 2917 | nv50_msto_atomic_check(struct drm_encoder *encoder, | ||
| 2918 | struct drm_crtc_state *crtc_state, | ||
| 2919 | struct drm_connector_state *conn_state) | ||
| 2920 | { | ||
| 2921 | struct nv50_mstc *mstc = nv50_mstc(conn_state->connector); | ||
| 2922 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 2923 | int bpp = conn_state->connector->display_info.bpc * 3; | ||
| 2924 | int slots; | ||
| 2925 | |||
| 2926 | mstc->pbn = drm_dp_calc_pbn_mode(crtc_state->adjusted_mode.clock, bpp); | ||
| 2927 | |||
| 2928 | slots = drm_dp_find_vcpi_slots(&mstm->mgr, mstc->pbn); | ||
| 2929 | if (slots < 0) | ||
| 2930 | return slots; | ||
| 2931 | |||
| 2932 | return nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, | ||
| 2933 | mstc->native); | ||
| 2934 | } | ||
| 2935 | |||
| 2936 | static void | ||
| 2937 | nv50_msto_enable(struct drm_encoder *encoder) | ||
| 2938 | { | ||
| 2939 | struct nv50_head *head = nv50_head(encoder->crtc); | ||
| 2940 | struct nv50_msto *msto = nv50_msto(encoder); | ||
| 2941 | struct nv50_mstc *mstc = NULL; | ||
| 2942 | struct nv50_mstm *mstm = NULL; | ||
| 2943 | struct drm_connector *connector; | ||
| 2944 | u8 proto, depth; | ||
| 2945 | int slots; | ||
| 2946 | bool r; | ||
| 2947 | |||
| 2948 | drm_for_each_connector(connector, encoder->dev) { | ||
| 2949 | if (connector->state->best_encoder == &msto->encoder) { | ||
| 2950 | mstc = nv50_mstc(connector); | ||
| 2951 | mstm = mstc->mstm; | ||
| 2952 | break; | ||
| 2953 | } | ||
| 2954 | } | ||
| 2955 | |||
| 2956 | if (WARN_ON(!mstc)) | ||
| 2957 | return; | ||
| 2958 | |||
| 2959 | r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, &slots); | ||
| 2960 | WARN_ON(!r); | ||
| 2961 | |||
| 2962 | if (mstm->outp->dcb->sorconf.link & 1) | ||
| 2963 | proto = 0x8; | ||
| 2964 | else | ||
| 2965 | proto = 0x9; | ||
| 2966 | |||
| 2967 | switch (mstc->connector.display_info.bpc) { | ||
| 2968 | case 6: depth = 0x2; break; | ||
| 2969 | case 8: depth = 0x5; break; | ||
| 2970 | case 10: | ||
| 2971 | default: depth = 0x6; break; | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | mstm->outp->update(mstm->outp, head->base.index, | ||
| 2975 | &head->base.base.state->adjusted_mode, proto, depth); | ||
| 2976 | |||
| 2977 | msto->head = head; | ||
| 2978 | msto->mstc = mstc; | ||
| 2979 | mstm->modified = true; | ||
| 2980 | } | ||
| 2981 | |||
| 2982 | static void | ||
| 2983 | nv50_msto_disable(struct drm_encoder *encoder) | ||
| 2984 | { | ||
| 2985 | struct nv50_msto *msto = nv50_msto(encoder); | ||
| 2986 | struct nv50_mstc *mstc = msto->mstc; | ||
| 2987 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 2988 | |||
| 2989 | if (mstc->port) | ||
| 2990 | drm_dp_mst_reset_vcpi_slots(&mstm->mgr, mstc->port); | ||
| 2991 | |||
| 2992 | mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); | ||
| 2993 | mstm->modified = true; | ||
| 2994 | msto->disabled = true; | ||
| 2995 | } | ||
| 2996 | |||
| 2997 | static const struct drm_encoder_helper_funcs | ||
| 2998 | nv50_msto_help = { | ||
| 2999 | .disable = nv50_msto_disable, | ||
| 3000 | .enable = nv50_msto_enable, | ||
| 3001 | .atomic_check = nv50_msto_atomic_check, | ||
| 3002 | }; | ||
| 3003 | |||
| 3004 | static void | ||
| 3005 | nv50_msto_destroy(struct drm_encoder *encoder) | ||
| 3006 | { | ||
| 3007 | struct nv50_msto *msto = nv50_msto(encoder); | ||
| 3008 | drm_encoder_cleanup(&msto->encoder); | ||
| 3009 | kfree(msto); | ||
| 3010 | } | ||
| 3011 | |||
| 3012 | static const struct drm_encoder_funcs | ||
| 3013 | nv50_msto = { | ||
| 3014 | .destroy = nv50_msto_destroy, | ||
| 3015 | }; | ||
| 3016 | |||
| 3017 | static int | ||
| 3018 | nv50_msto_new(struct drm_device *dev, u32 heads, const char *name, int id, | ||
| 3019 | struct nv50_msto **pmsto) | ||
| 3020 | { | ||
| 3021 | struct nv50_msto *msto; | ||
| 3022 | int ret; | ||
| 3023 | |||
| 3024 | if (!(msto = *pmsto = kzalloc(sizeof(*msto), GFP_KERNEL))) | ||
| 3025 | return -ENOMEM; | ||
| 3026 | |||
| 3027 | ret = drm_encoder_init(dev, &msto->encoder, &nv50_msto, | ||
| 3028 | DRM_MODE_ENCODER_DPMST, "%s-mst-%d", name, id); | ||
| 3029 | if (ret) { | ||
| 3030 | kfree(*pmsto); | ||
| 3031 | *pmsto = NULL; | ||
| 3032 | return ret; | ||
| 3033 | } | ||
| 3034 | |||
| 3035 | drm_encoder_helper_add(&msto->encoder, &nv50_msto_help); | ||
| 3036 | msto->encoder.possible_crtcs = heads; | ||
| 3037 | return 0; | ||
| 3038 | } | ||
| 3039 | |||
| 3040 | static struct drm_encoder * | ||
| 3041 | nv50_mstc_atomic_best_encoder(struct drm_connector *connector, | ||
| 3042 | struct drm_connector_state *connector_state) | ||
| 3043 | { | ||
| 3044 | struct nv50_head *head = nv50_head(connector_state->crtc); | ||
| 3045 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3046 | if (mstc->port) { | ||
| 3047 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 3048 | return &mstm->msto[head->base.index]->encoder; | ||
| 3049 | } | ||
| 3050 | return NULL; | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | static struct drm_encoder * | ||
| 3054 | nv50_mstc_best_encoder(struct drm_connector *connector) | ||
| 3055 | { | ||
| 3056 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3057 | if (mstc->port) { | ||
| 3058 | struct nv50_mstm *mstm = mstc->mstm; | ||
| 3059 | return &mstm->msto[0]->encoder; | ||
| 3060 | } | ||
| 3061 | return NULL; | ||
| 3062 | } | ||
| 3063 | |||
| 3064 | static enum drm_mode_status | ||
| 3065 | nv50_mstc_mode_valid(struct drm_connector *connector, | ||
| 3066 | struct drm_display_mode *mode) | ||
| 3067 | { | ||
| 3068 | return MODE_OK; | ||
| 3069 | } | ||
| 3070 | |||
| 3071 | static int | ||
| 3072 | nv50_mstc_get_modes(struct drm_connector *connector) | ||
| 3073 | { | ||
| 3074 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3075 | int ret = 0; | ||
| 3076 | |||
| 3077 | mstc->edid = drm_dp_mst_get_edid(&mstc->connector, mstc->port->mgr, mstc->port); | ||
| 3078 | drm_mode_connector_update_edid_property(&mstc->connector, mstc->edid); | ||
| 3079 | if (mstc->edid) { | ||
| 3080 | ret = drm_add_edid_modes(&mstc->connector, mstc->edid); | ||
| 3081 | drm_edid_to_eld(&mstc->connector, mstc->edid); | ||
| 3082 | } | ||
| 3083 | |||
| 3084 | if (!mstc->connector.display_info.bpc) | ||
| 3085 | mstc->connector.display_info.bpc = 8; | ||
| 3086 | |||
| 3087 | if (mstc->native) | ||
| 3088 | drm_mode_destroy(mstc->connector.dev, mstc->native); | ||
| 3089 | mstc->native = nouveau_conn_native_mode(&mstc->connector); | ||
| 3090 | return ret; | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | static const struct drm_connector_helper_funcs | ||
| 3094 | nv50_mstc_help = { | ||
| 3095 | .get_modes = nv50_mstc_get_modes, | ||
| 3096 | .mode_valid = nv50_mstc_mode_valid, | ||
| 3097 | .best_encoder = nv50_mstc_best_encoder, | ||
| 3098 | .atomic_best_encoder = nv50_mstc_atomic_best_encoder, | ||
| 3099 | }; | ||
| 3100 | |||
| 3101 | static enum drm_connector_status | ||
| 3102 | nv50_mstc_detect(struct drm_connector *connector, bool force) | ||
| 3103 | { | ||
| 3104 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3105 | if (!mstc->port) | ||
| 3106 | return connector_status_disconnected; | ||
| 3107 | return drm_dp_mst_detect_port(connector, mstc->port->mgr, mstc->port); | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | static void | ||
| 3111 | nv50_mstc_destroy(struct drm_connector *connector) | ||
| 3112 | { | ||
| 3113 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3114 | drm_connector_cleanup(&mstc->connector); | ||
| 3115 | kfree(mstc); | ||
| 3116 | } | ||
| 3117 | |||
| 3118 | static const struct drm_connector_funcs | ||
| 3119 | nv50_mstc = { | ||
| 3120 | .dpms = drm_atomic_helper_connector_dpms, | ||
| 3121 | .reset = nouveau_conn_reset, | ||
| 3122 | .detect = nv50_mstc_detect, | ||
| 3123 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 3124 | .set_property = drm_atomic_helper_connector_set_property, | ||
| 3125 | .destroy = nv50_mstc_destroy, | ||
| 3126 | .atomic_duplicate_state = nouveau_conn_atomic_duplicate_state, | ||
| 3127 | .atomic_destroy_state = nouveau_conn_atomic_destroy_state, | ||
| 3128 | .atomic_set_property = nouveau_conn_atomic_set_property, | ||
| 3129 | .atomic_get_property = nouveau_conn_atomic_get_property, | ||
| 3130 | }; | ||
| 3131 | |||
| 3132 | static int | ||
| 3133 | nv50_mstc_new(struct nv50_mstm *mstm, struct drm_dp_mst_port *port, | ||
| 3134 | const char *path, struct nv50_mstc **pmstc) | ||
| 3135 | { | ||
| 3136 | struct drm_device *dev = mstm->outp->base.base.dev; | ||
| 3137 | struct nv50_mstc *mstc; | ||
| 3138 | int ret, i; | ||
| 3139 | |||
| 3140 | if (!(mstc = *pmstc = kzalloc(sizeof(*mstc), GFP_KERNEL))) | ||
| 3141 | return -ENOMEM; | ||
| 3142 | mstc->mstm = mstm; | ||
| 3143 | mstc->port = port; | ||
| 3144 | |||
| 3145 | ret = drm_connector_init(dev, &mstc->connector, &nv50_mstc, | ||
| 3146 | DRM_MODE_CONNECTOR_DisplayPort); | ||
| 3147 | if (ret) { | ||
| 3148 | kfree(*pmstc); | ||
| 3149 | *pmstc = NULL; | ||
| 3150 | return ret; | ||
| 3151 | } | ||
| 3152 | |||
| 3153 | drm_connector_helper_add(&mstc->connector, &nv50_mstc_help); | ||
| 3154 | |||
| 3155 | mstc->connector.funcs->reset(&mstc->connector); | ||
| 3156 | nouveau_conn_attach_properties(&mstc->connector); | ||
| 3157 | |||
| 3158 | for (i = 0; i < ARRAY_SIZE(mstm->msto) && mstm->msto; i++) | ||
| 3159 | drm_mode_connector_attach_encoder(&mstc->connector, &mstm->msto[i]->encoder); | ||
| 3160 | |||
| 3161 | drm_object_attach_property(&mstc->connector.base, dev->mode_config.path_property, 0); | ||
| 3162 | drm_object_attach_property(&mstc->connector.base, dev->mode_config.tile_property, 0); | ||
| 3163 | drm_mode_connector_set_path_property(&mstc->connector, path); | ||
| 3164 | return 0; | ||
| 3165 | } | ||
| 3166 | |||
| 3167 | static void | ||
| 3168 | nv50_mstm_cleanup(struct nv50_mstm *mstm) | ||
| 3169 | { | ||
| 3170 | struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); | ||
| 3171 | struct drm_encoder *encoder; | ||
| 3172 | int ret; | ||
| 3173 | |||
| 3174 | NV_ATOMIC(drm, "%s: mstm cleanup\n", mstm->outp->base.base.name); | ||
| 3175 | ret = drm_dp_check_act_status(&mstm->mgr); | ||
| 3176 | |||
| 3177 | ret = drm_dp_update_payload_part2(&mstm->mgr); | ||
| 3178 | |||
| 3179 | drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { | ||
| 3180 | if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { | ||
| 3181 | struct nv50_msto *msto = nv50_msto(encoder); | ||
| 3182 | struct nv50_mstc *mstc = msto->mstc; | ||
| 3183 | if (mstc && mstc->mstm == mstm) | ||
| 3184 | nv50_msto_cleanup(msto); | ||
| 3185 | } | ||
| 3186 | } | ||
| 3187 | |||
| 3188 | mstm->modified = false; | ||
| 3189 | } | ||
| 3190 | |||
| 3191 | static void | ||
| 3192 | nv50_mstm_prepare(struct nv50_mstm *mstm) | ||
| 3193 | { | ||
| 3194 | struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); | ||
| 3195 | struct drm_encoder *encoder; | ||
| 3196 | int ret; | ||
| 3197 | |||
| 3198 | NV_ATOMIC(drm, "%s: mstm prepare\n", mstm->outp->base.base.name); | ||
| 3199 | ret = drm_dp_update_payload_part1(&mstm->mgr); | ||
| 3200 | |||
| 3201 | drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { | ||
| 3202 | if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { | ||
| 3203 | struct nv50_msto *msto = nv50_msto(encoder); | ||
| 3204 | struct nv50_mstc *mstc = msto->mstc; | ||
| 3205 | if (mstc && mstc->mstm == mstm) | ||
| 3206 | nv50_msto_prepare(msto); | ||
| 3207 | } | ||
| 3208 | } | ||
| 3209 | } | ||
| 3210 | |||
| 3211 | static void | ||
| 3212 | nv50_mstm_hotplug(struct drm_dp_mst_topology_mgr *mgr) | ||
| 3213 | { | ||
| 3214 | struct nv50_mstm *mstm = nv50_mstm(mgr); | ||
| 3215 | drm_kms_helper_hotplug_event(mstm->outp->base.base.dev); | ||
| 3216 | } | ||
| 3217 | |||
| 3218 | static void | ||
| 3219 | nv50_mstm_destroy_connector(struct drm_dp_mst_topology_mgr *mgr, | ||
| 3220 | struct drm_connector *connector) | ||
| 3221 | { | ||
| 3222 | struct nouveau_drm *drm = nouveau_drm(connector->dev); | ||
| 3223 | struct nv50_mstc *mstc = nv50_mstc(connector); | ||
| 3224 | |||
| 3225 | drm_connector_unregister(&mstc->connector); | ||
| 3226 | |||
| 3227 | drm_modeset_lock_all(drm->dev); | ||
| 3228 | drm_fb_helper_remove_one_connector(&drm->fbcon->helper, &mstc->connector); | ||
| 3229 | mstc->port = NULL; | ||
| 3230 | drm_modeset_unlock_all(drm->dev); | ||
| 3231 | |||
| 3232 | drm_connector_unreference(&mstc->connector); | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | static void | ||
| 3236 | nv50_mstm_register_connector(struct drm_connector *connector) | ||
| 3237 | { | ||
| 3238 | struct nouveau_drm *drm = nouveau_drm(connector->dev); | ||
| 3239 | |||
| 3240 | drm_modeset_lock_all(drm->dev); | ||
| 3241 | drm_fb_helper_add_one_connector(&drm->fbcon->helper, connector); | ||
| 3242 | drm_modeset_unlock_all(drm->dev); | ||
| 3243 | |||
| 3244 | drm_connector_register(connector); | ||
| 3245 | } | ||
| 3246 | |||
| 3247 | static struct drm_connector * | ||
| 3248 | nv50_mstm_add_connector(struct drm_dp_mst_topology_mgr *mgr, | ||
| 3249 | struct drm_dp_mst_port *port, const char *path) | ||
| 3250 | { | ||
| 3251 | struct nv50_mstm *mstm = nv50_mstm(mgr); | ||
| 3252 | struct nv50_mstc *mstc; | ||
| 3253 | int ret; | ||
| 3254 | |||
| 3255 | ret = nv50_mstc_new(mstm, port, path, &mstc); | ||
| 3256 | if (ret) { | ||
| 3257 | if (mstc) | ||
| 3258 | mstc->connector.funcs->destroy(&mstc->connector); | ||
| 3259 | return NULL; | ||
| 3260 | } | ||
| 3261 | |||
| 3262 | return &mstc->connector; | ||
| 3263 | } | ||
| 3264 | |||
| 3265 | static const struct drm_dp_mst_topology_cbs | ||
| 3266 | nv50_mstm = { | ||
| 3267 | .add_connector = nv50_mstm_add_connector, | ||
| 3268 | .register_connector = nv50_mstm_register_connector, | ||
| 3269 | .destroy_connector = nv50_mstm_destroy_connector, | ||
| 3270 | .hotplug = nv50_mstm_hotplug, | ||
| 3271 | }; | ||
| 3272 | |||
| 3273 | void | ||
| 3274 | nv50_mstm_service(struct nv50_mstm *mstm) | ||
| 3275 | { | ||
| 3276 | struct drm_dp_aux *aux = mstm->mgr.aux; | ||
| 3277 | bool handled = true; | ||
| 3278 | int ret; | ||
| 3279 | u8 esi[8] = {}; | ||
| 3280 | |||
| 3281 | while (handled) { | ||
| 3282 | ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8); | ||
| 3283 | if (ret != 8) { | ||
| 3284 | drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); | ||
| 3285 | return; | ||
| 3286 | } | ||
| 3287 | |||
| 3288 | drm_dp_mst_hpd_irq(&mstm->mgr, esi, &handled); | ||
| 3289 | if (!handled) | ||
| 3290 | break; | ||
| 3291 | |||
| 3292 | drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], 3); | ||
| 3293 | } | ||
| 3294 | } | ||
| 3295 | |||
| 3296 | void | ||
| 3297 | nv50_mstm_remove(struct nv50_mstm *mstm) | ||
| 3298 | { | ||
| 3299 | if (mstm) | ||
| 3300 | drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); | ||
| 3301 | } | ||
| 3302 | |||
| 2813 | static int | 3303 | static int |
| 2814 | nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) | 3304 | nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) |
| 2815 | { | 3305 | { |
| @@ -2873,6 +3363,20 @@ nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow) | |||
| 2873 | } | 3363 | } |
| 2874 | 3364 | ||
| 2875 | static void | 3365 | static void |
| 3366 | nv50_mstm_fini(struct nv50_mstm *mstm) | ||
| 3367 | { | ||
| 3368 | if (mstm && mstm->mgr.mst_state) | ||
| 3369 | drm_dp_mst_topology_mgr_suspend(&mstm->mgr); | ||
| 3370 | } | ||
| 3371 | |||
| 3372 | static void | ||
| 3373 | nv50_mstm_init(struct nv50_mstm *mstm) | ||
| 3374 | { | ||
| 3375 | if (mstm && mstm->mgr.mst_state) | ||
| 3376 | drm_dp_mst_topology_mgr_resume(&mstm->mgr); | ||
| 3377 | } | ||
| 3378 | |||
| 3379 | static void | ||
| 2876 | nv50_mstm_del(struct nv50_mstm **pmstm) | 3380 | nv50_mstm_del(struct nv50_mstm **pmstm) |
| 2877 | { | 3381 | { |
| 2878 | struct nv50_mstm *mstm = *pmstm; | 3382 | struct nv50_mstm *mstm = *pmstm; |
| @@ -2889,17 +3393,36 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, | |||
| 2889 | const int max_payloads = hweight8(outp->dcb->heads); | 3393 | const int max_payloads = hweight8(outp->dcb->heads); |
| 2890 | struct drm_device *dev = outp->base.base.dev; | 3394 | struct drm_device *dev = outp->base.base.dev; |
| 2891 | struct nv50_mstm *mstm; | 3395 | struct nv50_mstm *mstm; |
| 2892 | int ret; | 3396 | int ret, i; |
| 3397 | u8 dpcd; | ||
| 3398 | |||
| 3399 | /* This is a workaround for some monitors not functioning | ||
| 3400 | * correctly in MST mode on initial module load. I think | ||
| 3401 | * some bad interaction with the VBIOS may be responsible. | ||
| 3402 | * | ||
| 3403 | * A good ol' off and on again seems to work here ;) | ||
| 3404 | */ | ||
| 3405 | ret = drm_dp_dpcd_readb(aux, DP_DPCD_REV, &dpcd); | ||
| 3406 | if (ret >= 0 && dpcd >= 0x12) | ||
| 3407 | drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0); | ||
| 2893 | 3408 | ||
| 2894 | if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL))) | 3409 | if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL))) |
| 2895 | return -ENOMEM; | 3410 | return -ENOMEM; |
| 2896 | mstm->outp = outp; | 3411 | mstm->outp = outp; |
| 3412 | mstm->mgr.cbs = &nv50_mstm; | ||
| 2897 | 3413 | ||
| 2898 | ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev->dev, aux, aux_max, | 3414 | ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev->dev, aux, aux_max, |
| 2899 | max_payloads, conn_base_id); | 3415 | max_payloads, conn_base_id); |
| 2900 | if (ret) | 3416 | if (ret) |
| 2901 | return ret; | 3417 | return ret; |
| 2902 | 3418 | ||
| 3419 | for (i = 0; i < max_payloads; i++) { | ||
| 3420 | ret = nv50_msto_new(dev, outp->dcb->heads, outp->base.base.name, | ||
| 3421 | i, &mstm->msto[i]); | ||
| 3422 | if (ret) | ||
| 3423 | return ret; | ||
| 3424 | } | ||
| 3425 | |||
| 2903 | return 0; | 3426 | return 0; |
| 2904 | } | 3427 | } |
| 2905 | 3428 | ||
| @@ -3364,10 +3887,20 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock) | |||
| 3364 | { | 3887 | { |
| 3365 | struct nv50_disp *disp = nv50_disp(drm->dev); | 3888 | struct nv50_disp *disp = nv50_disp(drm->dev); |
| 3366 | struct nv50_dmac *core = &disp->mast.base; | 3889 | struct nv50_dmac *core = &disp->mast.base; |
| 3890 | struct nv50_mstm *mstm; | ||
| 3891 | struct drm_encoder *encoder; | ||
| 3367 | u32 *push; | 3892 | u32 *push; |
| 3368 | 3893 | ||
| 3369 | NV_ATOMIC(drm, "commit core %08x\n", interlock); | 3894 | NV_ATOMIC(drm, "commit core %08x\n", interlock); |
| 3370 | 3895 | ||
| 3896 | drm_for_each_encoder(encoder, drm->dev) { | ||
| 3897 | if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { | ||
| 3898 | mstm = nouveau_encoder(encoder)->dp.mstm; | ||
| 3899 | if (mstm && mstm->modified) | ||
| 3900 | nv50_mstm_prepare(mstm); | ||
| 3901 | } | ||
| 3902 | } | ||
| 3903 | |||
| 3371 | if ((push = evo_wait(core, 5))) { | 3904 | if ((push = evo_wait(core, 5))) { |
| 3372 | evo_mthd(push, 0x0084, 1); | 3905 | evo_mthd(push, 0x0084, 1); |
| 3373 | evo_data(push, 0x80000000); | 3906 | evo_data(push, 0x80000000); |
| @@ -3383,6 +3916,14 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock) | |||
| 3383 | ) < 0) | 3916 | ) < 0) |
| 3384 | NV_ERROR(drm, "EVO timeout\n"); | 3917 | NV_ERROR(drm, "EVO timeout\n"); |
| 3385 | } | 3918 | } |
| 3919 | |||
| 3920 | drm_for_each_encoder(encoder, drm->dev) { | ||
| 3921 | if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { | ||
| 3922 | mstm = nouveau_encoder(encoder)->dp.mstm; | ||
| 3923 | if (mstm && mstm->modified) | ||
| 3924 | nv50_mstm_cleanup(mstm); | ||
| 3925 | } | ||
| 3926 | } | ||
| 3386 | } | 3927 | } |
| 3387 | 3928 | ||
| 3388 | static void | 3929 | static void |
| @@ -3792,6 +4333,8 @@ nv50_disp_func = { | |||
| 3792 | void | 4333 | void |
| 3793 | nv50_display_fini(struct drm_device *dev) | 4334 | nv50_display_fini(struct drm_device *dev) |
| 3794 | { | 4335 | { |
| 4336 | struct nouveau_encoder *nv_encoder; | ||
| 4337 | struct drm_encoder *encoder; | ||
| 3795 | struct drm_plane *plane; | 4338 | struct drm_plane *plane; |
| 3796 | 4339 | ||
| 3797 | drm_for_each_plane(plane, dev) { | 4340 | drm_for_each_plane(plane, dev) { |
| @@ -3800,6 +4343,13 @@ nv50_display_fini(struct drm_device *dev) | |||
| 3800 | continue; | 4343 | continue; |
| 3801 | nv50_wndw_fini(wndw); | 4344 | nv50_wndw_fini(wndw); |
| 3802 | } | 4345 | } |
| 4346 | |||
| 4347 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
| 4348 | if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { | ||
| 4349 | nv_encoder = nouveau_encoder(encoder); | ||
| 4350 | nv50_mstm_fini(nv_encoder->dp.mstm); | ||
| 4351 | } | ||
| 4352 | } | ||
| 3803 | } | 4353 | } |
| 3804 | 4354 | ||
| 3805 | int | 4355 | int |
| @@ -3827,6 +4377,8 @@ nv50_display_init(struct drm_device *dev) | |||
| 3827 | help = encoder->helper_private; | 4377 | help = encoder->helper_private; |
| 3828 | if (help && help->dpms) | 4378 | if (help && help->dpms) |
| 3829 | help->dpms(encoder, DRM_MODE_DPMS_ON); | 4379 | help->dpms(encoder, DRM_MODE_DPMS_ON); |
| 4380 | |||
| 4381 | nv50_mstm_init(nv_encoder->dp.mstm); | ||
| 3830 | } | 4382 | } |
| 3831 | } | 4383 | } |
| 3832 | 4384 | ||
