drm/amd/display: Don't ask PSP to load DMCUB for backdoor load
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / display / amdgpu_dm / amdgpu_dm.c
index 504278d..d09fabe 100644 (file)
@@ -801,10 +801,20 @@ static int dm_dmub_hw_init(struct amdgpu_device *adev)
 
        fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
 
-       memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
-              fw_inst_const_size);
+       /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
+        * amdgpu_ucode_init_single_fw will load dmub firmware
+        * fw_inst_const part to cw0; otherwise, the firmware back door load
+        * will be done by dm_dmub_hw_init
+        */
+       if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
+               memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
+                               fw_inst_const_size);
+       }
+
        memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr, fw_bss_data,
               fw_bss_data_size);
+
+       /* Copy firmware bios info into FB memory. */
        memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
               adev->bios_size);
 
@@ -823,6 +833,10 @@ static int dm_dmub_hw_init(struct amdgpu_device *adev)
        hw_params.fb_base = adev->gmc.fb_start;
        hw_params.fb_offset = adev->gmc.aper_base;
 
+       /* backdoor load firmware and trigger dmub running */
+       if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+               hw_params.load_inst_const = true;
+
        if (dmcu)
                hw_params.psp_version = dmcu->psp_version;
 
@@ -940,14 +954,14 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                goto error;
        }
 
-       dc_hardware_init(adev->dm.dc);
-
        r = dm_dmub_hw_init(adev);
        if (r) {
                DRM_ERROR("DMUB interface failed to initialize: status=%d\n", r);
                goto error;
        }
 
+       dc_hardware_init(adev->dm.dc);
+
        adev->dm.freesync_module = mod_freesync_create(adev->dm.dc);
        if (!adev->dm.freesync_module) {
                DRM_ERROR(
@@ -960,7 +974,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
 
 #ifdef CONFIG_DRM_AMD_DC_HDCP
        if (adev->asic_type >= CHIP_RAVEN) {
-               adev->dm.hdcp_workqueue = hdcp_create_workqueue(&adev->psp, &init_params.cp_psp, adev->dm.dc);
+               adev->dm.hdcp_workqueue = hdcp_create_workqueue(adev, &init_params.cp_psp, adev->dm.dc);
 
                if (!adev->dm.hdcp_workqueue)
                        DRM_ERROR("amdgpu: failed to initialize hdcp_workqueue.\n");
@@ -1192,22 +1206,21 @@ static int dm_dmub_sw_init(struct amdgpu_device *adev)
                return 0;
        }
 
-       if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
-               DRM_WARN("Only PSP firmware loading is supported for DMUB\n");
-               return 0;
-       }
-
        hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
-       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
-               AMDGPU_UCODE_ID_DMCUB;
-       adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw = adev->dm.dmub_fw;
-       adev->firmware.fw_size +=
-               ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
 
-       adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
+       if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+               adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
+                       AMDGPU_UCODE_ID_DMCUB;
+               adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
+                       adev->dm.dmub_fw;
+               adev->firmware.fw_size +=
+                       ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
 
-       DRM_INFO("Loading DMUB firmware via PSP: version=0x%08X\n",
-                adev->dm.dmcub_fw_version);
+               DRM_INFO("Loading DMUB firmware via PSP: version=0x%08X\n",
+                        adev->dm.dmcub_fw_version);
+       }
+
+       adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
 
        adev->dm.dmub_srv = kzalloc(sizeof(*adev->dm.dmub_srv), GFP_KERNEL);
        dmub_srv = adev->dm.dmub_srv;
@@ -1758,6 +1771,61 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
        .atomic_commit_tail = amdgpu_dm_atomic_commit_tail
 };
 
+static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
+{
+       u32 max_cll, min_cll, max, min, q, r;
+       struct amdgpu_dm_backlight_caps *caps;
+       struct amdgpu_display_manager *dm;
+       struct drm_connector *conn_base;
+       struct amdgpu_device *adev;
+       static const u8 pre_computed_values[] = {
+               50, 51, 52, 53, 55, 56, 57, 58, 59, 61, 62, 63, 65, 66, 68, 69,
+               71, 72, 74, 75, 77, 79, 81, 82, 84, 86, 88, 90, 92, 94, 96, 98};
+
+       if (!aconnector || !aconnector->dc_link)
+               return;
+
+       conn_base = &aconnector->base;
+       adev = conn_base->dev->dev_private;
+       dm = &adev->dm;
+       caps = &dm->backlight_caps;
+       caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
+       caps->aux_support = false;
+       max_cll = conn_base->hdr_sink_metadata.hdmi_type1.max_cll;
+       min_cll = conn_base->hdr_sink_metadata.hdmi_type1.min_cll;
+
+       if (caps->ext_caps->bits.oled == 1 ||
+           caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
+           caps->ext_caps->bits.hdr_aux_backlight_control == 1)
+               caps->aux_support = true;
+
+       /* From the specification (CTA-861-G), for calculating the maximum
+        * luminance we need to use:
+        *      Luminance = 50*2**(CV/32)
+        * Where CV is a one-byte value.
+        * For calculating this expression we may need float point precision;
+        * to avoid this complexity level, we take advantage that CV is divided
+        * by a constant. From the Euclids division algorithm, we know that CV
+        * can be written as: CV = 32*q + r. Next, we replace CV in the
+        * Luminance expression and get 50*(2**q)*(2**(r/32)), hence we just
+        * need to pre-compute the value of r/32. For pre-computing the values
+        * We just used the following Ruby line:
+        *      (0...32).each {|cv| puts (50*2**(cv/32.0)).round}
+        * The results of the above expressions can be verified at
+        * pre_computed_values.
+        */
+       q = max_cll >> 5;
+       r = max_cll % 32;
+       max = (1 << q) * pre_computed_values[r];
+
+       // min luminance: maxLum * (CV/255)^2 / 100
+       q = DIV_ROUND_CLOSEST(min_cll, 255);
+       min = max * DIV_ROUND_CLOSEST((q * q), 100);
+
+       caps->aux_max_input_signal = max;
+       caps->aux_min_input_signal = min;
+}
+
 static void
 amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
 {
@@ -1872,7 +1940,7 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector)
                                            aconnector->edid);
                }
                amdgpu_dm_update_freesync_caps(connector, aconnector->edid);
-
+               update_connector_ext_caps(aconnector);
        } else {
                drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
                amdgpu_dm_update_freesync_caps(connector, NULL);
@@ -2484,6 +2552,7 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
 
 #define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
 #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
+#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
 
 #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
        defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
@@ -2498,9 +2567,11 @@ static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm)
 
        amdgpu_acpi_get_backlight_caps(dm->adev, &caps);
        if (caps.caps_valid) {
+               dm->backlight_caps.caps_valid = true;
+               if (caps.aux_support)
+                       return;
                dm->backlight_caps.min_input_signal = caps.min_input_signal;
                dm->backlight_caps.max_input_signal = caps.max_input_signal;
-               dm->backlight_caps.caps_valid = true;
        } else {
                dm->backlight_caps.min_input_signal =
                                AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
@@ -2508,40 +2579,95 @@ static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm)
                                AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
        }
 #else
+       if (dm->backlight_caps.aux_support)
+               return;
+
        dm->backlight_caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
        dm->backlight_caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
 #endif
 }
 
+static int set_backlight_via_aux(struct dc_link *link, uint32_t brightness)
+{
+       bool rc;
+
+       if (!link)
+               return 1;
+
+       rc = dc_link_set_backlight_level_nits(link, true, brightness,
+                                             AUX_BL_DEFAULT_TRANSITION_TIME_MS);
+
+       return rc ? 0 : 1;
+}
+
+static u32 convert_brightness(const struct amdgpu_dm_backlight_caps *caps,
+                             const uint32_t user_brightness)
+{
+       u32 min, max, conversion_pace;
+       u32 brightness = user_brightness;
+
+       if (!caps)
+               goto out;
+
+       if (!caps->aux_support) {
+               max = caps->max_input_signal;
+               min = caps->min_input_signal;
+               /*
+                * The brightness input is in the range 0-255
+                * It needs to be rescaled to be between the
+                * requested min and max input signal
+                * It also needs to be scaled up by 0x101 to
+                * match the DC interface which has a range of
+                * 0 to 0xffff
+                */
+               conversion_pace = 0x101;
+               brightness =
+                       user_brightness
+                       * conversion_pace
+                       * (max - min)
+                       / AMDGPU_MAX_BL_LEVEL
+                       + min * conversion_pace;
+       } else {
+               /* TODO
+                * We are doing a linear interpolation here, which is OK but
+                * does not provide the optimal result. We probably want
+                * something close to the Perceptual Quantizer (PQ) curve.
+                */
+               max = caps->aux_max_input_signal;
+               min = caps->aux_min_input_signal;
+
+               brightness = (AMDGPU_MAX_BL_LEVEL - user_brightness) * min
+                              + user_brightness * max;
+               // Multiple the value by 1000 since we use millinits
+               brightness *= 1000;
+               brightness = DIV_ROUND_CLOSEST(brightness, AMDGPU_MAX_BL_LEVEL);
+       }
+
+out:
+       return brightness;
+}
+
 static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
 {
        struct amdgpu_display_manager *dm = bl_get_data(bd);
        struct amdgpu_dm_backlight_caps caps;
-       uint32_t brightness = bd->props.brightness;
+       struct dc_link *link = NULL;
+       u32 brightness;
+       bool rc;
 
        amdgpu_dm_update_backlight_caps(dm);
        caps = dm->backlight_caps;
-       /*
-        * The brightness input is in the range 0-255
-        * It needs to be rescaled to be between the
-        * requested min and max input signal
-        *
-        * It also needs to be scaled up by 0x101 to
-        * match the DC interface which has a range of
-        * 0 to 0xffff
-        */
-       brightness =
-               brightness
-               * 0x101
-               * (caps.max_input_signal - caps.min_input_signal)
-               / AMDGPU_MAX_BL_LEVEL
-               + caps.min_input_signal * 0x101;
-
-       if (dc_link_set_backlight_level(dm->backlight_link,
-                       brightness, 0))
-               return 0;
-       else
-               return 1;
+
+       link = (struct dc_link *)dm->backlight_link;
+
+       brightness = convert_brightness(&caps, bd->props.brightness);
+       // Change brightness based on AUX property
+       if (caps.aux_support)
+               return set_backlight_via_aux(link, brightness);
+
+       rc = dc_link_set_backlight_level(dm->backlight_link, brightness, 0);
+
+       return rc ? 0 : 1;
 }
 
 static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
@@ -7759,24 +7885,27 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
        struct drm_crtc_state *new_crtc_state, *old_crtc_state;
        struct dm_crtc_state *new_dm_crtc_state, *old_dm_crtc_state;
        struct dc_stream_status *status = NULL;
-
-       struct dc_surface_update *updates;
        enum surface_update_type update_type = UPDATE_TYPE_FAST;
+       struct surface_info_bundle {
+               struct dc_surface_update surface_updates[MAX_SURFACES];
+               struct dc_plane_info plane_infos[MAX_SURFACES];
+               struct dc_scaling_info scaling_infos[MAX_SURFACES];
+               struct dc_flip_addrs flip_addrs[MAX_SURFACES];
+               struct dc_stream_update stream_update;
+       } *bundle;
 
-       updates = kcalloc(MAX_SURFACES, sizeof(*updates), GFP_KERNEL);
+       bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
 
-       if (!updates) {
-               DRM_ERROR("Failed to allocate plane updates\n");
+       if (!bundle) {
+               DRM_ERROR("Failed to allocate update bundle\n");
                /* Set type to FULL to avoid crashing in DC*/
                update_type = UPDATE_TYPE_FULL;
                goto cleanup;
        }
 
        for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
-               struct dc_scaling_info scaling_info;
-               struct dc_stream_update stream_update;
 
-               memset(&stream_update, 0, sizeof(stream_update));
+               memset(bundle, 0, sizeof(struct surface_info_bundle));
 
                new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
                old_dm_crtc_state = to_dm_crtc_state(old_crtc_state);
@@ -7793,8 +7922,9 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
                for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, j) {
                        const struct amdgpu_framebuffer *amdgpu_fb =
                                to_amdgpu_framebuffer(new_plane_state->fb);
-                       struct dc_plane_info plane_info;
-                       struct dc_flip_addrs flip_addr;
+                       struct dc_plane_info *plane_info = &bundle->plane_infos[num_plane];
+                       struct dc_flip_addrs *flip_addr = &bundle->flip_addrs[num_plane];
+                       struct dc_scaling_info *scaling_info = &bundle->scaling_infos[num_plane];
                        uint64_t tiling_flags;
 
                        new_plane_crtc = new_plane_state->crtc;
@@ -7812,49 +7942,48 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
                        if (crtc != new_plane_crtc)
                                continue;
 
-                       updates[num_plane].surface = new_dm_plane_state->dc_state;
+                       bundle->surface_updates[num_plane].surface =
+                                       new_dm_plane_state->dc_state;
 
                        if (new_crtc_state->mode_changed) {
-                               stream_update.dst = new_dm_crtc_state->stream->dst;
-                               stream_update.src = new_dm_crtc_state->stream->src;
+                               bundle->stream_update.dst = new_dm_crtc_state->stream->dst;
+                               bundle->stream_update.src = new_dm_crtc_state->stream->src;
                        }
 
                        if (new_crtc_state->color_mgmt_changed) {
-                               updates[num_plane].gamma =
+                               bundle->surface_updates[num_plane].gamma =
                                                new_dm_plane_state->dc_state->gamma_correction;
-                               updates[num_plane].in_transfer_func =
+                               bundle->surface_updates[num_plane].in_transfer_func =
                                                new_dm_plane_state->dc_state->in_transfer_func;
-                               stream_update.gamut_remap =
+                               bundle->stream_update.gamut_remap =
                                                &new_dm_crtc_state->stream->gamut_remap_matrix;
-                               stream_update.output_csc_transform =
+                               bundle->stream_update.output_csc_transform =
                                                &new_dm_crtc_state->stream->csc_color_matrix;
-                               stream_update.out_transfer_func =
+                               bundle->stream_update.out_transfer_func =
                                                new_dm_crtc_state->stream->out_transfer_func;
                        }
 
                        ret = fill_dc_scaling_info(new_plane_state,
-                                                  &scaling_info);
+                                                  scaling_info);
                        if (ret)
                                goto cleanup;
 
-                       updates[num_plane].scaling_info = &scaling_info;
+                       bundle->surface_updates[num_plane].scaling_info = scaling_info;
 
                        if (amdgpu_fb) {
                                ret = get_fb_info(amdgpu_fb, &tiling_flags);
                                if (ret)
                                        goto cleanup;
 
-                               memset(&flip_addr, 0, sizeof(flip_addr));
-
                                ret = fill_dc_plane_info_and_addr(
                                        dm->adev, new_plane_state, tiling_flags,
-                                       &plane_info,
-                                       &flip_addr.address);
+                                       plane_info,
+                                       &flip_addr->address);
                                if (ret)
                                        goto cleanup;
 
-                               updates[num_plane].plane_info = &plane_info;
-                               updates[num_plane].flip_addr = &flip_addr;
+                               bundle->surface_updates[num_plane].plane_info = plane_info;
+                               bundle->surface_updates[num_plane].flip_addr = flip_addr;
                        }
 
                        num_plane++;
@@ -7875,14 +8004,15 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
 
                status = dc_stream_get_status_from_state(old_dm_state->context,
                                                         new_dm_crtc_state->stream);
-               stream_update.stream = new_dm_crtc_state->stream;
+               bundle->stream_update.stream = new_dm_crtc_state->stream;
                /*
                 * TODO: DC modifies the surface during this call so we need
                 * to lock here - find a way to do this without locking.
                 */
                mutex_lock(&dm->dc_lock);
-               update_type = dc_check_update_surfaces_for_stream(dc, updates, num_plane,
-                                                                 &stream_update, status);
+               update_type = dc_check_update_surfaces_for_stream(
+                               dc,     bundle->surface_updates, num_plane,
+                               &bundle->stream_update, status);
                mutex_unlock(&dm->dc_lock);
 
                if (update_type > UPDATE_TYPE_MED) {
@@ -7892,7 +8022,7 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
        }
 
 cleanup:
-       kfree(updates);
+       kfree(bundle);
 
        *out_type = update_type;
        return ret;
@@ -8163,6 +8293,16 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
                        goto fail;
 #endif
 
+               /*
+                * Perform validation of MST topology in the state:
+                * We need to perform MST atomic check before calling
+                * dc_validate_global_state(), or there is a chance
+                * to get stuck in an infinite loop and hang eventually.
+                */
+               ret = drm_dp_mst_atomic_check(state);
+               if (ret)
+                       goto fail;
+
                if (dc_validate_global_state(dc, dm_state->context, false) != DC_OK) {
                        ret = -EINVAL;
                        goto fail;
@@ -8191,10 +8331,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
                                dc_retain_state(old_dm_state->context);
                }
        }
-       /* Perform validation of MST topology in the state*/
-       ret = drm_dp_mst_atomic_check(state);
-       if (ret)
-               goto fail;
 
        /* Store the overall update type for use later in atomic check. */
        for_each_new_crtc_in_state (state, crtc, new_crtc_state, i) {
@@ -8393,17 +8529,38 @@ static bool amdgpu_dm_link_setup_psr(struct dc_stream_state *stream)
 bool amdgpu_dm_psr_enable(struct dc_stream_state *stream)
 {
        struct dc_link *link = stream->link;
-       struct dc_static_screen_events triggers = {0};
+       unsigned int vsync_rate_hz = 0;
+       struct dc_static_screen_params params = {0};
+       /* Calculate number of static frames before generating interrupt to
+        * enter PSR.
+        */
+       // Init fail safe of 2 frames static
+       unsigned int num_frames_static = 2;
 
        DRM_DEBUG_DRIVER("Enabling psr...\n");
 
-       triggers.cursor_update = true;
-       triggers.overlay_update = true;
-       triggers.surface_update = true;
+       vsync_rate_hz = div64_u64(div64_u64((
+                       stream->timing.pix_clk_100hz * 100),
+                       stream->timing.v_total),
+                       stream->timing.h_total);
+
+       /* Round up
+        * Calculate number of frames such that at least 30 ms of time has
+        * passed.
+        */
+       if (vsync_rate_hz != 0) {
+               unsigned int frame_time_microsec = 1000000 / vsync_rate_hz;
+               num_frames_static = (30000 / frame_time_microsec) + 1;
+       }
+
+       params.triggers.cursor_update = true;
+       params.triggers.overlay_update = true;
+       params.triggers.surface_update = true;
+       params.num_frames = num_frames_static;
 
-       dc_stream_set_static_screen_events(link->ctx->dc,
+       dc_stream_set_static_screen_params(link->ctx->dc,
                                           &stream, 1,
-                                          &triggers);
+                                          &params);
 
        return dc_link_set_psr_allow_active(link, true, false);
 }