diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
7 files changed, 711 insertions, 136 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 279541517a99..a4256780e70e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -98,6 +98,9 @@ MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB); #define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU); +#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin" +MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); + /* Number of bytes in PSP header for firmware. */ #define PSP_HEADER_BYTES 0x100 @@ -129,9 +132,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev); /* removes and deallocates the drm structures, created by the above function */ static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm); -static void -amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector); - static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, struct drm_plane *plane, unsigned long possible_crtcs, @@ -383,8 +383,8 @@ static void dm_pflip_high_irq(void *interrupt_params) * of pageflip completion, so last_flip_vblank is the forbidden count * for queueing new pageflips if vsync + VRR is enabled. */ - amdgpu_crtc->last_flip_vblank = amdgpu_get_vblank_counter_kms(adev->ddev, - amdgpu_crtc->crtc_id); + amdgpu_crtc->last_flip_vblank = + amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base); amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE; spin_unlock_irqrestore(&adev->ddev->event_lock, flags); @@ -407,8 +407,9 @@ static void dm_vupdate_high_irq(void *interrupt_params) if (acrtc) { acrtc_state = to_dm_crtc_state(acrtc->base.state); - DRM_DEBUG_DRIVER("crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - amdgpu_dm_vrr_active(acrtc_state)); + DRM_DEBUG_VBL("crtc:%d, vupdate-vrr:%d\n", + acrtc->crtc_id, + amdgpu_dm_vrr_active(acrtc_state)); /* Core vblank handling is done here after end of front-porch in * vrr mode, as vblank timestamping will give valid results @@ -458,8 +459,9 @@ static void dm_crtc_high_irq(void *interrupt_params) if (acrtc) { acrtc_state = to_dm_crtc_state(acrtc->base.state); - DRM_DEBUG_DRIVER("crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - amdgpu_dm_vrr_active(acrtc_state)); + DRM_DEBUG_VBL("crtc:%d, vupdate-vrr:%d\n", + acrtc->crtc_id, + amdgpu_dm_vrr_active(acrtc_state)); /* Core vblank handling at start of front-porch is only possible * in non-vrr mode, as only there vblank timestamping will give @@ -522,8 +524,8 @@ static void dm_dcn_crtc_high_irq(void *interrupt_params) acrtc_state = to_dm_crtc_state(acrtc->base.state); - DRM_DEBUG_DRIVER("crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - amdgpu_dm_vrr_active(acrtc_state)); + DRM_DEBUG_VBL("crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, + amdgpu_dm_vrr_active(acrtc_state)); amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); drm_crtc_handle_vblank(&acrtc->base); @@ -801,10 +803,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 +835,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; @@ -885,7 +901,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.asic_id.chip_family = adev->family; - init_data.asic_id.pci_revision_id = adev->rev_id; + init_data.asic_id.pci_revision_id = adev->pdev->revision; init_data.asic_id.hw_internal_rev = adev->external_rev_id; init_data.asic_id.vram_width = adev->gmc.vram_width; @@ -960,7 +976,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"); @@ -991,11 +1007,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) goto error; } -#if defined(CONFIG_DEBUG_FS) - if (dtn_debugfs_init(adev)) - DRM_ERROR("amdgpu: failed initialize dtn debugfs support.\n"); -#endif - DRM_DEBUG_DRIVER("KMS initialized.\n"); return 0; @@ -1079,9 +1090,11 @@ static int load_dmcu_fw(struct amdgpu_device *adev) case CHIP_VEGA20: case CHIP_NAVI10: case CHIP_NAVI14: - case CHIP_NAVI12: case CHIP_RENOIR: return 0; + case CHIP_NAVI12: + fw_name_dmcu = FIRMWARE_NAVI12_DMCU; + break; case CHIP_RAVEN: if (ASICREV_IS_PICASSO(adev->external_rev_id)) fw_name_dmcu = FIRMWARE_RAVEN_DMCU; @@ -1192,22 +1205,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; @@ -1422,6 +1434,73 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) drm_kms_helper_hotplug_event(dev); } +static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) +{ + struct smu_context *smu = &adev->smu; + int ret = 0; + + if (!is_support_sw_smu(adev)) + return 0; + + /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends + * on window driver dc implementation. + * For Navi1x, clock settings of dcn watermarks are fixed. the settings + * should be passed to smu during boot up and resume from s3. + * boot up: dc calculate dcn watermark clock settings within dc_create, + * dcn20_resource_construct + * then call pplib functions below to pass the settings to smu: + * smu_set_watermarks_for_clock_ranges + * smu_set_watermarks_table + * navi10_set_watermarks_table + * smu_write_watermarks_table + * + * For Renoir, clock settings of dcn watermark are also fixed values. + * dc has implemented different flow for window driver: + * dc_hardware_init / dc_set_power_state + * dcn10_init_hw + * notify_wm_ranges + * set_wm_ranges + * -- Linux + * smu_set_watermarks_for_clock_ranges + * renoir_set_watermarks_table + * smu_write_watermarks_table + * + * For Linux, + * dc_hardware_init -> amdgpu_dm_init + * dc_set_power_state --> dm_resume + * + * therefore, this function apply to navi10/12/14 but not Renoir + * * + */ + switch(adev->asic_type) { + case CHIP_NAVI10: + case CHIP_NAVI14: + case CHIP_NAVI12: + break; + default: + return 0; + } + + mutex_lock(&smu->mutex); + + /* pass data to smu controller */ + if ((smu->watermarks_bitmap & WATERMARKS_EXIST) && + !(smu->watermarks_bitmap & WATERMARKS_LOADED)) { + ret = smu_write_watermarks_table(smu); + + if (ret) { + mutex_unlock(&smu->mutex); + DRM_ERROR("Failed to update WMTABLE!\n"); + return ret; + } + smu->watermarks_bitmap |= WATERMARKS_LOADED; + } + + mutex_unlock(&smu->mutex); + + return 0; +} + /** * dm_hw_init() - Initialize DC device * @handle: The base driver device containing the amdgpu_dm device. @@ -1700,6 +1779,8 @@ static int dm_resume(void *handle) amdgpu_dm_irq_resume_late(adev); + amdgpu_dm_smu_write_watermarks_table(adev); + return 0; } @@ -1758,8 +1839,63 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = { .atomic_commit_tail = amdgpu_dm_atomic_commit_tail }; -static void -amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) +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; +} + +void amdgpu_dm_update_connector_after_detect( + struct amdgpu_dm_connector *aconnector) { struct drm_connector *connector = &aconnector->base; struct drm_device *dev = connector->dev; @@ -1872,7 +2008,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); @@ -1911,7 +2047,7 @@ static void handle_hpd_irq(void *param) mutex_lock(&aconnector->hpd_lock); #ifdef CONFIG_DRM_AMD_DC_HDCP - if (adev->asic_type >= CHIP_RAVEN) + if (adev->dm.hdcp_workqueue) hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index); #endif if (aconnector->fake_enable) @@ -2088,8 +2224,10 @@ static void handle_hpd_rx_irq(void *param) } } #ifdef CONFIG_DRM_AMD_DC_HDCP - if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) - hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index); + if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) { + if (adev->dm.hdcp_workqueue) + hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index); + } #endif if ((dc_link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) || (dc_link->type == dc_connection_mst_branch)) @@ -2484,6 +2622,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 +2637,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 +2649,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) @@ -2826,6 +3022,9 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) if (adev->asic_type != CHIP_CARRIZO && adev->asic_type != CHIP_STONEY) dm->dc->debug.disable_stutter = amdgpu_pp_feature_mask & PP_STUTTER_MODE ? false : true; + /* No userspace support. */ + dm->dc->debug.disable_tri_buf = true; + return 0; fail: kfree(aencoder); @@ -4117,9 +4316,22 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, struct dmcu *dmcu = core_dc->res_pool->dmcu; stream->psr_version = dmcu->dmcu_version.psr_version; - mod_build_vsc_infopacket(stream, - &stream->vsc_infopacket, - &stream->use_vsc_sdp_for_colorimetry); + + // + // should decide stream support vsc sdp colorimetry capability + // before building vsc info packet + // + stream->use_vsc_sdp_for_colorimetry = false; + if (aconnector->dc_sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + stream->use_vsc_sdp_for_colorimetry = + aconnector->dc_sink->is_vsc_sdp_colorimetry_supported; + } else { + if (stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 && + stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED) { + stream->use_vsc_sdp_for_colorimetry = true; + } + } + mod_build_vsc_infopacket(stream, &stream->vsc_infopacket); } } finish: @@ -4269,8 +4481,10 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { .set_crc_source = amdgpu_dm_crtc_set_crc_source, .verify_crc_source = amdgpu_dm_crtc_verify_crc_source, .get_crc_sources = amdgpu_dm_crtc_get_crc_sources, + .get_vblank_counter = amdgpu_get_vblank_counter_kms, .enable_vblank = dm_enable_vblank, .disable_vblank = dm_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, }; static enum drm_connector_status @@ -4491,6 +4705,19 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) return &new_state->base; } +static int +amdgpu_dm_connector_late_register(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + +#if defined(CONFIG_DEBUG_FS) + connector_debugfs_init(amdgpu_dm_connector); +#endif + + return 0; +} + static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { .reset = amdgpu_dm_connector_funcs_reset, .detect = amdgpu_dm_connector_detect, @@ -4500,6 +4727,7 @@ static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_set_property = amdgpu_dm_connector_atomic_set_property, .atomic_get_property = amdgpu_dm_connector_atomic_get_property, + .late_register = amdgpu_dm_connector_late_register, .early_unregister = amdgpu_dm_connector_unregister }; @@ -4876,7 +5104,8 @@ static bool dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = { .disable = dm_crtc_helper_disable, .atomic_check = dm_crtc_helper_atomic_check, - .mode_fixup = dm_crtc_helper_mode_fixup + .mode_fixup = dm_crtc_helper_mode_fixup, + .get_scanout_position = amdgpu_crtc_get_scanout_position, }; static void dm_encoder_helper_disable(struct drm_encoder *encoder) @@ -5702,7 +5931,7 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, drm_connector_attach_vrr_capable_property( &aconnector->base); #ifdef CONFIG_DRM_AMD_DC_HDCP - if (adev->asic_type >= CHIP_RAVEN) + if (adev->dm.hdcp_workqueue) drm_connector_attach_content_protection_property(&aconnector->base, true); #endif } @@ -5839,13 +6068,6 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, drm_connector_attach_encoder( &aconnector->base, &aencoder->base); - drm_connector_register(&aconnector->base); -#if defined(CONFIG_DEBUG_FS) - connector_debugfs_init(aconnector); - aconnector->debugfs_dpcd_address = 0; - aconnector->debugfs_dpcd_size = 0; -#endif - if (connector_type == DRM_MODE_CONNECTOR_DisplayPort || connector_type == DRM_MODE_CONNECTOR_eDP) amdgpu_dm_initialize_dp_connector(dm, aconnector); @@ -6331,7 +6553,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, uint32_t target_vblank, last_flip_vblank; bool vrr_active = amdgpu_dm_vrr_active(acrtc_state); bool pflip_present = false; - bool swizzle = true; struct { struct dc_surface_update surface_updates[MAX_SURFACES]; struct dc_plane_info plane_infos[MAX_SURFACES]; @@ -6377,9 +6598,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, dc_plane = dm_new_plane_state->dc_state; - if (dc_plane && !dc_plane->tiling_info.gfx9.swizzle) - swizzle = false; - bundle->surface_updates[planes_count].surface = dc_plane; if (new_pcrtc_state->color_mgmt_changed) { bundle->surface_updates[planes_count].gamma = dc_plane->gamma_correction; @@ -6480,7 +6698,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * clients using the GLX_OML_sync_control extension or * DRI3/Present extension with defined target_msc. */ - last_flip_vblank = amdgpu_get_vblank_counter_kms(dm->ddev, acrtc_attach->crtc_id); + last_flip_vblank = amdgpu_get_vblank_counter_kms(pcrtc); } else { /* For variable refresh rate mode only: @@ -6509,7 +6727,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && (int)(target_vblank - - amdgpu_get_vblank_counter_kms(dm->ddev, acrtc_attach->crtc_id)) > 0)) { + amdgpu_get_vblank_counter_kms(pcrtc)) > 0)) { usleep_range(1000, 1100); } @@ -6588,8 +6806,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, amdgpu_dm_link_setup_psr(acrtc_state->stream); else if ((acrtc_state->update_type == UPDATE_TYPE_FAST) && acrtc_state->stream->link->psr_feature_enabled && - !acrtc_state->stream->link->psr_allow_active && - swizzle) { + !acrtc_state->stream->link->psr_allow_active) { amdgpu_dm_psr_enable(acrtc_state->stream); } @@ -8408,7 +8625,6 @@ bool amdgpu_dm_psr_enable(struct dc_stream_state *stream) /* Calculate number of static frames before generating interrupt to * enter PSR. */ - unsigned int frame_time_microsec = 1000000 / vsync_rate_hz; // Init fail safe of 2 frames static unsigned int num_frames_static = 2; @@ -8423,8 +8639,10 @@ bool amdgpu_dm_psr_enable(struct dc_stream_state *stream) * Calculate number of frames such that at least 30 ms of time has * passed. */ - if (vsync_rate_hz != 0) + 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; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 7ea9acb0358d..5cab3e65d992 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -90,15 +90,41 @@ struct dm_comressor_info { }; /** - * struct amdgpu_dm_backlight_caps - Usable range of backlight values from ACPI - * @min_input_signal: minimum possible input in range 0-255 - * @max_input_signal: maximum possible input in range 0-255 - * @caps_valid: true if these values are from the ACPI interface + * struct amdgpu_dm_backlight_caps - Information about backlight + * + * Describe the backlight support for ACPI or eDP AUX. */ struct amdgpu_dm_backlight_caps { + /** + * @ext_caps: Keep the data struct with all the information about the + * display support for HDR. + */ + union dpcd_sink_ext_caps *ext_caps; + /** + * @aux_min_input_signal: Min brightness value supported by the display + */ + u32 aux_min_input_signal; + /** + * @aux_max_input_signal: Max brightness value supported by the display + * in nits. + */ + u32 aux_max_input_signal; + /** + * @min_input_signal: minimum possible input in range 0-255. + */ int min_input_signal; + /** + * @max_input_signal: maximum possible input in range 0-255. + */ int max_input_signal; + /** + * @caps_valid: true if these values are from the ACPI interface. + */ bool caps_valid; + /** + * @aux_support: Describes if the display supports AUX backlight. + */ + bool aux_support; }; /** @@ -457,6 +483,9 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc); int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, struct dc_plane_state *dc_plane_state); +void amdgpu_dm_update_connector_after_detect( + struct amdgpu_dm_connector *aconnector); + extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs; #endif /* __AMDGPU_DM_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index f81d3439ee8c..0461fecd68db 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -32,6 +32,19 @@ #include "amdgpu_dm.h" #include "amdgpu_dm_debugfs.h" #include "dm_helpers.h" +#include "dmub/inc/dmub_srv.h" + +struct dmub_debugfs_trace_header { + uint32_t entry_count; + uint32_t reserved[3]; +}; + +struct dmub_debugfs_trace_entry { + uint32_t trace_code; + uint32_t tick_count; + uint32_t param0; + uint32_t param1; +}; /* function description * get/ set DP configuration: lane_count, link_rate, spread_spectrum @@ -675,6 +688,73 @@ static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __us return bytes_from_user; } +/** + * Returns the DMCUB tracebuffer contents. + * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_tracebuffer + */ +static int dmub_tracebuffer_show(struct seq_file *m, void *data) +{ + struct amdgpu_device *adev = m->private; + struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; + struct dmub_debugfs_trace_entry *entries; + uint8_t *tbuf_base; + uint32_t tbuf_size, max_entries, num_entries, i; + + if (!fb_info) + return 0; + + tbuf_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr; + if (!tbuf_base) + return 0; + + tbuf_size = fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size; + max_entries = (tbuf_size - sizeof(struct dmub_debugfs_trace_header)) / + sizeof(struct dmub_debugfs_trace_entry); + + num_entries = + ((struct dmub_debugfs_trace_header *)tbuf_base)->entry_count; + + num_entries = min(num_entries, max_entries); + + entries = (struct dmub_debugfs_trace_entry + *)(tbuf_base + + sizeof(struct dmub_debugfs_trace_header)); + + for (i = 0; i < num_entries; ++i) { + struct dmub_debugfs_trace_entry *entry = &entries[i]; + + seq_printf(m, + "trace_code=%u tick_count=%u param0=%u param1=%u\n", + entry->trace_code, entry->tick_count, entry->param0, + entry->param1); + } + + return 0; +} + +/** + * Returns the DMCUB firmware state contents. + * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_fw_state + */ +static int dmub_fw_state_show(struct seq_file *m, void *data) +{ + struct amdgpu_device *adev = m->private; + struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; + uint8_t *state_base; + uint32_t state_size; + + if (!fb_info) + return 0; + + state_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr; + if (!state_base) + return 0; + + state_size = fb_info->fb[DMUB_WINDOW_6_FW_STATE].size; + + return seq_write(m, state_base, state_size); +} + /* * Returns the current and maximum output bpc for the connector. * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc @@ -880,6 +960,8 @@ static ssize_t dp_dpcd_data_read(struct file *f, char __user *buf, return read_size - r; } +DEFINE_SHOW_ATTRIBUTE(dmub_fw_state); +DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer); DEFINE_SHOW_ATTRIBUTE(output_bpc); DEFINE_SHOW_ATTRIBUTE(vrr_range); @@ -1008,6 +1090,9 @@ void connector_debugfs_init(struct amdgpu_dm_connector *connector) debugfs_create_file_unsafe("force_yuv420_output", 0644, dir, connector, &force_yuv420_output_fops); + connector->debugfs_dpcd_address = 0; + connector->debugfs_dpcd_size = 0; + } /* @@ -1188,5 +1273,11 @@ int dtn_debugfs_init(struct amdgpu_device *adev) debugfs_create_file_unsafe("amdgpu_dm_visual_confirm", 0644, root, adev, &visual_confirm_fops); + debugfs_create_file_unsafe("amdgpu_dm_dmub_tracebuffer", 0644, root, + adev, &dmub_tracebuffer_fops); + + debugfs_create_file_unsafe("amdgpu_dm_dmub_fw_state", 0644, root, + adev, &dmub_fw_state_fops); + return 0; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index 0acd3409dd6c..5b70ed3cdb88 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -28,6 +28,13 @@ #include "amdgpu_dm.h" #include "dm_helpers.h" #include <drm/drm_hdcp.h> +#include "hdcp_psp.h" + +/* + * If the SRM version being loaded is less than or equal to the + * currently loaded SRM, psp will return 0xFFFF as the version + */ +#define PSP_SRM_VERSION_MAX 0xFFFF static bool lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) @@ -67,6 +74,59 @@ lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size) return dm_helpers_dp_read_dpcd(link->ctx, link, address, data, size); } +static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size) +{ + + struct ta_hdcp_shared_memory *hdcp_cmd; + + if (!psp->hdcp_context.hdcp_initialized) { + DRM_WARN("Failed to get hdcp srm. HDCP TA is not initialized."); + return NULL; + } + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP_GET_SRM; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return NULL; + + *srm_version = hdcp_cmd->out_msg.hdcp_get_srm.srm_version; + *srm_size = hdcp_cmd->out_msg.hdcp_get_srm.srm_buf_size; + + + return hdcp_cmd->out_msg.hdcp_get_srm.srm_buf; +} + +static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size, uint32_t *srm_version) +{ + + struct ta_hdcp_shared_memory *hdcp_cmd; + + if (!psp->hdcp_context.hdcp_initialized) { + DRM_WARN("Failed to get hdcp srm. HDCP TA is not initialized."); + return -EINVAL; + } + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + memcpy(hdcp_cmd->in_msg.hdcp_set_srm.srm_buf, srm, srm_size); + hdcp_cmd->in_msg.hdcp_set_srm.srm_buf_size = srm_size; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP_SET_SRM; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS || hdcp_cmd->out_msg.hdcp_set_srm.valid_signature != 1 || + hdcp_cmd->out_msg.hdcp_set_srm.srm_version == PSP_SRM_VERSION_MAX) + return -EINVAL; + + *srm_version = hdcp_cmd->out_msg.hdcp_set_srm.srm_version; + return 0; +} + static void process_output(struct hdcp_workqueue *hdcp_work) { struct mod_hdcp_output output = hdcp_work->output; @@ -88,6 +148,18 @@ static void process_output(struct hdcp_workqueue *hdcp_work) schedule_delayed_work(&hdcp_work->property_validate_dwork, msecs_to_jiffies(0)); } +static void link_lock(struct hdcp_workqueue *work, bool lock) +{ + + int i = 0; + + for (i = 0; i < work->max_link; i++) { + if (lock) + mutex_lock(&work[i].mutex); + else + mutex_unlock(&work[i].mutex); + } +} void hdcp_update_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index, struct amdgpu_dm_connector *aconnector, @@ -112,6 +184,13 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; if (enable_encryption) { + /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp + * (s3 resume case) + */ + if (hdcp_work->srm_size > 0) + psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, hdcp_work->srm_size, + &hdcp_work->srm_version); + display->adjust.disable = 0; if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; @@ -301,8 +380,9 @@ void hdcp_destroy(struct hdcp_workqueue *hdcp_work) cancel_delayed_work_sync(&hdcp_work[i].watchdog_timer_dwork); } + kfree(hdcp_work->srm); + kfree(hdcp_work->srm_temp); kfree(hdcp_work); - } static void update_config(void *handle, struct cp_psp_stream_config *config) @@ -332,26 +412,170 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) link->dig_be = config->link_enc_inst; link->ddc_line = aconnector->dc_link->ddc_hw_inst + 1; link->dp.rev = aconnector->dc_link->dpcd_caps.dpcd_rev.raw; + link->dp.mst_supported = config->mst_supported; display->adjust.disable = 1; link->adjust.auth_delay = 2; hdcp_update_display(hdcp_work, link_index, aconnector, DRM_MODE_HDCP_CONTENT_TYPE0, false); } -struct hdcp_workqueue *hdcp_create_workqueue(void *psp_context, struct cp_psp *cp_psp, struct dc *dc) + +/* NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel + * will automatically call once or twice depending on the size + * + * call: "cat file > /sys/class/drm/card0/device/hdcp_srm" from usermode no matter what the size is + * + * The kernel can only send PAGE_SIZE at once and since MAX_SRM_FILE(5120) > PAGE_SIZE(4096), + * srm_data_write can be called multiple times. + * + * sysfs interface doesn't tell us the size we will get so we are sending partial SRMs to psp and on + * the last call we will send the full SRM. PSP will fail on every call before the last. + * + * This means we don't know if the SRM is good until the last call. And because of this limitation we + * cannot throw errors early as it will stop the kernel from writing to sysfs + * + * Example 1: + * Good SRM size = 5096 + * first call to write 4096 -> PSP fails + * Second call to write 1000 -> PSP Pass -> SRM is set + * + * Example 2: + * Bad SRM size = 4096 + * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this + * is the last call) + * + * Solution?: + * 1: Parse the SRM? -> It is signed so we don't know the EOF + * 2: We can have another sysfs that passes the size before calling set. -> simpler solution + * below + * + * Easy Solution: + * Always call get after Set to verify if set was successful. + * +----------------------+ + * | Why it works: | + * +----------------------+ + * PSP will only update its srm if its older than the one we are trying to load. + * Always do set first than get. + * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer + * version and save it + * + * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the + * same(newer) version back and save it + * + * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is + * incorrect/corrupted and we should correct our SRM by getting it from PSP + */ +static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) +{ + struct hdcp_workqueue *work; + uint32_t srm_version = 0; + + work = container_of(bin_attr, struct hdcp_workqueue, attr); + link_lock(work, true); + + memcpy(work->srm_temp + pos, buffer, count); + + if (!psp_set_srm(work->hdcp.config.psp.handle, work->srm_temp, pos + count, &srm_version)) { + DRM_DEBUG_DRIVER("HDCP SRM SET version 0x%X", srm_version); + memcpy(work->srm, work->srm_temp, pos + count); + work->srm_size = pos + count; + work->srm_version = srm_version; + } + + + link_lock(work, false); + + return count; +} + +static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) +{ + struct hdcp_workqueue *work; + uint8_t *srm = NULL; + uint32_t srm_version; + uint32_t srm_size; + size_t ret = count; + + work = container_of(bin_attr, struct hdcp_workqueue, attr); + + link_lock(work, true); + + srm = psp_get_srm(work->hdcp.config.psp.handle, &srm_version, &srm_size); + + if (!srm) + return -EINVAL; + + if (pos >= srm_size) + ret = 0; + + if (srm_size - pos < count) { + memcpy(buffer, srm + pos, srm_size - pos); + ret = srm_size - pos; + goto ret; + } + + memcpy(buffer, srm + pos, count); + +ret: + link_lock(work, false); + return ret; +} + +/* From the hdcp spec (5.Renewability) SRM needs to be stored in a non-volatile memory. + * + * For example, + * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B" + * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent + * across boot/reboots/suspend/resume/shutdown + * + * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP we need + * to make the SRM persistent. + * + * -PSP owns the checking of SRM but doesn't have the ability to store it in a non-volatile memory. + * -The kernel cannot write to the file systems. + * -So we need usermode to do this for us, which is why an interface for usermode is needed + * + * + * + * Usermode can read/write to/from PSP using the sysfs interface + * For example: + * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile + * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm + */ +static const struct bin_attribute data_attr = { + .attr = {.name = "hdcp_srm", .mode = 0664}, + .size = PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, /* Limit SRM size */ + .write = srm_data_write, + .read = srm_data_read, +}; + + +struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc) { int max_caps = dc->caps.max_links; - struct hdcp_workqueue *hdcp_work = kzalloc(max_caps*sizeof(*hdcp_work), GFP_KERNEL); + struct hdcp_workqueue *hdcp_work; int i = 0; + hdcp_work = kcalloc(max_caps, sizeof(*hdcp_work), GFP_KERNEL); if (hdcp_work == NULL) + return NULL; + + hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm), GFP_KERNEL); + + if (hdcp_work->srm == NULL) + goto fail_alloc_context; + + hdcp_work->srm_temp = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm_temp), GFP_KERNEL); + + if (hdcp_work->srm_temp == NULL) goto fail_alloc_context; hdcp_work->max_link = max_caps; for (i = 0; i < max_caps; i++) { - mutex_init(&hdcp_work[i].mutex); INIT_WORK(&hdcp_work[i].cpirq_work, event_cpirq); @@ -360,7 +584,7 @@ struct hdcp_workqueue *hdcp_create_workqueue(void *psp_context, struct cp_psp *c INIT_DELAYED_WORK(&hdcp_work[i].watchdog_timer_dwork, event_watchdog_timer); INIT_DELAYED_WORK(&hdcp_work[i].property_validate_dwork, event_property_validate); - hdcp_work[i].hdcp.config.psp.handle = psp_context; + hdcp_work[i].hdcp.config.psp.handle = &adev->psp; hdcp_work[i].hdcp.config.ddc.handle = dc_get_link_at_index(dc, i); hdcp_work[i].hdcp.config.ddc.funcs.write_i2c = lp_write_i2c; hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c; @@ -371,9 +595,17 @@ struct hdcp_workqueue *hdcp_create_workqueue(void *psp_context, struct cp_psp *c cp_psp->funcs.update_stream_config = update_config; cp_psp->handle = hdcp_work; + /* File created at /sys/class/drm/card0/device/hdcp_srm*/ + hdcp_work[0].attr = data_attr; + + if (sysfs_create_bin_file(&adev->dev->kobj, &hdcp_work[0].attr)) + DRM_WARN("Failed to create device file hdcp_srm"); + return hdcp_work; fail_alloc_context: + kfree(hdcp_work->srm); + kfree(hdcp_work->srm_temp); kfree(hdcp_work); return NULL; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h index 6abde86bce4a..5159b3a5e5b0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h @@ -30,6 +30,7 @@ #include "hdcp.h" #include "dc.h" #include "dm_cp_psp.h" +#include "amdgpu.h" struct mod_hdcp; struct mod_hdcp_link; @@ -52,6 +53,12 @@ struct hdcp_workqueue { enum mod_hdcp_encryption_status encryption_status; uint8_t max_link; + + uint8_t *srm; + uint8_t *srm_temp; + uint32_t srm_version; + uint32_t srm_size; + struct bin_attribute attr; }; void hdcp_update_display(struct hdcp_workqueue *hdcp_work, @@ -64,6 +71,6 @@ void hdcp_reset_display(struct hdcp_workqueue *work, unsigned int link_index); void hdcp_handle_cpirq(struct hdcp_workqueue *work, unsigned int link_index); void hdcp_destroy(struct hdcp_workqueue *work); -struct hdcp_workqueue *hdcp_create_workqueue(void *psp_context, struct cp_psp *cp_psp, struct dc *dc); +struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc); #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 318b474ff20e..c20fb08c450b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -400,8 +400,8 @@ bool dm_helpers_dp_mst_start_top_mgr( struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { - DRM_ERROR("Failed to found connector for link!"); - return false; + DRM_ERROR("Failed to find connector for link!"); + return false; } if (boot) { @@ -423,8 +423,8 @@ void dm_helpers_dp_mst_stop_top_mgr( struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { - DRM_ERROR("Failed to found connector for link!"); - return; + DRM_ERROR("Failed to find connector for link!"); + return; } DRM_INFO("DM_MST: stopping TM on aconnector: %p [id: %d]\n", @@ -445,7 +445,7 @@ bool dm_helpers_dp_read_dpcd( struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { - DRM_ERROR("Failed to found connector for link!"); + DRM_ERROR("Failed to find connector for link!"); return false; } @@ -463,7 +463,7 @@ bool dm_helpers_dp_write_dpcd( struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { - DRM_ERROR("Failed to found connector for link!"); + DRM_ERROR("Failed to find connector for link!"); return false; } @@ -483,7 +483,7 @@ bool dm_helpers_submit_i2c( bool result; if (!aconnector) { - DRM_ERROR("Failed to found connector for link!"); + DRM_ERROR("Failed to find connector for link!"); return false; } @@ -538,7 +538,7 @@ bool dm_helpers_is_dp_sink_present(struct dc_link *link) struct amdgpu_dm_connector *aconnector = link->priv; if (!aconnector) { - BUG_ON("Failed to found connector for link!"); + BUG_ON("Failed to find connector for link!"); return true; } @@ -580,6 +580,20 @@ enum dc_edid_status dm_helpers_read_local_edid( /* We don't need the original edid anymore */ kfree(edid); + /* connector->display_info will be parsed from EDID and saved + * into drm_connector->display_info from edid by call stack + * below: + * drm_parse_ycbcr420_deep_color_info + * drm_parse_hdmi_forum_vsdb + * drm_parse_cea_ext + * drm_add_display_info + * drm_connector_update_edid_property + * + * drm_connector->display_info will be used by amdgpu_dm funcs, + * like fill_stream_properties_from_drm_display_mode + */ + amdgpu_dm_update_connector_after_detect(aconnector); + edid_status = dm_helpers_parse_edid_caps( ctx, &sink->dc_edid, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 5672f7765919..e8208df420d9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -154,15 +154,18 @@ amdgpu_dm_mst_connector_late_register(struct drm_connector *connector) { struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); - struct drm_dp_mst_port *port = amdgpu_dm_connector->port; + int r; + + amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev; + r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux); + if (r) + return r; #if defined(CONFIG_DEBUG_FS) connector_debugfs_init(amdgpu_dm_connector); - amdgpu_dm_connector->debugfs_dpcd_address = 0; - amdgpu_dm_connector->debugfs_dpcd_size = 0; #endif - return drm_dp_mst_connector_late_register(connector, port); + return r; } static void @@ -204,7 +207,7 @@ static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnecto if (!dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc, dsc_caps, NULL, - &dc_sink->sink_dsc_caps.dsc_dec_caps)) + &dc_sink->dsc_caps.dsc_dec_caps)) return false; return true; @@ -259,8 +262,8 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) #if defined(CONFIG_DRM_AMD_DC_DCN) if (!validate_dsc_caps_on_connector(aconnector)) - memset(&aconnector->dc_sink->sink_dsc_caps, - 0, sizeof(aconnector->dc_sink->sink_dsc_caps)); + memset(&aconnector->dc_sink->dsc_caps, + 0, sizeof(aconnector->dc_sink->dsc_caps)); #endif } } @@ -437,9 +440,6 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_connector *connector) { - struct amdgpu_dm_connector *master = container_of(mgr, struct amdgpu_dm_connector, mst_mgr); - struct drm_device *dev = master->base.dev; - struct amdgpu_device *adev = dev->dev_private; struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); DRM_INFO("DM_MST: Disabling connector: %p [id: %d] [master: %p]\n", @@ -451,42 +451,26 @@ static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, aconnector->dc_sink); dc_sink_release(aconnector->dc_sink); aconnector->dc_sink = NULL; + aconnector->dc_link->cur_link_settings.lane_count = 0; } drm_connector_unregister(connector); - if (adev->mode_info.rfbdev) - drm_fb_helper_remove_one_connector(&adev->mode_info.rfbdev->helper, connector); drm_connector_put(connector); } -static void dm_dp_mst_register_connector(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct amdgpu_device *adev = dev->dev_private; - - if (adev->mode_info.rfbdev) - drm_fb_helper_add_one_connector(&adev->mode_info.rfbdev->helper, connector); - else - DRM_ERROR("adev->mode_info.rfbdev is NULL\n"); - - drm_connector_register(connector); -} - static const struct drm_dp_mst_topology_cbs dm_mst_cbs = { .add_connector = dm_dp_add_mst_connector, .destroy_connector = dm_dp_destroy_mst_connector, - .register_connector = dm_dp_mst_register_connector }; void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector) { aconnector->dm_dp_aux.aux.name = "dmdc"; - aconnector->dm_dp_aux.aux.dev = aconnector->base.kdev; aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer; aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc; - drm_dp_aux_register(&aconnector->dm_dp_aux.aux); + drm_dp_aux_init(&aconnector->dm_dp_aux.aux); drm_dp_cec_register_connector(&aconnector->dm_dp_aux.aux, &aconnector->base); @@ -547,7 +531,7 @@ static void set_dsc_configs_from_fairness_vars(struct dsc_mst_fairness_params *p memset(¶ms[i].timing->dsc_cfg, 0, sizeof(params[i].timing->dsc_cfg)); if (vars[i].dsc_enabled && dc_dsc_compute_config( params[i].sink->ctx->dc->res_pool->dscs[0], - ¶ms[i].sink->sink_dsc_caps.dsc_dec_caps, + ¶ms[i].sink->dsc_caps.dsc_dec_caps, params[i].sink->ctx->dc->debug.dsc_min_slice_height_override, 0, params[i].timing, @@ -568,7 +552,7 @@ static int bpp_x16_from_pbn(struct dsc_mst_fairness_params param, int pbn) kbps = div_u64((u64)pbn * 994 * 8 * 54, 64); dc_dsc_compute_config( param.sink->ctx->dc->res_pool->dscs[0], - ¶m.sink->sink_dsc_caps.dsc_dec_caps, + ¶m.sink->dsc_caps.dsc_dec_caps, param.sink->ctx->dc->debug.dsc_min_slice_height_override, (int) kbps, param.timing, &dsc_config); @@ -765,14 +749,14 @@ static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, params[count].sink = stream->sink; aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context; params[count].port = aconnector->port; - params[count].compression_possible = stream->sink->sink_dsc_caps.dsc_dec_caps.is_dsc_supported; + params[count].compression_possible = stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported; dc_dsc_get_policy_for_timing(params[count].timing, &dsc_policy); if (!dc_dsc_compute_bandwidth_range( stream->sink->ctx->dc->res_pool->dscs[0], stream->sink->ctx->dc->debug.dsc_min_slice_height_override, dsc_policy.min_target_bpp, dsc_policy.max_target_bpp, - &stream->sink->sink_dsc_caps.dsc_dec_caps, + &stream->sink->dsc_caps.dsc_dec_caps, &stream->timing, ¶ms[count].bw_range)) params[count].bw_range.stream_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing); @@ -854,7 +838,7 @@ bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state, if (!aconnector || !aconnector->dc_sink) continue; - if (!aconnector->dc_sink->sink_dsc_caps.dsc_dec_caps.is_dsc_supported) + if (!aconnector->dc_sink->dsc_caps.dsc_dec_caps.is_dsc_supported) continue; if (computed_streams[i]) |