diff options
author | Dave Airlie <airlied@redhat.com> | 2018-09-21 02:52:34 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-09-21 02:52:53 +0300 |
commit | 36c9c3c91128e2b892c9be0dd9ee9bd82cbe82ad (patch) | |
tree | 687db2e37b7fdcb4bd756a078812d049da18c804 /drivers/gpu/drm/amd/display | |
parent | 0320ac5188eab5c6e8b92b110d1eae967ac272d2 (diff) | |
parent | 846311ae68f3c78365ebf3dff505c99e7da861cf (diff) | |
download | linux-36c9c3c91128e2b892c9be0dd9ee9bd82cbe82ad.tar.xz |
Merge branch 'drm-next-4.20' of git://people.freedesktop.org/~agd5f/linux into drm-next
This is a new pull for drm-next on top of last weeks with the following
changes:
- Fixed 64 bit divide
- Fixed vram type on vega20
- Misc vega20 fixes
- Misc DC fixes
- Fix GDS/GWS/OA domain handling
Previous changes from last week:
amdgpu/kfd:
- Picasso (new APU) support
- Raven2 (new APU) support
- Vega20 enablement
- ACP powergating improvements
- Add ABGR/XBGR display support
- VCN JPEG engine support
- Initial xGMI support
- Use load balancing for engine scheduling
- Lots of new documentation
- Rework and clean up i2c and aux handling in DC
- Add DP YCbCr 4:2:0 support in DC
- Add DMCU firmware loading for Raven (used for ABM and PSR)
- New debugfs features in DC
- LVDS support in DC
- Implement wave kill for gfx/compute (light weight reset for shaders)
- Use AGP aperture to avoid gart mappings when possible
- GPUVM performance improvements
- Bulk moves for more efficient GPUVM LRU handling
- Merge amdgpu and amdkfd into one module
- Enable gfxoff and stutter mode on Raven
- Misc cleanups
Scheduler:
- Load balancing support
- Bug fixes
ttm:
- Bulk move functionality
- Bug fixes
radeon:
- Misc cleanups
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Alex Deucher <alexdeucher@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20180920150438.12693-1-alexander.deucher@amd.com
Diffstat (limited to 'drivers/gpu/drm/amd/display')
92 files changed, 5753 insertions, 3830 deletions
diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index ed654a76c76a..13a6ce9c8e94 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -5,6 +5,7 @@ config DRM_AMD_DC bool "AMD DC - Enable new display engine" default y select DRM_AMD_DC_DCN1_0 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) + select DRM_AMD_DC_DCN1_01 if X86 && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS) help Choose this option if you want to use the new display engine support for AMDGPU. This adds required support for Vega and @@ -15,6 +16,11 @@ config DRM_AMD_DC_DCN1_0 help RV family support for display engine +config DRM_AMD_DC_DCN1_01 + def_bool n + help + RV2 family for display engine + config DEBUG_KERNEL_DC bool "Enable kgdb break in DC" depends on DRM_AMD_DC diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile index a2c5be493555..c97dc9613325 100644 --- a/drivers/gpu/drm/amd/display/Makefile +++ b/drivers/gpu/drm/amd/display/Makefile @@ -31,11 +31,12 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/inc/hw subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/inc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/freesync subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/color +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/info_packet #TODO: remove when Timing Sync feature is complete subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 -DAL_LIBS = amdgpu_dm dc modules/freesync modules/color +DAL_LIBS = amdgpu_dm dc modules/freesync modules/color modules/info_packet AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/,$(DAL_LIBS))) 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 af6adffba788..6c849e055038 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -30,6 +30,7 @@ #include "vid.h" #include "amdgpu.h" #include "amdgpu_display.h" +#include "amdgpu_ucode.h" #include "atom.h" #include "amdgpu_dm.h" #include "amdgpu_pm.h" @@ -50,6 +51,7 @@ #include <linux/version.h> #include <linux/types.h> #include <linux/pm_runtime.h> +#include <linux/firmware.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -71,13 +73,15 @@ #include "modules/inc/mod_freesync.h" -#include "i2caux_interface.h" +#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" +MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU); /* basic init/fini API */ static int amdgpu_dm_init(struct amdgpu_device *adev); static void amdgpu_dm_fini(struct amdgpu_device *adev); -/* initializes drm_device display related structures, based on the information +/* + * initializes drm_device display related structures, based on the information * provided by DAL. The drm strcutures are: drm_crtc, drm_connector, * drm_encoder, drm_mode_config * @@ -239,10 +243,6 @@ get_crtc_by_otg_inst(struct amdgpu_device *adev, struct drm_crtc *crtc; struct amdgpu_crtc *amdgpu_crtc; - /* - * following if is check inherited from both functions where this one is - * used now. Need to be checked why it could happen. - */ if (otg_inst == -1) { WARN_ON(1); return adev->mode_info.crtcs[0]; @@ -268,7 +268,7 @@ static void dm_pflip_high_irq(void *interrupt_params) amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP); /* IRQ could occur when in initial stage */ - /*TODO work and BO cleanup */ + /* TODO work and BO cleanup */ if (amdgpu_crtc == NULL) { DRM_DEBUG_DRIVER("CRTC is null, returning.\n"); return; @@ -287,9 +287,9 @@ static void dm_pflip_high_irq(void *interrupt_params) } - /* wakeup usersapce */ + /* wake up userspace */ if (amdgpu_crtc->event) { - /* Update to correct count/ts if racing with vblank irq */ + /* Update to correct count(s) if racing with vblank irq */ drm_crtc_accurate_vblank_count(&amdgpu_crtc->base); drm_crtc_send_vblank_event(&amdgpu_crtc->base, amdgpu_crtc->event); @@ -313,16 +313,14 @@ static void dm_crtc_high_irq(void *interrupt_params) { struct common_irq_params *irq_params = interrupt_params; struct amdgpu_device *adev = irq_params->adev; - uint8_t crtc_index = 0; struct amdgpu_crtc *acrtc; acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); - if (acrtc) - crtc_index = acrtc->crtc_id; - - drm_handle_vblank(adev->ddev, crtc_index); - amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + if (acrtc) { + drm_crtc_handle_vblank(&acrtc->base); + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + } } static int dm_set_clockgating_state(void *handle, @@ -389,8 +387,8 @@ static void amdgpu_dm_fbc_init(struct drm_connector *connector) } - -/* Init display KMS +/* + * Init display KMS * * Returns 0 on success */ @@ -429,8 +427,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.cgs_device = adev->dm.cgs_device; - adev->dm.dal = NULL; - init_data.dce_environment = DCE_ENV_PRODUCTION_DRV; /* @@ -484,13 +480,18 @@ 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; error: amdgpu_dm_fini(adev); - return -1; + return -EINVAL; } static void amdgpu_dm_fini(struct amdgpu_device *adev) @@ -516,13 +517,99 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev) return; } -static int dm_sw_init(void *handle) +static int load_dmcu_fw(struct amdgpu_device *adev) { + const char *fw_name_dmcu; + int r; + const struct dmcu_firmware_header_v1_0 *hdr; + + switch(adev->asic_type) { + case CHIP_BONAIRE: + case CHIP_HAWAII: + case CHIP_KAVERI: + case CHIP_KABINI: + case CHIP_MULLINS: + case CHIP_TONGA: + case CHIP_FIJI: + case CHIP_CARRIZO: + case CHIP_STONEY: + case CHIP_POLARIS11: + case CHIP_POLARIS10: + case CHIP_POLARIS12: + case CHIP_VEGAM: + case CHIP_VEGA10: + case CHIP_VEGA12: + case CHIP_VEGA20: + return 0; + case CHIP_RAVEN: + fw_name_dmcu = FIRMWARE_RAVEN_DMCU; + break; + default: + DRM_ERROR("Unsupported ASIC type: 0x%X\n", adev->asic_type); + return -EINVAL; + } + + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { + DRM_DEBUG_KMS("dm: DMCU firmware not supported on direct or SMU loading\n"); + return 0; + } + + r = request_firmware_direct(&adev->dm.fw_dmcu, fw_name_dmcu, adev->dev); + if (r == -ENOENT) { + /* DMCU firmware is not necessary, so don't raise a fuss if it's missing */ + DRM_DEBUG_KMS("dm: DMCU firmware not found\n"); + adev->dm.fw_dmcu = NULL; + return 0; + } + if (r) { + dev_err(adev->dev, "amdgpu_dm: Can't load firmware \"%s\"\n", + fw_name_dmcu); + return r; + } + + r = amdgpu_ucode_validate(adev->dm.fw_dmcu); + if (r) { + dev_err(adev->dev, "amdgpu_dm: Can't validate firmware \"%s\"\n", + fw_name_dmcu); + release_firmware(adev->dm.fw_dmcu); + adev->dm.fw_dmcu = NULL; + return r; + } + + hdr = (const struct dmcu_firmware_header_v1_0 *)adev->dm.fw_dmcu->data; + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].ucode_id = AMDGPU_UCODE_ID_DMCU_ERAM; + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].fw = adev->dm.fw_dmcu; + adev->firmware.fw_size += + ALIGN(le32_to_cpu(hdr->header.ucode_size_bytes) - le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE); + + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].ucode_id = AMDGPU_UCODE_ID_DMCU_INTV; + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].fw = adev->dm.fw_dmcu; + adev->firmware.fw_size += + ALIGN(le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE); + + adev->dm.dmcu_fw_version = le32_to_cpu(hdr->header.ucode_version); + + DRM_DEBUG_KMS("PSP loading DMCU firmware\n"); + return 0; } +static int dm_sw_init(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + return load_dmcu_fw(adev); +} + static int dm_sw_fini(void *handle) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if(adev->dm.fw_dmcu) { + release_firmware(adev->dm.fw_dmcu); + adev->dm.fw_dmcu = NULL; + } + return 0; } @@ -694,7 +781,7 @@ static int dm_resume(void *handle) mutex_unlock(&aconnector->hpd_lock); } - /* Force mode set in atomic comit */ + /* Force mode set in atomic commit */ for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) new_crtc_state->active_changed = true; @@ -825,35 +912,37 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) sink = aconnector->dc_link->local_sink; - /* Edid mgmt connector gets first update only in mode_valid hook and then + /* + * Edid mgmt connector gets first update only in mode_valid hook and then * the connector sink is set to either fake or physical sink depends on link status. - * don't do it here if u are during boot + * Skip if already done during boot. */ if (aconnector->base.force != DRM_FORCE_UNSPECIFIED && aconnector->dc_em_sink) { - /* For S3 resume with headless use eml_sink to fake stream - * because on resume connecotr->sink is set ti NULL + /* + * For S3 resume with headless use eml_sink to fake stream + * because on resume connector->sink is set to NULL */ mutex_lock(&dev->mode_config.mutex); if (sink) { if (aconnector->dc_sink) { - amdgpu_dm_remove_sink_from_freesync_module( - connector); - /* retain and release bellow are used for - * bump up refcount for sink because the link don't point - * to it anymore after disconnect so on next crtc to connector + amdgpu_dm_update_freesync_caps(connector, NULL); + /* + * retain and release below are used to + * bump up refcount for sink because the link doesn't point + * to it anymore after disconnect, so on next crtc to connector * reshuffle by UMD we will get into unwanted dc_sink release */ if (aconnector->dc_sink != aconnector->dc_em_sink) dc_sink_release(aconnector->dc_sink); } aconnector->dc_sink = sink; - amdgpu_dm_add_sink_to_freesync_module( - connector, aconnector->edid); + amdgpu_dm_update_freesync_caps(connector, + aconnector->edid); } else { - amdgpu_dm_remove_sink_from_freesync_module(connector); + amdgpu_dm_update_freesync_caps(connector, NULL); if (!aconnector->dc_sink) aconnector->dc_sink = aconnector->dc_em_sink; else if (aconnector->dc_sink != aconnector->dc_em_sink) @@ -872,8 +961,10 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) return; if (aconnector->dc_sink == sink) { - /* We got a DP short pulse (Link Loss, DP CTS, etc...). - * Do nothing!! */ + /* + * We got a DP short pulse (Link Loss, DP CTS, etc...). + * Do nothing!! + */ DRM_DEBUG_DRIVER("DCHPD: connector_id=%d: dc_sink didn't change.\n", aconnector->connector_id); return; @@ -884,14 +975,17 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) mutex_lock(&dev->mode_config.mutex); - /* 1. Update status of the drm connector - * 2. Send an event and let userspace tell us what to do */ + /* + * 1. Update status of the drm connector + * 2. Send an event and let userspace tell us what to do + */ if (sink) { - /* TODO: check if we still need the S3 mode update workaround. - * If yes, put it here. */ + /* + * TODO: check if we still need the S3 mode update workaround. + * If yes, put it here. + */ if (aconnector->dc_sink) - amdgpu_dm_remove_sink_from_freesync_module( - connector); + amdgpu_dm_update_freesync_caps(connector, NULL); aconnector->dc_sink = sink; if (sink->dc_edid.length == 0) { @@ -907,11 +1001,11 @@ amdgpu_dm_update_connector_after_detect(struct amdgpu_dm_connector *aconnector) drm_dp_cec_set_edid(&aconnector->dm_dp_aux.aux, aconnector->edid); } - amdgpu_dm_add_sink_to_freesync_module(connector, aconnector->edid); + amdgpu_dm_update_freesync_caps(connector, aconnector->edid); } else { drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); - amdgpu_dm_remove_sink_from_freesync_module(connector); + amdgpu_dm_update_freesync_caps(connector, NULL); drm_connector_update_edid_property(connector, NULL); aconnector->num_modes = 0; aconnector->dc_sink = NULL; @@ -927,8 +1021,9 @@ static void handle_hpd_irq(void *param) struct drm_connector *connector = &aconnector->base; struct drm_device *dev = connector->dev; - /* In case of failure or MST no need to update connector status or notify the OS - * since (for MST case) MST does this in it's own context. + /* + * In case of failure or MST no need to update connector status or notify the OS + * since (for MST case) MST does this in its own context. */ mutex_lock(&aconnector->hpd_lock); @@ -1011,7 +1106,7 @@ static void dm_handle_hpd_rx_irq(struct amdgpu_dm_connector *aconnector) break; } - /* check if there is new irq to be handle */ + /* check if there is new irq to be handled */ dret = drm_dp_dpcd_read( &aconnector->dm_dp_aux.aux, dpcd_addr, @@ -1036,7 +1131,8 @@ static void handle_hpd_rx_irq(void *param) struct dc_link *dc_link = aconnector->dc_link; bool is_mst_root_connector = aconnector->mst_mgr.mst_state; - /* TODO:Temporary add mutex to protect hpd interrupt not have a gpio + /* + * TODO:Temporary add mutex to protect hpd interrupt not have a gpio * conflict, after implement i2c helper, this mutex should be * retired. */ @@ -1129,7 +1225,8 @@ static int dce110_register_irq_handlers(struct amdgpu_device *adev) int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - /* Actions of amdgpu_irq_add_id(): + /* + * Actions of amdgpu_irq_add_id(): * 1. Register a set() function with base driver. * Base driver will call set() function to enable/disable an * interrupt in DC hardware. @@ -1209,7 +1306,8 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - /* Actions of amdgpu_irq_add_id(): + /* + * Actions of amdgpu_irq_add_id(): * 1. Register a set() function with base driver. * Base driver will call set() function to enable/disable an * interrupt in DC hardware. @@ -1218,7 +1316,7 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) * coming from DC hardware. * amdgpu_dm_irq_handler() will re-direct the interrupt to DC * for acknowledging and handling. - * */ + */ /* Use VSTARTUP interrupt */ for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; @@ -1296,7 +1394,7 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev) adev->ddev->mode_config.preferred_depth = 24; adev->ddev->mode_config.prefer_shadow = 1; - /* indicate support of immediate flip */ + /* indicates support for immediate flip */ adev->ddev->mode_config.async_page_flip = true; adev->ddev->mode_config.fb_base = adev->gmc.aper_base; @@ -1382,7 +1480,7 @@ static int initialize_plane(struct amdgpu_display_manager *dm, plane->base.type = mode_info->plane_type[plane_id]; /* - * HACK: IGT tests expect that each plane can only have one + * HACK: IGT tests expect that each plane can only have * one possible CRTC. For now, set one CRTC for each * plane that is not an underlay, but still allow multiple * CRTCs for underlay planes. @@ -1410,7 +1508,8 @@ static void register_backlight_device(struct amdgpu_display_manager *dm, if ((link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) && link->type != dc_connection_none) { - /* Event if registration failed, we should continue with + /* + * Event if registration failed, we should continue with * DM initialization because not having a backlight control * is better then a black screen. */ @@ -1423,7 +1522,8 @@ static void register_backlight_device(struct amdgpu_display_manager *dm, } -/* In this architecture, the association +/* + * In this architecture, the association * connector -> encoder -> crtc * id not really requried. The crtc and connector will hold the * display_index as an abstraction to use with DAL component @@ -1443,7 +1543,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) link_cnt = dm->dc->caps.max_links; if (amdgpu_dm_mode_config_init(dm->adev)) { DRM_ERROR("DM: Failed to initialize mode config\n"); - return -1; + return -EINVAL; } /* Identify the number of planes to be initialized */ @@ -1558,7 +1658,7 @@ fail: kfree(aconnector); for (i = 0; i < dm->dc->caps.max_planes; i++) kfree(mode_info->planes[i]); - return -1; + return -EINVAL; } static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm) @@ -1571,7 +1671,7 @@ static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm) * amdgpu_display_funcs functions *****************************************************************************/ -/** +/* * dm_bandwidth_update - program display watermarks * * @adev: amdgpu_device pointer @@ -1586,26 +1686,68 @@ static void dm_bandwidth_update(struct amdgpu_device *adev) static int amdgpu_notify_freesync(struct drm_device *dev, void *data, struct drm_file *filp) { - struct mod_freesync_params freesync_params; - uint8_t num_streams; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc *crtc; + struct drm_connector *connector; + struct drm_connector_state *old_con_state, *new_con_state; + int ret = 0; uint8_t i; + bool enable = false; - struct amdgpu_device *adev = dev->dev_private; - int r = 0; + drm_modeset_acquire_init(&ctx, 0); - /* Get freesync enable flag from DRM */ + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out; + } + state->acquire_ctx = &ctx; + +retry: + drm_for_each_crtc(crtc, dev) { + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + goto fail; + + /* TODO rework amdgpu_dm_commit_planes so we don't need this */ + ret = drm_atomic_add_affected_planes(state, crtc); + if (ret) + goto fail; + } + + for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { + struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state); + struct drm_crtc_state *new_crtc_state; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc); + struct dm_crtc_state *dm_new_crtc_state; - num_streams = dc_get_current_stream_count(adev->dm.dc); + if (!acrtc) { + ASSERT(0); + continue; + } - for (i = 0; i < num_streams; i++) { - struct dc_stream_state *stream; - stream = dc_get_stream_at_index(adev->dm.dc, i); + new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); - mod_freesync_update_state(adev->dm.freesync_module, - &stream, 1, &freesync_params); + dm_new_crtc_state->freesync_enabled = enable; } - return r; + ret = drm_atomic_commit(state); + +fail: + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_atomic_state_put(state); + +out: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return ret; } static const struct amdgpu_display_funcs dm_display_funcs = { @@ -1738,9 +1880,11 @@ static int dm_early_init(void *handle) if (adev->mode_info.funcs == NULL) adev->mode_info.funcs = &dm_display_funcs; - /* Note: Do NOT change adev->audio_endpt_rreg and + /* + * Note: Do NOT change adev->audio_endpt_rreg and * adev->audio_endpt_wreg because they are initialised in - * amdgpu_device_init() */ + * amdgpu_device_init() + */ #if defined(CONFIG_DEBUG_KERNEL_DC) device_create_file( adev->ddev->dev, @@ -1786,7 +1930,7 @@ static bool fill_rects_from_plane_state(const struct drm_plane_state *state, { plane_state->src_rect.x = state->src_x >> 16; plane_state->src_rect.y = state->src_y >> 16; - /*we ignore for now mantissa and do not to deal with floating pixels :(*/ + /* we ignore the mantissa for now and do not deal with floating pixels :( */ plane_state->src_rect.width = state->src_w >> 16; if (plane_state->src_rect.width == 0) @@ -1838,7 +1982,7 @@ static int get_fb_info(const struct amdgpu_framebuffer *amdgpu_fb, int r = amdgpu_bo_reserve(rbo, false); if (unlikely(r)) { - // Don't show error msg. when return -ERESTARTSYS + /* Don't show error message when returning -ERESTARTSYS */ if (r != -ERESTARTSYS) DRM_ERROR("Unable to reserve buffer: %d\n", r); return r; @@ -1888,6 +2032,10 @@ static int fill_plane_attributes_from_fb(struct amdgpu_device *adev, case DRM_FORMAT_ABGR2101010: plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010; break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR8888; + break; case DRM_FORMAT_NV21: plane_state->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr; break; @@ -2030,8 +2178,6 @@ static int fill_plane_attributes(struct amdgpu_device *adev, return ret; } -/*****************************************************************************/ - static void update_stream_scaling_settings(const struct drm_display_mode *mode, const struct dm_connector_state *dm_state, struct dc_stream_state *stream) @@ -2094,7 +2240,8 @@ convert_color_depth_from_display_info(const struct drm_connector *connector) switch (bpc) { case 0: - /* Temporary Work around, DRM don't parse color depth for + /* + * Temporary Work around, DRM doesn't parse color depth for * EDID revision before 1.4 * TODO: Fix edid parsing */ @@ -2206,7 +2353,6 @@ static void adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_ } while (timing_out->display_color_depth > COLOR_DEPTH_888); } -/*****************************************************************************/ static void fill_stream_properties_from_drm_display_mode(struct dc_stream_state *stream, @@ -2406,9 +2552,10 @@ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) for (i = 0; i < context->stream_count ; i++) { if (!context->streams[i]) continue; - /* TODO: add a function to read AMD VSDB bits and will set + /* + * TODO: add a function to read AMD VSDB bits and set * crtc_sync_master.multi_sync_enabled flag - * For now its set to false + * For now it's set to false */ set_multisync_trigger_params(context->streams[i]); } @@ -2471,7 +2618,8 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, head); if (preferred_mode == NULL) { - /* This may not be an error, the use case is when we we have no + /* + * This may not be an error, the use case is when we have no * usermode calls to reset and set mode upon hotplug. In this * case, we call set mode ourselves to restore the previous mode * and the modelist may not be filled in in time. @@ -2565,6 +2713,10 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc) dc_stream_retain(state->stream); } + state->adjust = cur->adjust; + state->vrr_infopacket = cur->vrr_infopacket; + state->freesync_enabled = cur->freesync_enabled; + /* TODO Duplicate dc_stream after objects are stream object is flattened */ return &state->base; @@ -2612,10 +2764,12 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) bool connected; struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - /* Notes: + /* + * Notes: * 1. This interface is NOT called in context of HPD irq. * 2. This interface *is called* in context of user-mode ioctl. Which - * makes it a bad place for *any* MST-related activit. */ + * makes it a bad place for *any* MST-related activity. + */ if (aconnector->base.force == DRM_FORCE_UNSPECIFIED && !aconnector->fake_enable) @@ -2774,13 +2928,15 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) struct dm_connector_state *new_state = kmemdup(state, sizeof(*state), GFP_KERNEL); - if (new_state) { - __drm_atomic_helper_connector_duplicate_state(connector, - &new_state->base); - return &new_state->base; - } + if (!new_state) + return NULL; - return NULL; + __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); + + new_state->freesync_capable = state->freesync_capable; + new_state->freesync_enable = state->freesync_enable; + + return &new_state->base; } static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { @@ -2794,28 +2950,6 @@ static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { .atomic_get_property = amdgpu_dm_connector_atomic_get_property }; -static struct drm_encoder *best_encoder(struct drm_connector *connector) -{ - int enc_id = connector->encoder_ids[0]; - struct drm_mode_object *obj; - struct drm_encoder *encoder; - - DRM_DEBUG_DRIVER("Finding the best encoder\n"); - - /* pick the encoder ids */ - if (enc_id) { - obj = drm_mode_object_find(connector->dev, NULL, enc_id, DRM_MODE_OBJECT_ENCODER); - if (!obj) { - DRM_ERROR("Couldn't find a matching encoder for our connector\n"); - return NULL; - } - encoder = obj_to_encoder(obj); - return encoder; - } - DRM_ERROR("No encoder id\n"); - return NULL; -} - static int get_modes(struct drm_connector *connector) { return amdgpu_dm_connector_get_modes(connector); @@ -2858,7 +2992,8 @@ static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) { struct dc_link *link = (struct dc_link *)aconnector->dc_link; - /* In case of headless boot with force on for DP managed connector + /* + * In case of headless boot with force on for DP managed connector * Those settings have to be != 0 to get initial modeset */ if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) { @@ -2886,7 +3021,8 @@ enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connec (mode->flags & DRM_MODE_FLAG_DBLSCAN)) return result; - /* Only run this the first time mode_valid is called to initilialize + /* + * Only run this the first time mode_valid is called to initilialize * EDID mgmt */ if (aconnector->base.force != DRM_FORCE_UNSPECIFIED && @@ -2927,14 +3063,14 @@ fail: static const struct drm_connector_helper_funcs amdgpu_dm_connector_helper_funcs = { /* - * If hotplug a second bigger display in FB Con mode, bigger resolution + * If hotplugging a second bigger display in FB Con mode, bigger resolution * modes will be filtered by drm_mode_validate_size(), and those modes - * is missing after user start lightdm. So we need to renew modes list. + * are missing after user start lightdm. So we need to renew modes list. * in get_modes call back, not just return the modes count */ .get_modes = get_modes, .mode_valid = amdgpu_dm_connector_mode_valid, - .best_encoder = best_encoder + .best_encoder = drm_atomic_helper_best_encoder }; static void dm_crtc_helper_disable(struct drm_crtc *crtc) @@ -2955,7 +3091,7 @@ static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc, return ret; } - /* In some use cases, like reset, no stream is attached */ + /* In some use cases, like reset, no stream is attached */ if (!dm_crtc_state->stream) return 0; @@ -3004,7 +3140,7 @@ static void dm_drm_plane_reset(struct drm_plane *plane) amdgpu_state = kzalloc(sizeof(*amdgpu_state), GFP_KERNEL); WARN_ON(amdgpu_state == NULL); - + if (amdgpu_state) { plane->state = &amdgpu_state->base; plane->state->plane = plane; @@ -3182,7 +3318,7 @@ static const struct drm_plane_helper_funcs dm_plane_helper_funcs = { * TODO: these are currently initialized to rgb formats only. * For future use cases we should either initialize them dynamically based on * plane capabilities, or initialize this array to all formats, so internal drm - * check will succeed, and let DC to implement proper check + * check will succeed, and let DC implement proper check */ static const uint32_t rgb_formats[] = { DRM_FORMAT_RGB888, @@ -3193,6 +3329,8 @@ static const uint32_t rgb_formats[] = { DRM_FORMAT_XBGR2101010, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, }; static const uint32_t yuv_formats[] = { @@ -3294,6 +3432,7 @@ static int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, acrtc->crtc_id = crtc_index; acrtc->base.enabled = false; + acrtc->otg_inst = -1; dm->adev->mode_info.crtcs[crtc_index] = acrtc; drm_crtc_enable_color_mgmt(&acrtc->base, MAX_COLOR_LUT_ENTRIES, @@ -3316,6 +3455,8 @@ static int to_drm_connector_type(enum signal_type st) return DRM_MODE_CONNECTOR_HDMIA; case SIGNAL_TYPE_EDP: return DRM_MODE_CONNECTOR_eDP; + case SIGNAL_TYPE_LVDS: + return DRM_MODE_CONNECTOR_LVDS; case SIGNAL_TYPE_RGB: return DRM_MODE_CONNECTOR_VGA; case SIGNAL_TYPE_DISPLAY_PORT: @@ -3476,7 +3617,8 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) encoder = helper->best_encoder(connector); if (!edid || !drm_edid_is_valid(edid)) { - drm_add_modes_noedid(connector, 640, 480); + amdgpu_dm_connector->num_modes = + drm_add_modes_noedid(connector, 640, 480); } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); amdgpu_dm_connector_add_common_modes(encoder, connector); @@ -3503,7 +3645,8 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */ mutex_init(&aconnector->hpd_lock); - /* configure support HPD hot plug connector_>polled default value is 0 + /* + * configure support HPD hot plug connector_>polled default value is 0 * which means HPD hot plug not supported */ switch (connector_type) { @@ -3565,9 +3708,9 @@ static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, cmd.payloads[i].data = msgs[i].buf; } - if (dal_i2caux_submit_i2c_command( - ddc_service->ctx->i2caux, - ddc_service->ddc_pin, + if (dc_submit_i2c( + ddc_service->ctx->dc, + ddc_service->ddc_pin->hw_info.ddc_channel, &cmd)) result = num; @@ -3603,12 +3746,14 @@ create_i2c(struct ddc_service *ddc_service, snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", link_index); i2c_set_adapdata(&i2c->base, i2c); i2c->ddc_service = ddc_service; + i2c->ddc_service->ddc_pin->hw_info.ddc_channel = link_index; return i2c; } -/* Note: this function assumes that dc_link_detect() was called for the +/* + * Note: this function assumes that dc_link_detect() was called for the * dc_link which will be represented by this aconnector. */ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, @@ -3787,8 +3932,6 @@ static void remove_stream(struct amdgpu_device *adev, struct dc_stream_state *stream) { /* this is the update mode case */ - if (adev->dm.freesync_module) - mod_freesync_remove_stream(adev->dm.freesync_module, stream); acrtc->otg_inst = -1; acrtc->enabled = false; @@ -3942,7 +4085,8 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) + amdgpu_get_vblank_counter_kms(crtc->dev, acrtc->crtc_id); - /* TODO This might fail and hence better not used, wait + /* + * TODO This might fail and hence better not used, wait * explicitly on fences instead * and in general should be called for * blocking commit to as per framework helpers @@ -3959,7 +4103,8 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, amdgpu_bo_unreserve(abo); - /* Wait until we're out of the vertical blank period before the one + /* + * Wait until we're out of the vertical blank period before the one * targeted by the flip */ while ((acrtc->enabled && @@ -4056,6 +4201,11 @@ static bool commit_planes_to_stream( stream_update->dst = dc_stream->dst; stream_update->out_transfer_func = dc_stream->out_transfer_func; + if (dm_new_crtc_state->freesync_enabled != dm_old_crtc_state->freesync_enabled) { + stream_update->vrr_infopacket = &dc_stream->vrr_infopacket; + stream_update->adjust = &dc_stream->adjust; + } + for (i = 0; i < new_plane_count; i++) { updates[i].surface = plane_states[i]; updates[i].gamma = @@ -4191,6 +4341,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); } + dc_stream_attach->adjust = acrtc_state->adjust; + dc_stream_attach->vrr_infopacket = acrtc_state->vrr_infopacket; if (false == commit_planes_to_stream(dm->dc, plane_states_constructed, @@ -4204,7 +4356,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, } } -/** +/* * amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC * @crtc_state: the DRM CRTC state * @stream_state: the DC stream state. @@ -4241,8 +4393,10 @@ static int amdgpu_dm_atomic_commit(struct drm_device *dev, if (drm_atomic_crtc_needs_modeset(new_crtc_state) && dm_old_crtc_state->stream) manage_dm_interrupts(adev, acrtc, false); } - /* Add check here for SoC's that support hardware cursor plane, to - * unset legacy_cursor_update */ + /* + * Add check here for SoC's that support hardware cursor plane, to + * unset legacy_cursor_update + */ return drm_atomic_helper_commit(dev, state, nonblock); @@ -4307,8 +4461,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) * this could happen because of issues with * userspace notifications delivery. * In this case userspace tries to set mode on - * display which is disconnect in fact. - * dc_sink in NULL in this case on aconnector. + * display which is disconnected in fact. + * dc_sink is NULL in this case on aconnector. * We expect reset mode will come soon. * * This can also happen when unplug is done @@ -4340,62 +4494,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) } } /* for_each_crtc_in_state() */ - /* - * Add streams after required streams from new and replaced streams - * are removed from freesync module - */ - if (adev->dm.freesync_module) { - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, - new_crtc_state, i) { - struct amdgpu_dm_connector *aconnector = NULL; - struct dm_connector_state *dm_new_con_state = NULL; - struct amdgpu_crtc *acrtc = NULL; - bool modeset_needed; - - dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); - dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); - modeset_needed = modeset_required( - new_crtc_state, - dm_new_crtc_state->stream, - dm_old_crtc_state->stream); - /* We add stream to freesync if: - * 1. Said stream is not null, and - * 2. A modeset is requested. This means that the - * stream was removed previously, and needs to be - * replaced. - */ - if (dm_new_crtc_state->stream == NULL || - !modeset_needed) - continue; - - acrtc = to_amdgpu_crtc(crtc); - - aconnector = - amdgpu_dm_find_first_crtc_matching_connector( - state, crtc); - if (!aconnector) { - DRM_DEBUG_DRIVER("Atomic commit: Failed to " - "find connector for acrtc " - "id:%d skipping freesync " - "init\n", - acrtc->crtc_id); - continue; - } - - mod_freesync_add_stream(adev->dm.freesync_module, - dm_new_crtc_state->stream, - &aconnector->caps); - new_con_state = drm_atomic_get_new_connector_state( - state, &aconnector->base); - dm_new_con_state = to_dm_connector_state(new_con_state); - - mod_freesync_set_user_enable(adev->dm.freesync_module, - &dm_new_crtc_state->stream, - 1, - &dm_new_con_state->user_enable); - } - } - if (dm_state->context) { dm_enable_per_frame_crtc_master_sync(dm_state->context); WARN_ON(!dc_commit_state(dm->dc, dm_state->context)); @@ -4433,7 +4531,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) if (!acrtc || drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; - /* Skip any thing not scale or underscan changes */ + /* Skip anything that is not scaling or underscan changes */ if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state)) continue; @@ -4449,6 +4547,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) WARN_ON(!status); WARN_ON(!status->plane_count); + dm_new_crtc_state->stream->adjust = dm_new_crtc_state->adjust; + dm_new_crtc_state->stream->vrr_infopacket = dm_new_crtc_state->vrr_infopacket; + /*TODO How it works with MPO ?*/ if (!commit_planes_to_stream( dm->dc, @@ -4481,11 +4582,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) if (dm_new_crtc_state->stream == NULL || !modeset_needed) continue; - if (adev->dm.freesync_module) - mod_freesync_notify_mode_change( - adev->dm.freesync_module, - &dm_new_crtc_state->stream, 1); - manage_dm_interrupts(adev, acrtc, true); } @@ -4520,7 +4616,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_cleanup_planes(dev, state); - /* Finally, drop a runtime PM reference for each newly disabled CRTC, + /* + * Finally, drop a runtime PM reference for each newly disabled CRTC, * so we can put the GPU into runtime suspend if we're not driving any * displays anymore */ @@ -4588,9 +4685,9 @@ err: } /* - * This functions handle all cases when set mode does not come upon hotplug. - * This include when the same display is unplugged then plugged back into the - * same port and when we are running without usermode desktop manager supprot + * This function handles all cases when set mode does not come upon hotplug. + * This includes when a display is unplugged then plugged back into the + * same port and when running without usermode desktop manager supprot */ void dm_restore_drm_connector_state(struct drm_device *dev, struct drm_connector *connector) @@ -4619,7 +4716,7 @@ void dm_restore_drm_connector_state(struct drm_device *dev, dm_force_atomic_commit(&aconnector->base); } -/*` +/* * Grabs all modesetting locks to serialize against any blocking commits, * Waits for completion of all non blocking commits. */ @@ -4630,7 +4727,8 @@ static int do_aquire_global_lock(struct drm_device *dev, struct drm_crtc_commit *commit; long ret; - /* Adding all modeset locks to aquire_ctx will + /* + * Adding all modeset locks to aquire_ctx will * ensure that when the framework release it the * extra locks we are locking here will get released to */ @@ -4649,7 +4747,8 @@ static int do_aquire_global_lock(struct drm_device *dev, if (!commit) continue; - /* Make sure all pending HW programming completed and + /* + * Make sure all pending HW programming completed and * page flips done */ ret = wait_for_completion_interruptible_timeout(&commit->hw_done, 10*HZ); @@ -4668,7 +4767,43 @@ static int do_aquire_global_lock(struct drm_device *dev, return ret < 0 ? ret : 0; } -static int dm_update_crtcs_state(struct dc *dc, +void set_freesync_on_stream(struct amdgpu_display_manager *dm, + struct dm_crtc_state *new_crtc_state, + struct dm_connector_state *new_con_state, + struct dc_stream_state *new_stream) +{ + struct mod_freesync_config config = {0}; + struct mod_vrr_params vrr = {0}; + struct dc_info_packet vrr_infopacket = {0}; + struct amdgpu_dm_connector *aconnector = + to_amdgpu_dm_connector(new_con_state->base.connector); + + if (new_con_state->freesync_capable && + new_con_state->freesync_enable) { + config.state = new_crtc_state->freesync_enabled ? + VRR_STATE_ACTIVE_VARIABLE : + VRR_STATE_INACTIVE; + config.min_refresh_in_uhz = + aconnector->min_vfreq * 1000000; + config.max_refresh_in_uhz = + aconnector->max_vfreq * 1000000; + config.vsif_supported = true; + } + + mod_freesync_build_vrr_params(dm->freesync_module, + new_stream, + &config, &vrr); + + mod_freesync_build_vrr_infopacket(dm->freesync_module, + new_stream, + &vrr, + &vrr_infopacket); + + new_crtc_state->adjust = vrr.adjust; + new_crtc_state->vrr_infopacket = vrr_infopacket; +} + +static int dm_update_crtcs_state(struct amdgpu_display_manager *dm, struct drm_atomic_state *state, bool enable, bool *lock_and_validation_needed) @@ -4681,8 +4816,10 @@ static int dm_update_crtcs_state(struct dc *dc, struct dc_stream_state *new_stream; int ret = 0; - /*TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set */ - /* update changed items */ + /* + * TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set + * update changed items + */ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct amdgpu_crtc *acrtc = NULL; struct amdgpu_dm_connector *aconnector = NULL; @@ -4707,7 +4844,7 @@ static int dm_update_crtcs_state(struct dc *dc, /* TODO This hack should go away */ if (aconnector && enable) { - // Make sure fake sink is created in plug-in scenario + /* Make sure fake sink is created in plug-in scenario */ drm_new_conn_state = drm_atomic_get_new_connector_state(state, &aconnector->base); drm_old_conn_state = drm_atomic_get_old_connector_state(state, @@ -4727,9 +4864,9 @@ static int dm_update_crtcs_state(struct dc *dc, /* * we can have no stream on ACTION_SET if a display - * was disconnected during S3, in this case it not and + * was disconnected during S3, in this case it is not an * error, the OS will be updated after detection, and - * do the right thing on next atomic commit + * will do the right thing on next atomic commit */ if (!new_stream) { @@ -4738,6 +4875,9 @@ static int dm_update_crtcs_state(struct dc *dc, break; } + set_freesync_on_stream(dm, dm_new_crtc_state, + dm_new_conn_state, new_stream); + if (dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) { new_crtc_state->mode_changed = false; @@ -4746,6 +4886,9 @@ static int dm_update_crtcs_state(struct dc *dc, } } + if (dm_old_crtc_state->freesync_enabled != dm_new_crtc_state->freesync_enabled) + new_crtc_state->mode_changed = true; + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) goto next_crtc; @@ -4772,7 +4915,7 @@ static int dm_update_crtcs_state(struct dc *dc, /* i.e. reset mode */ if (dc_remove_stream_from_ctx( - dc, + dm->dc, dm_state->context, dm_old_crtc_state->stream) != DC_OK) { ret = -EINVAL; @@ -4809,7 +4952,7 @@ static int dm_update_crtcs_state(struct dc *dc, crtc->base.id); if (dc_add_stream_to_ctx( - dc, + dm->dc, dm_state->context, dm_new_crtc_state->stream) != DC_OK) { ret = -EINVAL; @@ -4858,6 +5001,8 @@ next_crtc: goto fail; amdgpu_dm_set_ctm(dm_new_crtc_state); } + + } return ret; @@ -5001,6 +5146,100 @@ static int dm_update_planes_state(struct dc *dc, return ret; } +enum surface_update_type dm_determine_update_type_for_commit(struct dc *dc, struct drm_atomic_state *state) +{ + + + int i, j, num_plane; + struct drm_plane_state *old_plane_state, *new_plane_state; + struct dm_plane_state *new_dm_plane_state, *old_dm_plane_state; + struct drm_crtc *new_plane_crtc, *old_plane_crtc; + struct drm_plane *plane; + + struct drm_crtc *crtc; + 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 = kzalloc(MAX_SURFACES * sizeof(struct dc_surface_update), GFP_KERNEL); + struct dc_plane_state *surface = kzalloc(MAX_SURFACES * sizeof(struct dc_plane_state), GFP_KERNEL); + struct dc_stream_update stream_update; + enum surface_update_type update_type = UPDATE_TYPE_FAST; + + + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + new_dm_crtc_state = to_dm_crtc_state(new_crtc_state); + old_dm_crtc_state = to_dm_crtc_state(old_crtc_state); + num_plane = 0; + + if (new_dm_crtc_state->stream) { + + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, j) { + new_plane_crtc = new_plane_state->crtc; + old_plane_crtc = old_plane_state->crtc; + new_dm_plane_state = to_dm_plane_state(new_plane_state); + old_dm_plane_state = to_dm_plane_state(old_plane_state); + + if (plane->type == DRM_PLANE_TYPE_CURSOR) + continue; + + if (!state->allow_modeset) + continue; + + if (crtc == new_plane_crtc) { + updates[num_plane].surface = &surface[num_plane]; + + if (new_crtc_state->mode_changed) { + updates[num_plane].surface->src_rect = + new_dm_plane_state->dc_state->src_rect; + updates[num_plane].surface->dst_rect = + new_dm_plane_state->dc_state->dst_rect; + updates[num_plane].surface->rotation = + new_dm_plane_state->dc_state->rotation; + updates[num_plane].surface->in_transfer_func = + new_dm_plane_state->dc_state->in_transfer_func; + stream_update.dst = new_dm_crtc_state->stream->dst; + stream_update.src = new_dm_crtc_state->stream->src; + } + + if (new_crtc_state->color_mgmt_changed) { + updates[num_plane].gamma = + new_dm_plane_state->dc_state->gamma_correction; + updates[num_plane].in_transfer_func = + new_dm_plane_state->dc_state->in_transfer_func; + stream_update.gamut_remap = + &new_dm_crtc_state->stream->gamut_remap_matrix; + stream_update.out_transfer_func = + new_dm_crtc_state->stream->out_transfer_func; + } + + num_plane++; + } + } + + if (num_plane > 0) { + status = dc_stream_get_status(new_dm_crtc_state->stream); + update_type = dc_check_update_surfaces_for_stream(dc, updates, num_plane, + &stream_update, status); + + if (update_type > UPDATE_TYPE_MED) { + update_type = UPDATE_TYPE_FULL; + goto ret; + } + } + + } else if (!new_dm_crtc_state->stream && old_dm_crtc_state->stream) { + update_type = UPDATE_TYPE_FULL; + goto ret; + } + } + +ret: + kfree(updates); + kfree(surface); + + return update_type; +} static int amdgpu_dm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) @@ -5012,6 +5251,9 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, struct drm_connector_state *old_con_state, *new_con_state; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; + enum surface_update_type update_type = UPDATE_TYPE_FAST; + enum surface_update_type overall_update_type = UPDATE_TYPE_FAST; + int ret, i; /* @@ -5025,8 +5267,12 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, goto fail; for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + struct dm_crtc_state *dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + if (!drm_atomic_crtc_needs_modeset(new_crtc_state) && - !new_crtc_state->color_mgmt_changed) + !new_crtc_state->color_mgmt_changed && + (dm_old_crtc_state->freesync_enabled == dm_new_crtc_state->freesync_enabled)) continue; if (!new_crtc_state->enable) @@ -5052,13 +5298,13 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, } /* Disable all crtcs which require disable */ - ret = dm_update_crtcs_state(dc, state, false, &lock_and_validation_needed); + ret = dm_update_crtcs_state(&adev->dm, state, false, &lock_and_validation_needed); if (ret) { goto fail; } /* Enable all crtcs which require enable */ - ret = dm_update_crtcs_state(dc, state, true, &lock_and_validation_needed); + ret = dm_update_crtcs_state(&adev->dm, state, true, &lock_and_validation_needed); if (ret) { goto fail; } @@ -5075,7 +5321,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, goto fail; /* Check scaling and underscan changes*/ - /*TODO Removed scaling changes validation due to inability to commit + /* TODO Removed scaling changes validation due to inability to commit * new stream into context w\o causing full reset. Need to * decide how to handle. */ @@ -5093,20 +5339,37 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state)) continue; + overall_update_type = UPDATE_TYPE_FULL; lock_and_validation_needed = true; } /* * For full updates case when - * removing/adding/updating streams on once CRTC while flipping + * removing/adding/updating streams on one CRTC while flipping * on another CRTC, * acquiring global lock will guarantee that any such full * update commit * will wait for completion of any outstanding flip using DRMs * synchronization events. */ + update_type = dm_determine_update_type_for_commit(dc, state); + + if (overall_update_type < update_type) + overall_update_type = update_type; + + /* + * lock_and_validation_needed was an old way to determine if we need to set + * the global lock. Leaving it in to check if we broke any corner cases + * lock_and_validation_needed true = UPDATE_TYPE_FULL or UPDATE_TYPE_MED + * lock_and_validation_needed false = UPDATE_TYPE_FAST + */ + if (lock_and_validation_needed && overall_update_type <= UPDATE_TYPE_FAST) + WARN(1, "Global lock should be Set, overall_update_type should be UPDATE_TYPE_MED or UPDATE_TYPE_FULL"); + else if (!lock_and_validation_needed && overall_update_type > UPDATE_TYPE_FAST) + WARN(1, "Global lock should NOT be set, overall_update_type should be UPDATE_TYPE_FAST"); - if (lock_and_validation_needed) { + + if (overall_update_type > UPDATE_TYPE_FAST) { ret = do_aquire_global_lock(dev, state); if (ret) @@ -5151,8 +5414,8 @@ static bool is_dp_capable_without_timing_msa(struct dc *dc, return capable; } -void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, - struct edid *edid) +void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + struct edid *edid) { int i; bool edid_check_required; @@ -5171,6 +5434,18 @@ void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, return; } + if (!edid) { + dm_con_state = to_dm_connector_state(connector->state); + + amdgpu_dm_connector->min_vfreq = 0; + amdgpu_dm_connector->max_vfreq = 0; + amdgpu_dm_connector->pixel_clock_mhz = 0; + + dm_con_state->freesync_capable = false; + dm_con_state->freesync_enable = false; + return; + } + dm_con_state = to_dm_connector_state(connector->state); edid_check_required = false; @@ -5221,28 +5496,10 @@ void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, } if (amdgpu_dm_connector->max_vfreq - - amdgpu_dm_connector->min_vfreq > 10) { - amdgpu_dm_connector->caps.supported = true; - amdgpu_dm_connector->caps.min_refresh_in_micro_hz = - amdgpu_dm_connector->min_vfreq * 1000000; - amdgpu_dm_connector->caps.max_refresh_in_micro_hz = - amdgpu_dm_connector->max_vfreq * 1000000; + amdgpu_dm_connector->min_vfreq > 10) { + dm_con_state->freesync_capable = true; } } - - /* - * TODO figure out how to notify user-mode or DRM of freesync caps - * once we figure out how to deal with freesync in an upstreamable - * fashion - */ - } -void amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector) -{ - /* - * TODO fill in once we figure out how to deal with freesync in - * an upstreamable fashion - */ -} 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 54056d180003..d4f1bdf93207 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -54,13 +54,6 @@ struct drm_device; struct amdgpu_dm_irq_handler_data; struct dc; -struct amdgpu_dm_prev_state { - struct drm_framebuffer *fb; - int32_t x; - int32_t y; - struct drm_display_mode mode; -}; - struct common_irq_params { struct amdgpu_device *adev; enum dc_irq_source irq_src; @@ -78,9 +71,7 @@ struct dm_comressor_info { uint64_t gpu_addr; }; - struct amdgpu_display_manager { - struct dal *dal; struct dc *dc; struct cgs_device *cgs_device; @@ -88,8 +79,6 @@ struct amdgpu_display_manager { struct drm_device *ddev; /*DRM base driver*/ u16 display_indexes_num; - struct amdgpu_dm_prev_state prev_state; - /* * 'irq_source_handler_table' holds a list of handlers * per (DAL) IRQ source. @@ -129,6 +118,9 @@ struct amdgpu_display_manager { struct drm_atomic_state *cached_state; struct dm_comressor_info compressor; + + const struct firmware *fw_dmcu; + uint32_t dmcu_fw_version; }; struct amdgpu_dm_connector { @@ -167,9 +159,6 @@ struct amdgpu_dm_connector { int max_vfreq ; int pixel_clock_mhz; - /*freesync caps*/ - struct mod_freesync_caps caps; - struct mutex hpd_lock; bool fake_enable; @@ -197,9 +186,13 @@ struct dm_crtc_state { int crc_skip_count; bool crc_enabled; + + bool freesync_enabled; + struct dc_crtc_timing_adjust adjust; + struct dc_info_packet vrr_infopacket; }; -#define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base) +#define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base) struct dm_atomic_state { struct drm_atomic_state base; @@ -216,7 +209,7 @@ struct dm_connector_state { uint8_t underscan_vborder; uint8_t underscan_hborder; bool underscan_enable; - struct mod_freesync_user_enable user_enable; + bool freesync_enable; bool freesync_capable; }; @@ -250,11 +243,8 @@ enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connec void dm_restore_drm_connector_state(struct drm_device *dev, struct drm_connector *connector); -void amdgpu_dm_add_sink_to_freesync_module(struct drm_connector *connector, - struct edid *edid); - -void -amdgpu_dm_remove_sink_from_freesync_module(struct drm_connector *connector); +void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + struct edid *edid); /* amdgpu_dm_crc.c */ #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 326f6fb7e0bc..be19e6861189 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -22,7 +22,7 @@ * Authors: AMD * */ - +#include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" #include "dc.h" @@ -122,6 +122,8 @@ int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc) { struct drm_property_blob *blob = crtc->base.gamma_lut; struct dc_stream_state *stream = crtc->stream; + struct amdgpu_device *adev = (struct amdgpu_device *) + crtc->base.state->dev->dev_private; struct drm_color_lut *lut; uint32_t lut_size; struct dc_gamma *gamma; @@ -162,7 +164,7 @@ int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc) */ stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; ret = mod_color_calculate_regamma_params(stream->out_transfer_func, - gamma, true); + gamma, true, adev->asic_type <= CHIP_RAVEN); dc_gamma_release(&gamma); if (!ret) { stream->out_transfer_func->type = old_type; 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 0d9e410ca01e..0ef4a40d2247 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 @@ -720,3 +720,86 @@ int connector_debugfs_init(struct amdgpu_dm_connector *connector) return 0; } +/* + * Writes DTN log state to the user supplied buffer. + * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log + */ +static ssize_t dtn_log_read( + struct file *f, + char __user *buf, + size_t size, + loff_t *pos) +{ + struct amdgpu_device *adev = file_inode(f)->i_private; + struct dc *dc = adev->dm.dc; + struct dc_log_buffer_ctx log_ctx = { 0 }; + ssize_t result = 0; + + if (!buf || !size) + return -EINVAL; + + if (!dc->hwss.log_hw_state) + return 0; + + dc->hwss.log_hw_state(dc, &log_ctx); + + if (*pos < log_ctx.pos) { + size_t to_copy = log_ctx.pos - *pos; + + to_copy = min(to_copy, size); + + if (!copy_to_user(buf, log_ctx.buf + *pos, to_copy)) { + *pos += to_copy; + result = to_copy; + } + } + + kfree(log_ctx.buf); + + return result; +} + +/* + * Writes DTN log state to dmesg when triggered via a write. + * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log + */ +static ssize_t dtn_log_write( + struct file *f, + const char __user *buf, + size_t size, + loff_t *pos) +{ + struct amdgpu_device *adev = file_inode(f)->i_private; + struct dc *dc = adev->dm.dc; + + /* Write triggers log output via dmesg. */ + if (size == 0) + return 0; + + if (dc->hwss.log_hw_state) + dc->hwss.log_hw_state(dc, NULL); + + return size; +} + +int dtn_debugfs_init(struct amdgpu_device *adev) +{ + static const struct file_operations dtn_log_fops = { + .owner = THIS_MODULE, + .read = dtn_log_read, + .write = dtn_log_write, + .llseek = default_llseek + }; + + struct drm_minor *minor = adev->ddev->primary; + struct dentry *root = minor->debugfs_root; + + struct dentry *ent = debugfs_create_file( + "amdgpu_dm_dtn_log", + 0644, + root, + adev, + &dtn_log_fops); + + return PTR_ERR_OR_ZERO(ent); +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h index d9ed1b2aa811..bdef1587b0a0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h @@ -30,5 +30,6 @@ #include "amdgpu_dm.h" int connector_debugfs_init(struct amdgpu_dm_connector *connector); +int dtn_debugfs_init(struct amdgpu_device *adev); #endif 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 8403b6a9a77b..39997d977efb 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 @@ -335,15 +335,92 @@ bool dm_helpers_dp_mst_send_payload_allocation( return true; } -void dm_dtn_log_begin(struct dc_context *ctx) -{} +void dm_dtn_log_begin(struct dc_context *ctx, + struct dc_log_buffer_ctx *log_ctx) +{ + static const char msg[] = "[dtn begin]\n"; + + if (!log_ctx) { + pr_info("%s", msg); + return; + } + + dm_dtn_log_append_v(ctx, log_ctx, "%s", msg); +} void dm_dtn_log_append_v(struct dc_context *ctx, - const char *pMsg, ...) -{} + struct dc_log_buffer_ctx *log_ctx, + const char *msg, ...) +{ + va_list args; + size_t total; + int n; -void dm_dtn_log_end(struct dc_context *ctx) -{} + if (!log_ctx) { + /* No context, redirect to dmesg. */ + struct va_format vaf; + + vaf.fmt = msg; + vaf.va = &args; + + va_start(args, msg); + pr_info("%pV", &vaf); + va_end(args); + + return; + } + + /* Measure the output. */ + va_start(args, msg); + n = vsnprintf(NULL, 0, msg, args); + va_end(args); + + if (n <= 0) + return; + + /* Reallocate the string buffer as needed. */ + total = log_ctx->pos + n + 1; + + if (total > log_ctx->size) { + char *buf = (char *)kvcalloc(total, sizeof(char), GFP_KERNEL); + + if (buf) { + memcpy(buf, log_ctx->buf, log_ctx->pos); + kfree(log_ctx->buf); + + log_ctx->buf = buf; + log_ctx->size = total; + } + } + + if (!log_ctx->buf) + return; + + /* Write the formatted string to the log buffer. */ + va_start(args, msg); + n = vscnprintf( + log_ctx->buf + log_ctx->pos, + log_ctx->size - log_ctx->pos, + msg, + args); + va_end(args); + + if (n > 0) + log_ctx->pos += n; +} + +void dm_dtn_log_end(struct dc_context *ctx, + struct dc_log_buffer_ctx *log_ctx) +{ + static const char msg[] = "[dtn end]\n"; + + if (!log_ctx) { + pr_info("%s", msg); + return; + } + + dm_dtn_log_append_v(ctx, log_ctx, "%s", msg); +} bool dm_helpers_dp_mst_start_top_mgr( struct dc_context *ctx, 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 18a3a6e5ffa0..03601d717fed 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 @@ -234,8 +234,9 @@ void dm_dp_mst_dc_sink_create(struct drm_connector *connector) dc_sink->priv = aconnector; aconnector->dc_sink = dc_sink; - amdgpu_dm_add_sink_to_freesync_module( - connector, aconnector->edid); + if (aconnector->dc_sink) + amdgpu_dm_update_freesync_caps( + connector, aconnector->edid); } static int dm_dp_mst_get_modes(struct drm_connector *connector) @@ -275,8 +276,9 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) aconnector->dc_sink = dc_sink; if (aconnector->dc_sink) - amdgpu_dm_add_sink_to_freesync_module( + amdgpu_dm_update_freesync_caps( connector, aconnector->edid); + } drm_connector_update_edid_property( @@ -439,7 +441,7 @@ static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, aconnector->port = NULL; if (aconnector->dc_sink) { - amdgpu_dm_remove_sink_from_freesync_module(connector); + amdgpu_dm_update_freesync_caps(connector, NULL); dc_link_remove_remote_sink(aconnector->dc_link, aconnector->dc_sink); dc_sink_release(aconnector->dc_sink); aconnector->dc_sink = NULL; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c index 4ba0003a9d32..6d16b4a0353d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c @@ -101,18 +101,10 @@ bool dm_pp_apply_display_requirements( adev->pm.pm_display_cfg.displays[i].controller_id = dc_cfg->pipe_idx + 1; } - /* TODO: complete implementation of - * pp_display_configuration_change(). - * Follow example of: - * PHM_StoreDALConfigurationData - powerplay\hwmgr\hardwaremanager.c - * PP_IRI_DisplayConfigurationChange - powerplay\eventmgr\iri.c */ if (adev->powerplay.pp_funcs->display_configuration_change) adev->powerplay.pp_funcs->display_configuration_change( adev->powerplay.pp_handle, &adev->pm.pm_display_cfg); - - /* TODO: replace by a separate call to 'apply display cfg'? */ - amdgpu_pm_compute_clocks(adev); } return true; @@ -478,7 +470,7 @@ bool dm_pp_get_static_clocks( void pp_rv_set_display_requirement(struct pp_smu *pp, struct pp_smu_display_requirement_rv *req) { - struct dc_context *ctx = pp->ctx; + const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; void *pp_handle = adev->powerplay.pp_handle; const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; @@ -499,7 +491,7 @@ void pp_rv_set_display_requirement(struct pp_smu *pp, void pp_rv_set_wm_ranges(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges) { - struct dc_context *ctx = pp->ctx; + const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; void *pp_handle = adev->powerplay.pp_handle; const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; @@ -548,7 +540,7 @@ void pp_rv_set_wm_ranges(struct pp_smu *pp, void pp_rv_set_pme_wa_enable(struct pp_smu *pp) { - struct dc_context *ctx = pp->ctx; + const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; void *pp_handle = adev->powerplay.pp_handle; const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; @@ -563,7 +555,7 @@ void dm_pp_get_funcs_rv( struct dc_context *ctx, struct pp_smu_funcs_rv *funcs) { - funcs->pp_smu.ctx = ctx; + funcs->pp_smu.dm = ctx; funcs->set_display_requirement = pp_rv_set_display_requirement; funcs->set_wm_ranges = pp_rv_set_wm_ranges; funcs->set_pme_wa_enable = pp_rv_set_pme_wa_enable; diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index be8a2494355a..0e1dc1b1a48d 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -42,7 +42,7 @@ #include "bios_parser_interface.h" #include "bios_parser_common.h" -/* TODO remove - only needed for default i2c speed */ + #include "dc.h" #define THREE_PERCENT_OF_10000 300 @@ -52,24 +52,13 @@ #define DC_LOGGER \ bp->base.ctx->logger -/* GUID to validate external display connection info table (aka OPM module) */ -static const uint8_t ext_display_connection_guid[NUMBER_OF_UCHAR_FOR_GUID] = { - 0x91, 0x6E, 0x57, 0x09, - 0x3F, 0x6D, 0xD2, 0x11, - 0x39, 0x8E, 0x00, 0xA0, - 0xC9, 0x69, 0x72, 0x3B}; - #define DATA_TABLES(table) (bp->master_data_tbl->ListOfDataTables.table) static void get_atom_data_table_revision( ATOM_COMMON_TABLE_HEADER *atom_data_tbl, struct atom_data_revision *tbl_revision); -static uint32_t get_dst_number_from_object(struct bios_parser *bp, - ATOM_OBJECT *object); static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, uint16_t **id_list); -static uint32_t get_dest_obj_list(struct bios_parser *bp, - ATOM_OBJECT *object, uint16_t **id_list); static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, struct graphics_object_id id); static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, @@ -163,29 +152,6 @@ static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb) le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset)); } -static struct graphics_object_id bios_parser_get_encoder_id( - struct dc_bios *dcb, - uint32_t i) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - struct graphics_object_id object_id = dal_graphics_object_id_init( - 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); - - uint32_t encoder_table_offset = bp->object_info_tbl_offset - + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); - - ATOM_OBJECT_TABLE *tbl = - GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); - - if (tbl && tbl->ucNumberOfObjects > i) { - const uint16_t id = le16_to_cpu(tbl->asObjects[i].usObjectID); - - object_id = object_id_from_bios_object_id(id); - } - - return object_id; -} - static struct graphics_object_id bios_parser_get_connector_id( struct dc_bios *dcb, uint8_t i) @@ -217,15 +183,6 @@ static struct graphics_object_id bios_parser_get_connector_id( return object_id; } -static uint32_t bios_parser_get_dst_number(struct dc_bios *dcb, - struct graphics_object_id id) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - ATOM_OBJECT *object = get_bios_object(bp, id); - - return get_dst_number_from_object(bp, object); -} - static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, struct graphics_object_id object_id, uint32_t index, struct graphics_object_id *src_object_id) @@ -255,30 +212,6 @@ static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, return BP_RESULT_OK; } -static enum bp_result bios_parser_get_dst_obj(struct dc_bios *dcb, - struct graphics_object_id object_id, uint32_t index, - struct graphics_object_id *dest_object_id) -{ - uint32_t number; - uint16_t *id = NULL; - ATOM_OBJECT *object; - struct bios_parser *bp = BP_FROM_DCB(dcb); - - if (!dest_object_id) - return BP_RESULT_BADINPUT; - - object = get_bios_object(bp, object_id); - - number = get_dest_obj_list(bp, object, &id); - - if (number <= index || !id) - return BP_RESULT_BADINPUT; - - *dest_object_id = object_id_from_bios_object_id(id[index]); - - return BP_RESULT_OK; -} - static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, struct graphics_object_id id, struct graphics_object_i2c_info *info) @@ -325,196 +258,6 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, return BP_RESULT_NORECORD; } -static enum bp_result get_voltage_ddc_info_v1(uint8_t *i2c_line, - ATOM_COMMON_TABLE_HEADER *header, - uint8_t *address) -{ - enum bp_result result = BP_RESULT_NORECORD; - ATOM_VOLTAGE_OBJECT_INFO *info = - (ATOM_VOLTAGE_OBJECT_INFO *) address; - - uint8_t *voltage_current_object = (uint8_t *) &info->asVoltageObj[0]; - - while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { - ATOM_VOLTAGE_OBJECT *object = - (ATOM_VOLTAGE_OBJECT *) voltage_current_object; - - if ((object->ucVoltageType == SET_VOLTAGE_INIT_MODE) && - (object->ucVoltageType & - VOLTAGE_CONTROLLED_BY_I2C_MASK)) { - - *i2c_line = object->asControl.ucVoltageControlI2cLine - ^ 0x90; - result = BP_RESULT_OK; - break; - } - - voltage_current_object += object->ucSize; - } - return result; -} - -static enum bp_result get_voltage_ddc_info_v3(uint8_t *i2c_line, - uint32_t index, - ATOM_COMMON_TABLE_HEADER *header, - uint8_t *address) -{ - enum bp_result result = BP_RESULT_NORECORD; - ATOM_VOLTAGE_OBJECT_INFO_V3_1 *info = - (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *) address; - - uint8_t *voltage_current_object = - (uint8_t *) (&(info->asVoltageObj[0])); - - while ((address + le16_to_cpu(header->usStructureSize)) > voltage_current_object) { - ATOM_I2C_VOLTAGE_OBJECT_V3 *object = - (ATOM_I2C_VOLTAGE_OBJECT_V3 *) voltage_current_object; - - if (object->sHeader.ucVoltageMode == - ATOM_INIT_VOLTAGE_REGULATOR) { - if (object->sHeader.ucVoltageType == index) { - *i2c_line = object->ucVoltageControlI2cLine - ^ 0x90; - result = BP_RESULT_OK; - break; - } - } - - voltage_current_object += le16_to_cpu(object->sHeader.usSize); - } - return result; -} - -static enum bp_result bios_parser_get_thermal_ddc_info( - struct dc_bios *dcb, - uint32_t i2c_channel_id, - struct graphics_object_i2c_info *info) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - ATOM_I2C_ID_CONFIG_ACCESS *config; - ATOM_I2C_RECORD record; - - if (!info) - return BP_RESULT_BADINPUT; - - config = (ATOM_I2C_ID_CONFIG_ACCESS *) &i2c_channel_id; - - record.sucI2cId.bfHW_Capable = config->sbfAccess.bfHW_Capable; - record.sucI2cId.bfI2C_LineMux = config->sbfAccess.bfI2C_LineMux; - record.sucI2cId.bfHW_EngineID = config->sbfAccess.bfHW_EngineID; - - return get_gpio_i2c_info(bp, &record, info); -} - -static enum bp_result bios_parser_get_voltage_ddc_info(struct dc_bios *dcb, - uint32_t index, - struct graphics_object_i2c_info *info) -{ - uint8_t i2c_line = 0; - enum bp_result result = BP_RESULT_NORECORD; - uint8_t *voltage_info_address; - ATOM_COMMON_TABLE_HEADER *header; - struct atom_data_revision revision = {0}; - struct bios_parser *bp = BP_FROM_DCB(dcb); - - if (!DATA_TABLES(VoltageObjectInfo)) - return result; - - voltage_info_address = bios_get_image(&bp->base, DATA_TABLES(VoltageObjectInfo), sizeof(ATOM_COMMON_TABLE_HEADER)); - - header = (ATOM_COMMON_TABLE_HEADER *) voltage_info_address; - - get_atom_data_table_revision(header, &revision); - - switch (revision.major) { - case 1: - case 2: - result = get_voltage_ddc_info_v1(&i2c_line, header, - voltage_info_address); - break; - case 3: - if (revision.minor != 1) - break; - result = get_voltage_ddc_info_v3(&i2c_line, index, header, - voltage_info_address); - break; - } - - if (result == BP_RESULT_OK) - result = bios_parser_get_thermal_ddc_info(dcb, - i2c_line, info); - - return result; -} - -/* TODO: temporary commented out to suppress 'defined but not used' warning */ -#if 0 -static enum bp_result bios_parser_get_ddc_info_for_i2c_line( - struct bios_parser *bp, - uint8_t i2c_line, struct graphics_object_i2c_info *info) -{ - uint32_t offset; - ATOM_OBJECT *object; - ATOM_OBJECT_TABLE *table; - uint32_t i; - - if (!info) - return BP_RESULT_BADINPUT; - - offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); - - offset += bp->object_info_tbl_offset; - - table = GET_IMAGE(ATOM_OBJECT_TABLE, offset); - - if (!table) - return BP_RESULT_BADBIOSTABLE; - - for (i = 0; i < table->ucNumberOfObjects; i++) { - object = &table->asObjects[i]; - - if (!object) { - BREAK_TO_DEBUGGER(); /* Invalid object id */ - return BP_RESULT_BADINPUT; - } - - offset = le16_to_cpu(object->usRecordOffset) - + bp->object_info_tbl_offset; - - for (;;) { - ATOM_COMMON_RECORD_HEADER *header = - GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); - - if (!header) - return BP_RESULT_BADBIOSTABLE; - - offset += header->ucRecordSize; - - if (LAST_RECORD_TYPE == header->ucRecordType || - !header->ucRecordSize) - break; - - if (ATOM_I2C_RECORD_TYPE == header->ucRecordType - && sizeof(ATOM_I2C_RECORD) <= - header->ucRecordSize) { - ATOM_I2C_RECORD *record = - (ATOM_I2C_RECORD *) header; - - if (i2c_line != record->sucI2cId.bfI2C_LineMux) - continue; - - /* get the I2C info */ - if (get_gpio_i2c_info(bp, record, info) == - BP_RESULT_OK) - return BP_RESULT_OK; - } - } - } - - return BP_RESULT_NORECORD; -} -#endif - static enum bp_result bios_parser_get_hpd_info(struct dc_bios *dcb, struct graphics_object_id id, struct graphics_object_hpd_info *info) @@ -1129,62 +872,6 @@ static bool bios_parser_is_device_id_supported( return (le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport) & mask) != 0; } -static enum bp_result bios_parser_crt_control( - struct dc_bios *dcb, - enum engine_id engine_id, - bool enable, - uint32_t pixel_clock) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - uint8_t standard; - - if (!bp->cmd_tbl.dac1_encoder_control && - engine_id == ENGINE_ID_DACA) - return BP_RESULT_FAILURE; - if (!bp->cmd_tbl.dac2_encoder_control && - engine_id == ENGINE_ID_DACB) - return BP_RESULT_FAILURE; - /* validate params */ - switch (engine_id) { - case ENGINE_ID_DACA: - case ENGINE_ID_DACB: - break; - default: - /* unsupported engine */ - return BP_RESULT_FAILURE; - } - - standard = ATOM_DAC1_PS2; /* == ATOM_DAC2_PS2 */ - - if (enable) { - if (engine_id == ENGINE_ID_DACA) { - bp->cmd_tbl.dac1_encoder_control(bp, enable, - pixel_clock, standard); - if (bp->cmd_tbl.dac1_output_control != NULL) - bp->cmd_tbl.dac1_output_control(bp, enable); - } else { - bp->cmd_tbl.dac2_encoder_control(bp, enable, - pixel_clock, standard); - if (bp->cmd_tbl.dac2_output_control != NULL) - bp->cmd_tbl.dac2_output_control(bp, enable); - } - } else { - if (engine_id == ENGINE_ID_DACA) { - if (bp->cmd_tbl.dac1_output_control != NULL) - bp->cmd_tbl.dac1_output_control(bp, enable); - bp->cmd_tbl.dac1_encoder_control(bp, enable, - pixel_clock, standard); - } else { - if (bp->cmd_tbl.dac2_output_control != NULL) - bp->cmd_tbl.dac2_output_control(bp, enable); - bp->cmd_tbl.dac2_encoder_control(bp, enable, - pixel_clock, standard); - } - } - - return BP_RESULT_OK; -} - static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, ATOM_OBJECT *object) { @@ -1219,49 +906,6 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, return NULL; } -/** - * Get I2C information of input object id - * - * search all records to find the ATOM_I2C_RECORD_TYPE record IR - */ -static ATOM_I2C_RECORD *get_i2c_record( - struct bios_parser *bp, - ATOM_OBJECT *object) -{ - uint32_t offset; - ATOM_COMMON_RECORD_HEADER *record_header; - - if (!object) { - BREAK_TO_DEBUGGER(); - /* Invalid object */ - return NULL; - } - - offset = le16_to_cpu(object->usRecordOffset) - + bp->object_info_tbl_offset; - - for (;;) { - record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); - - if (!record_header) - return NULL; - - if (LAST_RECORD_TYPE == record_header->ucRecordType || - 0 == record_header->ucRecordSize) - break; - - if (ATOM_I2C_RECORD_TYPE == record_header->ucRecordType && - sizeof(ATOM_I2C_RECORD) <= - record_header->ucRecordSize) { - return (ATOM_I2C_RECORD *)record_header; - } - - offset += record_header->ucRecordSize; - } - - return NULL; -} - static enum bp_result get_ss_info_from_ss_info_table( struct bios_parser *bp, uint32_t id, @@ -2356,40 +2000,6 @@ static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, return NULL; } -static uint32_t get_dest_obj_list(struct bios_parser *bp, - ATOM_OBJECT *object, uint16_t **id_list) -{ - uint32_t offset; - uint8_t *number; - - if (!object) { - BREAK_TO_DEBUGGER(); /* Invalid object id */ - return 0; - } - - offset = le16_to_cpu(object->usSrcDstTableOffset) - + bp->object_info_tbl_offset; - - number = GET_IMAGE(uint8_t, offset); - if (!number) - return 0; - - offset += sizeof(uint8_t); - offset += sizeof(uint16_t) * (*number); - - number = GET_IMAGE(uint8_t, offset); - if ((!number) || (!*number)) - return 0; - - offset += sizeof(uint8_t); - *id_list = (uint16_t *)bios_get_image(&bp->base, offset, *number * sizeof(uint16_t)); - - if (!*id_list) - return 0; - - return *number; -} - static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, uint16_t **id_list) { @@ -2417,35 +2027,6 @@ static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, return *number; } -static uint32_t get_dst_number_from_object(struct bios_parser *bp, - ATOM_OBJECT *object) -{ - uint32_t offset; - uint8_t *number; - - if (!object) { - BREAK_TO_DEBUGGER(); /* Invalid encoder object id*/ - return 0; - } - - offset = le16_to_cpu(object->usSrcDstTableOffset) - + bp->object_info_tbl_offset; - - number = GET_IMAGE(uint8_t, offset); - if (!number) - return 0; - - offset += sizeof(uint8_t); - offset += sizeof(uint16_t) * (*number); - - number = GET_IMAGE(uint8_t, offset); - - if (!number) - return 0; - - return *number; -} - static struct device_id device_type_from_device_id(uint16_t device_id) { @@ -2625,752 +2206,6 @@ static uint32_t get_support_mask_for_device_id(struct device_id device_id) } /** - * HwContext interface for writing MM registers - */ - -static bool i2c_read( - struct bios_parser *bp, - struct graphics_object_i2c_info *i2c_info, - uint8_t *buffer, - uint32_t length) -{ - struct ddc *ddc; - uint8_t offset[2] = { 0, 0 }; - bool result = false; - struct i2c_command cmd; - struct gpio_ddc_hw_info hw_info = { - i2c_info->i2c_hw_assist, - i2c_info->i2c_line }; - - ddc = dal_gpio_create_ddc(bp->base.ctx->gpio_service, - i2c_info->gpio_info.clk_a_register_index, - (1 << i2c_info->gpio_info.clk_a_shift), &hw_info); - - if (!ddc) - return result; - - /*Using SW engine */ - cmd.engine = I2C_COMMAND_ENGINE_SW; - cmd.speed = ddc->ctx->dc->caps.i2c_speed_in_khz; - - { - struct i2c_payload payloads[] = { - { - .address = i2c_info->i2c_slave_address >> 1, - .data = offset, - .length = sizeof(offset), - .write = true - }, - { - .address = i2c_info->i2c_slave_address >> 1, - .data = buffer, - .length = length, - .write = false - } - }; - - cmd.payloads = payloads; - cmd.number_of_payloads = ARRAY_SIZE(payloads); - - /* TODO route this through drm i2c_adapter */ - result = dal_i2caux_submit_i2c_command( - ddc->ctx->i2caux, - ddc, - &cmd); - } - - dal_gpio_destroy_ddc(&ddc); - - return result; -} - -/** - * Read external display connection info table through i2c. - * validate the GUID and checksum. - * - * @return enum bp_result whether all data was sucessfully read - */ -static enum bp_result get_ext_display_connection_info( - struct bios_parser *bp, - ATOM_OBJECT *opm_object, - ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *ext_display_connection_info_tbl) -{ - bool config_tbl_present = false; - ATOM_I2C_RECORD *i2c_record = NULL; - uint32_t i = 0; - - if (opm_object == NULL) - return BP_RESULT_BADINPUT; - - i2c_record = get_i2c_record(bp, opm_object); - - if (i2c_record != NULL) { - ATOM_GPIO_I2C_INFO *gpio_i2c_header; - struct graphics_object_i2c_info i2c_info; - - gpio_i2c_header = GET_IMAGE(ATOM_GPIO_I2C_INFO, - bp->master_data_tbl->ListOfDataTables.GPIO_I2C_Info); - - if (NULL == gpio_i2c_header) - return BP_RESULT_BADBIOSTABLE; - - if (get_gpio_i2c_info(bp, i2c_record, &i2c_info) != - BP_RESULT_OK) - return BP_RESULT_BADBIOSTABLE; - - if (i2c_read(bp, - &i2c_info, - (uint8_t *)ext_display_connection_info_tbl, - sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO))) { - config_tbl_present = true; - } - } - - /* Validate GUID */ - if (config_tbl_present) - for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; i++) { - if (ext_display_connection_info_tbl->ucGuid[i] - != ext_display_connection_guid[i]) { - config_tbl_present = false; - break; - } - } - - /* Validate checksum */ - if (config_tbl_present) { - uint8_t check_sum = 0; - uint8_t *buf = - (uint8_t *)ext_display_connection_info_tbl; - - for (i = 0; i < sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO); - i++) { - check_sum += buf[i]; - } - - if (check_sum != 0) - config_tbl_present = false; - } - - if (config_tbl_present) - return BP_RESULT_OK; - else - return BP_RESULT_FAILURE; -} - -/* - * Gets the first device ID in the same group as the given ID for enumerating. - * For instance, if any DFP device ID is passed, returns the device ID for DFP1. - * - * The first device ID in the same group as the passed device ID, or 0 if no - * matching device group found. - */ -static uint32_t enum_first_device_id(uint32_t dev_id) -{ - /* Return the first in the group that this ID belongs to. */ - if (dev_id & ATOM_DEVICE_CRT_SUPPORT) - return ATOM_DEVICE_CRT1_SUPPORT; - else if (dev_id & ATOM_DEVICE_DFP_SUPPORT) - return ATOM_DEVICE_DFP1_SUPPORT; - else if (dev_id & ATOM_DEVICE_LCD_SUPPORT) - return ATOM_DEVICE_LCD1_SUPPORT; - else if (dev_id & ATOM_DEVICE_TV_SUPPORT) - return ATOM_DEVICE_TV1_SUPPORT; - else if (dev_id & ATOM_DEVICE_CV_SUPPORT) - return ATOM_DEVICE_CV_SUPPORT; - - /* No group found for this device ID. */ - - dm_error("%s: incorrect input %d\n", __func__, dev_id); - /* No matching support flag for given device ID */ - return 0; -} - -/* - * Gets the next device ID in the group for a given device ID. - * - * The current device ID being enumerated on. - * - * The next device ID in the group, or 0 if no device exists. - */ -static uint32_t enum_next_dev_id(uint32_t dev_id) -{ - /* Get next device ID in the group. */ - switch (dev_id) { - case ATOM_DEVICE_CRT1_SUPPORT: - return ATOM_DEVICE_CRT2_SUPPORT; - case ATOM_DEVICE_LCD1_SUPPORT: - return ATOM_DEVICE_LCD2_SUPPORT; - case ATOM_DEVICE_DFP1_SUPPORT: - return ATOM_DEVICE_DFP2_SUPPORT; - case ATOM_DEVICE_DFP2_SUPPORT: - return ATOM_DEVICE_DFP3_SUPPORT; - case ATOM_DEVICE_DFP3_SUPPORT: - return ATOM_DEVICE_DFP4_SUPPORT; - case ATOM_DEVICE_DFP4_SUPPORT: - return ATOM_DEVICE_DFP5_SUPPORT; - case ATOM_DEVICE_DFP5_SUPPORT: - return ATOM_DEVICE_DFP6_SUPPORT; - } - - /* Done enumerating through devices. */ - return 0; -} - -/* - * Returns the new device tag record for patched BIOS object. - * - * [IN] pExtDisplayPath - External display path to copy device tag from. - * [IN] deviceSupport - Bit vector for device ID support flags. - * [OUT] pDeviceTag - Device tag structure to fill with patched data. - * - * True if a compatible device ID was found, false otherwise. - */ -static bool get_patched_device_tag( - struct bios_parser *bp, - EXT_DISPLAY_PATH *ext_display_path, - uint32_t device_support, - ATOM_CONNECTOR_DEVICE_TAG *device_tag) -{ - uint32_t dev_id; - /* Use fallback behaviour if not supported. */ - if (!bp->remap_device_tags) { - device_tag->ulACPIDeviceEnum = - cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); - device_tag->usDeviceID = - cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceTag)); - return true; - } - - /* Find the first unused in the same group. */ - dev_id = enum_first_device_id(le16_to_cpu(ext_display_path->usDeviceTag)); - while (dev_id != 0) { - /* Assign this device ID if supported. */ - if ((device_support & dev_id) != 0) { - device_tag->ulACPIDeviceEnum = - cpu_to_le32((uint32_t) le16_to_cpu(ext_display_path->usDeviceACPIEnum)); - device_tag->usDeviceID = cpu_to_le16((USHORT) dev_id); - return true; - } - - dev_id = enum_next_dev_id(dev_id); - } - - /* No compatible device ID found. */ - return false; -} - -/* - * Adds a device tag to a BIOS object's device tag record if there is - * matching device ID supported. - * - * pObject - Pointer to the BIOS object to add the device tag to. - * pExtDisplayPath - Display path to retrieve base device ID from. - * pDeviceSupport - Pointer to bit vector for supported device IDs. - */ -static void add_device_tag_from_ext_display_path( - struct bios_parser *bp, - ATOM_OBJECT *object, - EXT_DISPLAY_PATH *ext_display_path, - uint32_t *device_support) -{ - /* Get device tag record for object. */ - ATOM_CONNECTOR_DEVICE_TAG *device_tag = NULL; - ATOM_CONNECTOR_DEVICE_TAG_RECORD *device_tag_record = NULL; - enum bp_result result = - bios_parser_get_device_tag_record( - bp, object, &device_tag_record); - - if ((le16_to_cpu(ext_display_path->usDeviceTag) != CONNECTOR_OBJECT_ID_NONE) - && (result == BP_RESULT_OK)) { - uint8_t index; - - if ((device_tag_record->ucNumberOfDevice == 1) && - (le16_to_cpu(device_tag_record->asDeviceTag[0].usDeviceID) == 0)) { - /*Workaround bug in current VBIOS releases where - * ucNumberOfDevice = 1 but there is no actual device - * tag data. This w/a is temporary until the updated - * VBIOS is distributed. */ - device_tag_record->ucNumberOfDevice = - device_tag_record->ucNumberOfDevice - 1; - } - - /* Attempt to find a matching device ID. */ - index = device_tag_record->ucNumberOfDevice; - device_tag = &device_tag_record->asDeviceTag[index]; - if (get_patched_device_tag( - bp, - ext_display_path, - *device_support, - device_tag)) { - /* Update cached device support to remove assigned ID. - */ - *device_support &= ~le16_to_cpu(device_tag->usDeviceID); - device_tag_record->ucNumberOfDevice++; - } - } -} - -/* - * Read out a single EXT_DISPLAY_PATH from the external display connection info - * table. The specific entry in the table is determined by the enum_id passed - * in. - * - * EXT_DISPLAY_PATH describing a single Configuration table entry - */ - -#define INVALID_CONNECTOR 0xffff - -static EXT_DISPLAY_PATH *get_ext_display_path_entry( - ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO *config_table, - uint32_t bios_object_id) -{ - EXT_DISPLAY_PATH *ext_display_path; - uint32_t ext_display_path_index = - ((bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT) - 1; - - if (ext_display_path_index >= MAX_NUMBER_OF_EXT_DISPLAY_PATH) - return NULL; - - ext_display_path = &config_table->sPath[ext_display_path_index]; - - if (le16_to_cpu(ext_display_path->usDeviceConnector) == INVALID_CONNECTOR) - ext_display_path->usDeviceConnector = cpu_to_le16(0); - - return ext_display_path; -} - -/* - * Get AUX/DDC information of input object id - * - * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record - * IR - */ -static ATOM_CONNECTOR_AUXDDC_LUT_RECORD *get_ext_connector_aux_ddc_lut_record( - struct bios_parser *bp, - ATOM_OBJECT *object) -{ - uint32_t offset; - ATOM_COMMON_RECORD_HEADER *header; - - if (!object) { - BREAK_TO_DEBUGGER(); - /* Invalid object */ - return NULL; - } - - offset = le16_to_cpu(object->usRecordOffset) - + bp->object_info_tbl_offset; - - for (;;) { - header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); - - if (!header) - return NULL; - - if (LAST_RECORD_TYPE == header->ucRecordType || - 0 == header->ucRecordSize) - break; - - if (ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE == - header->ucRecordType && - sizeof(ATOM_CONNECTOR_AUXDDC_LUT_RECORD) <= - header->ucRecordSize) - return (ATOM_CONNECTOR_AUXDDC_LUT_RECORD *)(header); - - offset += header->ucRecordSize; - } - - return NULL; -} - -/* - * Get AUX/DDC information of input object id - * - * search all records to find the ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE record - * IR - */ -static ATOM_CONNECTOR_HPDPIN_LUT_RECORD *get_ext_connector_hpd_pin_lut_record( - struct bios_parser *bp, - ATOM_OBJECT *object) -{ - uint32_t offset; - ATOM_COMMON_RECORD_HEADER *header; - - if (!object) { - BREAK_TO_DEBUGGER(); - /* Invalid object */ - return NULL; - } - - offset = le16_to_cpu(object->usRecordOffset) - + bp->object_info_tbl_offset; - - for (;;) { - header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); - - if (!header) - return NULL; - - if (LAST_RECORD_TYPE == header->ucRecordType || - 0 == header->ucRecordSize) - break; - - if (ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE == - header->ucRecordType && - sizeof(ATOM_CONNECTOR_HPDPIN_LUT_RECORD) <= - header->ucRecordSize) - return (ATOM_CONNECTOR_HPDPIN_LUT_RECORD *)header; - - offset += header->ucRecordSize; - } - - return NULL; -} - -/* - * Check whether we need to patch the VBIOS connector info table with - * data from an external display connection info table. This is - * necessary to support MXM boards with an OPM (output personality - * module). With these designs, the VBIOS connector info table - * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves - * the external connection info table through i2c and then looks up the - * connector ID to find the real connector type (e.g. DFP1). - * - */ -static enum bp_result patch_bios_image_from_ext_display_connection_info( - struct bios_parser *bp) -{ - ATOM_OBJECT_TABLE *connector_tbl; - uint32_t connector_tbl_offset; - struct graphics_object_id object_id; - ATOM_OBJECT *object; - ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO ext_display_connection_info_tbl; - EXT_DISPLAY_PATH *ext_display_path; - ATOM_CONNECTOR_AUXDDC_LUT_RECORD *aux_ddc_lut_record = NULL; - ATOM_I2C_RECORD *i2c_record = NULL; - ATOM_CONNECTOR_HPDPIN_LUT_RECORD *hpd_pin_lut_record = NULL; - ATOM_HPD_INT_RECORD *hpd_record = NULL; - ATOM_OBJECT_TABLE *encoder_table; - uint32_t encoder_table_offset; - ATOM_OBJECT *opm_object = NULL; - uint32_t i = 0; - struct graphics_object_id opm_object_id = - dal_graphics_object_id_init( - GENERIC_ID_MXM_OPM, - ENUM_ID_1, - OBJECT_TYPE_GENERIC); - ATOM_CONNECTOR_DEVICE_TAG_RECORD *dev_tag_record; - uint32_t cached_device_support = - le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport); - - uint32_t dst_number; - uint16_t *dst_object_id_list; - - opm_object = get_bios_object(bp, opm_object_id); - if (!opm_object) - return BP_RESULT_UNSUPPORTED; - - memset(&ext_display_connection_info_tbl, 0, - sizeof(ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO)); - - connector_tbl_offset = bp->object_info_tbl_offset - + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); - connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); - - /* Read Connector info table from EEPROM through i2c */ - if (get_ext_display_connection_info(bp, - opm_object, - &ext_display_connection_info_tbl) != BP_RESULT_OK) { - - DC_LOG_WARNING("%s: Failed to read Connection Info Table", __func__); - return BP_RESULT_UNSUPPORTED; - } - - /* Get pointer to AUX/DDC and HPD LUTs */ - aux_ddc_lut_record = - get_ext_connector_aux_ddc_lut_record(bp, opm_object); - hpd_pin_lut_record = - get_ext_connector_hpd_pin_lut_record(bp, opm_object); - - if ((aux_ddc_lut_record == NULL) || (hpd_pin_lut_record == NULL)) - return BP_RESULT_UNSUPPORTED; - - /* Cache support bits for currently unmapped device types. */ - if (bp->remap_device_tags) { - for (i = 0; i < connector_tbl->ucNumberOfObjects; ++i) { - uint32_t j; - /* Remove support for all non-MXM connectors. */ - object = &connector_tbl->asObjects[i]; - object_id = object_id_from_bios_object_id( - le16_to_cpu(object->usObjectID)); - if ((OBJECT_TYPE_CONNECTOR != object_id.type) || - (CONNECTOR_ID_MXM == object_id.id)) - continue; - - /* Remove support for all device tags. */ - if (bios_parser_get_device_tag_record( - bp, object, &dev_tag_record) != BP_RESULT_OK) - continue; - - for (j = 0; j < dev_tag_record->ucNumberOfDevice; ++j) { - ATOM_CONNECTOR_DEVICE_TAG *device_tag = - &dev_tag_record->asDeviceTag[j]; - cached_device_support &= - ~le16_to_cpu(device_tag->usDeviceID); - } - } - } - - /* Find all MXM connector objects and patch them with connector info - * from the external display connection info table. */ - for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { - uint32_t j; - - object = &connector_tbl->asObjects[i]; - object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); - if ((OBJECT_TYPE_CONNECTOR != object_id.type) || - (CONNECTOR_ID_MXM != object_id.id)) - continue; - - /* Get the correct connection info table entry based on the enum - * id. */ - ext_display_path = get_ext_display_path_entry( - &ext_display_connection_info_tbl, - le16_to_cpu(object->usObjectID)); - if (!ext_display_path) - return BP_RESULT_FAILURE; - - /* Patch device connector ID */ - object->usObjectID = - cpu_to_le16(le16_to_cpu(ext_display_path->usDeviceConnector)); - - /* Patch device tag, ulACPIDeviceEnum. */ - add_device_tag_from_ext_display_path( - bp, - object, - ext_display_path, - &cached_device_support); - - /* Patch HPD info */ - if (ext_display_path->ucExtHPDPINLutIndex < - MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES) { - hpd_record = get_hpd_record(bp, object); - if (hpd_record) { - uint8_t index = - ext_display_path->ucExtHPDPINLutIndex; - hpd_record->ucHPDIntGPIOID = - hpd_pin_lut_record->ucHPDPINMap[index]; - } else { - BREAK_TO_DEBUGGER(); - /* Invalid hpd record */ - return BP_RESULT_FAILURE; - } - } - - /* Patch I2C/AUX info */ - if (ext_display_path->ucExtHPDPINLutIndex < - MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES) { - i2c_record = get_i2c_record(bp, object); - if (i2c_record) { - uint8_t index = - ext_display_path->ucExtAUXDDCLutIndex; - i2c_record->sucI2cId = - aux_ddc_lut_record->ucAUXDDCMap[index]; - } else { - BREAK_TO_DEBUGGER(); - /* Invalid I2C record */ - return BP_RESULT_FAILURE; - } - } - - /* Merge with other MXM connectors that map to the same physical - * connector. */ - for (j = i + 1; - j < connector_tbl->ucNumberOfObjects; j++) { - ATOM_OBJECT *next_object; - struct graphics_object_id next_object_id; - EXT_DISPLAY_PATH *next_ext_display_path; - - next_object = &connector_tbl->asObjects[j]; - next_object_id = object_id_from_bios_object_id( - le16_to_cpu(next_object->usObjectID)); - - if ((OBJECT_TYPE_CONNECTOR != next_object_id.type) && - (CONNECTOR_ID_MXM == next_object_id.id)) - continue; - - next_ext_display_path = get_ext_display_path_entry( - &ext_display_connection_info_tbl, - le16_to_cpu(next_object->usObjectID)); - - if (next_ext_display_path == NULL) - return BP_RESULT_FAILURE; - - /* Merge if using same connector. */ - if ((le16_to_cpu(next_ext_display_path->usDeviceConnector) == - le16_to_cpu(ext_display_path->usDeviceConnector)) && - (le16_to_cpu(ext_display_path->usDeviceConnector) != 0)) { - /* Clear duplicate connector from table. */ - next_object->usObjectID = cpu_to_le16(0); - add_device_tag_from_ext_display_path( - bp, - object, - ext_display_path, - &cached_device_support); - } - } - } - - /* Find all encoders which have an MXM object as their destination. - * Replace the MXM object with the real connector Id from the external - * display connection info table */ - - encoder_table_offset = bp->object_info_tbl_offset - + le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); - encoder_table = GET_IMAGE(ATOM_OBJECT_TABLE, encoder_table_offset); - - for (i = 0; i < encoder_table->ucNumberOfObjects; i++) { - uint32_t j; - - object = &encoder_table->asObjects[i]; - - dst_number = get_dest_obj_list(bp, object, &dst_object_id_list); - - for (j = 0; j < dst_number; j++) { - object_id = object_id_from_bios_object_id( - dst_object_id_list[j]); - - if ((OBJECT_TYPE_CONNECTOR != object_id.type) || - (CONNECTOR_ID_MXM != object_id.id)) - continue; - - /* Get the correct connection info table entry based on - * the enum id. */ - ext_display_path = - get_ext_display_path_entry( - &ext_display_connection_info_tbl, - dst_object_id_list[j]); - - if (ext_display_path == NULL) - return BP_RESULT_FAILURE; - - dst_object_id_list[j] = - le16_to_cpu(ext_display_path->usDeviceConnector); - } - } - - return BP_RESULT_OK; -} - -/* - * Check whether we need to patch the VBIOS connector info table with - * data from an external display connection info table. This is - * necessary to support MXM boards with an OPM (output personality - * module). With these designs, the VBIOS connector info table - * specifies an MXM_CONNECTOR with a unique ID. The driver retrieves - * the external connection info table through i2c and then looks up the - * connector ID to find the real connector type (e.g. DFP1). - * - */ - -static void process_ext_display_connection_info(struct bios_parser *bp) -{ - ATOM_OBJECT_TABLE *connector_tbl; - uint32_t connector_tbl_offset; - struct graphics_object_id object_id; - ATOM_OBJECT *object; - bool mxm_connector_found = false; - bool null_entry_found = false; - uint32_t i = 0; - - connector_tbl_offset = bp->object_info_tbl_offset + - le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); - connector_tbl = GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); - - /* Look for MXM connectors to determine whether we need patch the VBIOS - * connector info table. Look for null entries to determine whether we - * need to compact connector table. */ - for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { - object = &connector_tbl->asObjects[i]; - object_id = object_id_from_bios_object_id(le16_to_cpu(object->usObjectID)); - - if ((OBJECT_TYPE_CONNECTOR == object_id.type) && - (CONNECTOR_ID_MXM == object_id.id)) { - /* Once we found MXM connector - we can break */ - mxm_connector_found = true; - break; - } else if (OBJECT_TYPE_CONNECTOR != object_id.type) { - /* We need to continue looping - to check if MXM - * connector present */ - null_entry_found = true; - } - } - - /* Patch BIOS image */ - if (mxm_connector_found || null_entry_found) { - uint32_t connectors_num = 0; - uint8_t *original_bios; - /* Step 1: Replace bios image with the new copy which will be - * patched */ - bp->base.bios_local_image = kzalloc(bp->base.bios_size, - GFP_KERNEL); - if (bp->base.bios_local_image == NULL) { - BREAK_TO_DEBUGGER(); - /* Failed to alloc bp->base.bios_local_image */ - return; - } - - memmove(bp->base.bios_local_image, bp->base.bios, bp->base.bios_size); - original_bios = bp->base.bios; - bp->base.bios = bp->base.bios_local_image; - connector_tbl = - GET_IMAGE(ATOM_OBJECT_TABLE, connector_tbl_offset); - - /* Step 2: (only if MXM connector found) Patch BIOS image with - * info from external module */ - if (mxm_connector_found && - patch_bios_image_from_ext_display_connection_info(bp) != - BP_RESULT_OK) { - /* Patching the bios image has failed. We will copy - * again original image provided and afterwards - * only remove null entries */ - memmove( - bp->base.bios_local_image, - original_bios, - bp->base.bios_size); - } - - /* Step 3: Compact connector table (remove null entries, valid - * entries moved to beginning) */ - for (i = 0; i < connector_tbl->ucNumberOfObjects; i++) { - object = &connector_tbl->asObjects[i]; - object_id = object_id_from_bios_object_id( - le16_to_cpu(object->usObjectID)); - - if (OBJECT_TYPE_CONNECTOR != object_id.type) - continue; - - if (i != connectors_num) { - memmove( - &connector_tbl-> - asObjects[connectors_num], - object, - sizeof(ATOM_OBJECT)); - } - ++connectors_num; - } - connector_tbl->ucNumberOfObjects = (uint8_t)connectors_num; - } -} - -static void bios_parser_post_init(struct dc_bios *dcb) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - - process_ext_display_connection_info(bp); -} - -/** * bios_parser_set_scratch_critical_state * * @brief @@ -3961,22 +2796,12 @@ static enum bp_result bios_get_board_layout_info( static const struct dc_vbios_funcs vbios_funcs = { .get_connectors_number = bios_parser_get_connectors_number, - .get_encoder_id = bios_parser_get_encoder_id, - .get_connector_id = bios_parser_get_connector_id, - .get_dst_number = bios_parser_get_dst_number, - .get_src_obj = bios_parser_get_src_obj, - .get_dst_obj = bios_parser_get_dst_obj, - .get_i2c_info = bios_parser_get_i2c_info, - .get_voltage_ddc_info = bios_parser_get_voltage_ddc_info, - - .get_thermal_ddc_info = bios_parser_get_thermal_ddc_info, - .get_hpd_info = bios_parser_get_hpd_info, .get_device_tag = bios_parser_get_device_tag, @@ -3995,7 +2820,6 @@ static const struct dc_vbios_funcs vbios_funcs = { /* bios scratch register communication */ .is_accelerated_mode = bios_is_accelerated_mode, - .get_vga_enabled_displays = bios_get_vga_enabled_displays, .set_scratch_critical_state = bios_parser_set_scratch_critical_state, @@ -4006,8 +2830,6 @@ static const struct dc_vbios_funcs vbios_funcs = { .transmitter_control = bios_parser_transmitter_control, - .crt_control = bios_parser_crt_control, /* not used in DAL3. keep for now in case we need to support VGA on Bonaire */ - .enable_crtc = bios_parser_enable_crtc, .adjust_pixel_clock = bios_parser_adjust_pixel_clock, @@ -4027,7 +2849,6 @@ static const struct dc_vbios_funcs vbios_funcs = { .enable_disp_power_gating = bios_parser_enable_disp_power_gating, /* SW init and patch */ - .post_init = bios_parser_post_init, /* patch vbios table for mxm module by reading i2c */ .bios_parser_destroy = bios_parser_destroy, diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index eab007e1793c..ff764da21b6f 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -166,21 +166,6 @@ static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb) return count; } -static struct graphics_object_id bios_parser_get_encoder_id( - struct dc_bios *dcb, - uint32_t i) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - struct graphics_object_id object_id = dal_graphics_object_id_init( - 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); - - if (bp->object_info_tbl.v1_4->number_of_path > i) - object_id = object_id_from_bios_object_id( - bp->object_info_tbl.v1_4->display_path[i].encoderobjid); - - return object_id; -} - static struct graphics_object_id bios_parser_get_connector_id( struct dc_bios *dcb, uint8_t i) @@ -204,26 +189,6 @@ static struct graphics_object_id bios_parser_get_connector_id( return object_id; } - -/* TODO: GetNumberOfSrc*/ - -static uint32_t bios_parser_get_dst_number(struct dc_bios *dcb, - struct graphics_object_id id) -{ - /* connector has 1 Dest, encoder has 0 Dest */ - switch (id.type) { - case OBJECT_TYPE_ENCODER: - return 0; - case OBJECT_TYPE_CONNECTOR: - return 1; - default: - return 0; - } -} - -/* removed getSrcObjList, getDestObjList*/ - - static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, struct graphics_object_id object_id, uint32_t index, struct graphics_object_id *src_object_id) @@ -283,52 +248,10 @@ static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, return bp_result; } -static enum bp_result bios_parser_get_dst_obj(struct dc_bios *dcb, - struct graphics_object_id object_id, uint32_t index, - struct graphics_object_id *dest_object_id) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - unsigned int i; - enum bp_result bp_result = BP_RESULT_BADINPUT; - struct graphics_object_id obj_id = {0}; - struct object_info_table *tbl = &bp->object_info_tbl; - - if (!dest_object_id) - return BP_RESULT_BADINPUT; - - switch (object_id.type) { - case OBJECT_TYPE_ENCODER: - /* TODO: since num of src must be less than 2. - * If found in for loop, should break. - * DAL2 implementation may be changed too - */ - for (i = 0; i < tbl->v1_4->number_of_path; i++) { - obj_id = object_id_from_bios_object_id( - tbl->v1_4->display_path[i].encoderobjid); - if (object_id.type == obj_id.type && - object_id.id == obj_id.id && - object_id.enum_id == - obj_id.enum_id) { - *dest_object_id = - object_id_from_bios_object_id( - tbl->v1_4->display_path[i].display_objid); - /* break; */ - } - } - bp_result = BP_RESULT_OK; - break; - default: - break; - } - - return bp_result; -} - - /* from graphics_object_id, find display path which includes the object_id */ static struct atom_display_object_path_v2 *get_bios_object( - struct bios_parser *bp, - struct graphics_object_id id) + struct bios_parser *bp, + struct graphics_object_id id) { unsigned int i; struct graphics_object_id obj_id = {0}; @@ -337,27 +260,22 @@ static struct atom_display_object_path_v2 *get_bios_object( case OBJECT_TYPE_ENCODER: for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) { obj_id = object_id_from_bios_object_id( - bp->object_info_tbl.v1_4->display_path[i].encoderobjid); - if (id.type == obj_id.type && - id.id == obj_id.id && - id.enum_id == obj_id.enum_id) - return - &bp->object_info_tbl.v1_4->display_path[i]; + bp->object_info_tbl.v1_4->display_path[i].encoderobjid); + if (id.type == obj_id.type && id.id == obj_id.id + && id.enum_id == obj_id.enum_id) + return &bp->object_info_tbl.v1_4->display_path[i]; } case OBJECT_TYPE_CONNECTOR: case OBJECT_TYPE_GENERIC: /* Both Generic and Connector Object ID * will be stored on display_objid - */ + */ for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) { obj_id = object_id_from_bios_object_id( - bp->object_info_tbl.v1_4->display_path[i].display_objid - ); - if (id.type == obj_id.type && - id.id == obj_id.id && - id.enum_id == obj_id.enum_id) - return - &bp->object_info_tbl.v1_4->display_path[i]; + bp->object_info_tbl.v1_4->display_path[i].display_objid); + if (id.type == obj_id.type && id.id == obj_id.id + && id.enum_id == obj_id.enum_id) + return &bp->object_info_tbl.v1_4->display_path[i]; } default: return NULL; @@ -489,99 +407,6 @@ static enum bp_result get_gpio_i2c_info( return BP_RESULT_OK; } -static enum bp_result get_voltage_ddc_info_v4( - uint8_t *i2c_line, - uint32_t index, - struct atom_common_table_header *header, - uint8_t *address) -{ - enum bp_result result = BP_RESULT_NORECORD; - struct atom_voltage_objects_info_v4_1 *info = - (struct atom_voltage_objects_info_v4_1 *) address; - - uint8_t *voltage_current_object = - (uint8_t *) (&(info->voltage_object[0])); - - while ((address + le16_to_cpu(header->structuresize)) > - voltage_current_object) { - struct atom_i2c_voltage_object_v4 *object = - (struct atom_i2c_voltage_object_v4 *) - voltage_current_object; - - if (object->header.voltage_mode == - ATOM_INIT_VOLTAGE_REGULATOR) { - if (object->header.voltage_type == index) { - *i2c_line = object->i2c_id ^ 0x90; - result = BP_RESULT_OK; - break; - } - } - - voltage_current_object += - le16_to_cpu(object->header.object_size); - } - return result; -} - -static enum bp_result bios_parser_get_thermal_ddc_info( - struct dc_bios *dcb, - uint32_t i2c_channel_id, - struct graphics_object_i2c_info *info) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - struct i2c_id_config_access *config; - struct atom_i2c_record record; - - if (!info) - return BP_RESULT_BADINPUT; - - config = (struct i2c_id_config_access *) &i2c_channel_id; - - record.i2c_id = config->bfHW_Capable; - record.i2c_id |= config->bfI2C_LineMux; - record.i2c_id |= config->bfHW_EngineID; - - return get_gpio_i2c_info(bp, &record, info); -} - -static enum bp_result bios_parser_get_voltage_ddc_info(struct dc_bios *dcb, - uint32_t index, - struct graphics_object_i2c_info *info) -{ - uint8_t i2c_line = 0; - enum bp_result result = BP_RESULT_NORECORD; - uint8_t *voltage_info_address; - struct atom_common_table_header *header; - struct atom_data_revision revision = {0}; - struct bios_parser *bp = BP_FROM_DCB(dcb); - - if (!DATA_TABLES(voltageobject_info)) - return result; - - voltage_info_address = bios_get_image(&bp->base, - DATA_TABLES(voltageobject_info), - sizeof(struct atom_common_table_header)); - - header = (struct atom_common_table_header *) voltage_info_address; - - get_atom_data_table_revision(header, &revision); - - switch (revision.major) { - case 4: - if (revision.minor != 1) - break; - result = get_voltage_ddc_info_v4(&i2c_line, index, header, - voltage_info_address); - break; - } - - if (result == BP_RESULT_OK) - result = bios_parser_get_thermal_ddc_info(dcb, - i2c_line, info); - - return result; -} - static enum bp_result bios_parser_get_hpd_info( struct dc_bios *dcb, struct graphics_object_id id, @@ -997,8 +822,8 @@ static enum bp_result bios_parser_get_spread_spectrum_info( } static enum bp_result get_embedded_panel_info_v2_1( - struct bios_parser *bp, - struct embedded_panel_info *info) + struct bios_parser *bp, + struct embedded_panel_info *info) { struct lcd_info_v2_1 *lvds; @@ -1021,92 +846,78 @@ static enum bp_result get_embedded_panel_info_v2_1( memset(info, 0, sizeof(struct embedded_panel_info)); /* We need to convert from 10KHz units into KHz units */ - info->lcd_timing.pixel_clk = - le16_to_cpu(lvds->lcd_timing.pixclk) * 10; + info->lcd_timing.pixel_clk = le16_to_cpu(lvds->lcd_timing.pixclk) * 10; /* usHActive does not include borders, according to VBIOS team */ - info->lcd_timing.horizontal_addressable = - le16_to_cpu(lvds->lcd_timing.h_active); + info->lcd_timing.horizontal_addressable = le16_to_cpu(lvds->lcd_timing.h_active); /* usHBlanking_Time includes borders, so we should really be * subtractingborders duing this translation, but LVDS generally * doesn't have borders, so we should be okay leaving this as is for * now. May need to revisit if we ever have LVDS with borders */ - info->lcd_timing.horizontal_blanking_time = - le16_to_cpu(lvds->lcd_timing.h_blanking_time); + info->lcd_timing.horizontal_blanking_time = le16_to_cpu(lvds->lcd_timing.h_blanking_time); /* usVActive does not include borders, according to VBIOS team*/ - info->lcd_timing.vertical_addressable = - le16_to_cpu(lvds->lcd_timing.v_active); + info->lcd_timing.vertical_addressable = le16_to_cpu(lvds->lcd_timing.v_active); /* usVBlanking_Time includes borders, so we should really be * subtracting borders duing this translation, but LVDS generally * doesn't have borders, so we should be okay leaving this as is for * now. May need to revisit if we ever have LVDS with borders */ - info->lcd_timing.vertical_blanking_time = - le16_to_cpu(lvds->lcd_timing.v_blanking_time); - info->lcd_timing.horizontal_sync_offset = - le16_to_cpu(lvds->lcd_timing.h_sync_offset); - info->lcd_timing.horizontal_sync_width = - le16_to_cpu(lvds->lcd_timing.h_sync_width); - info->lcd_timing.vertical_sync_offset = - le16_to_cpu(lvds->lcd_timing.v_sync_offset); - info->lcd_timing.vertical_sync_width = - le16_to_cpu(lvds->lcd_timing.v_syncwidth); + info->lcd_timing.vertical_blanking_time = le16_to_cpu(lvds->lcd_timing.v_blanking_time); + info->lcd_timing.horizontal_sync_offset = le16_to_cpu(lvds->lcd_timing.h_sync_offset); + info->lcd_timing.horizontal_sync_width = le16_to_cpu(lvds->lcd_timing.h_sync_width); + info->lcd_timing.vertical_sync_offset = le16_to_cpu(lvds->lcd_timing.v_sync_offset); + info->lcd_timing.vertical_sync_width = le16_to_cpu(lvds->lcd_timing.v_syncwidth); info->lcd_timing.horizontal_border = lvds->lcd_timing.h_border; info->lcd_timing.vertical_border = lvds->lcd_timing.v_border; /* not provided by VBIOS */ info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = 0; - info->lcd_timing.misc_info.H_SYNC_POLARITY = - ~(uint32_t) - (lvds->lcd_timing.miscinfo & ATOM_HSYNC_POLARITY); - info->lcd_timing.misc_info.V_SYNC_POLARITY = - ~(uint32_t) - (lvds->lcd_timing.miscinfo & ATOM_VSYNC_POLARITY); + info->lcd_timing.misc_info.H_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo + & ATOM_HSYNC_POLARITY); + info->lcd_timing.misc_info.V_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo + & ATOM_VSYNC_POLARITY); /* not provided by VBIOS */ info->lcd_timing.misc_info.VERTICAL_CUT_OFF = 0; - info->lcd_timing.misc_info.H_REPLICATION_BY2 = - !!(lvds->lcd_timing.miscinfo & ATOM_H_REPLICATIONBY2); - info->lcd_timing.misc_info.V_REPLICATION_BY2 = - !!(lvds->lcd_timing.miscinfo & ATOM_V_REPLICATIONBY2); - info->lcd_timing.misc_info.COMPOSITE_SYNC = - !!(lvds->lcd_timing.miscinfo & ATOM_COMPOSITESYNC); - info->lcd_timing.misc_info.INTERLACE = - !!(lvds->lcd_timing.miscinfo & ATOM_INTERLACE); + info->lcd_timing.misc_info.H_REPLICATION_BY2 = !!(lvds->lcd_timing.miscinfo + & ATOM_H_REPLICATIONBY2); + info->lcd_timing.misc_info.V_REPLICATION_BY2 = !!(lvds->lcd_timing.miscinfo + & ATOM_V_REPLICATIONBY2); + info->lcd_timing.misc_info.COMPOSITE_SYNC = !!(lvds->lcd_timing.miscinfo + & ATOM_COMPOSITESYNC); + info->lcd_timing.misc_info.INTERLACE = !!(lvds->lcd_timing.miscinfo & ATOM_INTERLACE); /* not provided by VBIOS*/ info->lcd_timing.misc_info.DOUBLE_CLOCK = 0; /* not provided by VBIOS*/ info->ss_id = 0; - info->realtek_eDPToLVDS = - !!(lvds->dplvdsrxid == eDP_TO_LVDS_REALTEK_ID); + info->realtek_eDPToLVDS = !!(lvds->dplvdsrxid == eDP_TO_LVDS_REALTEK_ID); return BP_RESULT_OK; } static enum bp_result bios_parser_get_embedded_panel_info( - struct dc_bios *dcb, - struct embedded_panel_info *info) + struct dc_bios *dcb, + struct embedded_panel_info *info) { - struct bios_parser *bp = BP_FROM_DCB(dcb); + struct bios_parser + *bp = BP_FROM_DCB(dcb); struct atom_common_table_header *header; struct atom_data_revision tbl_revision; if (!DATA_TABLES(lcd_info)) return BP_RESULT_FAILURE; - header = GET_IMAGE(struct atom_common_table_header, - DATA_TABLES(lcd_info)); + header = GET_IMAGE(struct atom_common_table_header, DATA_TABLES(lcd_info)); if (!header) return BP_RESULT_BADBIOSTABLE; get_atom_data_table_revision(header, &tbl_revision); - switch (tbl_revision.major) { case 2: switch (tbl_revision.minor) { @@ -1174,12 +985,6 @@ static bool bios_parser_is_device_id_supported( mask) != 0; } -static void bios_parser_post_init( - struct dc_bios *dcb) -{ - /* TODO for OPM module. Need implement later */ -} - static uint32_t bios_parser_get_ss_entry_number( struct dc_bios *dcb, enum as_signal_type signal) @@ -1238,17 +1043,6 @@ static enum bp_result bios_parser_set_dce_clock( return bp->cmd_tbl.set_dce_clock(bp, bp_params); } -static unsigned int bios_parser_get_smu_clock_info( - struct dc_bios *dcb) -{ - struct bios_parser *bp = BP_FROM_DCB(dcb); - - if (!bp->cmd_tbl.get_smu_clock_info) - return BP_RESULT_FAILURE; - - return bp->cmd_tbl.get_smu_clock_info(bp, 0); -} - static enum bp_result bios_parser_program_crtc_timing( struct dc_bios *dcb, struct bp_hw_crtc_timing_parameters *bp_params) @@ -1306,13 +1100,6 @@ static bool bios_parser_is_accelerated_mode( return bios_is_accelerated_mode(dcb); } -static uint32_t bios_parser_get_vga_enabled_displays( - struct dc_bios *bios) -{ - return bios_get_vga_enabled_displays(bios); -} - - /** * bios_parser_set_scratch_critical_state * @@ -2071,22 +1858,12 @@ static enum bp_result bios_get_board_layout_info( static const struct dc_vbios_funcs vbios_funcs = { .get_connectors_number = bios_parser_get_connectors_number, - .get_encoder_id = bios_parser_get_encoder_id, - .get_connector_id = bios_parser_get_connector_id, - .get_dst_number = bios_parser_get_dst_number, - .get_src_obj = bios_parser_get_src_obj, - .get_dst_obj = bios_parser_get_dst_obj, - .get_i2c_info = bios_parser_get_i2c_info, - .get_voltage_ddc_info = bios_parser_get_voltage_ddc_info, - - .get_thermal_ddc_info = bios_parser_get_thermal_ddc_info, - .get_hpd_info = bios_parser_get_hpd_info, .get_device_tag = bios_parser_get_device_tag, @@ -2105,10 +1882,7 @@ static const struct dc_vbios_funcs vbios_funcs = { .is_device_id_supported = bios_parser_is_device_id_supported, - - .is_accelerated_mode = bios_parser_is_accelerated_mode, - .get_vga_enabled_displays = bios_parser_get_vga_enabled_displays, .set_scratch_critical_state = bios_parser_set_scratch_critical_state, @@ -2126,20 +1900,12 @@ static const struct dc_vbios_funcs vbios_funcs = { .program_crtc_timing = bios_parser_program_crtc_timing, - /* .blank_crtc = bios_parser_blank_crtc, */ - .crtc_source_select = bios_parser_crtc_source_select, - /* .external_encoder_control = bios_parser_external_encoder_control, */ - .enable_disp_power_gating = bios_parser_enable_disp_power_gating, - .post_init = bios_parser_post_init, - .bios_parser_destroy = firmware_parser_destroy, - .get_smu_clock_info = bios_parser_get_smu_clock_info, - .get_board_layout_info = bios_get_board_layout_info, }; diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.c b/drivers/gpu/drm/amd/display/dc/bios/command_table.c index a558bfaa0c46..2bd7cd97e00d 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/command_table.c +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.c @@ -2201,6 +2201,9 @@ static enum bp_result program_clock_v6( if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + if (bp_params->flags.SET_DISPCLK_DFS_BYPASS) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_DPREFCLK_BYPASS; + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) { /* True display clock is returned by VBIOS if DFS bypass * is enabled. */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c index bbbcef566c55..65b006ad372e 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c @@ -61,6 +61,11 @@ bool dal_bios_parser_init_cmd_tbl_helper2( return true; #endif +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + case DCN_VERSION_1_01: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; +#endif case DCE_VERSION_12_0: *h = dal_cmd_tbl_helper_dce112_get_table2(); return true; diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c index 160d11a15eac..9ebe30ba4dab 100644 --- a/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/calcs/dce_calcs.c @@ -2881,6 +2881,7 @@ static void populate_initial_data( /* Pipes without underlay after */ for (i = 0; i < pipe_count; i++) { + unsigned int pixel_clock_khz; if (!pipe[i].stream || pipe[i].bottom_pipe) continue; @@ -2889,7 +2890,10 @@ static void populate_initial_data( data->lpt_en[num_displays + 4] = false; data->h_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.h_total); data->v_total[num_displays + 4] = bw_int_to_fixed(pipe[i].stream->timing.v_total); - data->pixel_rate[num_displays + 4] = bw_frc_to_fixed(pipe[i].stream->timing.pix_clk_khz, 1000); + pixel_clock_khz = pipe[i].stream->timing.pix_clk_khz; + if (pipe[i].stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + pixel_clock_khz *= 2; + data->pixel_rate[num_displays + 4] = bw_frc_to_fixed(pixel_clock_khz, 1000); if (pipe[i].plane_state) { data->src_width[num_displays + 4] = bw_int_to_fixed(pipe[i].plane_res.scl_data.viewport.width); data->pitch_in_pixels[num_displays + 4] = data->src_width[num_displays + 4]; diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c index bd039322f697..80ec09eef44f 100644 --- a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c @@ -37,6 +37,13 @@ #define DC_LOGGER \ dc->ctx->logger + +#define WM_SET_COUNT 4 +#define WM_A 0 +#define WM_B 1 +#define WM_C 2 +#define WM_D 3 + /* * NOTE: * This file is gcc-parseable HW gospel, coming straight from HW engineers. @@ -845,8 +852,9 @@ bool dcn_validate_bandwidth( v->v_sync_plus_back_porch[input_idx] = pipe->stream->timing.v_total - v->vactive[input_idx] - pipe->stream->timing.v_front_porch; - v->pixel_clock[input_idx] = pipe->stream->timing.pix_clk_khz / 1000.0f; - + v->pixel_clock[input_idx] = pipe->stream->timing.pix_clk_khz/1000.0; + if (pipe->stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + v->pixel_clock[input_idx] *= 2; if (!pipe->plane_state) { v->dcc_enable[input_idx] = dcn_bw_yes; v->source_pixel_format[input_idx] = dcn_bw_rgb_sub_32; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 6ae050dc3220..1c438eedf77a 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -54,6 +54,9 @@ #include "hubp.h" #include "dc_link_dp.h" + +#include "dce/dce_i2c.h" + #define DC_LOGGER \ dc->ctx->logger @@ -188,11 +191,9 @@ failed_alloc: ***************************************************************************** */ bool dc_stream_adjust_vmin_vmax(struct dc *dc, - struct dc_stream_state **streams, int num_streams, - int vmin, int vmax) + struct dc_stream_state *stream, + struct dc_crtc_timing_adjust *adjust) { - /* TODO: Support multiple streams */ - struct dc_stream_state *stream = streams[0]; int i = 0; bool ret = false; @@ -200,11 +201,11 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe->stream == stream && pipe->stream_res.stream_enc) { - dc->hwss.set_drr(&pipe, 1, vmin, vmax); - - /* build and update the info frame */ - resource_build_info_frame(pipe); - dc->hwss.update_info_frame(pipe); + pipe->stream->adjust = *adjust; + dc->hwss.set_drr(&pipe, + 1, + adjust->v_total_min, + adjust->v_total_max); ret = true; } @@ -217,7 +218,7 @@ bool dc_stream_get_crtc_position(struct dc *dc, unsigned int *v_pos, unsigned int *nom_v_pos) { /* TODO: Support multiple streams */ - struct dc_stream_state *stream = streams[0]; + const struct dc_stream_state *stream = streams[0]; int i = 0; bool ret = false; struct crtc_position position; @@ -361,6 +362,44 @@ void dc_stream_set_dither_option(struct dc_stream_state *stream, opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms); } +bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream) +{ + int i = 0; + bool ret = false; + struct pipe_ctx *pipes; + + for (i = 0; i < MAX_PIPES; i++) { + if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { + pipes = &dc->current_state->res_ctx.pipe_ctx[i]; + dc->hwss.program_gamut_remap(pipes); + ret = true; + } + } + + return ret; +} + +bool dc_stream_program_csc_matrix(struct dc *dc, struct dc_stream_state *stream) +{ + int i = 0; + bool ret = false; + struct pipe_ctx *pipes; + + for (i = 0; i < MAX_PIPES; i++) { + if (dc->current_state->res_ctx.pipe_ctx[i].stream + == stream) { + + pipes = &dc->current_state->res_ctx.pipe_ctx[i]; + dc->hwss.program_csc_matrix(pipes, + stream->output_color_space, + stream->csc_color_matrix.matrix); + ret = true; + } + } + + return ret; +} + void dc_stream_set_static_screen_events(struct dc *dc, struct dc_stream_state **streams, int num_streams, @@ -706,8 +745,6 @@ struct dc *dc_create(const struct dc_init_data *init_params) DC_LOG_DC("Display Core initialized\n"); - /* TODO: missing feature to be enabled */ - dc->debug.disable_dfs_bypass = true; return dc; @@ -1108,6 +1145,9 @@ static enum surface_update_type get_plane_info_update_type(const struct dc_surfa if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha) update_flags->bits.per_pixel_alpha_change = 1; + if (u->plane_info->global_alpha_value != u->surface->global_alpha_value) + update_flags->bits.global_alpha_change = 1; + if (u->plane_info->dcc.enable != u->surface->dcc.enable || u->plane_info->dcc.grph.independent_64b_blks != u->surface->dcc.grph.independent_64b_blks || u->plane_info->dcc.grph.meta_pitch != u->surface->dcc.grph.meta_pitch) @@ -1256,8 +1296,25 @@ static enum surface_update_type check_update_surfaces_for_stream( if (stream_status == NULL || stream_status->plane_count != surface_count) return UPDATE_TYPE_FULL; - if (stream_update) - return UPDATE_TYPE_FULL; + /* some stream updates require passive update */ + if (stream_update) { + if ((stream_update->src.height != 0) && + (stream_update->src.width != 0)) + return UPDATE_TYPE_FULL; + + if ((stream_update->dst.height != 0) && + (stream_update->dst.width != 0)) + return UPDATE_TYPE_FULL; + + if (stream_update->out_transfer_func) + return UPDATE_TYPE_FULL; + + if (stream_update->abm_level) + return UPDATE_TYPE_FULL; + + if (stream_update->dpms_off) + return UPDATE_TYPE_FULL; + } for (i = 0 ; i < surface_count; i++) { enum surface_update_type type = @@ -1310,6 +1367,111 @@ static struct dc_stream_status *stream_get_status( static const enum surface_update_type update_surface_trace_level = UPDATE_TYPE_FULL; +static void notify_display_count_to_smu( + struct dc *dc, + struct dc_state *context) +{ + int i, display_count; + struct pp_smu_funcs_rv *pp_smu = dc->res_pool->pp_smu; + + /* + * if function pointer not set up, this message is + * sent as part of pplib_apply_display_requirements. + * So just return. + */ + if (!pp_smu->set_display_count) + return; + + display_count = 0; + for (i = 0; i < context->stream_count; i++) { + const struct dc_stream_state *stream = context->streams[i]; + + /* only notify active stream */ + if (stream->dpms_off) + continue; + + display_count++; + } + + pp_smu->set_display_count(&pp_smu->pp_smu, display_count); +} + +static void commit_planes_do_stream_update(struct dc *dc, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + enum surface_update_type update_type, + struct dc_state *context) +{ + int j; + + // Stream updates + for (j = 0; j < dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (!pipe_ctx->top_pipe && + pipe_ctx->stream && + pipe_ctx->stream == stream) { + + /* Fast update*/ + // VRR program can be done as part of FAST UPDATE + if (stream_update->adjust) + dc->hwss.set_drr(&pipe_ctx, 1, + stream_update->adjust->v_total_min, + stream_update->adjust->v_total_max); + + if (stream_update->periodic_fn_vsync_delta && + pipe_ctx->stream_res.tg && + pipe_ctx->stream_res.tg->funcs->program_vline_interrupt) + pipe_ctx->stream_res.tg->funcs->program_vline_interrupt( + pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, + pipe_ctx->stream->periodic_fn_vsync_delta); + + if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) || + stream_update->vrr_infopacket || + stream_update->vsc_infopacket) { + resource_build_info_frame(pipe_ctx); + dc->hwss.update_info_frame(pipe_ctx); + } + + if (stream_update->gamut_remap) + dc_stream_set_gamut_remap(dc, stream); + + if (stream_update->output_csc_transform) + dc_stream_program_csc_matrix(dc, stream); + + /* Full fe update*/ + if (update_type == UPDATE_TYPE_FAST) + continue; + + if (stream_update->dpms_off) { + if (*stream_update->dpms_off) { + core_link_disable_stream(pipe_ctx, KEEP_ACQUIRED_RESOURCE); + dc->hwss.pplib_apply_display_requirements( + dc, dc->current_state); + notify_display_count_to_smu(dc, dc->current_state); + } else { + dc->hwss.pplib_apply_display_requirements( + dc, dc->current_state); + notify_display_count_to_smu(dc, dc->current_state); + core_link_enable_stream(dc->current_state, pipe_ctx); + } + } + + + + if (stream_update->abm_level && pipe_ctx->stream_res.abm) { + if (pipe_ctx->stream_res.tg->funcs->is_blanked) { + // if otg funcs defined check if blanked before programming + if (!pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) + pipe_ctx->stream_res.abm->funcs->set_abm_level( + pipe_ctx->stream_res.abm, stream->abm_level); + } else + pipe_ctx->stream_res.abm->funcs->set_abm_level( + pipe_ctx->stream_res.abm, stream->abm_level); + } + } + } +} static void commit_planes_for_stream(struct dc *dc, struct dc_surface_update *srf_updates, @@ -1327,16 +1489,20 @@ static void commit_planes_for_stream(struct dc *dc, context_clock_trace(dc, context); } + // Stream updates + if (stream_update) + commit_planes_do_stream_update(dc, stream, stream_update, update_type, context); + if (surface_count == 0) { /* * In case of turning off screen, no need to program front end a second time. - * just return after program front end. + * just return after program blank. */ - dc->hwss.apply_ctx_for_surface(dc, stream, surface_count, context); + dc->hwss.apply_ctx_for_surface(dc, stream, 0, context); return; } - /* Full fe update*/ + // Update Type FULL, Surface updates for (j = 0; j < dc->res_pool->pipe_count; j++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; @@ -1347,42 +1513,30 @@ static void commit_planes_for_stream(struct dc *dc, top_pipe_to_program = pipe_ctx; - if (update_type == UPDATE_TYPE_FAST || !pipe_ctx->plane_state) + if (!pipe_ctx->plane_state) + continue; + + /* Full fe update*/ + if (update_type == UPDATE_TYPE_FAST) continue; stream_status = - stream_get_status(context, pipe_ctx->stream); + stream_get_status(context, pipe_ctx->stream); dc->hwss.apply_ctx_for_surface( dc, pipe_ctx->stream, stream_status->plane_count, context); - - if (stream_update && stream_update->abm_level && pipe_ctx->stream_res.abm) { - if (pipe_ctx->stream_res.tg->funcs->is_blanked) { - // if otg funcs defined check if blanked before programming - if (!pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) - pipe_ctx->stream_res.abm->funcs->set_abm_level( - pipe_ctx->stream_res.abm, stream->abm_level); - } else - pipe_ctx->stream_res.abm->funcs->set_abm_level( - pipe_ctx->stream_res.abm, stream->abm_level); - } - - if (stream_update && stream_update->periodic_fn_vsync_delta && - pipe_ctx->stream_res.tg->funcs->program_vline_interrupt) - pipe_ctx->stream_res.tg->funcs->program_vline_interrupt( - pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, - pipe_ctx->stream->periodic_fn_vsync_delta); } } if (update_type == UPDATE_TYPE_FULL) context_timing_trace(dc, &context->res_ctx); - /* Lock the top pipe while updating plane addrs, since freesync requires - * plane addr update event triggers to be synchronized. - * top_pipe_to_program is expected to never be NULL - */ + // Update Type FAST, Surface updates if (update_type == UPDATE_TYPE_FAST) { + /* Lock the top pipe while updating plane addrs, since freesync requires + * plane addr update event triggers to be synchronized. + * top_pipe_to_program is expected to never be NULL + */ dc->hwss.pipe_control_lock(dc, top_pipe_to_program, true); /* Perform requested Updates */ @@ -1405,20 +1559,6 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false); } - - if (stream && stream_update && update_type > UPDATE_TYPE_FAST) - for (j = 0; j < dc->res_pool->pipe_count; j++) { - struct pipe_ctx *pipe_ctx = - &context->res_ctx.pipe_ctx[j]; - - if (pipe_ctx->stream != stream) - continue; - - if (stream_update->hdr_static_metadata) { - resource_build_info_frame(pipe_ctx); - dc->hwss.update_info_frame(pipe_ctx); - } - } } void dc_commit_updates_for_stream(struct dc *dc, @@ -1554,9 +1694,7 @@ void dc_set_power_state( dc->hwss.init_hw(dc); break; default: - - dc->hwss.power_down(dc); - + ASSERT(dc->current_state->stream_count == 0); /* Zero out the current context so that on resume we start with * clean state, and dc hw programming optimizations will not * cause any trouble. @@ -1592,9 +1730,8 @@ bool dc_submit_i2c( struct dc_link *link = dc->links[link_index]; struct ddc_service *ddc = link->ddc; - - return dal_i2caux_submit_i2c_command( - ddc->ctx->i2caux, + return dce_i2c_submit_command( + dc->res_pool, ddc->ddc_pin, cmd); } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 37eaf72ace54..bd58dbae7d3e 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -54,6 +54,9 @@ DC_LOG_HW_HOTPLUG( \ __VA_ARGS__) +#define RETIMER_REDRIVER_INFO(...) \ + DC_LOG_RETIMER_REDRIVER( \ + __VA_ARGS__) /******************************************************************************* * Private structures ******************************************************************************/ @@ -200,6 +203,11 @@ static bool detect_sink(struct dc_link *link, enum dc_connection_type *type) uint32_t is_hpd_high = 0; struct gpio *hpd_pin; + if (link->connector_signal == SIGNAL_TYPE_LVDS) { + *type = dc_connection_single; + return true; + } + /* todo: may need to lock gpio access */ hpd_pin = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); if (hpd_pin == NULL) @@ -613,6 +621,10 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) link->local_sink) return true; + if (link->connector_signal == SIGNAL_TYPE_LVDS && + link->local_sink) + return true; + prev_sink = link->local_sink; if (prev_sink != NULL) { dc_sink_retain(prev_sink); @@ -646,6 +658,12 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) break; } + case SIGNAL_TYPE_LVDS: { + sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; + sink_caps.signal = SIGNAL_TYPE_LVDS; + break; + } + case SIGNAL_TYPE_EDP: { detect_edp_sink_caps(link); sink_caps.transaction_type = @@ -872,6 +890,24 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) return true; } +bool dc_link_get_hpd_state(struct dc_link *dc_link) +{ + struct gpio *hpd_pin; + uint32_t state; + + hpd_pin = get_hpd_gpio(dc_link->ctx->dc_bios, + dc_link->link_id, dc_link->ctx->gpio_service); + if (hpd_pin == NULL) + ASSERT(false); + + dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); + dal_gpio_get_value(hpd_pin, &state); + dal_gpio_close(hpd_pin); + dal_gpio_destroy_irq(&hpd_pin); + + return state; +} + static enum hpd_source_id get_hpd_line( struct dc_link *link) { @@ -1088,6 +1124,9 @@ static bool construct( dal_irq_get_rx_source(hpd_gpio); } break; + case CONNECTOR_ID_LVDS: + link->connector_signal = SIGNAL_TYPE_LVDS; + break; default: DC_LOG_WARNING("Unsupported Connector type:%d!\n", link->link_id.id); goto create_fail; @@ -1531,8 +1570,8 @@ static bool i2c_write(struct pipe_ctx *pipe_ctx, payload.write = true; cmd.payloads = &payload; - if (dc_submit_i2c(pipe_ctx->stream->ctx->dc, - pipe_ctx->stream->sink->link->link_index, &cmd)) + if (dm_helpers_submit_i2c(pipe_ctx->stream->ctx, + pipe_ctx->stream->sink->link, &cmd)) return true; return false; @@ -1551,6 +1590,7 @@ static void write_i2c_retimer_setting( uint8_t value = 0; int i = 0; bool i2c_success = false; + DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); memset(&buffer, 0, sizeof(buffer)); @@ -1564,6 +1604,9 @@ static void write_i2c_retimer_setting( buffer[1] = settings->reg_settings[i].i2c_reg_val; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ @@ -1594,6 +1637,9 @@ static void write_i2c_retimer_setting( buffer[1] = value | apply_rx_tx_change; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1611,6 +1657,9 @@ static void write_i2c_retimer_setting( buffer[1] = settings->reg_settings_6g[i].i2c_reg_val; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("above 340Mhz: retimer write to slave_address = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ @@ -1641,6 +1690,9 @@ static void write_i2c_retimer_setting( buffer[1] = value | apply_rx_tx_change; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1657,6 +1709,9 @@ static void write_i2c_retimer_setting( buffer[1] = 0x01; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1666,6 +1721,9 @@ static void write_i2c_retimer_setting( buffer[1] = 0x23; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%d, reg_val = 0x%d, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1675,6 +1733,9 @@ static void write_i2c_retimer_setting( buffer[1] = 0x00; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ + offset = 0x%d, reg_val = 0x%d, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1690,6 +1751,7 @@ static void write_i2c_default_retimer_setting( uint8_t slave_address = (0xBA >> 1); uint8_t buffer[2]; bool i2c_success = false; + DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); memset(&buffer, 0, sizeof(buffer)); @@ -1699,6 +1761,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x13; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer writes default setting to slave_address = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1708,6 +1773,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x17; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1717,6 +1785,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = is_over_340mhz ? 0xDA : 0xD8; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1726,6 +1797,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x17; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1735,6 +1809,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = is_over_340mhz ? 0x1D : 0x91; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1744,6 +1821,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x17; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1757,6 +1837,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x01; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1766,6 +1849,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x23; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ + offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1775,6 +1861,9 @@ static void write_i2c_default_retimer_setting( buffer[1] = 0x00; i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("retimer write default setting to slave_addr = 0x%x,\ + offset = 0x%x, reg_val= 0x%x, i2c_success = %d end here\n", + slave_address, buffer[0], buffer[1], i2c_success?1:0); if (!i2c_success) /* Write failure */ ASSERT(i2c_success); @@ -1788,6 +1877,7 @@ static void write_i2c_redriver_setting( uint8_t slave_address = (0xF0 >> 1); uint8_t buffer[16]; bool i2c_success = false; + DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); memset(&buffer, 0, sizeof(buffer)); @@ -1799,6 +1889,11 @@ static void write_i2c_redriver_setting( i2c_success = i2c_write(pipe_ctx, slave_address, buffer, sizeof(buffer)); + RETIMER_REDRIVER_INFO("redriver write 0 to all 16 reg offset expect following:\n\ + \t slave_addr = 0x%x, offset[3] = 0x%x, offset[4] = 0x%x,\ + offset[5] = 0x%x,offset[6] is_over_340mhz = 0x%x,\ + i2c_success = %d\n", + slave_address, buffer[3], buffer[4], buffer[5], buffer[6], i2c_success?1:0); if (!i2c_success) /* Write failure */ @@ -1865,6 +1960,24 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) dal_ddc_service_read_scdc_data(link->ddc); } +static void enable_link_lvds(struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->sink->link; + + if (stream->phy_pix_clk == 0) + stream->phy_pix_clk = stream->timing.pix_clk_khz; + + memset(&stream->sink->link->cur_link_settings, 0, + sizeof(struct dc_link_settings)); + + link->link_enc->funcs->enable_lvds_output( + link->link_enc, + pipe_ctx->clock_source->id, + stream->phy_pix_clk); + +} + /****************************enable_link***********************************/ static enum dc_status enable_link( struct dc_state *state, @@ -1888,6 +2001,10 @@ static enum dc_status enable_link( enable_link_hdmi(pipe_ctx); status = DC_OK; break; + case SIGNAL_TYPE_LVDS: + enable_link_lvds(pipe_ctx); + status = DC_OK; + break; case SIGNAL_TYPE_VIRTUAL: status = DC_OK; break; @@ -2403,13 +2520,52 @@ void core_link_enable_stream( struct pipe_ctx *pipe_ctx) { struct dc *core_dc = pipe_ctx->stream->ctx->dc; + struct dc_stream_state *stream = pipe_ctx->stream; enum dc_status status; DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) { + stream->sink->link->link_enc->funcs->setup( + stream->sink->link->link_enc, + pipe_ctx->stream->signal); + pipe_ctx->stream_res.stream_enc->funcs->setup_stereo_sync( + pipe_ctx->stream_res.stream_enc, + pipe_ctx->stream_res.tg->inst, + stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE); + } + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + stream->output_color_space); + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->hdmi_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + stream->phy_pix_clk, + pipe_ctx->stream_res.audio != NULL); + + if (dc_is_dvi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->dvi_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing, + (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? + true : false); + + if (dc_is_lvds_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.stream_enc->funcs->lvds_set_stream_attribute( + pipe_ctx->stream_res.stream_enc, + &stream->timing); + + resource_build_info_frame(pipe_ctx); + core_dc->hwss.update_info_frame(pipe_ctx); + /* eDP lit up by bios already, no need to enable again. */ if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP && - core_dc->apply_edp_fast_boot_optimization) { - core_dc->apply_edp_fast_boot_optimization = false; + pipe_ctx->stream->apply_edp_fast_boot_optimization) { + pipe_ctx->stream->apply_edp_fast_boot_optimization = false; pipe_ctx->stream->dpms_off = false; return; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c index 8def0d9fa0ff..506a97e16956 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c @@ -666,13 +666,9 @@ int dc_link_aux_transfer(struct ddc_service *ddc, switch (operation_result) { case AUX_CHANNEL_OPERATION_SUCCEEDED: - res = returned_bytes; - - if (res <= size && res >= 0) - res = aux_engine->funcs->read_channel_reply(aux_engine, size, - buffer, reply, - &status); - + res = aux_engine->funcs->read_channel_reply(aux_engine, size, + buffer, reply, + &status); break; case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: res = 0; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index a7553b6d59c2..d91df5ef0cb3 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -2389,6 +2389,9 @@ static bool retrieve_link_cap(struct dc_link *link) dp_wa_power_up_0010FA(link, dpcd_data, sizeof(dpcd_data)); + down_strm_port_count.raw = dpcd_data[DP_DOWN_STREAM_PORT_COUNT - + DP_DPCD_REV]; + link->dpcd_caps.allow_invalid_MSA_timing_param = down_strm_port_count.bits.IGNORE_MSA_TIMING_PARAM; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index ea6beccfd89d..2d6a4300bfa4 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -88,6 +88,10 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) #if defined(CONFIG_DRM_AMD_DC_DCN1_0) case FAMILY_RV: dc_version = DCN_VERSION_1_0; +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + if (ASICREV_IS_RAVEN2(asic_id.hw_internal_rev)) + dc_version = DCN_VERSION_1_01; +#endif break; #endif default: @@ -138,6 +142,9 @@ struct resource_pool *dc_create_resource_pool( #if defined(CONFIG_DRM_AMD_DC_DCN1_0) case DCN_VERSION_1_0: +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + case DCN_VERSION_1_01: +#endif res_pool = dcn10_create_resource_pool( num_virtual_links, dc); break; @@ -356,6 +363,9 @@ bool resource_are_streams_timing_synchronizable( || !dc_is_dp_signal(stream2->signal))) return false; + if (stream1->view_format != stream2->view_format) + return false; + return true; } static bool is_dp_and_hdmi_sharable( @@ -366,8 +376,8 @@ static bool is_dp_and_hdmi_sharable( return false; if (stream1->clamping.c_depth != COLOR_DEPTH_888 || - stream2->clamping.c_depth != COLOR_DEPTH_888) - return false; + stream2->clamping.c_depth != COLOR_DEPTH_888) + return false; return true; @@ -487,6 +497,18 @@ static void calculate_viewport(struct pipe_ctx *pipe_ctx) pipe_ctx->bottom_pipe->plane_state == pipe_ctx->plane_state; bool sec_split = pipe_ctx->top_pipe && pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; + bool flip_vert_scan_dir = false, flip_horz_scan_dir = false; + + /* + * Need to calculate the scan direction for viewport to properly determine offset + */ + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_180) { + flip_vert_scan_dir = true; + flip_horz_scan_dir = true; + } else if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90) + flip_vert_scan_dir = true; + else if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) + flip_horz_scan_dir = true; if (stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE || stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) { @@ -530,6 +552,34 @@ static void calculate_viewport(struct pipe_ctx *pipe_ctx) data->viewport.height = clip.height * surf_src.height / plane_state->dst_rect.height; + /* To transfer the x, y to correct coordinate on mirror image (camera). + * deg 0 : transfer x, + * deg 90 : don't need to transfer, + * deg180 : transfer y, + * deg270 : transfer x and y. + * To transfer the x, y to correct coordinate on non-mirror image (video). + * deg 0 : don't need to transfer, + * deg 90 : transfer y, + * deg180 : transfer x and y, + * deg270 : transfer x. + */ + if (pipe_ctx->plane_state->horizontal_mirror) { + if (flip_horz_scan_dir && !flip_vert_scan_dir) { + data->viewport.y = surf_src.height - data->viewport.y - data->viewport.height; + data->viewport.x = surf_src.width - data->viewport.x - data->viewport.width; + } else if (flip_horz_scan_dir && flip_vert_scan_dir) + data->viewport.y = surf_src.height - data->viewport.y - data->viewport.height; + else { + if (!flip_horz_scan_dir && !flip_vert_scan_dir) + data->viewport.x = surf_src.width - data->viewport.x - data->viewport.width; + } + } else { + if (flip_horz_scan_dir) + data->viewport.x = surf_src.width - data->viewport.x - data->viewport.width; + if (flip_vert_scan_dir) + data->viewport.y = surf_src.height - data->viewport.y - data->viewport.height; + } + /* Round down, compensate in init */ data->viewport_c.x = data->viewport.x / vpc_div; data->viewport_c.y = data->viewport.y / vpc_div; @@ -549,8 +599,10 @@ static void calculate_viewport(struct pipe_ctx *pipe_ctx) data->viewport.width = (data->viewport.width + 1) / 2; data->viewport_c.width = (data->viewport_c.width + 1) / 2; } else if (pri_split) { - data->viewport.width /= 2; - data->viewport_c.width /= 2; + if (data->viewport.width > 1) + data->viewport.width /= 2; + if (data->viewport_c.width > 1) + data->viewport_c.width /= 2; } if (plane_state->rotation == ROTATION_ANGLE_90 || @@ -630,7 +682,8 @@ static void calculate_recout(struct pipe_ctx *pipe_ctx, struct rect *recout_full pipe_ctx->plane_res.scl_data.recout.width = (pipe_ctx->plane_res.scl_data.recout.width + 1) / 2; } else { - pipe_ctx->plane_res.scl_data.recout.width /= 2; + if (pipe_ctx->plane_res.scl_data.recout.width > 1) + pipe_ctx->plane_res.scl_data.recout.width /= 2; } } /* Unclipped recout offset = stream dst offset + ((surf dst offset - stream surf_src offset) @@ -725,6 +778,15 @@ static void calculate_inits_and_adj_vp(struct pipe_ctx *pipe_ctx, struct rect *r rect_swap_helper(&src); rect_swap_helper(&data->viewport_c); rect_swap_helper(&data->viewport); + + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270 && + pipe_ctx->plane_state->horizontal_mirror) { + flip_vert_scan_dir = true; + } + if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 && + pipe_ctx->plane_state->horizontal_mirror) { + flip_vert_scan_dir = false; + } } else if (pipe_ctx->plane_state->horizontal_mirror) flip_horz_scan_dir = !flip_horz_scan_dir; @@ -1526,6 +1588,20 @@ static bool is_hdr_static_meta_changed(struct dc_stream_state *cur_stream, return false; } +static bool is_vsc_info_packet_changed(struct dc_stream_state *cur_stream, + struct dc_stream_state *new_stream) +{ + if (cur_stream == NULL) + return true; + + if (memcmp(&cur_stream->vsc_infopacket, + &new_stream->vsc_infopacket, + sizeof(struct dc_info_packet)) != 0) + return true; + + return false; +} + static bool is_timing_changed(struct dc_stream_state *cur_stream, struct dc_stream_state *new_stream) { @@ -1563,6 +1639,12 @@ static bool are_stream_backends_same( if (is_hdr_static_meta_changed(stream_a, stream_b)) return false; + if (stream_a->dpms_off != stream_b->dpms_off) + return false; + + if (is_vsc_info_packet_changed(stream_a, stream_b)) + return false; + return true; } @@ -1690,7 +1772,7 @@ static struct stream_encoder *find_first_free_match_stream_enc_for_link( * required for non DP connectors. */ - if (j >= 0 && dc_is_dp_signal(stream->signal)) + if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) return pool->stream_enc[j]; return NULL; @@ -2423,119 +2505,13 @@ static void set_spd_info_packet( { /* SPD info packet for FreeSync */ - unsigned char checksum = 0; - unsigned int idx, payload_size = 0; - /* Check if Freesync is supported. Return if false. If true, * set the corresponding bit in the info packet */ - if (stream->freesync_ctx.supported == false) + if (!stream->vrr_infopacket.valid) return; - if (dc_is_hdmi_signal(stream->signal)) { - - /* HEADER */ - - /* HB0 = Packet Type = 0x83 (Source Product - * Descriptor InfoFrame) - */ - info_packet->hb0 = HDMI_INFOFRAME_TYPE_SPD; - - /* HB1 = Version = 0x01 */ - info_packet->hb1 = 0x01; - - /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */ - info_packet->hb2 = 0x08; - - payload_size = 0x08; - - } else if (dc_is_dp_signal(stream->signal)) { - - /* HEADER */ - - /* HB0 = Secondary-data Packet ID = 0 - Only non-zero - * when used to associate audio related info packets - */ - info_packet->hb0 = 0x00; - - /* HB1 = Packet Type = 0x83 (Source Product - * Descriptor InfoFrame) - */ - info_packet->hb1 = HDMI_INFOFRAME_TYPE_SPD; - - /* HB2 = [Bits 7:0 = Least significant eight bits - - * For INFOFRAME, the value must be 1Bh] - */ - info_packet->hb2 = 0x1B; - - /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1] - * [Bits 1:0 = Most significant two bits = 0x00] - */ - info_packet->hb3 = 0x04; - - payload_size = 0x1B; - } - - /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ - info_packet->sb[1] = 0x1A; - - /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ - info_packet->sb[2] = 0x00; - - /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ - info_packet->sb[3] = 0x00; - - /* PB4 = Reserved */ - info_packet->sb[4] = 0x00; - - /* PB5 = Reserved */ - info_packet->sb[5] = 0x00; - - /* PB6 = [Bits 7:3 = Reserved] */ - info_packet->sb[6] = 0x00; - - if (stream->freesync_ctx.supported == true) - /* PB6 = [Bit 0 = FreeSync Supported] */ - info_packet->sb[6] |= 0x01; - - if (stream->freesync_ctx.enabled == true) - /* PB6 = [Bit 1 = FreeSync Enabled] */ - info_packet->sb[6] |= 0x02; - - if (stream->freesync_ctx.active == true) - /* PB6 = [Bit 2 = FreeSync Active] */ - info_packet->sb[6] |= 0x04; - - /* PB7 = FreeSync Minimum refresh rate (Hz) */ - info_packet->sb[7] = (unsigned char) (stream->freesync_ctx. - min_refresh_in_micro_hz / 1000000); - - /* PB8 = FreeSync Maximum refresh rate (Hz) - * - * Note: We do not use the maximum capable refresh rate - * of the panel, because we should never go above the field - * rate of the mode timing set. - */ - info_packet->sb[8] = (unsigned char) (stream->freesync_ctx. - nominal_refresh_in_micro_hz / 1000000); - - /* PB9 - PB27 = Reserved */ - for (idx = 9; idx <= 27; idx++) - info_packet->sb[idx] = 0x00; - - /* Calculate checksum */ - checksum += info_packet->hb0; - checksum += info_packet->hb1; - checksum += info_packet->hb2; - checksum += info_packet->hb3; - - for (idx = 1; idx <= payload_size; idx++) - checksum += info_packet->sb[idx]; - - /* PB0 = Checksum (one byte complement) */ - info_packet->sb[0] = (unsigned char) (0x100 - checksum); - - info_packet->valid = true; + *info_packet = stream->vrr_infopacket; } static void set_hdr_static_info_packet( @@ -2555,43 +2531,10 @@ static void set_vsc_info_packet( struct dc_info_packet *info_packet, struct dc_stream_state *stream) { - unsigned int vscPacketRevision = 0; - unsigned int i; - - /*VSC packet set to 2 when DP revision >= 1.2*/ - if (stream->psr_version != 0) { - vscPacketRevision = 2; - } - - /* VSC packet not needed based on the features - * supported by this DP display - */ - if (vscPacketRevision == 0) + if (!stream->vsc_infopacket.valid) return; - if (vscPacketRevision == 0x2) { - /* Secondary-data Packet ID = 0*/ - info_packet->hb0 = 0x00; - /* 07h - Packet Type Value indicating Video - * Stream Configuration packet - */ - info_packet->hb1 = 0x07; - /* 02h = VSC SDP supporting 3D stereo and PSR - * (applies to eDP v1.3 or higher). - */ - info_packet->hb2 = 0x02; - /* 08h = VSC packet supporting 3D stereo + PSR - * (HB2 = 02h). - */ - info_packet->hb3 = 0x08; - - for (i = 0; i < 28; i++) - info_packet->sb[i] = 0; - - info_packet->valid = true; - } - - /*TODO: stereo 3D support and extend pixel encoding colorimetry*/ + *info_packet = stream->vsc_infopacket; } void dc_resource_state_destruct(struct dc_state *context) @@ -2770,6 +2713,12 @@ bool pipe_need_reprogram( if (is_hdr_static_meta_changed(pipe_ctx_old->stream, pipe_ctx->stream)) return true; + if (pipe_ctx_old->stream->dpms_off != pipe_ctx->stream->dpms_off) + return true; + + if (is_vsc_info_packet_changed(pipe_ctx_old->stream, pipe_ctx->stream)) + return true; + return false; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index fdcc8ab19bf3..2ac848a106ba 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -205,8 +205,6 @@ bool dc_stream_set_cursor_attributes( if (pipe_ctx->stream != stream) continue; - if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) - continue; if (!pipe_to_program) { pipe_to_program = pipe_ctx; diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 6c9990bef267..7691139363a9 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -38,7 +38,7 @@ #include "inc/compressor.h" #include "dml/display_mode_lib.h" -#define DC_VER "3.1.59" +#define DC_VER "3.1.66" #define MAX_SURFACES 3 #define MAX_STREAMS 6 @@ -311,8 +311,6 @@ struct dc { bool optimized_required; - bool apply_edp_fast_boot_optimization; - /* FBC compressor */ struct compressor *fbc_compressor; @@ -442,6 +440,7 @@ union surface_update_flags { uint32_t color_space_change:1; uint32_t horizontal_mirror_change:1; uint32_t per_pixel_alpha_change:1; + uint32_t global_alpha_change:1; uint32_t rotation_change:1; uint32_t swizzle_change:1; uint32_t scaling_change:1; @@ -496,6 +495,8 @@ struct dc_plane_state { bool is_tiling_rotated; bool per_pixel_alpha; + bool global_alpha; + int global_alpha_value; bool visible; bool flip_immediate; bool horizontal_mirror; @@ -522,6 +523,8 @@ struct dc_plane_info { bool horizontal_mirror; bool visible; bool per_pixel_alpha; + bool global_alpha; + int global_alpha_value; bool input_csc_enabled; }; diff --git a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h index 90082bab71f0..8130b95ccc53 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h @@ -41,38 +41,17 @@ struct dc_vbios_funcs { uint8_t (*get_connectors_number)(struct dc_bios *bios); - struct graphics_object_id (*get_encoder_id)( - struct dc_bios *bios, - uint32_t i); struct graphics_object_id (*get_connector_id)( struct dc_bios *bios, uint8_t connector_index); - uint32_t (*get_dst_number)( - struct dc_bios *bios, - struct graphics_object_id id); - enum bp_result (*get_src_obj)( struct dc_bios *bios, struct graphics_object_id object_id, uint32_t index, struct graphics_object_id *src_object_id); - enum bp_result (*get_dst_obj)( - struct dc_bios *bios, - struct graphics_object_id object_id, uint32_t index, - struct graphics_object_id *dest_object_id); - enum bp_result (*get_i2c_info)( struct dc_bios *dcb, struct graphics_object_id id, struct graphics_object_i2c_info *info); - - enum bp_result (*get_voltage_ddc_info)( - struct dc_bios *bios, - uint32_t index, - struct graphics_object_i2c_info *info); - enum bp_result (*get_thermal_ddc_info)( - struct dc_bios *bios, - uint32_t i2c_channel_id, - struct graphics_object_i2c_info *info); enum bp_result (*get_hpd_info)( struct dc_bios *bios, struct graphics_object_id id, @@ -105,35 +84,8 @@ struct dc_vbios_funcs { struct graphics_object_id object_id, struct bp_encoder_cap_info *info); - bool (*is_lid_status_changed)( - struct dc_bios *bios); - bool (*is_display_config_changed)( - struct dc_bios *bios); bool (*is_accelerated_mode)( struct dc_bios *bios); - uint32_t (*get_vga_enabled_displays)( - struct dc_bios *bios); - void (*get_bios_event_info)( - struct dc_bios *bios, - struct bios_event_info *info); - void (*update_requested_backlight_level)( - struct dc_bios *bios, - uint32_t backlight_8bit); - uint32_t (*get_requested_backlight_level)( - struct dc_bios *bios); - void (*take_backlight_control)( - struct dc_bios *bios, - bool cntl); - - bool (*is_active_display)( - struct dc_bios *bios, - enum signal_type signal, - const struct connector_device_tag_info *device_tag); - enum controller_id (*get_embedded_display_controller_id)( - struct dc_bios *bios); - uint32_t (*get_embedded_display_refresh_rate)( - struct dc_bios *bios); - void (*set_scratch_critical_state)( struct dc_bios *bios, bool state); @@ -149,11 +101,6 @@ struct dc_vbios_funcs { enum bp_result (*transmitter_control)( struct dc_bios *bios, struct bp_transmitter_control *cntl); - enum bp_result (*crt_control)( - struct dc_bios *bios, - enum engine_id engine_id, - bool enable, - uint32_t pixel_clock); enum bp_result (*enable_crtc)( struct dc_bios *bios, enum controller_id id, @@ -167,8 +114,6 @@ struct dc_vbios_funcs { enum bp_result (*set_dce_clock)( struct dc_bios *bios, struct bp_set_dce_clock_parameters *bp_params); - unsigned int (*get_smu_clock_info)( - struct dc_bios *bios); enum bp_result (*enable_spread_spectrum_on_ppll)( struct dc_bios *bios, struct bp_spread_spectrum_parameters *bp_params, @@ -183,20 +128,11 @@ struct dc_vbios_funcs { enum bp_result (*program_display_engine_pll)( struct dc_bios *bios, struct bp_pixel_clock_parameters *bp_params); - - enum signal_type (*dac_load_detect)( - struct dc_bios *bios, - struct graphics_object_id encoder, - struct graphics_object_id connector, - enum signal_type display_signal); - enum bp_result (*enable_disp_power_gating)( struct dc_bios *bios, enum controller_id controller_id, enum bp_pipe_control_action action); - void (*post_init)(struct dc_bios *bios); - void (*bios_parser_destroy)(struct dc_bios **dcb); enum bp_result (*get_board_layout_info)( diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c index e68077e65565..fcfd50b5dba0 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -219,12 +219,6 @@ uint32_t generic_reg_wait(const struct dc_context *ctx, /* something is terribly wrong if time out is > 200ms. (5Hz) */ ASSERT(delay_between_poll_us * time_out_num_tries <= 200000); - if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) { - /* 35 seconds */ - delay_between_poll_us = 35000; - time_out_num_tries = 1000; - } - for (i = 0; i <= time_out_num_tries; i++) { if (i) { if (delay_between_poll_us >= 1000) @@ -238,7 +232,8 @@ uint32_t generic_reg_wait(const struct dc_context *ctx, field_value = get_reg_field_value_ex(reg_val, mask, shift); if (field_value == condition_value) { - if (i * delay_between_poll_us > 1000) + if (i * delay_between_poll_us > 1000 && + !IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) dm_output_to_console("REG_WAIT taking a while: %dms in %s line:%d\n", delay_between_poll_us * i / 1000, func_name, line); diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h index b789cb2b354b..57f57cf0fe2a 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h @@ -708,12 +708,6 @@ struct crtc_trigger_info { enum trigger_delay delay; }; -enum vrr_state { - VRR_STATE_OFF = 0, - VRR_STATE_VARIABLE, - VRR_STATE_FIXED, -}; - struct dc_crtc_timing_adjust { uint32_t v_total_min; uint32_t v_total_max; diff --git a/drivers/gpu/drm/amd/display/dc/dc_link.h b/drivers/gpu/drm/amd/display/dc/dc_link.h index d43cefbc43d3..438fb35d87b8 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_link.h +++ b/drivers/gpu/drm/amd/display/dc/dc_link.h @@ -167,6 +167,7 @@ enum dc_detect_reason { }; bool dc_link_detect(struct dc_link *dc_link, enum dc_detect_reason reason); +bool dc_link_get_hpd_state(struct dc_link *dc_link); /* Notify DC about DP RX Interrupt (aka Short Pulse Interrupt). * Return: diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index cbfe418006cb..c5bd1fbb6982 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -45,19 +45,26 @@ struct dc_stream_status { struct dc_link *link; }; +// TODO: References to this needs to be removed.. +struct freesync_context { + bool dummy; +}; + struct dc_stream_state { struct dc_sink *sink; struct dc_crtc_timing timing; - struct dc_crtc_timing_adjust timing_adjust; - struct vrr_params vrr_params; + struct dc_crtc_timing_adjust adjust; + struct dc_info_packet vrr_infopacket; + struct dc_info_packet vsc_infopacket; struct rect src; /* composition area */ struct rect dst; /* stream addressable area */ - struct audio_info audio_info; - + // TODO: References to this needs to be removed.. struct freesync_context freesync_ctx; + struct audio_info audio_info; + struct dc_info_packet hdr_static_metadata; PHYSICAL_ADDRESS_LOC dmdata_address; bool use_dynamic_meta; @@ -95,6 +102,7 @@ struct dc_stream_state { int phy_pix_clk; enum signal_type signal; bool dpms_off; + bool apply_edp_fast_boot_optimization; struct dc_stream_status status; @@ -120,6 +128,17 @@ struct dc_stream_update { unsigned int *abm_level; unsigned long long *periodic_fn_vsync_delta; + struct dc_crtc_timing_adjust *adjust; + struct dc_info_packet *vrr_infopacket; + struct dc_info_packet *vsc_infopacket; + + bool *dpms_off; + + struct colorspace_transform *gamut_remap; + enum dc_color_space *output_color_space; + + struct dc_csc_transform *output_csc_transform; + }; bool dc_is_stream_unchanged( @@ -258,10 +277,8 @@ bool dc_stream_set_cursor_position( bool dc_stream_adjust_vmin_vmax(struct dc *dc, - struct dc_stream_state **stream, - int num_streams, - int vmin, - int vmax); + struct dc_stream_state *stream, + struct dc_crtc_timing_adjust *adjust); bool dc_stream_get_crtc_position(struct dc *dc, struct dc_stream_state **stream, @@ -288,12 +305,11 @@ void dc_stream_set_static_screen_events(struct dc *dc, void dc_stream_set_dither_option(struct dc_stream_state *stream, enum dc_dither_option option); +bool dc_stream_set_gamut_remap(struct dc *dc, + const struct dc_stream_state *stream); -bool dc_stream_adjust_vmin_vmax(struct dc *dc, - struct dc_stream_state **stream, - int num_streams, - int vmin, - int vmax); +bool dc_stream_program_csc_matrix(struct dc *dc, + struct dc_stream_state *stream); bool dc_stream_get_crtc_position(struct dc *dc, struct dc_stream_state **stream, diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 8c6eb78b0c3b..4fb62780a696 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -191,6 +191,7 @@ union display_content_support { }; struct dc_panel_patch { + unsigned int disconnect_delay; unsigned int dppowerup_delay; unsigned int extra_t12_ms; }; @@ -513,13 +514,11 @@ struct audio_info { struct audio_mode modes[DC_MAX_AUDIO_DESC_COUNT]; }; -struct vrr_params { - enum vrr_state state; - uint32_t window_min; - uint32_t window_max; - uint32_t inserted_frame_duration_in_us; - uint32_t frames_to_insert; - uint32_t frame_counter; +enum dc_infoframe_type { + DC_HDMI_INFOFRAME_TYPE_VENDOR = 0x81, + DC_HDMI_INFOFRAME_TYPE_AVI = 0x82, + DC_HDMI_INFOFRAME_TYPE_SPD = 0x83, + DC_HDMI_INFOFRAME_TYPE_AUDIO = 0x84, }; struct dc_info_packet { @@ -539,16 +538,6 @@ struct dc_plane_flip_time { unsigned int prev_update_time_in_us; }; -// Will combine with vrr_params at some point. -struct freesync_context { - bool supported; - bool enabled; - bool active; - - unsigned int min_refresh_in_micro_hz; - unsigned int nominal_refresh_in_micro_hz; -}; - struct psr_config { unsigned char psr_version; unsigned int psr_rfb_setup_time; diff --git a/drivers/gpu/drm/amd/display/dc/dce/Makefile b/drivers/gpu/drm/amd/display/dc/dce/Makefile index 825537bd4545..8f7f0e8b341f 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dce/Makefile @@ -28,8 +28,8 @@ DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \ dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o \ -dce_clocks.o dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o dce_aux.o - +dce_clocks.o dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o dce_aux.o \ +dce_i2c.o dce_i2c_hw.o dce_i2c_sw.o AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE)) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index ca137757a69e..723ce80ed89c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -75,6 +75,11 @@ static const struct spread_spectrum_data *get_ss_data_entry( entrys_num = clk_src->hdmi_ss_params_cnt; break; + case SIGNAL_TYPE_LVDS: + ss_parm = clk_src->lvds_ss_params; + entrys_num = clk_src->lvds_ss_params_cnt; + break; + case SIGNAL_TYPE_DISPLAY_PORT: case SIGNAL_TYPE_DISPLAY_PORT_MST: case SIGNAL_TYPE_EDP: @@ -579,115 +584,42 @@ static uint32_t dce110_get_pix_clk_dividers( return 0; } - switch (cs->ctx->dce_version) { - case DCE_VERSION_8_0: - case DCE_VERSION_8_1: - case DCE_VERSION_8_3: - case DCE_VERSION_10_0: - case DCE_VERSION_11_0: - pll_calc_error = - dce110_get_pix_clk_dividers_helper(clk_src, + pll_calc_error = dce110_get_pix_clk_dividers_helper(clk_src, pll_settings, pix_clk_params); - break; - case DCE_VERSION_11_2: - case DCE_VERSION_11_22: - case DCE_VERSION_12_0: -#if defined(CONFIG_DRM_AMD_DC_DCN1_0) - case DCN_VERSION_1_0: -#endif - - dce112_get_pix_clk_dividers_helper(clk_src, - pll_settings, pix_clk_params); - break; - default: - break; - } return pll_calc_error; } -static uint32_t dce110_get_pll_pixel_rate_in_hz( - struct clock_source *cs, - struct pixel_clk_params *pix_clk_params, - struct pll_settings *pll_settings) -{ - uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; - struct dc *dc_core = cs->ctx->dc; - struct dc_state *context = dc_core->current_state; - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; - - /* This function need separate to different DCE version, before separate, just use pixel clock */ - return pipe_ctx->stream->phy_pix_clk; - -} - -static uint32_t dce110_get_dp_pixel_rate_from_combo_phy_pll( - struct clock_source *cs, - struct pixel_clk_params *pix_clk_params, - struct pll_settings *pll_settings) -{ - uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; - struct dc *dc_core = cs->ctx->dc; - struct dc_state *context = dc_core->current_state; - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; - - /* This function need separate to different DCE version, before separate, just use pixel clock */ - return pipe_ctx->stream->phy_pix_clk; -} - -static uint32_t dce110_get_d_to_pixel_rate_in_hz( - struct clock_source *cs, - struct pixel_clk_params *pix_clk_params, - struct pll_settings *pll_settings) +static uint32_t dce112_get_pix_clk_dividers( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) { - uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - int dto_enabled = 0; - struct fixed31_32 pix_rate; - - REG_GET(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, &dto_enabled); - - if (dto_enabled) { - uint32_t phase = 0; - uint32_t modulo = 0; - REG_GET(PHASE[inst], DP_DTO0_PHASE, &phase); - REG_GET(MODULO[inst], DP_DTO0_MODULO, &modulo); + DC_LOGGER_INIT(); - if (modulo == 0) { - return 0; - } + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk == 0) { + DC_LOG_ERROR( + "%s: Invalid parameters!!\n", __func__); + return -1; + } - pix_rate = dc_fixpt_from_int(clk_src->ref_freq_khz); - pix_rate = dc_fixpt_mul_int(pix_rate, 1000); - pix_rate = dc_fixpt_mul_int(pix_rate, phase); - pix_rate = dc_fixpt_div_int(pix_rate, modulo); + memset(pll_settings, 0, sizeof(*pll_settings)); - return dc_fixpt_round(pix_rate); - } else { - return dce110_get_dp_pixel_rate_from_combo_phy_pll(cs, pix_clk_params, pll_settings); + if (cs->id == CLOCK_SOURCE_ID_DP_DTO || + cs->id == CLOCK_SOURCE_ID_EXTERNAL) { + pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; + pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + return -1; } -} -static uint32_t dce110_get_pix_rate_in_hz( - struct clock_source *cs, - struct pixel_clk_params *pix_clk_params, - struct pll_settings *pll_settings) -{ - uint32_t pix_rate = 0; - switch (pix_clk_params->signal_type) { - case SIGNAL_TYPE_DISPLAY_PORT: - case SIGNAL_TYPE_DISPLAY_PORT_MST: - case SIGNAL_TYPE_EDP: - case SIGNAL_TYPE_VIRTUAL: - pix_rate = dce110_get_d_to_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); - break; - case SIGNAL_TYPE_HDMI_TYPE_A: - default: - pix_rate = dce110_get_pll_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); - break; - } + dce112_get_pix_clk_dividers_helper(clk_src, + pll_settings, pix_clk_params); - return pix_rate; + return 0; } static bool disable_spread_spectrum(struct dce110_clk_src *clk_src) @@ -909,6 +841,65 @@ static bool dce110_program_pix_clk( struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); struct bp_pixel_clock_parameters bp_pc_params = {0}; + /* First disable SS + * ATOMBIOS will enable by default SS on PLL for DP, + * do not disable it here + */ + if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type) && + clock_source->ctx->dce_version <= DCE_VERSION_11_0) + disable_spread_spectrum(clk_src); + + /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ + bp_pc_params.controller_id = pix_clk_params->controller_id; + bp_pc_params.pll_id = clock_source->id; + bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + + bp_pc_params.reference_divider = pll_settings->reference_divider; + bp_pc_params.feedback_divider = pll_settings->feedback_divider; + bp_pc_params.fractional_feedback_divider = + pll_settings->fract_feedback_divider; + bp_pc_params.pixel_clock_post_divider = + pll_settings->pix_clk_post_divider; + bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = + pll_settings->use_external_clk; + + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Enable SS + * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), + * based on HW display PLL team, SS control settings should be programmed + * during PLL Reset, but they do not have effect + * until SS_EN is asserted.*/ + if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL + && !dc_is_dp_signal(pix_clk_params->signal_type)) { + + if (pix_clk_params->flags.ENABLE_SS) + if (!enable_spread_spectrum(clk_src, + pix_clk_params->signal_type, + pll_settings)) + return false; + + /* Resync deep color DTO */ + dce110_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth); + } + + return true; +} + +static bool dce112_program_pix_clk( + struct clock_source *clock_source, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) if (IS_FPGA_MAXIMUS_DC(clock_source->ctx->dce_environment)) { unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; @@ -940,78 +931,29 @@ static bool dce110_program_pix_clk( bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; bp_pc_params.signal_type = pix_clk_params->signal_type; - switch (clock_source->ctx->dce_version) { - case DCE_VERSION_8_0: - case DCE_VERSION_8_1: - case DCE_VERSION_8_3: - case DCE_VERSION_10_0: - case DCE_VERSION_11_0: - bp_pc_params.reference_divider = pll_settings->reference_divider; - bp_pc_params.feedback_divider = pll_settings->feedback_divider; - bp_pc_params.fractional_feedback_divider = - pll_settings->fract_feedback_divider; - bp_pc_params.pixel_clock_post_divider = - pll_settings->pix_clk_post_divider; - bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { + bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = pll_settings->use_external_clk; - - if (clk_src->bios->funcs->set_pixel_clock( - clk_src->bios, &bp_pc_params) != BP_RESULT_OK) - return false; - /* Enable SS - * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), - * based on HW display PLL team, SS control settings should be programmed - * during PLL Reset, but they do not have effect - * until SS_EN is asserted.*/ - if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL - && !dc_is_dp_signal(pix_clk_params->signal_type)) { - - if (pix_clk_params->flags.ENABLE_SS) - if (!enable_spread_spectrum(clk_src, - pix_clk_params->signal_type, - pll_settings)) - return false; - - /* Resync deep color DTO */ - dce110_program_pixel_clk_resync(clk_src, - pix_clk_params->signal_type, - pix_clk_params->color_depth); + bp_pc_params.flags.SET_XTALIN_REF_SRC = + !pll_settings->use_external_clk; + if (pix_clk_params->flags.SUPPORT_YCBCR420) { + bp_pc_params.flags.SUPPORT_YUV_420 = 1; } - - break; - case DCE_VERSION_11_2: - case DCE_VERSION_11_22: - case DCE_VERSION_12_0: -#if defined(CONFIG_DRM_AMD_DC_DCN1_0) - case DCN_VERSION_1_0: -#endif - - if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { - bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = - pll_settings->use_external_clk; - bp_pc_params.flags.SET_XTALIN_REF_SRC = - !pll_settings->use_external_clk; - if (pix_clk_params->flags.SUPPORT_YCBCR420) { - bp_pc_params.flags.SUPPORT_YUV_420 = 1; - } - } - if (clk_src->bios->funcs->set_pixel_clock( - clk_src->bios, &bp_pc_params) != BP_RESULT_OK) - return false; - /* Resync deep color DTO */ - if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) - dce112_program_pixel_clk_resync(clk_src, - pix_clk_params->signal_type, - pix_clk_params->color_depth, - pix_clk_params->flags.SUPPORT_YCBCR420); - break; - default: - break; } + if (clk_src->bios->funcs->set_pixel_clock( + clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + /* Resync deep color DTO */ + if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) + dce112_program_pixel_clk_resync(clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth, + pix_clk_params->flags.SUPPORT_YCBCR420); return true; } + static bool dce110_clock_source_power_down( struct clock_source *clk_src) { @@ -1038,13 +980,19 @@ static bool dce110_clock_source_power_down( /*****************************************/ /* Constructor */ /*****************************************/ + +static const struct clock_source_funcs dce112_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dce112_program_pix_clk, + .get_pix_clk_dividers = dce112_get_pix_clk_dividers +}; static const struct clock_source_funcs dce110_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dce110_program_pix_clk, - .get_pix_clk_dividers = dce110_get_pix_clk_dividers, - .get_pix_rate_in_hz = dce110_get_pix_rate_in_hz + .get_pix_clk_dividers = dce110_get_pix_clk_dividers }; + static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal, @@ -1184,6 +1132,11 @@ static void ss_info_from_atombios_create( AS_SIGNAL_TYPE_DVI, &clk_src->dvi_ss_params, &clk_src->dvi_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_LVDS, + &clk_src->lvds_ss_params, + &clk_src->lvds_ss_params_cnt); } static bool calc_pll_max_vco_construct( @@ -1295,81 +1248,70 @@ bool dce110_clk_src_construct( clk_src->ext_clk_khz = fw_info.external_clock_source_frequency_for_dp; - switch (clk_src->base.ctx->dce_version) { - case DCE_VERSION_8_0: - case DCE_VERSION_8_1: - case DCE_VERSION_8_3: - case DCE_VERSION_10_0: - case DCE_VERSION_11_0: - - /* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ - calc_pll_cs_init_data.bp = bios; - calc_pll_cs_init_data.min_pix_clk_pll_post_divider = 1; - calc_pll_cs_init_data.max_pix_clk_pll_post_divider = - clk_src->cs_mask->PLL_POST_DIV_PIXCLK; - calc_pll_cs_init_data.min_pll_ref_divider = 1; - calc_pll_cs_init_data.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; - /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ - calc_pll_cs_init_data.min_override_input_pxl_clk_pll_freq_khz = 0; - /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ - calc_pll_cs_init_data.max_override_input_pxl_clk_pll_freq_khz = 0; - /*numberOfFractFBDividerDecimalPoints*/ - calc_pll_cs_init_data.num_fract_fb_divider_decimal_point = - FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; - /*number of decimal point to round off for fractional feedback divider value*/ - calc_pll_cs_init_data.num_fract_fb_divider_decimal_point_precision = - FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; - calc_pll_cs_init_data.ctx = ctx; - - /*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ - calc_pll_cs_init_data_hdmi.bp = bios; - calc_pll_cs_init_data_hdmi.min_pix_clk_pll_post_divider = 1; - calc_pll_cs_init_data_hdmi.max_pix_clk_pll_post_divider = - clk_src->cs_mask->PLL_POST_DIV_PIXCLK; - calc_pll_cs_init_data_hdmi.min_pll_ref_divider = 1; - calc_pll_cs_init_data_hdmi.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; - /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ - calc_pll_cs_init_data_hdmi.min_override_input_pxl_clk_pll_freq_khz = 13500; - /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ - calc_pll_cs_init_data_hdmi.max_override_input_pxl_clk_pll_freq_khz = 27000; - /*numberOfFractFBDividerDecimalPoints*/ - calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point = - FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; - /*number of decimal point to round off for fractional feedback divider value*/ - calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point_precision = - FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; - calc_pll_cs_init_data_hdmi.ctx = ctx; - - clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; - - if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) - return true; - - /* PLL only from here on */ - ss_info_from_atombios_create(clk_src); - - if (!calc_pll_max_vco_construct( - &clk_src->calc_pll, - &calc_pll_cs_init_data)) { - ASSERT_CRITICAL(false); - goto unexpected_failure; - } + /* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ + calc_pll_cs_init_data.bp = bios; + calc_pll_cs_init_data.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data.min_pll_ref_divider = 1; + calc_pll_cs_init_data.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.min_override_input_pxl_clk_pll_freq_khz = 0; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data.max_override_input_pxl_clk_pll_freq_khz = 0; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data.ctx = ctx; + + /*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ + calc_pll_cs_init_data_hdmi.bp = bios; + calc_pll_cs_init_data_hdmi.min_pix_clk_pll_post_divider = 1; + calc_pll_cs_init_data_hdmi.max_pix_clk_pll_post_divider = + clk_src->cs_mask->PLL_POST_DIV_PIXCLK; + calc_pll_cs_init_data_hdmi.min_pll_ref_divider = 1; + calc_pll_cs_init_data_hdmi.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.min_override_input_pxl_clk_pll_freq_khz = 13500; + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + calc_pll_cs_init_data_hdmi.max_override_input_pxl_clk_pll_freq_khz = 27000; + /*numberOfFractFBDividerDecimalPoints*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + /*number of decimal point to round off for fractional feedback divider value*/ + calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point_precision = + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; + calc_pll_cs_init_data_hdmi.ctx = ctx; + + clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; + + if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) + return true; + + /* PLL only from here on */ + ss_info_from_atombios_create(clk_src); + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll, + &calc_pll_cs_init_data)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } - calc_pll_cs_init_data_hdmi. - min_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz/2; - calc_pll_cs_init_data_hdmi. - max_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz; + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz/2; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz; - if (!calc_pll_max_vco_construct( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { - ASSERT_CRITICAL(false); - goto unexpected_failure; - } - break; - default: - break; + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; } return true; @@ -1378,3 +1320,34 @@ unexpected_failure: return false; } +bool dce112_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask) +{ + struct dc_firmware_info fw_info = { { 0 } }; + + clk_src->base.ctx = ctx; + clk_src->bios = bios; + clk_src->base.id = id; + clk_src->base.funcs = &dce112_clk_src_funcs; + + clk_src->regs = regs; + clk_src->cs_shift = cs_shift; + clk_src->cs_mask = cs_mask; + + if (clk_src->bios->funcs->get_firmware_info( + clk_src->bios, &fw_info) != BP_RESULT_OK) { + ASSERT_CRITICAL(false); + return false; + } + + clk_src->ext_clk_khz = fw_info.external_clock_source_frequency_for_dp; + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h index c45e2f76189e..1ed7695a76d3 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h @@ -125,6 +125,8 @@ struct dce110_clk_src { uint32_t hdmi_ss_params_cnt; struct spread_spectrum_data *dvi_ss_params; uint32_t dvi_ss_params_cnt; + struct spread_spectrum_data *lvds_ss_params; + uint32_t lvds_ss_params_cnt; uint32_t ext_clk_khz; uint32_t ref_freq_khz; @@ -142,4 +144,13 @@ bool dce110_clk_src_construct( const struct dce110_clk_src_shift *cs_shift, const struct dce110_clk_src_mask *cs_mask); +bool dce112_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + const struct dce110_clk_src_shift *cs_shift, + const struct dce110_clk_src_mask *cs_mask); + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c index fb1f373d08a1..bf6261a1584b 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.c @@ -202,7 +202,7 @@ static int dce12_get_dp_ref_freq_khz(struct dccg *clk) { struct dce_dccg *clk_dce = TO_DCE_CLOCKS(clk); - return dccg_adjust_dp_ref_freq_for_ss(clk_dce, 600000); + return dccg_adjust_dp_ref_freq_for_ss(clk_dce, clk_dce->dprefclk_khz); } static enum dm_pp_clocks_state dce_get_required_clocks_state( @@ -255,10 +255,12 @@ static int dce_set_clock( pxl_clk_params.target_pixel_clock = requested_clk_khz; pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; - bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); + if (clk_dce->dfs_bypass_active) + pxl_clk_params.flags.SET_DISPCLK_DFS_BYPASS = true; - if (clk_dce->dfs_bypass_enabled) { + bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); + if (clk_dce->dfs_bypass_active) { /* Cache the fixed display clock*/ clk_dce->dfs_bypass_disp_clk = pxl_clk_params.dfs_bypass_display_clock; @@ -674,6 +676,61 @@ static void dce_update_clocks(struct dccg *dccg, } } +static bool dce_update_dfs_bypass( + struct dccg *dccg, + struct dc *dc, + struct dc_state *context, + int requested_clock_khz) +{ + struct dce_dccg *clk_dce = TO_DCE_CLOCKS(dccg); + struct resource_context *res_ctx = &context->res_ctx; + enum signal_type signal_type = SIGNAL_TYPE_NONE; + bool was_active = clk_dce->dfs_bypass_active; + int i; + + /* Disable DFS bypass by default. */ + clk_dce->dfs_bypass_active = false; + + /* Check that DFS bypass is available. */ + if (!clk_dce->dfs_bypass_enabled) + goto update; + + /* Check if the requested display clock is below the threshold. */ + if (requested_clock_khz >= 400000) + goto update; + + /* DFS-bypass should only be enabled on single stream setups */ + if (context->stream_count != 1) + goto update; + + /* Check that the stream's signal type is an embedded panel */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (res_ctx->pipe_ctx[i].stream) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + signal_type = pipe_ctx->stream->sink->link->connector_signal; + break; + } + } + + if (signal_type == SIGNAL_TYPE_EDP || + signal_type == SIGNAL_TYPE_LVDS) + clk_dce->dfs_bypass_active = true; + +update: + /* Update the clock state. We don't need to respect safe_to_lower + * because DFS bypass should always be greater than the current + * display clock frequency. + */ + if (was_active != clk_dce->dfs_bypass_active) { + dccg->clks.dispclk_khz = + dccg->funcs->set_dispclk(dccg, dccg->clks.dispclk_khz); + return true; + } + + return false; +} + #ifdef CONFIG_DRM_AMD_DC_DCN1_0 static const struct display_clock_funcs dcn1_funcs = { .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, @@ -697,7 +754,8 @@ static const struct display_clock_funcs dce112_funcs = { static const struct display_clock_funcs dce110_funcs = { .get_dp_ref_clk_frequency = dce_get_dp_ref_freq_khz, .set_dispclk = dce_psr_set_clock, - .update_clocks = dce_update_clocks + .update_clocks = dce_update_clocks, + .update_dfs_bypass = dce_update_dfs_bypass }; static const struct display_clock_funcs dce_funcs = { @@ -824,6 +882,7 @@ struct dccg *dce120_dccg_create(struct dc_context *ctx) dce_dccg_construct( clk_dce, ctx, NULL, NULL, NULL); + clk_dce->dprefclk_khz = 600000; clk_dce->base.funcs = &dce120_funcs; return &clk_dce->base; @@ -851,6 +910,7 @@ struct dccg *dcn1_dccg_create(struct dc_context *ctx) clk_dce->dprefclk_ss_divider = 1000; clk_dce->ss_on_dprefclk = false; + clk_dce->dprefclk_khz = 600000; if (bp->integrated_info) clk_dce->dentist_vco_freq_khz = bp->integrated_info->dentist_vco_freq; if (clk_dce->dentist_vco_freq_khz == 0) { diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h index 8a6b2d328467..34fdb386c884 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clocks.h @@ -78,6 +78,8 @@ struct dce_dccg { /* Cache the status of DFS-bypass feature*/ bool dfs_bypass_enabled; + /* True if the DFS-bypass feature is enabled and active. */ + bool dfs_bypass_active; /* Cache the display clock returned by VBIOS if DFS-bypass is enabled. * This is basically "Crystal Frequency In KHz" (XTALIN) frequency */ int dfs_bypass_disp_clk; @@ -88,6 +90,7 @@ struct dce_dccg { int dprefclk_ss_percentage; /* DPREFCLK SS percentage Divider (100 or 1000) */ int dprefclk_ss_divider; + int dprefclk_khz; }; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c new file mode 100644 index 000000000000..35a75398fcb4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.c @@ -0,0 +1,60 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dce_i2c.h" +#include "reg_helper.h" + +bool dce_i2c_submit_command( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd) +{ + struct dce_i2c_hw *dce_i2c_hw; + struct dce_i2c_sw *dce_i2c_sw; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!cmd) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* The software engine is only available on dce8 */ + dce_i2c_sw = dce_i2c_acquire_i2c_sw_engine(pool, ddc); + + if (!dce_i2c_sw) { + dce_i2c_hw = acquire_i2c_hw_engine(pool, ddc); + + if (!dce_i2c_hw) + return false; + + return dce_i2c_submit_command_hw(pool, ddc, cmd, dce_i2c_hw); + } + + return dce_i2c_submit_command_sw(pool, ddc, cmd, dce_i2c_sw); + +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h new file mode 100644 index 000000000000..a171c5cd8439 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c.h @@ -0,0 +1,38 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DCE_I2C_H__ +#define __DCE_I2C_H__ + +#include "inc/core_types.h" +#include "dce_i2c_hw.h" +#include "dce_i2c_sw.h" + +bool dce_i2c_submit_command( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c new file mode 100644 index 000000000000..40f2d6e0b122 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c @@ -0,0 +1,676 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dce_i2c.h" +#include "dce_i2c_hw.h" +#include "reg_helper.h" +#include "include/gpio_service_interface.h" + +#define CTX \ + dce_i2c_hw->ctx +#define REG(reg)\ + dce_i2c_hw->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + dce_i2c_hw->shifts->field_name, dce_i2c_hw->masks->field_name + +static void execute_transaction( + struct dce_i2c_hw *dce_i2c_hw) +{ + REG_UPDATE_N(SETUP, 5, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY), 0, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY), 0); + + + REG_UPDATE_5(DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SW_STATUS_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_GO, 0, + DC_I2C_TRANSACTION_COUNT, dce_i2c_hw->transaction_count - 1); + + /* start I2C transfer */ + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_GO, 1); + + /* all transactions were executed and HW buffer became empty + * (even though it actually happens when status becomes DONE) + */ + dce_i2c_hw->transaction_count = 0; + dce_i2c_hw->buffer_used_bytes = 0; +} + +static enum i2c_channel_operation_result get_channel_status( + struct dce_i2c_hw *dce_i2c_hw, + uint8_t *returned_bytes) +{ + uint32_t i2c_sw_status = 0; + uint32_t value = + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW) + return I2C_CHANNEL_OPERATION_ENGINE_BUSY; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_STOPPED_ON_NACK) + return I2C_CHANNEL_OPERATION_NO_RESPONSE; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_TIMEOUT) + return I2C_CHANNEL_OPERATION_TIMEOUT; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_ABORTED) + return I2C_CHANNEL_OPERATION_FAILED; + else if (value & dce_i2c_hw->masks->DC_I2C_SW_DONE) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + /* + * this is the case when HW used for communication, I2C_SW_STATUS + * could be zero + */ + return I2C_CHANNEL_OPERATION_SUCCEEDED; +} + +static uint32_t get_hw_buffer_available_size( + const struct dce_i2c_hw *dce_i2c_hw) +{ + return dce_i2c_hw->buffer_size - + dce_i2c_hw->buffer_used_bytes; +} + +uint32_t get_reference_clock( + struct dc_bios *bios) +{ + struct dc_firmware_info info = { { 0 } }; + + if (bios->funcs->get_firmware_info(bios, &info) != BP_RESULT_OK) + return 0; + + return info.pll_info.crystal_frequency; +} + +static uint32_t get_speed( + const struct dce_i2c_hw *dce_i2c_hw) +{ + uint32_t pre_scale = 0; + + REG_GET(SPEED, DC_I2C_DDC1_PRESCALE, &pre_scale); + + /* [anaumov] it seems following is unnecessary */ + /*ASSERT(value.bits.DC_I2C_DDC1_PRESCALE);*/ + return pre_scale ? + dce_i2c_hw->reference_frequency / pre_scale : + dce_i2c_hw->default_speed; +} + +static void process_channel_reply( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_payload *reply) +{ + uint32_t length = reply->length; + uint8_t *buffer = reply->data; + + REG_SET_3(DC_I2C_DATA, 0, + DC_I2C_INDEX, dce_i2c_hw->buffer_used_write, + DC_I2C_DATA_RW, 1, + DC_I2C_INDEX_WRITE, 1); + + while (length) { + /* after reading the status, + * if the I2C operation executed successfully + * (i.e. DC_I2C_STATUS_DONE = 1) then the I2C controller + * should read data bytes from I2C circular data buffer + */ + + uint32_t i2c_data; + + REG_GET(DC_I2C_DATA, DC_I2C_DATA, &i2c_data); + *buffer++ = i2c_data; + + --length; + } +} + +static bool process_transaction( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_request_transaction_data *request) +{ + uint32_t length = request->length; + uint8_t *buffer = request->data; + + bool last_transaction = false; + uint32_t value = 0; + + last_transaction = ((dce_i2c_hw->transaction_count == 3) || + (request->action == DCE_I2C_TRANSACTION_ACTION_I2C_WRITE) || + (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ)); + + + switch (dce_i2c_hw->transaction_count) { + case 0: + REG_UPDATE_5(DC_I2C_TRANSACTION0, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 1: + REG_UPDATE_5(DC_I2C_TRANSACTION1, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 2: + REG_UPDATE_5(DC_I2C_TRANSACTION2, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + case 3: + REG_UPDATE_5(DC_I2C_TRANSACTION3, + DC_I2C_STOP_ON_NACK0, 1, + DC_I2C_START0, 1, + DC_I2C_RW0, 0 != (request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ), + DC_I2C_COUNT0, length, + DC_I2C_STOP0, last_transaction ? 1 : 0); + break; + default: + /* TODO Warning ? */ + break; + } + + /* Write the I2C address and I2C data + * into the hardware circular buffer, one byte per entry. + * As an example, the 7-bit I2C slave address for CRT monitor + * for reading DDC/EDID information is 0b1010001. + * For an I2C send operation, the LSB must be programmed to 0; + * for I2C receive operation, the LSB must be programmed to 1. + */ + if (dce_i2c_hw->transaction_count == 0) { + value = REG_SET_4(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address, + DC_I2C_INDEX, 0, + DC_I2C_INDEX_WRITE, 1); + dce_i2c_hw->buffer_used_write = 0; + } else + value = REG_SET_2(DC_I2C_DATA, 0, + DC_I2C_DATA_RW, false, + DC_I2C_DATA, request->address); + + dce_i2c_hw->buffer_used_write++; + + if (!(request->action & DCE_I2C_TRANSACTION_ACTION_I2C_READ)) { + while (length) { + REG_SET_2(DC_I2C_DATA, value, + DC_I2C_INDEX_WRITE, 0, + DC_I2C_DATA, *buffer++); + dce_i2c_hw->buffer_used_write++; + --length; + } + } + + ++dce_i2c_hw->transaction_count; + dce_i2c_hw->buffer_used_bytes += length + 1; + + return last_transaction; +} + +static inline void reset_hw_engine(struct dce_i2c_hw *dce_i2c_hw) +{ + REG_UPDATE_2(DC_I2C_CONTROL, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); +} + +static void set_speed( + struct dce_i2c_hw *dce_i2c_hw, + uint32_t speed) +{ + + if (speed) { + if (dce_i2c_hw->masks->DC_I2C_DDC1_START_STOP_TIMING_CNTL) + REG_UPDATE_N(SPEED, 3, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), dce_i2c_hw->reference_frequency / speed, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL), speed > 50 ? 2:1); + else + REG_UPDATE_N(SPEED, 2, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE), dce_i2c_hw->reference_frequency / speed, + FN(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD), 2); + } +} + +static bool setup_engine( + struct dce_i2c_hw *dce_i2c_hw) +{ + uint32_t i2c_setup_limit = I2C_SETUP_TIME_LIMIT_DCE; + + if (dce_i2c_hw->setup_limit != 0) + i2c_setup_limit = dce_i2c_hw->setup_limit; + /* Program pin select */ + REG_UPDATE_6(DC_I2C_CONTROL, + DC_I2C_GO, 0, + DC_I2C_SOFT_RESET, 0, + DC_I2C_SEND_RESET, 0, + DC_I2C_SW_STATUS_RESET, 1, + DC_I2C_TRANSACTION_COUNT, 0, + DC_I2C_DDC_SELECT, dce_i2c_hw->engine_id); + + /* Program time limit */ + if (dce_i2c_hw->send_reset_length == 0) { + /*pre-dcn*/ + REG_UPDATE_N(SETUP, 2, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT), i2c_setup_limit, + FN(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE), 1); + } + /* Program HW priority + * set to High - interrupt software I2C at any time + * Enable restart of SW I2C that was interrupted by HW + * disable queuing of software while I2C is in use by HW + */ + REG_UPDATE_2(DC_I2C_ARBITRATION, + DC_I2C_NO_QUEUED_SW_GO, 0, + DC_I2C_SW_PRIORITY, DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL); + + return true; +} + +static bool is_hw_busy(struct dce_i2c_hw *dce_i2c_hw) +{ + uint32_t i2c_sw_status = 0; + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + if (i2c_sw_status == DC_I2C_STATUS__DC_I2C_STATUS_IDLE) + return false; + + reset_hw_engine(dce_i2c_hw); + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + return i2c_sw_status != DC_I2C_STATUS__DC_I2C_STATUS_IDLE; +} + +static void release_engine( + struct dce_i2c_hw *dce_i2c_hw) +{ + bool safe_to_reset; + + /* Restore original HW engine speed */ + + set_speed(dce_i2c_hw, dce_i2c_hw->original_speed); + + /* Release I2C */ + REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, 1); + + /* Reset HW engine */ + { + uint32_t i2c_sw_status = 0; + + REG_GET(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, &i2c_sw_status); + /* if used by SW, safe to reset */ + safe_to_reset = (i2c_sw_status == 1); + } + + if (safe_to_reset) + REG_UPDATE_2(DC_I2C_CONTROL, + DC_I2C_SOFT_RESET, 1, + DC_I2C_SW_STATUS_RESET, 1); + else + REG_UPDATE(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, 1); + /* HW I2c engine - clock gating feature */ + if (!dce_i2c_hw->engine_keep_power_up_count) + REG_UPDATE_N(SETUP, 1, FN(SETUP, DC_I2C_DDC1_ENABLE), 0); + +} + +struct dce_i2c_hw *acquire_i2c_hw_engine( + struct resource_pool *pool, + struct ddc *ddc) +{ + uint32_t counter = 0; + enum gpio_result result; + uint32_t current_speed; + struct dce_i2c_hw *dce_i2c_hw = NULL; + + if (!ddc) + return NULL; + + if (ddc->hw_info.hw_supported) { + enum gpio_ddc_line line = dal_ddc_get_line(ddc); + + if (line < pool->pipe_count) + dce_i2c_hw = pool->hw_i2cs[line]; + } + + if (!dce_i2c_hw) + return NULL; + + if (pool->i2c_hw_buffer_in_use) + return NULL; + + do { + result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result == GPIO_RESULT_OK) + break; + + /* i2c_engine is busy by VBios, lets wait and retry */ + + udelay(10); + + ++counter; + } while (counter < 2); + + if (result != GPIO_RESULT_OK) + return NULL; + + dce_i2c_hw->ddc = ddc; + + current_speed = get_speed(dce_i2c_hw); + + if (current_speed) + dce_i2c_hw->original_speed = current_speed; + + if (!setup_engine(dce_i2c_hw)) { + release_engine(dce_i2c_hw); + return NULL; + } + + pool->i2c_hw_buffer_in_use = true; + return dce_i2c_hw; +} + +enum i2c_channel_operation_result dce_i2c_hw_engine_wait_on_operation_result( + struct dce_i2c_hw *dce_i2c_hw, + uint32_t timeout, + enum i2c_channel_operation_result expected_result) +{ + enum i2c_channel_operation_result result; + uint32_t i = 0; + + if (!timeout) + return I2C_CHANNEL_OPERATION_SUCCEEDED; + + do { + + result = get_channel_status( + dce_i2c_hw, NULL); + + if (result != expected_result) + break; + + udelay(1); + + ++i; + } while (i < timeout); + return result; +} + +static void submit_channel_request_hw( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_request_transaction_data *request) +{ + request->status = I2C_CHANNEL_OPERATION_SUCCEEDED; + + if (!process_transaction(dce_i2c_hw, request)) + return; + + if (is_hw_busy(dce_i2c_hw)) { + request->status = I2C_CHANNEL_OPERATION_ENGINE_BUSY; + return; + } + + execute_transaction(dce_i2c_hw); + + +} + +static uint32_t get_transaction_timeout_hw( + const struct dce_i2c_hw *dce_i2c_hw, + uint32_t length) +{ + + uint32_t speed = get_speed(dce_i2c_hw); + + + + uint32_t period_timeout; + uint32_t num_of_clock_stretches; + + if (!speed) + return 0; + + period_timeout = (1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed; + + num_of_clock_stretches = 1 + (length << 3) + 1; + num_of_clock_stretches += + (dce_i2c_hw->buffer_used_bytes << 3) + + (dce_i2c_hw->transaction_count << 1); + + return period_timeout * num_of_clock_stretches; +} + +bool dce_i2c_hw_engine_submit_payload( + struct dce_i2c_hw *dce_i2c_hw, + struct i2c_payload *payload, + bool middle_of_transaction) +{ + + struct i2c_request_transaction_data request; + + uint32_t transaction_timeout; + + enum i2c_channel_operation_result operation_result; + + bool result = false; + + /* We need following: + * transaction length will not exceed + * the number of free bytes in HW buffer (minus one for address) + */ + + if (payload->length >= + get_hw_buffer_available_size(dce_i2c_hw)) { + return false; + } + + if (!payload->write) + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_READ; + else + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; + + + request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.length = payload->length; + request.data = payload->data; + + /* obtain timeout value before submitting request */ + + transaction_timeout = get_transaction_timeout_hw( + dce_i2c_hw, payload->length + 1); + + submit_channel_request_hw( + dce_i2c_hw, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || + (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) + return false; + + /* wait until transaction proceed */ + + operation_result = dce_i2c_hw_engine_wait_on_operation_result( + dce_i2c_hw, + transaction_timeout, + I2C_CHANNEL_OPERATION_ENGINE_BUSY); + + /* update transaction status */ + + if (operation_result == I2C_CHANNEL_OPERATION_SUCCEEDED) + result = true; + + if (result && (!payload->write)) + process_channel_reply(dce_i2c_hw, payload); + + return result; +} + +bool dce_i2c_submit_command_hw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_hw *dce_i2c_hw) +{ + uint8_t index_of_payload = 0; + bool result; + + set_speed(dce_i2c_hw, cmd->speed); + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + bool mot = (index_of_payload != cmd->number_of_payloads - 1); + + struct i2c_payload *payload = cmd->payloads + index_of_payload; + + if (!dce_i2c_hw_engine_submit_payload( + dce_i2c_hw, payload, mot)) { + result = false; + break; + } + + + + ++index_of_payload; + } + + pool->i2c_hw_buffer_in_use = false; + + release_engine(dce_i2c_hw); + dal_ddc_close(dce_i2c_hw->ddc); + + dce_i2c_hw->ddc = NULL; + + return result; +} + +void dce_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce_i2c_hw->ctx = ctx; + dce_i2c_hw->engine_id = engine_id; + dce_i2c_hw->reference_frequency = get_reference_clock(ctx->dc_bios) >> 1; + dce_i2c_hw->regs = regs; + dce_i2c_hw->shifts = shifts; + dce_i2c_hw->masks = masks; + dce_i2c_hw->buffer_used_bytes = 0; + dce_i2c_hw->transaction_count = 0; + dce_i2c_hw->engine_keep_power_up_count = 1; + dce_i2c_hw->original_speed = DEFAULT_I2C_HW_SPEED; + dce_i2c_hw->default_speed = DEFAULT_I2C_HW_SPEED; + dce_i2c_hw->send_reset_length = 0; + dce_i2c_hw->setup_limit = I2C_SETUP_TIME_LIMIT_DCE; + dce_i2c_hw->buffer_size = I2C_HW_BUFFER_SIZE_DCE; +} + +void dce100_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + + uint32_t xtal_ref_div = 0; + + dce_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->buffer_size = I2C_HW_BUFFER_SIZE_DCE100; + + REG_GET(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, &xtal_ref_div); + + if (xtal_ref_div == 0) + xtal_ref_div = 2; + + /*Calculating Reference Clock by divding original frequency by + * XTAL_REF_DIV. + * At upper level, uint32_t reference_frequency = + * dal_dce_i2c_get_reference_clock(as) >> 1 + * which already divided by 2. So we need x2 to get original + * reference clock from ppll_info + */ + dce_i2c_hw->reference_frequency = + (dce_i2c_hw->reference_frequency * 2) / xtal_ref_div; +} + +void dce112_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce100_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->default_speed = DEFAULT_I2C_HW_SPEED_100KHZ; +} + +void dcn1_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks) +{ + dce112_i2c_hw_construct(dce_i2c_hw, + ctx, + engine_id, + regs, + shifts, + masks); + dce_i2c_hw->setup_limit = I2C_SETUP_TIME_LIMIT_DCN; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h new file mode 100644 index 000000000000..7f19bb439665 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h @@ -0,0 +1,301 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DCE_I2C_HW_H__ +#define __DCE_I2C_HW_H__ + +enum dc_i2c_status { + DC_I2C_STATUS__DC_I2C_STATUS_IDLE, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_SW, + DC_I2C_STATUS__DC_I2C_STATUS_USED_BY_HW +}; + +enum dc_i2c_arbitration { + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_NORMAL, + DC_I2C_ARBITRATION__DC_I2C_SW_PRIORITY_HIGH +}; + +enum i2c_channel_operation_result { + I2C_CHANNEL_OPERATION_SUCCEEDED, + I2C_CHANNEL_OPERATION_FAILED, + I2C_CHANNEL_OPERATION_NOT_GRANTED, + I2C_CHANNEL_OPERATION_IS_BUSY, + I2C_CHANNEL_OPERATION_NO_HANDLE_PROVIDED, + I2C_CHANNEL_OPERATION_CHANNEL_IN_USE, + I2C_CHANNEL_OPERATION_CHANNEL_CLIENT_MAX_ALLOWED, + I2C_CHANNEL_OPERATION_ENGINE_BUSY, + I2C_CHANNEL_OPERATION_TIMEOUT, + I2C_CHANNEL_OPERATION_NO_RESPONSE, + I2C_CHANNEL_OPERATION_HW_REQUEST_I2C_BUS, + I2C_CHANNEL_OPERATION_WRONG_PARAMETER, + I2C_CHANNEL_OPERATION_OUT_NB_OF_RETRIES, + I2C_CHANNEL_OPERATION_NOT_STARTED +}; + + +enum dce_i2c_transaction_action { + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE = 0x00, + DCE_I2C_TRANSACTION_ACTION_I2C_READ = 0x10, + DCE_I2C_TRANSACTION_ACTION_I2C_STATUS_REQUEST = 0x20, + + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT = 0x40, + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT = 0x50, + DCE_I2C_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT = 0x60, + + DCE_I2C_TRANSACTION_ACTION_DP_WRITE = 0x80, + DCE_I2C_TRANSACTION_ACTION_DP_READ = 0x90 +}; + +enum { + I2C_SETUP_TIME_LIMIT_DCE = 255, + I2C_SETUP_TIME_LIMIT_DCN = 3, + I2C_HW_BUFFER_SIZE_DCE100 = 538, + I2C_HW_BUFFER_SIZE_DCE = 144, + I2C_SEND_RESET_LENGTH_9 = 9, + I2C_SEND_RESET_LENGTH_10 = 10, + DEFAULT_I2C_HW_SPEED = 50, + DEFAULT_I2C_HW_SPEED_100KHZ = 100, + TRANSACTION_TIMEOUT_IN_I2C_CLOCKS = 32, +}; + +#define I2C_HW_ENGINE_COMMON_REG_LIST(id)\ + SRI(SETUP, DC_I2C_DDC, id),\ + SRI(SPEED, DC_I2C_DDC, id),\ + SR(DC_I2C_ARBITRATION),\ + SR(DC_I2C_CONTROL),\ + SR(DC_I2C_SW_STATUS),\ + SR(DC_I2C_TRANSACTION0),\ + SR(DC_I2C_TRANSACTION1),\ + SR(DC_I2C_TRANSACTION2),\ + SR(DC_I2C_TRANSACTION3),\ + SR(DC_I2C_DATA),\ + SR(MICROSECOND_TIME_BASE_DIV) + +#define I2C_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh)\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_ENABLE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_TIME_LIMIT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_CLK_DRIVE_EN, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_DATA_DRIVE_SEL, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_TRANSACTION_DELAY, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_INTRA_BYTE_DELAY, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_NO_QUEUED_SW_GO, mask_sh),\ + I2C_SF(DC_I2C_ARBITRATION, DC_I2C_SW_PRIORITY, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SOFT_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SW_STATUS_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_GO, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_SEND_RESET, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_TRANSACTION_COUNT, mask_sh),\ + I2C_SF(DC_I2C_CONTROL, DC_I2C_DDC_SELECT, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_PRESCALE, mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_THRESHOLD, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STOPPED_ON_NACK, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_TIMEOUT, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_ABORTED, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_DONE, mask_sh),\ + I2C_SF(DC_I2C_SW_STATUS, DC_I2C_SW_STATUS, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP_ON_NACK0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_START0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_RW0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_STOP0, mask_sh),\ + I2C_SF(DC_I2C_TRANSACTION0, DC_I2C_COUNT0, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA_RW, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_DATA, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX, mask_sh),\ + I2C_SF(DC_I2C_DATA, DC_I2C_INDEX_WRITE, mask_sh),\ + I2C_SF(MICROSECOND_TIME_BASE_DIV, XTAL_REF_DIV, mask_sh) + +#define I2C_COMMON_MASK_SH_LIST_DCE110(mask_sh)\ + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh),\ + I2C_SF(DC_I2C_DDC1_SPEED, DC_I2C_DDC1_START_STOP_TIMING_CNTL, mask_sh) + +struct dce_i2c_shift { + uint8_t DC_I2C_DDC1_ENABLE; + uint8_t DC_I2C_DDC1_TIME_LIMIT; + uint8_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint8_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint8_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint8_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint8_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint8_t DC_I2C_SW_DONE_USING_I2C_REG; + uint8_t DC_I2C_NO_QUEUED_SW_GO; + uint8_t DC_I2C_SW_PRIORITY; + uint8_t DC_I2C_SOFT_RESET; + uint8_t DC_I2C_SW_STATUS_RESET; + uint8_t DC_I2C_GO; + uint8_t DC_I2C_SEND_RESET; + uint8_t DC_I2C_TRANSACTION_COUNT; + uint8_t DC_I2C_DDC_SELECT; + uint8_t DC_I2C_DDC1_PRESCALE; + uint8_t DC_I2C_DDC1_THRESHOLD; + uint8_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint8_t DC_I2C_SW_STOPPED_ON_NACK; + uint8_t DC_I2C_SW_TIMEOUT; + uint8_t DC_I2C_SW_ABORTED; + uint8_t DC_I2C_SW_DONE; + uint8_t DC_I2C_SW_STATUS; + uint8_t DC_I2C_STOP_ON_NACK0; + uint8_t DC_I2C_START0; + uint8_t DC_I2C_RW0; + uint8_t DC_I2C_STOP0; + uint8_t DC_I2C_COUNT0; + uint8_t DC_I2C_DATA_RW; + uint8_t DC_I2C_DATA; + uint8_t DC_I2C_INDEX; + uint8_t DC_I2C_INDEX_WRITE; + uint8_t XTAL_REF_DIV; +}; + +struct dce_i2c_mask { + uint32_t DC_I2C_DDC1_ENABLE; + uint32_t DC_I2C_DDC1_TIME_LIMIT; + uint32_t DC_I2C_DDC1_DATA_DRIVE_EN; + uint32_t DC_I2C_DDC1_CLK_DRIVE_EN; + uint32_t DC_I2C_DDC1_DATA_DRIVE_SEL; + uint32_t DC_I2C_DDC1_INTRA_TRANSACTION_DELAY; + uint32_t DC_I2C_DDC1_INTRA_BYTE_DELAY; + uint32_t DC_I2C_SW_DONE_USING_I2C_REG; + uint32_t DC_I2C_NO_QUEUED_SW_GO; + uint32_t DC_I2C_SW_PRIORITY; + uint32_t DC_I2C_SOFT_RESET; + uint32_t DC_I2C_SW_STATUS_RESET; + uint32_t DC_I2C_GO; + uint32_t DC_I2C_SEND_RESET; + uint32_t DC_I2C_TRANSACTION_COUNT; + uint32_t DC_I2C_DDC_SELECT; + uint32_t DC_I2C_DDC1_PRESCALE; + uint32_t DC_I2C_DDC1_THRESHOLD; + uint32_t DC_I2C_DDC1_START_STOP_TIMING_CNTL; + uint32_t DC_I2C_SW_STOPPED_ON_NACK; + uint32_t DC_I2C_SW_TIMEOUT; + uint32_t DC_I2C_SW_ABORTED; + uint32_t DC_I2C_SW_DONE; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_STOP_ON_NACK0; + uint32_t DC_I2C_START0; + uint32_t DC_I2C_RW0; + uint32_t DC_I2C_STOP0; + uint32_t DC_I2C_COUNT0; + uint32_t DC_I2C_DATA_RW; + uint32_t DC_I2C_DATA; + uint32_t DC_I2C_INDEX; + uint32_t DC_I2C_INDEX_WRITE; + uint32_t XTAL_REF_DIV; +}; + +struct dce_i2c_registers { + uint32_t SETUP; + uint32_t SPEED; + uint32_t DC_I2C_ARBITRATION; + uint32_t DC_I2C_CONTROL; + uint32_t DC_I2C_SW_STATUS; + uint32_t DC_I2C_TRANSACTION0; + uint32_t DC_I2C_TRANSACTION1; + uint32_t DC_I2C_TRANSACTION2; + uint32_t DC_I2C_TRANSACTION3; + uint32_t DC_I2C_DATA; + uint32_t MICROSECOND_TIME_BASE_DIV; +}; + +enum dce_i2c_transaction_address_space { + DCE_I2C_TRANSACTION_ADDRESS_SPACE_I2C = 1, + DCE_I2C_TRANSACTION_ADDRESS_SPACE_DPCD +}; + +struct i2c_request_transaction_data { + enum dce_i2c_transaction_action action; + enum i2c_channel_operation_result status; + uint8_t address; + uint32_t length; + uint8_t *data; +}; + +struct dce_i2c_hw { + struct ddc *ddc; + uint32_t original_speed; + uint32_t engine_keep_power_up_count; + uint32_t transaction_count; + uint32_t buffer_used_bytes; + uint32_t buffer_used_write; + uint32_t reference_frequency; + uint32_t default_speed; + uint32_t engine_id; + uint32_t setup_limit; + uint32_t send_reset_length; + uint32_t buffer_size; + struct dc_context *ctx; + + const struct dce_i2c_registers *regs; + const struct dce_i2c_shift *shifts; + const struct dce_i2c_mask *masks; +}; + +void dce_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dce100_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dce112_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +void dcn1_i2c_hw_construct( + struct dce_i2c_hw *dce_i2c_hw, + struct dc_context *ctx, + uint32_t engine_id, + const struct dce_i2c_registers *regs, + const struct dce_i2c_shift *shifts, + const struct dce_i2c_mask *masks); + +bool dce_i2c_submit_command_hw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_hw *dce_i2c_hw); + +struct dce_i2c_hw *acquire_i2c_hw_engine( + struct resource_pool *pool, + struct ddc *ddc); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c new file mode 100644 index 000000000000..f0266694cb56 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.c @@ -0,0 +1,541 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "dce_i2c.h" +#include "dce_i2c_sw.h" +#include "include/gpio_service_interface.h" +#define SCL false +#define SDA true + +void dce_i2c_sw_construct( + struct dce_i2c_sw *dce_i2c_sw, + struct dc_context *ctx) +{ + dce_i2c_sw->ctx = ctx; +} + +static inline bool read_bit_from_ddc( + struct ddc *ddc, + bool data_nor_clock) +{ + uint32_t value = 0; + + if (data_nor_clock) + dal_gpio_get_value(ddc->pin_data, &value); + else + dal_gpio_get_value(ddc->pin_clock, &value); + + return (value != 0); +} + +static inline void write_bit_to_ddc( + struct ddc *ddc, + bool data_nor_clock, + bool bit) +{ + uint32_t value = bit ? 1 : 0; + + if (data_nor_clock) + dal_gpio_set_value(ddc->pin_data, value); + else + dal_gpio_set_value(ddc->pin_clock, value); +} + +static void release_engine_dce_sw( + struct resource_pool *pool, + struct dce_i2c_sw *dce_i2c_sw) +{ + dal_ddc_close(dce_i2c_sw->ddc); + dce_i2c_sw->ddc = NULL; +} + +static bool get_hw_supported_ddc_line( + struct ddc *ddc, + enum gpio_ddc_line *line) +{ + enum gpio_ddc_line line_found; + + *line = GPIO_DDC_LINE_UNKNOWN; + + if (!ddc) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!ddc->hw_info.hw_supported) + return false; + + line_found = dal_ddc_get_line(ddc); + + if (line_found >= GPIO_DDC_LINE_COUNT) + return false; + + *line = line_found; + + return true; +} +static bool wait_for_scl_high_sw( + struct dc_context *ctx, + struct ddc *ddc, + uint16_t clock_delay_div_4) +{ + uint32_t scl_retry = 0; + uint32_t scl_retry_max = I2C_SW_TIMEOUT_DELAY / clock_delay_div_4; + + udelay(clock_delay_div_4); + + do { + if (read_bit_from_ddc(ddc, SCL)) + return true; + + udelay(clock_delay_div_4); + + ++scl_retry; + } while (scl_retry <= scl_retry_max); + + return false; +} +static bool write_byte_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t byte) +{ + int32_t shift = 7; + bool ack; + + /* bits are transmitted serially, starting from MSB */ + + do { + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, (byte >> shift) & 1); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + --shift; + } while (shift >= 0); + + /* The display sends ACK by preventing the SDA from going high + * after the SCL pulse we use to send our last data bit. + * If the SDA goes high after that bit, it's a NACK + */ + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + /* read ACK bit */ + + ack = !read_bit_from_ddc(ddc_handle, SDA); + + udelay(clock_delay_div_4 << 1); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + return ack; +} + +static bool read_byte_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t *byte, + bool more) +{ + int32_t shift = 7; + + uint8_t data = 0; + + /* The data bits are read from MSB to LSB; + * bit is read while SCL is high + */ + + do { + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + if (read_bit_from_ddc(ddc_handle, SDA)) + data |= (1 << shift); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4 << 1); + + --shift; + } while (shift >= 0); + + /* read only whole byte */ + + *byte = data; + + udelay(clock_delay_div_4); + + /* send the acknowledge bit: + * SDA low means ACK, SDA high means NACK + */ + + write_bit_to_ddc(ddc_handle, SDA, !more); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, true); + + udelay(clock_delay_div_4); + + return true; +} +static bool stop_sync_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications stop signal is: + * the SDA going high from low, while the SCL is high. + */ + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + return false; + + write_bit_to_ddc(ddc_handle, SDA, true); + + do { + udelay(clock_delay_div_4); + + if (read_bit_from_ddc(ddc_handle, SDA)) + return true; + + ++retry; + } while (retry <= 2); + + return false; +} +static bool i2c_write_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + const uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, data[i])) + return false; + ++i; + } + + return true; +} + +static bool i2c_read_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4, + uint8_t address, + uint32_t length, + uint8_t *data) +{ + uint32_t i = 0; + + if (!write_byte_sw(ctx, ddc_handle, clock_delay_div_4, address)) + return false; + + while (i < length) { + if (!read_byte_sw(ctx, ddc_handle, clock_delay_div_4, data + i, + i < length - 1)) + return false; + ++i; + } + + return true; +} + + + +static bool start_sync_sw( + struct dc_context *ctx, + struct ddc *ddc_handle, + uint16_t clock_delay_div_4) +{ + uint32_t retry = 0; + + /* The I2C communications start signal is: + * the SDA going low from high, while the SCL is high. + */ + + write_bit_to_ddc(ddc_handle, SCL, true); + + udelay(clock_delay_div_4); + + do { + write_bit_to_ddc(ddc_handle, SDA, true); + + if (!read_bit_from_ddc(ddc_handle, SDA)) { + ++retry; + continue; + } + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, true); + + if (!wait_for_scl_high_sw(ctx, ddc_handle, clock_delay_div_4)) + break; + + write_bit_to_ddc(ddc_handle, SDA, false); + + udelay(clock_delay_div_4); + + write_bit_to_ddc(ddc_handle, SCL, false); + + udelay(clock_delay_div_4); + + return true; + } while (retry <= I2C_SW_RETRIES); + + return false; +} + +void dce_i2c_sw_engine_set_speed( + struct dce_i2c_sw *engine, + uint32_t speed) +{ + ASSERT(speed); + + engine->speed = speed ? speed : DCE_I2C_DEFAULT_I2C_SW_SPEED; + + engine->clock_delay = 1000 / engine->speed; + + if (engine->clock_delay < 12) + engine->clock_delay = 12; +} + +bool dce_i2c_sw_engine_acquire_engine( + struct dce_i2c_sw *engine, + struct ddc *ddc) +{ + enum gpio_result result; + + result = dal_ddc_open(ddc, GPIO_MODE_FAST_OUTPUT, + GPIO_DDC_CONFIG_TYPE_MODE_I2C); + + if (result != GPIO_RESULT_OK) + return false; + + engine->ddc = ddc; + + return true; +} +bool dce_i2c_engine_acquire_sw( + struct dce_i2c_sw *dce_i2c_sw, + struct ddc *ddc_handle) +{ + uint32_t counter = 0; + bool result; + + do { + + result = dce_i2c_sw_engine_acquire_engine( + dce_i2c_sw, ddc_handle); + + if (result) + break; + + /* i2c_engine is busy by VBios, lets wait and retry */ + + udelay(10); + + ++counter; + } while (counter < 2); + + return result; +} + + + + +void dce_i2c_sw_engine_submit_channel_request( + struct dce_i2c_sw *engine, + struct i2c_request_transaction_data *req) +{ + struct ddc *ddc = engine->ddc; + uint16_t clock_delay_div_4 = engine->clock_delay >> 2; + + /* send sync (start / repeated start) */ + + bool result = start_sync_sw(engine->ctx, ddc, clock_delay_div_4); + + /* process payload */ + + if (result) { + switch (req->action) { + case DCE_I2C_TRANSACTION_ACTION_I2C_WRITE: + case DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT: + result = i2c_write_sw(engine->ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + case DCE_I2C_TRANSACTION_ACTION_I2C_READ: + case DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT: + result = i2c_read_sw(engine->ctx, ddc, clock_delay_div_4, + req->address, req->length, req->data); + break; + default: + result = false; + break; + } + } + + /* send stop if not 'mot' or operation failed */ + + if (!result || + (req->action == DCE_I2C_TRANSACTION_ACTION_I2C_WRITE) || + (req->action == DCE_I2C_TRANSACTION_ACTION_I2C_READ)) + if (!stop_sync_sw(engine->ctx, ddc, clock_delay_div_4)) + result = false; + + req->status = result ? + I2C_CHANNEL_OPERATION_SUCCEEDED : + I2C_CHANNEL_OPERATION_FAILED; +} +bool dce_i2c_sw_engine_submit_payload( + struct dce_i2c_sw *engine, + struct i2c_payload *payload, + bool middle_of_transaction) +{ + struct i2c_request_transaction_data request; + + if (!payload->write) + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_READ_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_READ; + else + request.action = middle_of_transaction ? + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE_MOT : + DCE_I2C_TRANSACTION_ACTION_I2C_WRITE; + + request.address = (uint8_t) ((payload->address << 1) | !payload->write); + request.length = payload->length; + request.data = payload->data; + + dce_i2c_sw_engine_submit_channel_request(engine, &request); + + if ((request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY) || + (request.status == I2C_CHANNEL_OPERATION_FAILED)) + return false; + + return true; +} +bool dce_i2c_submit_command_sw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_sw *dce_i2c_sw) +{ + uint8_t index_of_payload = 0; + bool result; + + dce_i2c_sw_engine_set_speed(dce_i2c_sw, cmd->speed); + + result = true; + + while (index_of_payload < cmd->number_of_payloads) { + bool mot = (index_of_payload != cmd->number_of_payloads - 1); + + struct i2c_payload *payload = cmd->payloads + index_of_payload; + + if (!dce_i2c_sw_engine_submit_payload( + dce_i2c_sw, payload, mot)) { + result = false; + break; + } + + ++index_of_payload; + } + + release_engine_dce_sw(pool, dce_i2c_sw); + + return result; +} +struct dce_i2c_sw *dce_i2c_acquire_i2c_sw_engine( + struct resource_pool *pool, + struct ddc *ddc) +{ + enum gpio_ddc_line line; + struct dce_i2c_sw *engine = NULL; + + if (get_hw_supported_ddc_line(ddc, &line)) + engine = pool->sw_i2cs[line]; + + if (!engine) + return NULL; + + if (!dce_i2c_engine_acquire_sw(engine, ddc)) + return NULL; + + return engine; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h new file mode 100644 index 000000000000..5bbcdd455614 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_i2c_sw.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DCE_I2C_SW_H__ +#define __DCE_I2C_SW_H__ + +enum { + DCE_I2C_DEFAULT_I2C_SW_SPEED = 50, + I2C_SW_RETRIES = 10, + I2C_SW_TIMEOUT_DELAY = 3000, +}; + +struct dce_i2c_sw { + struct ddc *ddc; + struct dc_context *ctx; + uint32_t clock_delay; + uint32_t speed; +}; + +void dce_i2c_sw_construct( + struct dce_i2c_sw *dce_i2c_sw, + struct dc_context *ctx); + +bool dce_i2c_submit_command_sw( + struct resource_pool *pool, + struct ddc *ddc, + struct i2c_command *cmd, + struct dce_i2c_sw *dce_i2c_sw); + +struct dce_i2c_sw *dce_i2c_acquire_i2c_sw_engine( + struct resource_pool *pool, + struct ddc *ddc); + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c index eff7d22d78fb..4942590e8b9c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -102,6 +102,7 @@ static const struct link_encoder_funcs dce110_lnk_enc_funcs = { .enable_tmds_output = dce110_link_encoder_enable_tmds_output, .enable_dp_output = dce110_link_encoder_enable_dp_output, .enable_dp_mst_output = dce110_link_encoder_enable_dp_mst_output, + .enable_lvds_output = dce110_link_encoder_enable_lvds_output, .disable_output = dce110_link_encoder_disable_output, .dp_set_lane_settings = dce110_link_encoder_dp_set_lane_settings, .dp_set_phy_pattern = dce110_link_encoder_dp_set_phy_pattern, @@ -814,6 +815,7 @@ bool dce110_link_encoder_validate_output_with_stream( enc110, &stream->timing); break; case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_LVDS: is_valid = (stream->timing. pixel_encoding == PIXEL_ENCODING_RGB) ? true : false; @@ -955,6 +957,38 @@ void dce110_link_encoder_enable_tmds_output( } } +/* TODO: still need depth or just pass in adjusted pixel clock? */ +void dce110_link_encoder_enable_lvds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + uint32_t pixel_clock) +{ + struct dce110_link_encoder *enc110 = TO_DCE110_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + cntl.connector_obj_id = enc110->base.connector; + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc110->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_LVDS; + cntl.lanes_number = 4; + + cntl.hpd_sel = enc110->base.hpd_source; + + cntl.pixel_clock = pixel_clock; + + result = link_transmitter_control(enc110, &cntl); + + if (result != BP_RESULT_OK) { + DC_LOG_ERROR("%s: Failed to execute VBIOS command table!\n", + __func__); + BREAK_TO_DEBUGGER(); + } +} + /* enables DP PHY output */ void dce110_link_encoder_enable_dp_output( struct link_encoder *enc, diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h index 347069461a22..3c9368df4093 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h @@ -225,6 +225,12 @@ void dce110_link_encoder_enable_dp_mst_output( const struct dc_link_settings *link_settings, enum clock_source_id clock_source); +/* enables LVDS PHY output */ +void dce110_link_encoder_enable_lvds_output( + struct link_encoder *enc, + enum clock_source_id clock_source, + uint32_t pixel_clock); + /* disable PHY output */ void dce110_link_encoder_disable_output( struct link_encoder *enc, diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c index 91642e684858..c47c81883d3c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -674,6 +674,28 @@ static void dce110_stream_encoder_dvi_set_stream_attribute( dce110_stream_encoder_set_stream_attribute_helper(enc110, crtc_timing); } +/* setup stream encoder in LVDS mode */ +static void dce110_stream_encoder_lvds_set_stream_attribute( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing) +{ + struct dce110_stream_encoder *enc110 = DCE110STRENC_FROM_STRENC(enc); + struct bp_encoder_control cntl = {0}; + + cntl.action = ENCODER_CONTROL_SETUP; + cntl.engine_id = enc110->base.id; + cntl.signal = SIGNAL_TYPE_LVDS; + cntl.enable_dp_audio = false; + cntl.pixel_clock = crtc_timing->pix_clk_khz; + cntl.lanes_number = LANE_COUNT_FOUR; + + if (enc110->base.bp->funcs->encoder_control( + enc110->base.bp, &cntl) != BP_RESULT_OK) + return; + + ASSERT(crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB); +} + static void dce110_stream_encoder_set_mst_bandwidth( struct stream_encoder *enc, struct fixed31_32 avg_time_slots_per_mtp) @@ -1564,6 +1586,8 @@ static const struct stream_encoder_funcs dce110_str_enc_funcs = { dce110_stream_encoder_hdmi_set_stream_attribute, .dvi_set_stream_attribute = dce110_stream_encoder_dvi_set_stream_attribute, + .lvds_set_stream_attribute = + dce110_stream_encoder_lvds_set_stream_attribute, .set_mst_bandwidth = dce110_stream_encoder_set_mst_bandwidth, .update_hdmi_info_packets = diff --git a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c index 3f76e6019546..b1cc38827f09 100644 --- a/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce100/dce100_resource.c @@ -54,6 +54,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_abm.h" +#include "dce/dce_i2c.h" #ifndef mmMC_HUB_RDREQ_DMIF_LIMIT #include "gmc/gmc_8_2_d.h" @@ -371,7 +372,8 @@ static const struct resource_caps res_cap = { .num_timing_generator = 6, .num_audio = 6, .num_stream_encoder = 6, - .num_pll = 3 + .num_pll = 3, + .num_ddc = 6, }; #define CTX ctx @@ -602,7 +604,40 @@ struct aux_engine *dce100_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +struct dce_i2c_hw *dce100_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + dce100_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} struct clock_source *dce100_clock_source_create( struct dc_context *ctx, struct dc_bios *bios, @@ -658,7 +693,14 @@ static void destruct(struct dce110_resource_pool *pool) if (pool->base.engines[i] != NULL) dce110_engine_destroy(&pool->base.engines[i]); - + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.stream_enc_count; i++) { @@ -963,6 +1005,9 @@ static bool construct( "DC: failed to create output pixel processor!\n"); goto res_create_fail; } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { pool->base.engines[i] = dce100_aux_engine_create(ctx, i); if (pool->base.engines[i] == NULL) { BREAK_TO_DEBUGGER(); @@ -970,6 +1015,14 @@ static bool construct( "DC:failed to create aux engine!!\n"); goto res_create_fail; } + pool->base.hw_i2cs[i] = dce100_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; } dc->caps.max_planes = pool->base.pipe_count; diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index 14384d9675a8..dc1eed5ba996 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -1286,6 +1286,8 @@ static enum dc_status dce110_enable_stream_timing( struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. pipe_ctx[pipe_ctx->pipe_idx]; struct tg_color black_color = {0}; + struct drr_params params = {0}; + unsigned int event_triggers = 0; if (!pipe_ctx_old->stream) { @@ -1315,9 +1317,19 @@ static enum dc_status dce110_enable_stream_timing( &stream->timing, true); - pipe_ctx->stream_res.tg->funcs->set_static_screen_control( - pipe_ctx->stream_res.tg, - 0x182); + params.vertical_total_min = stream->adjust.v_total_min; + params.vertical_total_max = stream->adjust.v_total_max; + if (pipe_ctx->stream_res.tg->funcs->set_drr) + pipe_ctx->stream_res.tg->funcs->set_drr( + pipe_ctx->stream_res.tg, ¶ms); + + // DRR should set trigger event to monitor surface update event + if (stream->adjust.v_total_min != 0 && + stream->adjust.v_total_max != 0) + event_triggers = 0x80; + if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control) + pipe_ctx->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx->stream_res.tg, event_triggers); } if (!pipe_ctx_old->stream) { @@ -1328,8 +1340,6 @@ static enum dc_status dce110_enable_stream_timing( } } - - return DC_OK; } @@ -1339,8 +1349,6 @@ static enum dc_status apply_single_controller_ctx_to_hw( struct dc *dc) { struct dc_stream_state *stream = pipe_ctx->stream; - struct pipe_ctx *pipe_ctx_old = &dc->current_state->res_ctx. - pipe_ctx[pipe_ctx->pipe_idx]; if (pipe_ctx->stream_res.audio != NULL) { struct audio_output audio_output; @@ -1395,46 +1403,12 @@ static enum dc_status apply_single_controller_ctx_to_hw( stream->timing.display_color_depth, pipe_ctx->stream->signal); - if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) - stream->sink->link->link_enc->funcs->setup( - stream->sink->link->link_enc, - pipe_ctx->stream->signal); - - if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) - pipe_ctx->stream_res.stream_enc->funcs->setup_stereo_sync( - pipe_ctx->stream_res.stream_enc, - pipe_ctx->stream_res.tg->inst, - stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE); - - pipe_ctx->stream_res.opp->funcs->opp_program_fmt( pipe_ctx->stream_res.opp, &stream->bit_depth_params, &stream->clamping); - if (dc_is_dp_signal(pipe_ctx->stream->signal)) - pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute( - pipe_ctx->stream_res.stream_enc, - &stream->timing, - stream->output_color_space); - - if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) - pipe_ctx->stream_res.stream_enc->funcs->hdmi_set_stream_attribute( - pipe_ctx->stream_res.stream_enc, - &stream->timing, - stream->phy_pix_clk, - pipe_ctx->stream_res.audio != NULL); - - if (dc_is_dvi_signal(pipe_ctx->stream->signal)) - pipe_ctx->stream_res.stream_enc->funcs->dvi_set_stream_attribute( - pipe_ctx->stream_res.stream_enc, - &stream->timing, - (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? - true : false); - - resource_build_info_frame(pipe_ctx); - dce110_update_info_frame(pipe_ctx); - if (!pipe_ctx_old->stream) + if (!stream->dpms_off) core_link_enable_stream(context, pipe_ctx); pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; @@ -1583,32 +1557,40 @@ static struct dc_link *get_link_for_edp_not_in_use( */ void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context) { + int i; struct dc_link *edp_link_to_turnoff = NULL; struct dc_link *edp_link = get_link_for_edp(dc); - bool can_eDP_fast_boot_optimize = false; + bool can_edp_fast_boot_optimize = false; + bool apply_edp_fast_boot_optimization = false; if (edp_link) { /* this seems to cause blank screens on DCE8 */ if ((dc->ctx->dce_version == DCE_VERSION_8_0) || (dc->ctx->dce_version == DCE_VERSION_8_1) || (dc->ctx->dce_version == DCE_VERSION_8_3)) - can_eDP_fast_boot_optimize = false; + can_edp_fast_boot_optimize = false; else - can_eDP_fast_boot_optimize = + can_edp_fast_boot_optimize = edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc); } - if (can_eDP_fast_boot_optimize) { + if (can_edp_fast_boot_optimize) edp_link_to_turnoff = get_link_for_edp_not_in_use(dc, context); - /* if OS doesn't light up eDP and eDP link is available, we want to disable - * If resume from S4/S5, should optimization. - */ - if (!edp_link_to_turnoff) - dc->apply_edp_fast_boot_optimization = true; + /* if OS doesn't light up eDP and eDP link is available, we want to disable + * If resume from S4/S5, should optimization. + */ + if (can_edp_fast_boot_optimize && !edp_link_to_turnoff) { + /* Find eDP stream and set optimization flag */ + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->signal == SIGNAL_TYPE_EDP) { + context->streams[i]->apply_edp_fast_boot_optimization = true; + apply_edp_fast_boot_optimization = true; + } + } } - if (!dc->apply_edp_fast_boot_optimization) { + if (!apply_edp_fast_boot_optimization) { if (edp_link_to_turnoff) { /*turn off backlight before DP_blank and encoder powered down*/ dc->hwss.edp_backlight_control(edp_link_to_turnoff, false); @@ -1719,16 +1701,24 @@ static void set_drr(struct pipe_ctx **pipe_ctx, { int i = 0; struct drr_params params = {0}; + // DRR should set trigger event to monitor surface update event + unsigned int event_triggers = 0x80; params.vertical_total_max = vmax; params.vertical_total_min = vmin; /* TODO: If multiple pipes are to be supported, you need - * some GSL stuff + * some GSL stuff. Static screen triggers may be programmed differently + * as well. */ - for (i = 0; i < num_pipes; i++) { - pipe_ctx[i]->stream_res.tg->funcs->set_drr(pipe_ctx[i]->stream_res.tg, ¶ms); + pipe_ctx[i]->stream_res.tg->funcs->set_drr( + pipe_ctx[i]->stream_res.tg, ¶ms); + + if (vmax != 0 && vmin != 0) + pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx[i]->stream_res.tg, + event_triggers); } } @@ -2566,6 +2556,7 @@ void dce110_set_bandwidth( bool decrease_allowed) { struct dc_clocks req_clks; + struct dccg *dccg = dc->res_pool->dccg; req_clks.dispclk_khz = context->bw.dce.dispclk_khz; req_clks.phyclk_khz = get_max_pixel_clock_for_all_paths(dc, context); @@ -2575,8 +2566,15 @@ void dce110_set_bandwidth( else dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool); - dc->res_pool->dccg->funcs->update_clocks( - dc->res_pool->dccg, + if (dccg->funcs->update_dfs_bypass) + dccg->funcs->update_dfs_bypass( + dccg, + dc, + context, + req_clks.dispclk_khz); + + dccg->funcs->update_clocks( + dccg, &req_clks, decrease_allowed); pplib_apply_display_requirements(dc, context); diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c index e5e9e92521e9..b44cc7042249 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c @@ -52,6 +52,7 @@ #include "dce/dce_aux.h" #include "dce/dce_abm.h" #include "dce/dce_dmcu.h" +#include "dce/dce_i2c.h" #define DC_LOGGER \ dc->ctx->logger @@ -377,6 +378,7 @@ static const struct resource_caps carrizo_resource_cap = { .num_audio = 3, .num_stream_encoder = 3, .num_pll = 2, + .num_ddc = 3, }; static const struct resource_caps stoney_resource_cap = { @@ -385,6 +387,7 @@ static const struct resource_caps stoney_resource_cap = { .num_audio = 3, .num_stream_encoder = 3, .num_pll = 2, + .num_ddc = 3, }; #define CTX ctx @@ -620,7 +623,40 @@ struct aux_engine *dce110_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +struct dce_i2c_hw *dce110_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + dce100_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} struct clock_source *dce110_clock_source_create( struct dc_context *ctx, struct dc_bios *bios, @@ -687,7 +723,14 @@ static void destruct(struct dce110_resource_pool *pool) if (pool->base.engines[i] != NULL) dce110_engine_destroy(&pool->base.engines[i]); - + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.stream_enc_count; i++) { @@ -755,6 +798,9 @@ static void get_pixel_clock_parameters( if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) { pixel_clk_params->requested_pix_clk = pixel_clk_params->requested_pix_clk / 2; } + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + pixel_clk_params->requested_pix_clk *= 2; + } void dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx) @@ -1295,7 +1341,9 @@ static bool construct( "DC: failed to create output pixel processor!\n"); goto res_create_fail; } + } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { pool->base.engines[i] = dce110_aux_engine_create(ctx, i); if (pool->base.engines[i] == NULL) { BREAK_TO_DEBUGGER(); @@ -1303,6 +1351,14 @@ static bool construct( "DC:failed to create aux engine!!\n"); goto res_create_fail; } + pool->base.hw_i2cs[i] = dce110_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; } dc->fbc_compressor = dce110_compressor_create(ctx); diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c index 288129343c77..0f8332ea1160 100644 --- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c @@ -50,6 +50,7 @@ #include "dce/dce_abm.h" #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" +#include "dce/dce_i2c.h" #include "reg_helper.h" @@ -383,6 +384,7 @@ static const struct resource_caps polaris_10_resource_cap = { .num_audio = 6, .num_stream_encoder = 6, .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ + .num_ddc = 6, }; static const struct resource_caps polaris_11_resource_cap = { @@ -390,6 +392,7 @@ static const struct resource_caps polaris_11_resource_cap = { .num_audio = 5, .num_stream_encoder = 5, .num_pll = 8, /* why 8? 6 combo PHY PLL + 2 regular PLLs? */ + .num_ddc = 5, }; #define CTX ctx @@ -620,7 +623,40 @@ struct aux_engine *dce112_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +struct dce_i2c_hw *dce112_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + dce112_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} struct clock_source *dce112_clock_source_create( struct dc_context *ctx, struct dc_bios *bios, @@ -634,7 +670,7 @@ struct clock_source *dce112_clock_source_create( if (!clk_src) return NULL; - if (dce110_clk_src_construct(clk_src, ctx, bios, id, + if (dce112_clk_src_construct(clk_src, ctx, bios, id, regs, &cs_shift, &cs_mask)) { clk_src->base.dp_clk_src = dp_clk_src; return &clk_src->base; @@ -676,7 +712,14 @@ static void destruct(struct dce110_resource_pool *pool) kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); pool->base.timing_generators[i] = NULL; } - + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.stream_enc_count; i++) { @@ -1245,6 +1288,9 @@ static bool construct( "DC:failed to create output pixel processor!\n"); goto res_create_fail; } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { pool->base.engines[i] = dce112_aux_engine_create(ctx, i); if (pool->base.engines[i] == NULL) { BREAK_TO_DEBUGGER(); @@ -1252,6 +1298,14 @@ static bool construct( "DC:failed to create aux engine!!\n"); goto res_create_fail; } + pool->base.hw_i2cs[i] = dce112_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; } if (!resource_construct(num_virtual_links, dc, &pool->base, diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c index d43f37d99c7d..59055801af44 100644 --- a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -54,6 +54,7 @@ #include "dce/dce_abm.h" #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" +#include "dce/dce_i2c.h" #include "dce/dce_12_0_offset.h" #include "dce/dce_12_0_sh_mask.h" @@ -392,7 +393,40 @@ struct aux_engine *dce120_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; +struct dce_i2c_hw *dce120_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce112_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} static const struct bios_registers bios_regs = { .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) }; @@ -402,6 +436,7 @@ static const struct resource_caps res_cap = { .num_audio = 7, .num_stream_encoder = 6, .num_pll = 6, + .num_ddc = 6, }; static const struct dc_debug_options debug_defaults = { @@ -421,7 +456,7 @@ struct clock_source *dce120_clock_source_create( if (!clk_src) return NULL; - if (dce110_clk_src_construct(clk_src, ctx, bios, id, + if (dce112_clk_src_construct(clk_src, ctx, bios, id, regs, &cs_shift, &cs_mask)) { clk_src->base.dp_clk_src = dp_clk_src; return &clk_src->base; @@ -501,7 +536,14 @@ static void destruct(struct dce110_resource_pool *pool) if (pool->base.engines[i] != NULL) dce110_engine_destroy(&pool->base.engines[i]); - + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.audio_count; i++) { @@ -957,6 +999,7 @@ static bool construct( goto res_create_fail; } + irq_init_data.ctx = dc->ctx; pool->base.irqs = dal_irq_service_dce120_create(&irq_init_data); if (!pool->base.irqs) @@ -1020,18 +1063,29 @@ static bool construct( dm_error( "DC: failed to create output pixel processor!\n"); } - pool->base.engines[i] = dce120_aux_engine_create(ctx, i); - if (pool->base.engines[i] == NULL) { - BREAK_TO_DEBUGGER(); - dm_error( - "DC:failed to create aux engine!!\n"); - goto res_create_fail; - } /* check next valid pipe */ j++; } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce120_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce120_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + /* valid pipe num */ pool->base.pipe_count = j; pool->base.timing_generator_count = j; diff --git a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c index 604c62969ead..1dc590ccc5f9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce80/dce80_resource.c @@ -56,6 +56,7 @@ #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" #include "dce/dce_abm.h" +#include "dce/dce_i2c.h" /* TODO remove this include */ #ifndef mmMC_HUB_RDREQ_DMIF_LIMIT @@ -366,6 +367,7 @@ static const struct resource_caps res_cap = { .num_audio = 6, .num_stream_encoder = 6, .num_pll = 3, + .num_ddc = 6, }; static const struct resource_caps res_cap_81 = { @@ -373,6 +375,7 @@ static const struct resource_caps res_cap_81 = { .num_audio = 7, .num_stream_encoder = 7, .num_pll = 3, + .num_ddc = 6, }; static const struct resource_caps res_cap_83 = { @@ -380,6 +383,7 @@ static const struct resource_caps res_cap_83 = { .num_audio = 6, .num_stream_encoder = 6, .num_pll = 2, + .num_ddc = 2, }; static const struct dce_dmcu_registers dmcu_regs = { @@ -480,7 +484,54 @@ struct aux_engine *dce80_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +struct dce_i2c_hw *dce80_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} + +struct dce_i2c_sw *dce80_i2c_sw_create( + struct dc_context *ctx) +{ + struct dce_i2c_sw *dce_i2c_sw = + kzalloc(sizeof(struct dce_i2c_sw), GFP_KERNEL); + if (!dce_i2c_sw) + return NULL; + + dce_i2c_sw_construct(dce_i2c_sw, ctx); + + return dce_i2c_sw; +} static struct stream_encoder *dce80_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -691,6 +742,14 @@ static void destruct(struct dce110_resource_pool *pool) if (pool->base.engines[i] != NULL) dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.stream_enc_count; i++) { @@ -887,6 +946,7 @@ static bool dce80_construct( BREAK_TO_DEBUGGER(); goto res_create_fail; } + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) pool->base.dccg->max_clks_state = static_clk_info.max_clocks_state; @@ -935,7 +995,9 @@ static bool dce80_construct( dm_error("DC: failed to create output pixel processor!\n"); goto res_create_fail; } + } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { pool->base.engines[i] = dce80_aux_engine_create(ctx, i); if (pool->base.engines[i] == NULL) { BREAK_TO_DEBUGGER(); @@ -943,6 +1005,20 @@ static bool dce80_construct( "DC:failed to create aux engine!!\n"); goto res_create_fail; } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } } dc->caps.max_planes = pool->base.pipe_count; @@ -1131,6 +1207,30 @@ static bool dce81_construct( } } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce80_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + dc->caps.max_planes = pool->base.pipe_count; dc->caps.disable_dp_clk_share = true; @@ -1313,6 +1413,30 @@ static bool dce83_construct( } } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce80_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce80_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = dce80_i2c_sw_create(ctx); + if (pool->base.sw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create sw i2c!!\n"); + goto res_create_fail; + } + } + dc->caps.max_planes = pool->base.pipe_count; dc->caps.disable_dp_clk_share = true; diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/Makefile b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile index 84f52c63d95c..032f872be89c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile @@ -22,7 +22,7 @@ # # Makefile for DCN. -DCN10 = dcn10_resource.o dcn10_ipp.o dcn10_hw_sequencer.o \ +DCN10 = dcn10_resource.o dcn10_ipp.o dcn10_hw_sequencer.o dcn10_hw_sequencer_debug.o \ dcn10_dpp.o dcn10_opp.o dcn10_optc.o \ dcn10_hubp.o dcn10_mpc.o \ dcn10_dpp_dscl.o dcn10_dpp_cm.o dcn10_cm_common.o \ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c index bf8b68f8db4f..dcb3c5530236 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.c @@ -103,6 +103,8 @@ void dpp_read_state(struct dpp *dpp_base, { struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); + REG_GET(DPP_CONTROL, + DPP_CLOCK_ENABLE, &s->is_enabled); REG_GET(CM_IGAM_CONTROL, CM_IGAM_LUT_MODE, &s->igam_lut_mode); REG_GET(CM_IGAM_CONTROL, @@ -114,12 +116,14 @@ void dpp_read_state(struct dpp *dpp_base, REG_GET(CM_GAMUT_REMAP_CONTROL, CM_GAMUT_REMAP_MODE, &s->gamut_remap_mode); - s->gamut_remap_c11_c12 = REG_READ(CM_GAMUT_REMAP_C11_C12); - s->gamut_remap_c13_c14 = REG_READ(CM_GAMUT_REMAP_C13_C14); - s->gamut_remap_c21_c22 = REG_READ(CM_GAMUT_REMAP_C21_C22); - s->gamut_remap_c23_c24 = REG_READ(CM_GAMUT_REMAP_C23_C24); - s->gamut_remap_c31_c32 = REG_READ(CM_GAMUT_REMAP_C31_C32); - s->gamut_remap_c33_c34 = REG_READ(CM_GAMUT_REMAP_C33_C34); + if (s->gamut_remap_mode) { + s->gamut_remap_c11_c12 = REG_READ(CM_GAMUT_REMAP_C11_C12); + s->gamut_remap_c13_c14 = REG_READ(CM_GAMUT_REMAP_C13_C14); + s->gamut_remap_c21_c22 = REG_READ(CM_GAMUT_REMAP_C21_C22); + s->gamut_remap_c23_c24 = REG_READ(CM_GAMUT_REMAP_C23_C24); + s->gamut_remap_c31_c32 = REG_READ(CM_GAMUT_REMAP_C31_C32); + s->gamut_remap_c33_c34 = REG_READ(CM_GAMUT_REMAP_C33_C34); + } } /* Program gamut remap in bypass mode */ @@ -442,10 +446,12 @@ void dpp1_set_cursor_position( struct dpp *dpp_base, const struct dc_cursor_position *pos, const struct dc_cursor_mi_param *param, - uint32_t width) + uint32_t width, + uint32_t height) { struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); int src_x_offset = pos->x - pos->x_hotspot - param->viewport.x; + int src_y_offset = pos->y - pos->y_hotspot - param->viewport.y; uint32_t cur_en = pos->enable ? 1 : 0; if (src_x_offset >= (int)param->viewport.width) @@ -454,6 +460,12 @@ void dpp1_set_cursor_position( if (src_x_offset + (int)width <= 0) cur_en = 0; /* not visible beyond left edge*/ + if (src_y_offset >= (int)param->viewport.height) + cur_en = 0; /* not visible beyond bottom edge*/ + + if (src_y_offset < 0) + cur_en = 0; /* not visible beyond top edge*/ + REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, cur_en); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h index e2889e61b18c..282e22f9b175 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_dpp.h @@ -1374,7 +1374,8 @@ void dpp1_set_cursor_position( struct dpp *dpp_base, const struct dc_cursor_position *pos, const struct dc_cursor_mi_param *param, - uint32_t width); + uint32_t width, + uint32_t height); void dpp1_cnv_set_optional_cursor_attributes( struct dpp *dpp_base, diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c index 2138cd3c5d1d..74132a1f3046 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.c @@ -313,10 +313,24 @@ bool hubp1_program_surface_flip_and_addr( { struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); - /* program flip type */ - REG_SET(DCSURF_FLIP_CONTROL, 0, + + //program flip type + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, flip_immediate); + + if (address->type == PLN_ADDR_TYPE_GRPH_STEREO) { + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, 0x1); + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, 0x1); + + } else { + // turn off stereo if not in stereo + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, 0x0); + REG_UPDATE(DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, 0x0); + } + + + /* HW automatically latch rest of address register on write to * DCSURF_PRIMARY_SURFACE_ADDRESS if SURFACE_UPDATE_LOCK is not used * @@ -485,7 +499,8 @@ void hubp1_program_surface_config( union plane_size *plane_size, enum dc_rotation_angle rotation, struct dc_plane_dcc_param *dcc, - bool horizontal_mirror) + bool horizontal_mirror, + unsigned int compat_level) { hubp1_dcc_control(hubp, dcc->enable, dcc->grph.independent_64b_blks); hubp1_program_tiling(hubp, tiling_info, format); @@ -959,6 +974,9 @@ void hubp1_read_state(struct hubp *hubp) REG_GET(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, SURFACE_EARLIEST_INUSE_ADDRESS_HIGH, &s->inuse_addr_hi); + REG_GET(DCSURF_SURFACE_EARLIEST_INUSE, + SURFACE_EARLIEST_INUSE_ADDRESS, &s->inuse_addr_lo); + REG_GET_2(DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_WIDTH, &s->viewport_width, PRI_VIEWPORT_HEIGHT, &s->viewport_height); @@ -1069,6 +1087,7 @@ void hubp1_cursor_set_position( { struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); int src_x_offset = pos->x - pos->x_hotspot - param->viewport.x; + int src_y_offset = pos->y - pos->y_hotspot - param->viewport.y; int x_hotspot = pos->x_hotspot; int y_hotspot = pos->y_hotspot; uint32_t dst_x_offset; @@ -1112,6 +1131,12 @@ void hubp1_cursor_set_position( if (src_x_offset + (int)hubp->curs_attr.width <= 0) cur_en = 0; /* not visible beyond left edge*/ + if (src_y_offset >= (int)param->viewport.height) + cur_en = 0; /* not visible beyond bottom edge*/ + + if (src_y_offset < 0) //+ (int)hubp->curs_attr.height + cur_en = 0; /* not visible beyond top edge*/ + if (cur_en && REG_READ(CURSOR_SURFACE_ADDRESS) == 0) hubp->funcs->set_cursor_attributes(hubp, &hubp->curs_attr); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h index f689feace82d..4890273b632b 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubp.h @@ -270,6 +270,8 @@ HUBP_SF(HUBPREQ0_DCSURF_SURFACE_PITCH_C, META_PITCH_C, mask_sh),\ HUBP_SF(HUBP0_DCSURF_SURFACE_CONFIG, SURFACE_PIXEL_FORMAT, mask_sh),\ HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_TYPE, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_MODE_FOR_STEREOSYNC, mask_sh),\ + HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_IN_STEREOSYNC, mask_sh),\ HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_FLIP_PENDING, mask_sh),\ HUBP_SF(HUBPREQ0_DCSURF_FLIP_CONTROL, SURFACE_UPDATE_LOCK, mask_sh),\ HUBP_SF(HUBP0_DCSURF_PRI_VIEWPORT_DIMENSION, PRI_VIEWPORT_WIDTH, mask_sh),\ @@ -451,6 +453,8 @@ type H_MIRROR_EN;\ type SURFACE_PIXEL_FORMAT;\ type SURFACE_FLIP_TYPE;\ + type SURFACE_FLIP_MODE_FOR_STEREOSYNC;\ + type SURFACE_FLIP_IN_STEREOSYNC;\ type SURFACE_UPDATE_LOCK;\ type SURFACE_FLIP_PENDING;\ type PRI_VIEWPORT_WIDTH; \ @@ -635,6 +639,7 @@ struct dcn_hubp_state { struct _vcs_dpi_display_rq_regs_st rq_regs; uint32_t pixel_format; uint32_t inuse_addr_hi; + uint32_t inuse_addr_lo; uint32_t viewport_width; uint32_t viewport_height; uint32_t rotation_angle; @@ -664,7 +669,8 @@ void hubp1_program_surface_config( union plane_size *plane_size, enum dc_rotation_angle rotation, struct dc_plane_dcc_param *dcc, - bool horizontal_mirror); + bool horizontal_mirror, + unsigned int compat_level); void hubp1_program_deadline( struct hubp *hubp, diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index cfcc54f2ce65..6bd4ec39f869 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -58,9 +58,11 @@ /*print is 17 wide, first two characters are spaces*/ #define DTN_INFO_MICRO_SEC(ref_cycle) \ - print_microsec(dc_ctx, ref_cycle) + print_microsec(dc_ctx, log_ctx, ref_cycle) -void print_microsec(struct dc_context *dc_ctx, uint32_t ref_cycle) +void print_microsec(struct dc_context *dc_ctx, + struct dc_log_buffer_ctx *log_ctx, + uint32_t ref_cycle) { const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clock_inKhz / 1000; static const unsigned int frac = 1000; @@ -71,8 +73,8 @@ void print_microsec(struct dc_context *dc_ctx, uint32_t ref_cycle) us_x10 % frac); } - -static void log_mpc_crc(struct dc *dc) +static void log_mpc_crc(struct dc *dc, + struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct dce_hwseq *hws = dc->hwseq; @@ -85,10 +87,10 @@ static void log_mpc_crc(struct dc *dc) REG_READ(DPP_TOP0_DPP_CRC_VAL_B_A), REG_READ(DPP_TOP0_DPP_CRC_VAL_R_G)); } -void dcn10_log_hubbub_state(struct dc *dc) +void dcn10_log_hubbub_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; - struct dcn_hubbub_wm wm; + struct dcn_hubbub_wm wm = {0}; int i; hubbub1_wm_read_state(dc->res_pool->hubbub, &wm); @@ -112,7 +114,7 @@ void dcn10_log_hubbub_state(struct dc *dc) DTN_INFO("\n"); } -static void dcn10_log_hubp_states(struct dc *dc) +static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; @@ -227,7 +229,8 @@ static void dcn10_log_hubp_states(struct dc *dc) DTN_INFO("\n"); } -void dcn10_log_hw_state(struct dc *dc) +void dcn10_log_hw_state(struct dc *dc, + struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; @@ -235,19 +238,22 @@ void dcn10_log_hw_state(struct dc *dc) DTN_INFO_BEGIN(); - dcn10_log_hubbub_state(dc); + dcn10_log_hubbub_state(dc, log_ctx); - dcn10_log_hubp_states(dc); + dcn10_log_hubp_states(dc, log_ctx); DTN_INFO("DPP: IGAM format IGAM mode DGAM mode RGAM mode" " GAMUT mode C11 C12 C13 C14 C21 C22 C23 C24 " "C31 C32 C33 C34\n"); for (i = 0; i < pool->pipe_count; i++) { struct dpp *dpp = pool->dpps[i]; - struct dcn_dpp_state s; + struct dcn_dpp_state s = {0}; dpp->funcs->dpp_read_state(dpp, &s); + if (!s.is_enabled) + continue; + DTN_INFO("[%2d]: %11xh %-11s %-11s %-11s" "%8x %08xh %08xh %08xh %08xh %08xh %08xh", dpp->inst, @@ -345,7 +351,7 @@ void dcn10_log_hw_state(struct dc *dc) dc->current_state->bw.dcn.clk.fclk_khz, dc->current_state->bw.dcn.clk.socclk_khz); - log_mpc_crc(dc); + log_mpc_crc(dc, log_ctx); DTN_INFO_END(); } @@ -628,6 +634,8 @@ static enum dc_status dcn10_enable_stream_timing( struct dc_stream_state *stream = pipe_ctx->stream; enum dc_color_space color_space; struct tg_color black_color = {0}; + struct drr_params params = {0}; + unsigned int event_triggers = 0; /* by upper caller loop, pipe0 is parent pipe and be called first. * back end is set up by for pipe0. Other children pipe share back end @@ -695,6 +703,19 @@ static enum dc_status dcn10_enable_stream_timing( return DC_ERROR_UNEXPECTED; } + params.vertical_total_min = stream->adjust.v_total_min; + params.vertical_total_max = stream->adjust.v_total_max; + if (pipe_ctx->stream_res.tg->funcs->set_drr) + pipe_ctx->stream_res.tg->funcs->set_drr( + pipe_ctx->stream_res.tg, ¶ms); + + // DRR should set trigger event to monitor surface update event + if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0) + event_triggers = 0x80; + if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control) + pipe_ctx->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx->stream_res.tg, event_triggers); + /* TODO program crtc source select for non-virtual signal*/ /* TODO program FMT */ /* TODO setup link_enc */ @@ -840,7 +861,7 @@ void dcn10_verify_allow_pstate_change_high(struct dc *dc) if (!hubbub1_verify_allow_pstate_change_high(dc->res_pool->hubbub)) { if (should_log_hw_state) { - dcn10_log_hw_state(dc); + dcn10_log_hw_state(dc, NULL); } BREAK_TO_DEBUGGER(); if (dcn10_hw_wa_force_recovery(dc)) { @@ -1900,7 +1921,7 @@ static void update_dpp(struct dpp *dpp, struct dc_plane_state *plane_state) static void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) { struct hubp *hubp = pipe_ctx->plane_res.hubp; - struct mpcc_blnd_cfg blnd_cfg = {0}; + struct mpcc_blnd_cfg blnd_cfg = {{0}}; bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; int mpcc_id; struct mpcc *new_mpcc; @@ -1929,9 +1950,13 @@ static void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; blnd_cfg.overlap_only = false; - blnd_cfg.global_alpha = 0xff; blnd_cfg.global_gain = 0xff; + if (pipe_ctx->plane_state->global_alpha) + blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; + else + blnd_cfg.global_alpha = 0xff; + /* DCN1.0 has output CM before MPC which seems to screw with * pre-multiplied alpha. */ @@ -2004,6 +2029,7 @@ static void update_dchubp_dpp( struct dpp *dpp = pipe_ctx->plane_res.dpp; struct dc_plane_state *plane_state = pipe_ctx->plane_state; union plane_size size = plane_state->plane_size; + unsigned int compat_level = 0; /* depends on DML calculation, DPP clock value may change dynamically */ /* If request max dpp clk is lower than current dispclk, no need to @@ -2045,11 +2071,13 @@ static void update_dchubp_dpp( update_dpp(dpp, plane_state); if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.per_pixel_alpha_change) + plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_flags.bits.global_alpha_change) dc->hwss.update_mpcc(dc, pipe_ctx); if (plane_state->update_flags.bits.full_update || plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_flags.bits.global_alpha_change || plane_state->update_flags.bits.scaling_change || plane_state->update_flags.bits.position_change) { update_scaler(pipe_ctx); @@ -2095,7 +2123,8 @@ static void update_dchubp_dpp( &size, plane_state->rotation, &plane_state->dcc, - plane_state->horizontal_mirror); + plane_state->horizontal_mirror, + compat_level); } hubp->power_gated = false; @@ -2388,15 +2417,23 @@ static void set_drr(struct pipe_ctx **pipe_ctx, { int i = 0; struct drr_params params = {0}; + // DRR should set trigger event to monitor surface update event + unsigned int event_triggers = 0x80; params.vertical_total_max = vmax; params.vertical_total_min = vmin; /* TODO: If multiple pipes are to be supported, you need - * some GSL stuff + * some GSL stuff. Static screen triggers may be programmed differently + * as well. */ for (i = 0; i < num_pipes; i++) { - pipe_ctx[i]->stream_res.tg->funcs->set_drr(pipe_ctx[i]->stream_res.tg, ¶ms); + pipe_ctx[i]->stream_res.tg->funcs->set_drr( + pipe_ctx[i]->stream_res.tg, ¶ms); + if (vmax != 0 && vmin != 0) + pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx[i]->stream_res.tg, + event_triggers); } } @@ -2592,15 +2629,15 @@ static void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) .mirror = pipe_ctx->plane_state->horizontal_mirror }; + pos_cpy.x -= pipe_ctx->plane_state->dst_rect.x; + pos_cpy.y -= pipe_ctx->plane_state->dst_rect.y; + if (pipe_ctx->plane_state->address.type == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) pos_cpy.enable = false; - if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) - pos_cpy.enable = false; - hubp->funcs->set_cursor_position(hubp, &pos_cpy, ¶m); - dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width); + dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width, hubp->curs_attr.height); } static void dcn10_set_cursor_attribute(struct pipe_ctx *pipe_ctx) @@ -2678,6 +2715,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = { .setup_stereo = dcn10_setup_stereo, .set_avmute = dce110_set_avmute, .log_hw_state = dcn10_log_hw_state, + .get_hw_state = dcn10_get_hw_state, .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, .ready_shared_resources = ready_shared_resources, .optimize_shared_resources = optimize_shared_resources, diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h index 7139fb73e966..84d461e0ed3e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h @@ -46,4 +46,9 @@ void dcn10_program_pipe( struct pipe_ctx *pipe_ctx, struct dc_state *context); +void dcn10_get_hw_state( + struct dc *dc, + char *pBuf, unsigned int bufSize, + unsigned int mask); + #endif /* __DC_HWSS_DCN10_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c new file mode 100644 index 000000000000..64158900730f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c @@ -0,0 +1,561 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "core_types.h" +#include "resource.h" +#include "custom_float.h" +#include "dcn10_hw_sequencer.h" +#include "dce110/dce110_hw_sequencer.h" +#include "dce/dce_hwseq.h" +#include "abm.h" +#include "dmcu.h" +#include "dcn10_optc.h" +#include "dcn10/dcn10_dpp.h" +#include "dcn10/dcn10_mpc.h" +#include "timing_generator.h" +#include "opp.h" +#include "ipp.h" +#include "mpc.h" +#include "reg_helper.h" +#include "custom_float.h" +#include "dcn10_hubp.h" +#include "dcn10_hubbub.h" +#include "dcn10_cm_common.h" + +static unsigned int snprintf_count(char *pBuf, unsigned int bufSize, char *fmt, ...) +{ + unsigned int ret_vsnprintf; + unsigned int chars_printed; + + va_list args; + va_start(args, fmt); + + ret_vsnprintf = vsnprintf(pBuf, bufSize, fmt, args); + + va_end(args); + + if (ret_vsnprintf > 0) { + if (ret_vsnprintf < bufSize) + chars_printed = ret_vsnprintf; + else + chars_printed = bufSize - 1; + } else + chars_printed = 0; + + return chars_printed; +} + +static unsigned int dcn10_get_hubbub_state(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcn_hubbub_wm wm = {0}; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clock_inKhz / 1000; + static const unsigned int frac = 1000; + + hubbub1_wm_read_state(dc->res_pool->hubbub, &wm); + + chars_printed = snprintf_count(pBuf, remaining_buffer, "wm_set_index,data_urgent,pte_meta_urgent,sr_enter,sr_exit,dram_clk_chanage\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < 4; i++) { + struct dcn_hubbub_wm_set *s; + + s = &wm.sets[i]; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%d.%03d,%d.%03d,%d.%03d,%d.%03d,%d.%03d\n", + s->wm_set, + (s->data_urgent * frac) / ref_clk_mhz / frac, (s->data_urgent * frac) / ref_clk_mhz % frac, + (s->pte_meta_urgent * frac) / ref_clk_mhz / frac, (s->pte_meta_urgent * frac) / ref_clk_mhz % frac, + (s->sr_enter * frac) / ref_clk_mhz / frac, (s->sr_enter * frac) / ref_clk_mhz % frac, + (s->sr_exit * frac) / ref_clk_mhz / frac, (s->sr_exit * frac) / ref_clk_mhz % frac, + (s->dram_clk_chanage * frac) / ref_clk_mhz / frac, (s->dram_clk_chanage * frac) / ref_clk_mhz % frac); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_hubp_states(struct dc *dc, char *pBuf, unsigned int bufSize, bool invarOnly) +{ + struct dc_context *dc_ctx = dc->ctx; + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clock_inKhz / 1000; + static const unsigned int frac = 1000; + + if (invarOnly) + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,format,addr_hi,width,height,rotation,mirror,sw_mode,dcc_en,blank_en,ttu_dis,underflow," + "min_ttu_vblank,qos_low_wm,qos_high_wm" + "\n"); + else + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,format,addr_hi,addr_lo,width,height,rotation,mirror,sw_mode,dcc_en,blank_en,ttu_dis,underflow," + "min_ttu_vblank,qos_low_wm,qos_high_wm" + "\n"); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct hubp *hubp = pool->hubps[i]; + struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state); + + hubp->funcs->hubp_read_state(hubp); + + if (!s->blank_en) { + if (invarOnly) + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%d,%d,%x,%x,%x,%x,%x,%x,%x," + "%d.%03d,%d.%03d,%d.%03d" + "\n", + hubp->inst, + s->pixel_format, + s->inuse_addr_hi, + s->viewport_width, + s->viewport_height, + s->rotation_angle, + s->h_mirror_en, + s->sw_mode, + s->dcc_en, + s->blank_en, + s->ttu_disable, + s->underflow_status, + (s->min_ttu_vblank * frac) / ref_clk_mhz / frac, (s->min_ttu_vblank * frac) / ref_clk_mhz % frac, + (s->qos_level_low_wm * frac) / ref_clk_mhz / frac, (s->qos_level_low_wm * frac) / ref_clk_mhz % frac, + (s->qos_level_high_wm * frac) / ref_clk_mhz / frac, (s->qos_level_high_wm * frac) / ref_clk_mhz % frac); + else + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%x,%d,%d,%x,%x,%x,%x,%x,%x,%x," + "%d.%03d,%d.%03d,%d.%03d" + "\n", + hubp->inst, + s->pixel_format, + s->inuse_addr_hi, + s->inuse_addr_lo, + s->viewport_width, + s->viewport_height, + s->rotation_angle, + s->h_mirror_en, + s->sw_mode, + s->dcc_en, + s->blank_en, + s->ttu_disable, + s->underflow_status, + (s->min_ttu_vblank * frac) / ref_clk_mhz / frac, (s->min_ttu_vblank * frac) / ref_clk_mhz % frac, + (s->qos_level_low_wm * frac) / ref_clk_mhz / frac, (s->qos_level_low_wm * frac) / ref_clk_mhz % frac, + (s->qos_level_high_wm * frac) / ref_clk_mhz / frac, (s->qos_level_high_wm * frac) / ref_clk_mhz % frac); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_rq_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,drq_exp_m,prq_exp_m,mrq_exp_m,crq_exp_m,plane1_ba," + "luma_chunk_s,luma_min_chu_s,luma_meta_ch_s,luma_min_m_c_s,luma_dpte_gr_s,luma_mpte_gr_s,luma_swath_hei,luma_pte_row_h," + "chroma_chunk_s,chroma_min_chu_s,chroma_meta_ch_s,chroma_min_m_c_s,chroma_dpte_gr_s,chroma_mpte_gr_s,chroma_swath_hei,chroma_pte_row_h" + "\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); + struct _vcs_dpi_display_rq_regs_st *rq_regs = &s->rq_regs; + + if (!s->blank_en) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x,%x" + "\n", + pool->hubps[i]->inst, rq_regs->drq_expansion_mode, rq_regs->prq_expansion_mode, rq_regs->mrq_expansion_mode, + rq_regs->crq_expansion_mode, rq_regs->plane1_base_address, rq_regs->rq_regs_l.chunk_size, + rq_regs->rq_regs_l.min_chunk_size, rq_regs->rq_regs_l.meta_chunk_size, + rq_regs->rq_regs_l.min_meta_chunk_size, rq_regs->rq_regs_l.dpte_group_size, + rq_regs->rq_regs_l.mpte_group_size, rq_regs->rq_regs_l.swath_height, + rq_regs->rq_regs_l.pte_row_height_linear, rq_regs->rq_regs_c.chunk_size, rq_regs->rq_regs_c.min_chunk_size, + rq_regs->rq_regs_c.meta_chunk_size, rq_regs->rq_regs_c.min_meta_chunk_size, + rq_regs->rq_regs_c.dpte_group_size, rq_regs->rq_regs_c.mpte_group_size, + rq_regs->rq_regs_c.swath_height, rq_regs->rq_regs_c.pte_row_height_linear); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_dlg_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,rc_hbe,dlg_vbe,min_d_y_n,rc_per_ht,rc_x_a_s," + "dst_y_a_s,dst_y_pf,dst_y_vvb,dst_y_rvb,dst_y_vfl,dst_y_rfl,rf_pix_fq," + "vratio_pf,vrat_pf_c,rc_pg_vbl,rc_pg_vbc,rc_mc_vbl,rc_mc_vbc,rc_pg_fll," + "rc_pg_flc,rc_mc_fll,rc_mc_flc,pr_nom_l,pr_nom_c,rc_pg_nl,rc_pg_nc," + "mr_nom_l,mr_nom_c,rc_mc_nl,rc_mc_nc,rc_ld_pl,rc_ld_pc,rc_ld_l," + "rc_ld_c,cha_cur0,ofst_cur1,cha_cur1,vr_af_vc0,ddrq_limt,x_rt_dlay,x_rp_dlay,x_rr_sfl" + "\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); + struct _vcs_dpi_display_dlg_regs_st *dlg_regs = &s->dlg_attr; + + if (!s->blank_en) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x" + "\n", + pool->hubps[i]->inst, dlg_regs->refcyc_h_blank_end, dlg_regs->dlg_vblank_end, dlg_regs->min_dst_y_next_start, + dlg_regs->refcyc_per_htotal, dlg_regs->refcyc_x_after_scaler, dlg_regs->dst_y_after_scaler, + dlg_regs->dst_y_prefetch, dlg_regs->dst_y_per_vm_vblank, dlg_regs->dst_y_per_row_vblank, + dlg_regs->dst_y_per_vm_flip, dlg_regs->dst_y_per_row_flip, dlg_regs->ref_freq_to_pix_freq, + dlg_regs->vratio_prefetch, dlg_regs->vratio_prefetch_c, dlg_regs->refcyc_per_pte_group_vblank_l, + dlg_regs->refcyc_per_pte_group_vblank_c, dlg_regs->refcyc_per_meta_chunk_vblank_l, + dlg_regs->refcyc_per_meta_chunk_vblank_c, dlg_regs->refcyc_per_pte_group_flip_l, + dlg_regs->refcyc_per_pte_group_flip_c, dlg_regs->refcyc_per_meta_chunk_flip_l, + dlg_regs->refcyc_per_meta_chunk_flip_c, dlg_regs->dst_y_per_pte_row_nom_l, + dlg_regs->dst_y_per_pte_row_nom_c, dlg_regs->refcyc_per_pte_group_nom_l, + dlg_regs->refcyc_per_pte_group_nom_c, dlg_regs->dst_y_per_meta_row_nom_l, + dlg_regs->dst_y_per_meta_row_nom_c, dlg_regs->refcyc_per_meta_chunk_nom_l, + dlg_regs->refcyc_per_meta_chunk_nom_c, dlg_regs->refcyc_per_line_delivery_pre_l, + dlg_regs->refcyc_per_line_delivery_pre_c, dlg_regs->refcyc_per_line_delivery_l, + dlg_regs->refcyc_per_line_delivery_c, dlg_regs->chunk_hdl_adjust_cur0, dlg_regs->dst_y_offset_cur1, + dlg_regs->chunk_hdl_adjust_cur1, dlg_regs->vready_after_vcount0, dlg_regs->dst_y_delta_drq_limit, + dlg_regs->xfc_reg_transfer_delay, dlg_regs->xfc_reg_precharge_delay, + dlg_regs->xfc_reg_remote_surface_flip_latency); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_ttu_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,qos_ll_wm,qos_lh_wm,mn_ttu_vb,qos_l_flp,rc_rd_p_l,rc_rd_l,rc_rd_p_c," + "rc_rd_c,rc_rd_c0,rc_rd_pc0,rc_rd_c1,rc_rd_pc1,qos_lf_l,qos_rds_l," + "qos_lf_c,qos_rds_c,qos_lf_c0,qos_rds_c0,qos_lf_c1,qos_rds_c1" + "\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); + struct _vcs_dpi_display_ttu_regs_st *ttu_regs = &s->ttu_attr; + + if (!s->blank_en) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x,%x," + "%x,%x,%x,%x,%x,%x" + "\n", + pool->hubps[i]->inst, ttu_regs->qos_level_low_wm, ttu_regs->qos_level_high_wm, ttu_regs->min_ttu_vblank, + ttu_regs->qos_level_flip, ttu_regs->refcyc_per_req_delivery_pre_l, ttu_regs->refcyc_per_req_delivery_l, + ttu_regs->refcyc_per_req_delivery_pre_c, ttu_regs->refcyc_per_req_delivery_c, ttu_regs->refcyc_per_req_delivery_cur0, + ttu_regs->refcyc_per_req_delivery_pre_cur0, ttu_regs->refcyc_per_req_delivery_cur1, + ttu_regs->refcyc_per_req_delivery_pre_cur1, ttu_regs->qos_level_fixed_l, ttu_regs->qos_ramp_disable_l, + ttu_regs->qos_level_fixed_c, ttu_regs->qos_ramp_disable_c, ttu_regs->qos_level_fixed_cur0, + ttu_regs->qos_ramp_disable_cur0, ttu_regs->qos_level_fixed_cur1, ttu_regs->qos_ramp_disable_cur1); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_cm_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,igam_format,igam_mode,dgam_mode,rgam_mode,gamut_mode," + "c11_c12,c13_c14,c21_c22,c23_c24,c31_c32,c33_c34" + "\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct dpp *dpp = pool->dpps[i]; + struct dcn_dpp_state s = {0}; + + dpp->funcs->dpp_read_state(dpp, &s); + + if (s.is_enabled) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x," + "%s,%s,%s," + "%x,%08x,%08x,%08x,%08x,%08x,%08x" + "\n", + dpp->inst, s.igam_input_format, + (s.igam_lut_mode == 0) ? "BypassFixed" : + ((s.igam_lut_mode == 1) ? "BypassFloat" : + ((s.igam_lut_mode == 2) ? "RAM" : + ((s.igam_lut_mode == 3) ? "RAM" : + "Unknown"))), + (s.dgam_lut_mode == 0) ? "Bypass" : + ((s.dgam_lut_mode == 1) ? "sRGB" : + ((s.dgam_lut_mode == 2) ? "Ycc" : + ((s.dgam_lut_mode == 3) ? "RAM" : + ((s.dgam_lut_mode == 4) ? "RAM" : + "Unknown")))), + (s.rgam_lut_mode == 0) ? "Bypass" : + ((s.rgam_lut_mode == 1) ? "sRGB" : + ((s.rgam_lut_mode == 2) ? "Ycc" : + ((s.rgam_lut_mode == 3) ? "RAM" : + ((s.rgam_lut_mode == 4) ? "RAM" : + "Unknown")))), + s.gamut_remap_mode, s.gamut_remap_c11_c12, + s.gamut_remap_c13_c14, s.gamut_remap_c21_c22, s.gamut_remap_c23_c24, + s.gamut_remap_c31_c32, s.gamut_remap_c33_c34); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_mpcc_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,opp,dpp,mpccbot,mode,alpha_mode,premult,overlap_only,idle\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->pipe_count; i++) { + struct mpcc_state s = {0}; + + pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s); + + if (s.opp_id != 0xf) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%x,%x,%x,%x,%x,%x,%x,%x\n", + i, s.opp_id, s.dpp_id, s.bot_mpcc_id, + s.mode, s.alpha_mode, s.pre_multiplied_alpha, s.overlap_only, + s.idle); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_otg_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + struct resource_pool *pool = dc->res_pool; + int i; + + unsigned int chars_printed = 0; + unsigned int remaining_buffer = bufSize; + + chars_printed = snprintf_count(pBuf, remaining_buffer, "instance,v_bs,v_be,v_ss,v_se,vpol,vmax,vmin,vmax_sel,vmin_sel," + "h_bs,h_be,h_ss,h_se,hpol,htot,vtot,underflow\n"); + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + for (i = 0; i < pool->timing_generator_count; i++) { + struct timing_generator *tg = pool->timing_generators[i]; + struct dcn_otg_state s = {0}; + + optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s); + + //only print if OTG master is enabled + if (s.otg_enabled & 1) { + chars_printed = snprintf_count(pBuf, remaining_buffer, "%x,%d,%d,%d,%d,%d,%d,%d,%d,%d," + "%d,%d,%d,%d,%d,%d,%d,%d" + "\n", + tg->inst, + s.v_blank_start, + s.v_blank_end, + s.v_sync_a_start, + s.v_sync_a_end, + s.v_sync_a_pol, + s.v_total_max, + s.v_total_min, + s.v_total_max_sel, + s.v_total_min_sel, + s.h_blank_start, + s.h_blank_end, + s.h_sync_a_start, + s.h_sync_a_end, + s.h_sync_a_pol, + s.h_total, + s.v_total, + s.underflow_occurred_status); + + remaining_buffer -= chars_printed; + pBuf += chars_printed; + + // Clear underflow for debug purposes + // We want to keep underflow sticky bit on for the longevity tests outside of test environment. + // This function is called only from Windows or Diags test environment, hence it's safe to clear + // it from here without affecting the original intent. + tg->funcs->clear_optc_underflow(tg); + } + } + + return bufSize - remaining_buffer; +} + +static unsigned int dcn10_get_clock_states(struct dc *dc, char *pBuf, unsigned int bufSize) +{ + unsigned int chars_printed = 0; + + chars_printed = snprintf_count(pBuf, bufSize, "dcfclk_khz,dcfclk_deep_sleep_khz,dispclk_khz," + "dppclk_khz,max_supported_dppclk_khz,fclk_khz,socclk_khz\n" + "%d,%d,%d,%d,%d,%d,%d\n", + dc->current_state->bw.dcn.clk.dcfclk_khz, + dc->current_state->bw.dcn.clk.dcfclk_deep_sleep_khz, + dc->current_state->bw.dcn.clk.dispclk_khz, + dc->current_state->bw.dcn.clk.dppclk_khz, + dc->current_state->bw.dcn.clk.max_supported_dppclk_khz, + dc->current_state->bw.dcn.clk.fclk_khz, + dc->current_state->bw.dcn.clk.socclk_khz); + + return chars_printed; +} + +void dcn10_get_hw_state(struct dc *dc, char *pBuf, unsigned int bufSize, unsigned int mask) +{ + /* + * Mask Format + * Bit 0 - 15: Hardware block mask + * Bit 15: 1 = Invariant Only, 0 = All + */ + const unsigned int DC_HW_STATE_MASK_HUBBUB = 0x1; + const unsigned int DC_HW_STATE_MASK_HUBP = 0x2; + const unsigned int DC_HW_STATE_MASK_RQ = 0x4; + const unsigned int DC_HW_STATE_MASK_DLG = 0x8; + const unsigned int DC_HW_STATE_MASK_TTU = 0x10; + const unsigned int DC_HW_STATE_MASK_CM = 0x20; + const unsigned int DC_HW_STATE_MASK_MPCC = 0x40; + const unsigned int DC_HW_STATE_MASK_OTG = 0x80; + const unsigned int DC_HW_STATE_MASK_CLOCKS = 0x100; + const unsigned int DC_HW_STATE_INVAR_ONLY = 0x8000; + + unsigned int chars_printed = 0; + unsigned int remaining_buf_size = bufSize; + + if (mask == 0x0) + mask = 0xFFFF; // Default, capture all, invariant only + + if ((mask & DC_HW_STATE_MASK_HUBBUB) && remaining_buf_size > 0) { + chars_printed = dcn10_get_hubbub_state(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_HUBP) && remaining_buf_size > 0) { + chars_printed = dcn10_get_hubp_states(dc, pBuf, remaining_buf_size, mask & DC_HW_STATE_INVAR_ONLY); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_RQ) && remaining_buf_size > 0) { + chars_printed = dcn10_get_rq_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_DLG) && remaining_buf_size > 0) { + chars_printed = dcn10_get_dlg_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_TTU) && remaining_buf_size > 0) { + chars_printed = dcn10_get_ttu_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_CM) && remaining_buf_size > 0) { + chars_printed = dcn10_get_cm_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_MPCC) && remaining_buf_size > 0) { + chars_printed = dcn10_get_mpcc_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_OTG) && remaining_buf_size > 0) { + chars_printed = dcn10_get_otg_states(dc, pBuf, remaining_buf_size); + pBuf += chars_printed; + remaining_buf_size -= chars_printed; + } + + if ((mask & DC_HW_STATE_MASK_CLOCKS) && remaining_buf_size > 0) + chars_printed = dcn10_get_clock_states(dc, pBuf, remaining_buf_size); +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c index 6b44ed3697a4..cb1b134b8fcb 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c @@ -65,6 +65,7 @@ #include "dce/dce_abm.h" #include "dce/dce_dmcu.h" #include "dce/dce_aux.h" +#include "dce/dce_i2c.h" const struct _vcs_dpi_ip_params_st dcn1_0_ip = { .rob_buffer_size_kbytes = 64, @@ -151,7 +152,10 @@ enum dcn10_clk_src_array_id { DCN10_CLK_SRC_PLL1, DCN10_CLK_SRC_PLL2, DCN10_CLK_SRC_PLL3, - DCN10_CLK_SRC_TOTAL + DCN10_CLK_SRC_TOTAL, +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + DCN101_CLK_SRC_TOTAL = DCN10_CLK_SRC_PLL3 +#endif }; /* begin ********************* @@ -500,6 +504,7 @@ static const struct resource_caps res_cap = { .num_audio = 4, .num_stream_encoder = 4, .num_pll = 4, + .num_ddc = 4, }; static const struct dc_debug_options debug_defaults_drv = { @@ -610,7 +615,40 @@ struct aux_engine *dcn10_aux_engine_create( return &aux_engine->base; } +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +struct dce_i2c_hw *dcn10_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + dcn1_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} static struct mpc *dcn10_mpc_create(struct dc_context *ctx) { struct dcn10_mpc *mpc10 = kzalloc(sizeof(struct dcn10_mpc), @@ -711,7 +749,7 @@ struct clock_source *dcn10_clock_source_create( if (!clk_src) return NULL; - if (dce110_clk_src_construct(clk_src, ctx, bios, id, + if (dce112_clk_src_construct(clk_src, ctx, bios, id, regs, &cs_shift, &cs_mask)) { clk_src->base.dp_clk_src = dp_clk_src; return &clk_src->base; @@ -862,6 +900,14 @@ static void destruct(struct dcn10_resource_pool *pool) if (pool->base.engines[i] != NULL) pool->base.engines[i]->funcs->destroy_engine(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } } for (i = 0; i < pool->base.stream_enc_count; i++) @@ -934,6 +980,8 @@ static void get_pixel_clock_parameters( if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) pixel_clk_params->requested_pix_clk /= 2; + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) + pixel_clk_params->requested_pix_clk *= 2; } @@ -1120,6 +1168,10 @@ static bool construct( /* max pipe num for ASIC before check pipe fuses */ pool->base.pipe_count = pool->base.res_cap->num_timing_generator; +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + if (dc->ctx->dce_version == DCN_VERSION_1_01) + pool->base.pipe_count = 3; +#endif dc->caps.max_video_width = 3840; dc->caps.max_downscale_ratio = 200; dc->caps.i2c_speed_in_khz = 100; @@ -1151,13 +1203,28 @@ static bool construct( dcn10_clock_source_create(ctx, ctx->dc_bios, CLOCK_SOURCE_COMBO_PHY_PLL2, &clk_src_regs[2], false); + +#ifdef CONFIG_DRM_AMD_DC_DCN1_01 + if (dc->ctx->dce_version == DCN_VERSION_1_0) { + pool->base.clock_sources[DCN10_CLK_SRC_PLL3] = + dcn10_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + } +#else pool->base.clock_sources[DCN10_CLK_SRC_PLL3] = dcn10_clock_source_create(ctx, ctx->dc_bios, CLOCK_SOURCE_COMBO_PHY_PLL3, &clk_src_regs[3], false); +#endif pool->base.clk_src_count = DCN10_CLK_SRC_TOTAL; +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + if (dc->ctx->dce_version == DCN_VERSION_1_01) + pool->base.clk_src_count = DCN101_CLK_SRC_TOTAL; +#endif + pool->base.dp_clock_source = dcn10_clock_source_create(ctx, ctx->dc_bios, CLOCK_SOURCE_ID_DP_DTO, @@ -1203,6 +1270,18 @@ static bool construct( memcpy(dc->dcn_ip, &dcn10_ip_defaults, sizeof(dcn10_ip_defaults)); memcpy(dc->dcn_soc, &dcn10_soc_defaults, sizeof(dcn10_soc_defaults)); +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + if (dc->ctx->dce_version == DCN_VERSION_1_01) { + struct dcn_soc_bounding_box *dcn_soc = dc->dcn_soc; + struct dcn_ip_params *dcn_ip = dc->dcn_ip; + struct display_mode_lib *dml = &dc->dml; + + dml->ip.max_num_dpp = 3; + /* TODO how to handle 23.84? */ + dcn_soc->dram_clock_change_latency = 23; + dcn_ip->max_num_dpp = 3; + } +#endif if (ASICREV_IS_RV1_F0(dc->ctx->asic_id.hw_internal_rev)) { dc->dcn_soc->urgent_latency = 3; dc->debug.disable_dmcu = true; @@ -1292,7 +1371,11 @@ static bool construct( dm_error("DC: failed to create tg!\n"); goto fail; } + /* check next valid pipe */ + j++; + } + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { pool->base.engines[i] = dcn10_aux_engine_create(ctx, i); if (pool->base.engines[i] == NULL) { BREAK_TO_DEBUGGER(); @@ -1300,9 +1383,14 @@ static bool construct( "DC:failed to create aux engine!!\n"); goto fail; } - - /* check next valid pipe */ - j++; + pool->base.hw_i2cs[i] = dcn10_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create hw i2c!!\n"); + goto fail; + } + pool->base.sw_i2cs[i] = NULL; } /* valid pipe num */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.h index 6b3e4ded155b..67f3e4dd95c1 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.h @@ -260,6 +260,7 @@ struct dcn10_stream_enc_registers { SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP5_ENABLE, mask_sh),\ SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP6_ENABLE, mask_sh),\ SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, mask_sh),\ + SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP7_SEND, mask_sh),\ SE_SF(DP0_DP_DB_CNTL, DP_DB_DISABLE, mask_sh),\ SE_SF(DP0_DP_MSA_COLORIMETRY, DP_MSA_MISC0, mask_sh),\ SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_HTOTAL, mask_sh),\ @@ -364,6 +365,7 @@ struct dcn10_stream_enc_registers { type DP_SEC_GSP5_ENABLE;\ type DP_SEC_GSP6_ENABLE;\ type DP_SEC_GSP7_ENABLE;\ + type DP_SEC_GSP7_SEND;\ type DP_SEC_MPG_ENABLE;\ type DP_VID_STREAM_DIS_DEFER;\ type DP_VID_STREAM_ENABLE;\ diff --git a/drivers/gpu/drm/amd/display/dc/dm_event_log.h b/drivers/gpu/drm/amd/display/dc/dm_event_log.h new file mode 100644 index 000000000000..34a701ca879e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dm_event_log.h @@ -0,0 +1,38 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/** + * This file defines external dependencies of Display Core. + */ + +#ifndef __DM_EVENT_LOG_H__ + +#define __DM_EVENT_LOG_H__ + +#define EVENT_LOG_AUX_REQ(ddc, type, action, address, len, data) +#define EVENT_LOG_AUX_REP(ddc, type, replyStatus, len, data) + +#endif + diff --git a/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h b/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h index 58ed2055ef9f..f2ea8452d48f 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h +++ b/drivers/gpu/drm/amd/display/dc/dm_pp_smu.h @@ -30,33 +30,45 @@ * interface to PPLIB/SMU to setup clocks and pstate requirements on SoC */ +enum pp_smu_ver { + /* + * PP_SMU_INTERFACE_X should be interpreted as the interface defined + * starting from X, where X is some family of ASICs. This is as + * opposed to interfaces used only for X. There will be some degree + * of interface sharing between families of ASIcs. + */ + PP_SMU_UNSUPPORTED, + PP_SMU_VER_RV +}; struct pp_smu { - struct dc_context *ctx; -}; + enum pp_smu_ver ver; + const void *pp; -enum wm_set_id { - WM_A, - WM_B, - WM_C, - WM_D, - WM_SET_COUNT, + /* + * interim extra handle for backwards compatibility + * as some existing functionality not yet implemented + * by ppsmu + */ + const void *dm; }; struct pp_smu_wm_set_range { - enum wm_set_id wm_inst; + unsigned int wm_inst; uint32_t min_fill_clk_khz; uint32_t max_fill_clk_khz; uint32_t min_drain_clk_khz; uint32_t max_drain_clk_khz; }; +#define MAX_WATERMARK_SETS 4 + struct pp_smu_wm_range_sets { - uint32_t num_reader_wm_sets; - struct pp_smu_wm_set_range reader_wm_sets[WM_SET_COUNT]; + unsigned int num_reader_wm_sets; + struct pp_smu_wm_set_range reader_wm_sets[MAX_WATERMARK_SETS]; - uint32_t num_writer_wm_sets; - struct pp_smu_wm_set_range writer_wm_sets[WM_SET_COUNT]; + unsigned int num_writer_wm_sets; + struct pp_smu_wm_set_range writer_wm_sets[MAX_WATERMARK_SETS]; }; struct pp_smu_display_requirement_rv { @@ -85,48 +97,52 @@ struct pp_smu_display_requirement_rv { struct pp_smu_funcs_rv { struct pp_smu pp_smu; - void (*set_display_requirement)(struct pp_smu *pp, - struct pp_smu_display_requirement_rv *req); + /* PPSMC_MSG_SetDisplayCount + * 0 triggers S0i2 optimization + */ + void (*set_display_count)(struct pp_smu *pp, int count); /* which SMU message? are reader and writer WM separate SMU msg? */ void (*set_wm_ranges)(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges); - /* PME w/a */ - void (*set_pme_wa_enable)(struct pp_smu *pp); -}; -#if 0 -struct pp_smu_funcs_rv { + /* PPSMC_MSG_SetHardMinDcfclkByFreq + * fixed clock at requested freq, either from FCH bypass or DFS + */ + void (*set_hard_min_dcfclk_by_freq)(struct pp_smu *pp, int khz); - /* PPSMC_MSG_SetDisplayCount - * 0 triggers S0i2 optimization + /* PPSMC_MSG_SetMinDeepSleepDcfclk + * when DF is in cstate, dcf clock is further divided down + * to just above given frequency */ - void (*set_display_count)(struct pp_smu *pp, int count); + void (*set_min_deep_sleep_dcfclk)(struct pp_smu *pp, int mhz); /* PPSMC_MSG_SetHardMinFclkByFreq - * FCLK will vary with DPM, but never below requested hard min + * FCLK will vary with DPM, but never below requested hard min */ void (*set_hard_min_fclk_by_freq)(struct pp_smu *pp, int khz); - /* PPSMC_MSG_SetHardMinDcefclkByFreq - * fixed clock at requested freq, either from FCH bypass or DFS + /* PPSMC_MSG_SetHardMinSocclkByFreq + * Needed for DWB support */ - void (*set_hard_min_dcefclk_by_freq)(struct pp_smu *pp, int khz); + void (*set_hard_min_socclk_by_freq)(struct pp_smu *pp, int khz); - /* PPSMC_MSG_SetMinDeepSleepDcefclk - * when DF is in cstate, dcf clock is further divided down - * to just above given frequency - */ - void (*set_min_deep_sleep_dcefclk)(struct pp_smu *pp, int mhz); + /* PME w/a */ + void (*set_pme_wa_enable)(struct pp_smu *pp); - /* todo: aesthetic - * watermark range table + /* + * Legacy functions. Used for backwards comp. with existing + * PPlib code. */ + void (*set_display_requirement)(struct pp_smu *pp, + struct pp_smu_display_requirement_rv *req); +}; - /* todo: functional/feature - * PPSMC_MSG_SetHardMinSocclkByFreq: required to support DWB - */ +struct pp_smu_funcs { + struct pp_smu ctx; + union { + struct pp_smu_funcs_rv rv_funcs; + }; }; -#endif #endif /* DM_PP_SMU_IF__H */ diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h index eb5ab3978e84..28128c02de00 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_services.h +++ b/drivers/gpu/drm/amd/display/dc/dm_services.h @@ -359,8 +359,12 @@ void dm_perf_trace_timestamp(const char *func_name, unsigned int line); * Debug and verification hooks */ -void dm_dtn_log_begin(struct dc_context *ctx); -void dm_dtn_log_append_v(struct dc_context *ctx, const char *msg, ...); -void dm_dtn_log_end(struct dc_context *ctx); +void dm_dtn_log_begin(struct dc_context *ctx, + struct dc_log_buffer_ctx *log_ctx); +void dm_dtn_log_append_v(struct dc_context *ctx, + struct dc_log_buffer_ctx *log_ctx, + const char *msg, ...); +void dm_dtn_log_end(struct dc_context *ctx, + struct dc_log_buffer_ctx *log_ctx); #endif /* __DM_SERVICES_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h index 47c19f8fe7d1..bea4e61b94c7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h @@ -40,8 +40,8 @@ enum source_format_class { dm_422_8 = 5, dm_422_10 = 6, dm_444_8 = 7, - dm_mono_8, - dm_mono_16 + dm_mono_8 = dm_444_8, + dm_mono_16 = dm_444_16 }; enum output_bpc_class { dm_out_6 = 0, dm_out_8 = 1, dm_out_10 = 2, dm_out_12 = 3, dm_out_16 = 4 diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c index 0caee3523017..a683f4102e65 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c @@ -86,6 +86,11 @@ bool dal_hw_factory_init( dal_hw_factory_dcn10_init(factory); return true; #endif +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + case DCN_VERSION_1_01: + dal_hw_factory_dcn10_init(factory); + return true; +#endif default: ASSERT_CRITICAL(false); diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c index 55c707488541..096f45628630 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c @@ -83,6 +83,11 @@ bool dal_hw_translate_init( dal_hw_translate_dcn10_init(translate); return true; #endif +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + case DCN_VERSION_1_01: + dal_hw_translate_dcn10_init(translate); + return true; +#endif default: BREAK_TO_DEBUGGER(); diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c index 0afd2fa57bbe..8cbf38b2470d 100644 --- a/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c +++ b/drivers/gpu/drm/amd/display/dc/i2caux/aux_engine.c @@ -24,6 +24,7 @@ */ #include "dm_services.h" +#include "dm_event_log.h" /* * Pre-requisites: headers required by header of this unit diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c index ae5caa97caca..8eee8ace1259 100644 --- a/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c +++ b/drivers/gpu/drm/amd/display/dc/i2caux/dce110/aux_engine_dce110.c @@ -24,6 +24,7 @@ */ #include "dm_services.h" +#include "dm_event_log.h" /* * Pre-requisites: headers required by header of this unit @@ -273,6 +274,8 @@ static void submit_channel_request( REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, 10, aux110->timeout_period/10); REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); + EVENT_LOG_AUX_REQ(engine->base.ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_NATIVE, + request->action, request->address, request->length, request->data); } static int read_channel_reply(struct aux_engine *engine, uint32_t size, @@ -336,7 +339,10 @@ static void process_channel_reply( uint32_t sw_status; bytes_replied = read_channel_reply(engine, reply->length, reply->data, - &reply_result, &sw_status); + &reply_result, &sw_status); + EVENT_LOG_AUX_REP(engine->base.ddc->pin_data->en, + EVENT_LOG_AUX_ORIGIN_NATIVE, reply_result, + bytes_replied, reply->data); /* in case HPD is LOW, exit AUX transaction */ if ((sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) { diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c index 4b54fcfb28ec..141898533e8e 100644 --- a/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2c_hw_engine.c @@ -24,6 +24,7 @@ */ #include "dm_services.h" +#include "dm_event_log.h" /* * Pre-requisites: headers required by header of this unit @@ -120,6 +121,8 @@ bool dal_i2c_hw_engine_submit_request( hw_engine->base.funcs->submit_channel_request( &hw_engine->base, &request); + /* EVENT_LOG_AUX_REQ(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_I2C, */ + /* request.action, request.address, request.length, request.data); */ if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) { @@ -168,8 +171,12 @@ bool dal_i2c_hw_engine_submit_request( hw_engine->base.funcs-> process_channel_reply(&hw_engine->base, &reply); + /* EVENT_LOG_AUX_REP(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_I2C, */ + /* AUX_TRANSACTION_REPLY_I2C_ACK, reply.length, reply.data); */ } + + return result; } diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c index 9b0bcc6b769b..e56093f26eed 100644 --- a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c @@ -96,6 +96,10 @@ struct i2caux *dal_i2caux_create( return dal_i2caux_dcn10_create(ctx); #endif +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + case DCN_VERSION_1_01: + return dal_i2caux_dcn10_create(ctx); +#endif default: BREAK_TO_DEBUGGER(); return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/inc/clock_source.h b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h index ebcf67b5fc57..47ef90495376 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/clock_source.h +++ b/drivers/gpu/drm/amd/display/dc/inc/clock_source.h @@ -166,10 +166,6 @@ struct clock_source_funcs { struct clock_source *, struct pixel_clk_params *, struct pll_settings *); - uint32_t (*get_pix_rate_in_hz)( - struct clock_source *, - struct pixel_clk_params *, - struct pll_settings *); }; struct clock_source { diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index c0b9ca13393b..831a1bdf622c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -139,11 +139,14 @@ struct resource_pool { struct output_pixel_processor *opps[MAX_PIPES]; struct timing_generator *timing_generators[MAX_PIPES]; struct stream_encoder *stream_enc[MAX_PIPES * 2]; - struct aux_engine *engines[MAX_PIPES]; struct hubbub *hubbub; struct mpc *mpc; struct pp_smu_funcs_rv *pp_smu; struct pp_smu_display_requirement_rv pp_smu_req; + struct aux_engine *engines[MAX_PIPES]; + struct dce_i2c_hw *hw_i2cs[MAX_PIPES]; + struct dce_i2c_sw *sw_i2cs[MAX_PIPES]; + bool i2c_hw_buffer_in_use; unsigned int pipe_count; unsigned int underlay_pipe_index; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h b/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h index 3c7ccb68ecdb..689faa16c0ae 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/display_clock.h @@ -53,6 +53,11 @@ struct display_clock_funcs { int requested_clock_khz); int (*get_dp_ref_clk_frequency)(struct dccg *dccg); + + bool (*update_dfs_bypass)(struct dccg *dccg, + struct dc *dc, + struct dc_state *context, + int requested_clock_khz); }; #endif /* __DISPLAY_CLOCK_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h index 74ad94b0e4f0..e894e649ce5a 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h @@ -45,6 +45,7 @@ struct dpp_grph_csc_adjustment { }; struct dcn_dpp_state { + uint32_t is_enabled; uint32_t igam_lut_mode; uint32_t igam_input_format; uint32_t dgam_lut_mode; @@ -146,7 +147,8 @@ struct dpp_funcs { struct dpp *dpp_base, const struct dc_cursor_position *pos, const struct dc_cursor_mi_param *param, - uint32_t width + uint32_t width, + uint32_t height ); void (*dpp_set_hdr_multiplier)( struct dpp *dpp_base, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index 4f3f9e68ccfa..334c48cdafdc 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -96,7 +96,8 @@ struct hubp_funcs { union plane_size *plane_size, enum dc_rotation_angle rotation, struct dc_plane_dcc_param *dcc, - bool horizontal_mirror); + bool horizontal_mirror, + unsigned int compa_level); bool (*hubp_is_flip_pending)(struct hubp *hubp); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h index cf6df2e7beb2..58818920ed41 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h @@ -131,6 +131,9 @@ struct link_encoder_funcs { void (*enable_dp_mst_output)(struct link_encoder *enc, const struct dc_link_settings *link_settings, enum clock_source_id clock_source); + void (*enable_lvds_output)(struct link_encoder *enc, + enum clock_source_id clock_source, + uint32_t pixel_clock); void (*disable_output)(struct link_encoder *link_enc, enum signal_type signal); void (*dp_set_lane_settings)(struct link_encoder *enc, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h index cfa7ec9517ae..53a9b64df11a 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h @@ -101,6 +101,10 @@ struct stream_encoder_funcs { struct dc_crtc_timing *crtc_timing, bool is_dual_link); + void (*lvds_set_stream_attribute)( + struct stream_encoder *enc, + struct dc_crtc_timing *crtc_timing); + void (*set_mst_bandwidth)( struct stream_encoder *enc, struct fixed31_32 avg_time_slots_per_mtp); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h index a14ce4de80b2..26f29d5da3d8 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h @@ -202,7 +202,9 @@ struct hw_sequencer_funcs { void (*set_avmute)(struct pipe_ctx *pipe_ctx, bool enable); - void (*log_hw_state)(struct dc *dc); + void (*log_hw_state)(struct dc *dc, + struct dc_log_buffer_ctx *log_ctx); + void (*get_hw_state)(struct dc *dc, char *pBuf, unsigned int bufSize, unsigned int mask); void (*wait_for_mpcc_disconnect)(struct dc *dc, struct resource_pool *res_pool, diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 5b321008b0b5..76d00c6dbca9 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -44,6 +44,7 @@ struct resource_caps { int num_stream_encoder; int num_pll; int num_dwb; + int num_ddc; }; struct resource_straps { diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_types.h b/drivers/gpu/drm/amd/display/include/bios_parser_types.h index 0840f69cde99..f8dbfa5b89f2 100644 --- a/drivers/gpu/drm/amd/display/include/bios_parser_types.h +++ b/drivers/gpu/drm/amd/display/include/bios_parser_types.h @@ -234,6 +234,8 @@ struct bp_pixel_clock_parameters { uint32_t USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK:1; /* Use external reference clock (refDivSrc for PLL) */ uint32_t SET_EXTERNAL_REF_DIV_SRC:1; + /* Use DFS bypass for Display clock. */ + uint32_t SET_DISPCLK_DFS_BYPASS:1; /* Force program PHY PLL only */ uint32_t PROGRAM_PHY_PLL_ONLY:1; /* Support for YUV420 */ diff --git a/drivers/gpu/drm/amd/display/include/dal_asic_id.h b/drivers/gpu/drm/amd/display/include/dal_asic_id.h index 25029ed42d89..4f501ddcfb8d 100644 --- a/drivers/gpu/drm/amd/display/include/dal_asic_id.h +++ b/drivers/gpu/drm/amd/display/include/dal_asic_id.h @@ -131,8 +131,15 @@ #define INTERNAL_REV_RAVEN_A0 0x00 /* First spin of Raven */ #define RAVEN_A0 0x01 #define RAVEN_B0 0x21 +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) +/* DCN1_01 */ +#define RAVEN2_A0 0x81 +#endif #define RAVEN_UNKNOWN 0xFF +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) +#define ASICREV_IS_RAVEN2(eChipRev) ((eChipRev >= RAVEN2_A0) && (eChipRev < 0xF0)) +#endif /* DCN1_01 */ #define ASIC_REV_IS_RAVEN(eChipRev) ((eChipRev >= RAVEN_A0) && eChipRev < RAVEN_UNKNOWN) #define RAVEN1_F0 0xF0 #define ASICREV_IS_RV1_F0(eChipRev) ((eChipRev >= RAVEN1_F0) && (eChipRev < RAVEN_UNKNOWN)) diff --git a/drivers/gpu/drm/amd/display/include/dal_types.h b/drivers/gpu/drm/amd/display/include/dal_types.h index 840142b65f8b..89627133e188 100644 --- a/drivers/gpu/drm/amd/display/include/dal_types.h +++ b/drivers/gpu/drm/amd/display/include/dal_types.h @@ -44,6 +44,9 @@ enum dce_version { DCE_VERSION_12_0, DCE_VERSION_MAX, DCN_VERSION_1_0, +#if defined(CONFIG_DRM_AMD_DC_DCN1_01) + DCN_VERSION_1_01, +#endif /* DCN1_01 */ DCN_VERSION_MAX }; diff --git a/drivers/gpu/drm/amd/display/include/logger_interface.h b/drivers/gpu/drm/amd/display/include/logger_interface.h index e3c79616682d..a0b68c266dab 100644 --- a/drivers/gpu/drm/amd/display/include/logger_interface.h +++ b/drivers/gpu/drm/amd/display/include/logger_interface.h @@ -129,13 +129,13 @@ void context_clock_trace( * Display Test Next logging */ #define DTN_INFO_BEGIN() \ - dm_dtn_log_begin(dc_ctx) + dm_dtn_log_begin(dc_ctx, log_ctx) #define DTN_INFO(msg, ...) \ - dm_dtn_log_append_v(dc_ctx, msg, ##__VA_ARGS__) + dm_dtn_log_append_v(dc_ctx, log_ctx, msg, ##__VA_ARGS__) #define DTN_INFO_END() \ - dm_dtn_log_end(dc_ctx) + dm_dtn_log_end(dc_ctx, log_ctx) #define PERFORMANCE_TRACE_START() \ unsigned long long perf_trc_start_stmp = dm_get_timestamp(dc->ctx) diff --git a/drivers/gpu/drm/amd/display/include/logger_types.h b/drivers/gpu/drm/amd/display/include/logger_types.h index ad3695e67b76..d96550d6434d 100644 --- a/drivers/gpu/drm/amd/display/include/logger_types.h +++ b/drivers/gpu/drm/amd/display/include/logger_types.h @@ -62,9 +62,16 @@ #define DC_LOG_EVENT_UNDERFLOW(...) DRM_DEBUG_KMS(__VA_ARGS__) #define DC_LOG_IF_TRACE(...) pr_debug("[IF_TRACE]:"__VA_ARGS__) #define DC_LOG_PERF_TRACE(...) DRM_DEBUG_KMS(__VA_ARGS__) +#define DC_LOG_RETIMER_REDRIVER(...) DRM_DEBUG_KMS(__VA_ARGS__) struct dal_logger; +struct dc_log_buffer_ctx { + char *buf; + size_t pos; + size_t size; +}; + enum dc_log_type { LOG_ERROR = 0, LOG_WARNING, @@ -99,7 +106,7 @@ enum dc_log_type { LOG_IF_TRACE, LOG_PERF_TRACE, LOG_DISPLAYSTATS, - + LOG_HDMI_RETIMER_REDRIVER, LOG_SECTION_TOTAL_COUNT }; diff --git a/drivers/gpu/drm/amd/display/include/set_mode_types.h b/drivers/gpu/drm/amd/display/include/set_mode_types.h index fee2b6ffcfc1..2b836e582c08 100644 --- a/drivers/gpu/drm/amd/display/include/set_mode_types.h +++ b/drivers/gpu/drm/amd/display/include/set_mode_types.h @@ -90,18 +90,6 @@ union hdmi_info_packet { struct info_packet_raw_data packet_raw_data; }; -struct info_packet { - enum info_frame_flag flags; - union hdmi_info_packet info_packet_hdmi; -}; - -struct info_frame { - struct info_packet avi_info_packet; - struct info_packet gamut_packet; - struct info_packet vendor_info_packet; - struct info_packet spd_info_packet; -}; - #pragma pack(pop) #endif /* __DAL_SET_MODE_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/signal_types.h b/drivers/gpu/drm/amd/display/include/signal_types.h index 199c5db67cbc..03476b142d8e 100644 --- a/drivers/gpu/drm/amd/display/include/signal_types.h +++ b/drivers/gpu/drm/amd/display/include/signal_types.h @@ -68,6 +68,11 @@ static inline bool dc_is_embedded_signal(enum signal_type signal) return (signal == SIGNAL_TYPE_EDP || signal == SIGNAL_TYPE_LVDS); } +static inline bool dc_is_lvds_signal(enum signal_type signal) +{ + return (signal == SIGNAL_TYPE_LVDS); +} + static inline bool dc_is_dvi_signal(enum signal_type signal) { switch (signal) { diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index bf29733958c3..15427f4fc990 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -1352,7 +1352,7 @@ static bool map_regamma_hw_to_x_user( #define _EXTRA_POINTS 3 bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, - const struct dc_gamma *ramp, bool mapUserRamp) + const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed) { struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; struct dividers dividers; @@ -1368,7 +1368,7 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, return false; /* we can use hardcoded curve for plain SRGB TF */ - if (output_tf->type == TF_TYPE_PREDEFINED && + if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && output_tf->tf == TRANSFER_FUNCTION_SRGB && (!mapUserRamp && ramp->type == GAMMA_RGB_256)) return true; @@ -1430,7 +1430,6 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, MAX_HW_POINTS, coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? true:false); } - map_regamma_hw_to_x_user(ramp, coeff, rgb_user, coordinates_x, axix_x, rgb_regamma, MAX_HW_POINTS, tf_pts, @@ -1581,7 +1580,8 @@ bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf, /* we can use hardcoded curve for plain SRGB TF */ if (input_tf->type == TF_TYPE_PREDEFINED && input_tf->tf == TRANSFER_FUNCTION_SRGB && - (!mapUserRamp && ramp->type == GAMMA_RGB_256)) + (!mapUserRamp && + (ramp->type == GAMMA_RGB_256 || ramp->num_entries == 0))) return true; input_tf->type = TF_TYPE_DISTRIBUTED_POINTS; @@ -1659,7 +1659,8 @@ rgb_user_alloc_fail: bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, - struct dc_transfer_func_distributed_points *points) + struct dc_transfer_func_distributed_points *points, + uint32_t sdr_ref_white_level) { uint32_t i; bool ret = false; @@ -1693,7 +1694,7 @@ bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, build_pq(rgb_regamma, MAX_HW_POINTS, coordinates_x, - 80); + sdr_ref_white_level); for (i = 0; i <= MAX_HW_POINTS ; i++) { points->red[i] = rgb_regamma[i].r; points->green[i] = rgb_regamma[i].g; diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h index b64048991a95..63ccb9c91224 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h @@ -78,13 +78,14 @@ void precompute_pq(void); void precompute_de_pq(void); bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, - const struct dc_gamma *ramp, bool mapUserRamp); + const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed); bool mod_color_calculate_degamma_params(struct dc_transfer_func *output_tf, const struct dc_gamma *ramp, bool mapUserRamp); bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, - struct dc_transfer_func_distributed_points *points); + struct dc_transfer_func_distributed_points *points, + uint32_t sdr_ref_white_level); bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, struct dc_transfer_func_distributed_points *points); diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index fa344ceafc17..e1688902a1b0 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -30,6 +30,7 @@ #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 +#define MIN_REFRESH_RANGE_IN_US 10000000 /* Refresh rate ramp at a fixed rate of 65 Hz/second */ #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) /* Number of elements in the render times cache array */ @@ -40,103 +41,9 @@ #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 #define FIXED_REFRESH_EXIT_FRAME_COUNT 5 -#define FREESYNC_REGISTRY_NAME "freesync_v1" - -#define FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY "DalFreeSyncNoStaticForExternalDp" - -#define FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY "DalFreeSyncNoStaticForInternal" - -#define FREESYNC_DEFAULT_REGKEY "LCDFreeSyncDefault" - -struct gradual_static_ramp { - bool ramp_is_active; - bool ramp_direction_is_up; - unsigned int ramp_current_frame_duration_in_ns; -}; - -struct freesync_time { - /* video (48Hz feature) related */ - unsigned int update_duration_in_ns; - - /* BTR/fixed refresh related */ - unsigned int prev_time_stamp_in_us; - - unsigned int min_render_time_in_us; - unsigned int max_render_time_in_us; - - unsigned int render_times_index; - unsigned int render_times[RENDER_TIMES_MAX_COUNT]; - - unsigned int min_window; - unsigned int max_window; -}; - -struct below_the_range { - bool btr_active; - bool program_btr; - - unsigned int mid_point_in_us; - - unsigned int inserted_frame_duration_in_us; - unsigned int frames_to_insert; - unsigned int frame_counter; -}; - -struct fixed_refresh { - bool fixed_active; - bool program_fixed; - unsigned int frame_counter; -}; - -struct freesync_range { - unsigned int min_refresh; - unsigned int max_frame_duration; - unsigned int vmax; - - unsigned int max_refresh; - unsigned int min_frame_duration; - unsigned int vmin; -}; - -struct freesync_state { - bool fullscreen; - bool static_screen; - bool video; - - unsigned int vmin; - unsigned int vmax; - - struct freesync_time time; - - unsigned int nominal_refresh_rate_in_micro_hz; - bool windowed_fullscreen; - - struct gradual_static_ramp static_ramp; - struct below_the_range btr; - struct fixed_refresh fixed_refresh; - struct freesync_range freesync_range; -}; - -struct freesync_entity { - struct dc_stream_state *stream; - struct mod_freesync_caps *caps; - struct freesync_state state; - struct mod_freesync_user_enable user_enable; -}; - -struct freesync_registry_options { - bool drr_external_supported; - bool drr_internal_supported; - bool lcd_freesync_default_set; - int lcd_freesync_default_value; -}; - struct core_freesync { struct mod_freesync public; struct dc *dc; - struct freesync_registry_options opts; - struct freesync_entity *map; - int num_entities; }; #define MOD_FREESYNC_TO_CORE(mod_freesync)\ @@ -147,69 +54,16 @@ struct mod_freesync *mod_freesync_create(struct dc *dc) struct core_freesync *core_freesync = kzalloc(sizeof(struct core_freesync), GFP_KERNEL); - - struct persistent_data_flag flag; - - int i, data = 0; - if (core_freesync == NULL) goto fail_alloc_context; - core_freesync->map = kcalloc(MOD_FREESYNC_MAX_CONCURRENT_STREAMS, - sizeof(struct freesync_entity), - GFP_KERNEL); - - if (core_freesync->map == NULL) - goto fail_alloc_map; - - for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) - core_freesync->map[i].stream = NULL; - - core_freesync->num_entities = 0; - if (dc == NULL) goto fail_construct; core_freesync->dc = dc; - - /* Create initial module folder in registry for freesync enable data */ - flag.save_per_edid = true; - flag.save_per_link = false; - dm_write_persistent_data(dc->ctx, NULL, FREESYNC_REGISTRY_NAME, - NULL, NULL, 0, &flag); - flag.save_per_edid = false; - flag.save_per_link = false; - - if (dm_read_persistent_data(dc->ctx, NULL, NULL, - FREESYNC_NO_STATIC_FOR_INTERNAL_REGKEY, - &data, sizeof(data), &flag)) { - core_freesync->opts.drr_internal_supported = - (data & 1) ? false : true; - } - - if (dm_read_persistent_data(dc->ctx, NULL, NULL, - FREESYNC_NO_STATIC_FOR_EXTERNAL_DP_REGKEY, - &data, sizeof(data), &flag)) { - core_freesync->opts.drr_external_supported = - (data & 1) ? false : true; - } - - if (dm_read_persistent_data(dc->ctx, NULL, NULL, - FREESYNC_DEFAULT_REGKEY, - &data, sizeof(data), &flag)) { - core_freesync->opts.lcd_freesync_default_set = true; - core_freesync->opts.lcd_freesync_default_value = data; - } else { - core_freesync->opts.lcd_freesync_default_set = false; - core_freesync->opts.lcd_freesync_default_value = 0; - } - return &core_freesync->public; fail_construct: - kfree(core_freesync->map); - -fail_alloc_map: kfree(core_freesync); fail_alloc_context: @@ -218,968 +72,384 @@ fail_alloc_context: void mod_freesync_destroy(struct mod_freesync *mod_freesync) { - if (mod_freesync != NULL) { - int i; - struct core_freesync *core_freesync = - MOD_FREESYNC_TO_CORE(mod_freesync); - - for (i = 0; i < core_freesync->num_entities; i++) - if (core_freesync->map[i].stream) - dc_stream_release(core_freesync->map[i].stream); - - kfree(core_freesync->map); - - kfree(core_freesync); - } -} - -/* Given a specific dc_stream* this function finds its equivalent - * on the core_freesync->map and returns the corresponding index - */ -static unsigned int map_index_from_stream(struct core_freesync *core_freesync, - struct dc_stream_state *stream) -{ - unsigned int index = 0; - - for (index = 0; index < core_freesync->num_entities; index++) { - if (core_freesync->map[index].stream == stream) { - return index; - } - } - /* Could not find stream requested */ - ASSERT(false); - return index; -} - -bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, struct mod_freesync_caps *caps) -{ - struct dc *dc = NULL; struct core_freesync *core_freesync = NULL; - int persistent_freesync_enable = 0; - struct persistent_data_flag flag; - unsigned int nom_refresh_rate_uhz; - unsigned long long temp; - if (mod_freesync == NULL) - return false; - + return; core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - dc = core_freesync->dc; - - flag.save_per_edid = true; - flag.save_per_link = false; - - if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { - - dc_stream_retain(stream); - - temp = stream->timing.pix_clk_khz; - temp *= 1000ULL * 1000ULL * 1000ULL; - temp = div_u64(temp, stream->timing.h_total); - temp = div_u64(temp, stream->timing.v_total); - - nom_refresh_rate_uhz = (unsigned int) temp; - - core_freesync->map[core_freesync->num_entities].stream = stream; - core_freesync->map[core_freesync->num_entities].caps = caps; - - core_freesync->map[core_freesync->num_entities].state. - fullscreen = false; - core_freesync->map[core_freesync->num_entities].state. - static_screen = false; - core_freesync->map[core_freesync->num_entities].state. - video = false; - core_freesync->map[core_freesync->num_entities].state.time. - update_duration_in_ns = 0; - core_freesync->map[core_freesync->num_entities].state. - static_ramp.ramp_is_active = false; - - /* get persistent data from registry */ - if (dm_read_persistent_data(dc->ctx, stream->sink, - FREESYNC_REGISTRY_NAME, - "userenable", &persistent_freesync_enable, - sizeof(int), &flag)) { - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_gaming = - (persistent_freesync_enable & 1) ? true : false; - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_static = - (persistent_freesync_enable & 2) ? true : false; - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_video = - (persistent_freesync_enable & 4) ? true : false; - /* If FreeSync display and LCDFreeSyncDefault is set, use as default values write back to userenable */ - } else if (caps->supported && (core_freesync->opts.lcd_freesync_default_set)) { - core_freesync->map[core_freesync->num_entities].user_enable.enable_for_gaming = - (core_freesync->opts.lcd_freesync_default_value & 1) ? true : false; - core_freesync->map[core_freesync->num_entities].user_enable.enable_for_static = - (core_freesync->opts.lcd_freesync_default_value & 2) ? true : false; - core_freesync->map[core_freesync->num_entities].user_enable.enable_for_video = - (core_freesync->opts.lcd_freesync_default_value & 4) ? true : false; - dm_write_persistent_data(dc->ctx, stream->sink, - FREESYNC_REGISTRY_NAME, - "userenable", &core_freesync->opts.lcd_freesync_default_value, - sizeof(int), &flag); - } else { - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_gaming = false; - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_static = false; - core_freesync->map[core_freesync->num_entities].user_enable. - enable_for_video = false; - } - - if (caps->supported && - nom_refresh_rate_uhz >= caps->min_refresh_in_micro_hz && - nom_refresh_rate_uhz <= caps->max_refresh_in_micro_hz) - stream->ignore_msa_timing_param = 1; - - core_freesync->num_entities++; - return true; - } - return false; + kfree(core_freesync); } -bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream) +#if 0 /* unused currently */ +static unsigned int calc_refresh_in_uhz_from_duration( + unsigned int duration_in_ns) { - int i = 0; - struct core_freesync *core_freesync = NULL; - unsigned int index = 0; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - dc_stream_release(core_freesync->map[index].stream); - core_freesync->map[index].stream = NULL; - /* To remove this entity, shift everything after down */ - for (i = index; i < core_freesync->num_entities - 1; i++) - core_freesync->map[i] = core_freesync->map[i + 1]; - core_freesync->num_entities--; - return true; + unsigned int refresh_in_uhz = + ((unsigned int)(div64_u64((1000000000ULL * 1000000), + duration_in_ns))); + return refresh_in_uhz; } +#endif -static void adjust_vmin_vmax(struct core_freesync *core_freesync, - struct dc_stream_state **streams, - int num_streams, - int map_index, - unsigned int v_total_min, - unsigned int v_total_max) +static unsigned int calc_duration_in_us_from_refresh_in_uhz( + unsigned int refresh_in_uhz) { - if (num_streams == 0 || streams == NULL || num_streams > 1) - return; - - core_freesync->map[map_index].state.vmin = v_total_min; - core_freesync->map[map_index].state.vmax = v_total_max; - - dc_stream_adjust_vmin_vmax(core_freesync->dc, streams, - num_streams, v_total_min, - v_total_max); + unsigned int duration_in_us = + ((unsigned int)(div64_u64((1000000000ULL * 1000), + refresh_in_uhz))); + return duration_in_us; } - -static void update_stream_freesync_context(struct core_freesync *core_freesync, - struct dc_stream_state *stream) +static unsigned int calc_duration_in_us_from_v_total( + const struct dc_stream_state *stream, + const struct mod_vrr_params *in_vrr, + unsigned int v_total) { - unsigned int index; - struct freesync_context *ctx; + unsigned int duration_in_us = + (unsigned int)(div64_u64(((unsigned long long)(v_total) + * 1000) * stream->timing.h_total, + stream->timing.pix_clk_khz)); - ctx = &stream->freesync_ctx; + return duration_in_us; +} - index = map_index_from_stream(core_freesync, stream); +static unsigned int calc_v_total_from_refresh( + const struct dc_stream_state *stream, + unsigned int refresh_in_uhz) +{ + unsigned int v_total = stream->timing.v_total; + unsigned int frame_duration_in_ns; - ctx->supported = core_freesync->map[index].caps->supported; - ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || - core_freesync->map[index].user_enable.enable_for_video || - core_freesync->map[index].user_enable.enable_for_static); - ctx->active = (core_freesync->map[index].state.fullscreen || - core_freesync->map[index].state.video || - core_freesync->map[index].state.static_ramp.ramp_is_active); - ctx->min_refresh_in_micro_hz = - core_freesync->map[index].caps->min_refresh_in_micro_hz; - ctx->nominal_refresh_in_micro_hz = core_freesync-> - map[index].state.nominal_refresh_rate_in_micro_hz; + frame_duration_in_ns = + ((unsigned int)(div64_u64((1000000000ULL * 1000000), + refresh_in_uhz))); -} + v_total = div64_u64(div64_u64(((unsigned long long)( + frame_duration_in_ns) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000000); -static void update_stream(struct core_freesync *core_freesync, - struct dc_stream_state *stream) -{ - unsigned int index = map_index_from_stream(core_freesync, stream); - if (core_freesync->map[index].caps->supported) { - stream->ignore_msa_timing_param = 1; - update_stream_freesync_context(core_freesync, stream); + /* v_total cannot be less than nominal */ + if (v_total < stream->timing.v_total) { + ASSERT(v_total < stream->timing.v_total); + v_total = stream->timing.v_total; } + + return v_total; } -static void calc_freesync_range(struct core_freesync *core_freesync, - struct dc_stream_state *stream, - struct freesync_state *state, - unsigned int min_refresh_in_uhz, - unsigned int max_refresh_in_uhz) +static unsigned int calc_v_total_from_duration( + const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + unsigned int duration_in_us) { - unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; - unsigned int index = map_index_from_stream(core_freesync, stream); - uint32_t vtotal = stream->timing.v_total; + unsigned int v_total = 0; - if ((min_refresh_in_uhz == 0) || (max_refresh_in_uhz == 0)) { - state->freesync_range.min_refresh = - state->nominal_refresh_rate_in_micro_hz; - state->freesync_range.max_refresh = - state->nominal_refresh_rate_in_micro_hz; + if (duration_in_us < vrr->min_duration_in_us) + duration_in_us = vrr->min_duration_in_us; - state->freesync_range.max_frame_duration = 0; - state->freesync_range.min_frame_duration = 0; - - state->freesync_range.vmax = vtotal; - state->freesync_range.vmin = vtotal; - - return; - } + if (duration_in_us > vrr->max_duration_in_us) + duration_in_us = vrr->max_duration_in_us; - min_frame_duration_in_ns = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - max_refresh_in_uhz))); - max_frame_duration_in_ns = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - min_refresh_in_uhz))); - - state->freesync_range.min_refresh = min_refresh_in_uhz; - state->freesync_range.max_refresh = max_refresh_in_uhz; - - state->freesync_range.max_frame_duration = max_frame_duration_in_ns; - state->freesync_range.min_frame_duration = min_frame_duration_in_ns; - - state->freesync_range.vmax = div64_u64(div64_u64(((unsigned long long)( - max_frame_duration_in_ns) * stream->timing.pix_clk_khz), - stream->timing.h_total), 1000000); - state->freesync_range.vmin = div64_u64(div64_u64(((unsigned long long)( - min_frame_duration_in_ns) * stream->timing.pix_clk_khz), - stream->timing.h_total), 1000000); - - /* vmin/vmax cannot be less than vtotal */ - if (state->freesync_range.vmin < vtotal) { - /* Error of 1 is permissible */ - ASSERT((state->freesync_range.vmin + 1) >= vtotal); - state->freesync_range.vmin = vtotal; - } + v_total = div64_u64(div64_u64(((unsigned long long)( + duration_in_us) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000); - if (state->freesync_range.vmax < vtotal) { - /* Error of 1 is permissible */ - ASSERT((state->freesync_range.vmax + 1) >= vtotal); - state->freesync_range.vmax = vtotal; + /* v_total cannot be less than nominal */ + if (v_total < stream->timing.v_total) { + ASSERT(v_total < stream->timing.v_total); + v_total = stream->timing.v_total; } - /* Determine whether BTR can be supported */ - if (max_frame_duration_in_ns >= - 2 * min_frame_duration_in_ns) - core_freesync->map[index].caps->btr_supported = true; - else - core_freesync->map[index].caps->btr_supported = false; - - /* Cache the time variables */ - state->time.max_render_time_in_us = - max_frame_duration_in_ns / 1000; - state->time.min_render_time_in_us = - min_frame_duration_in_ns / 1000; - state->btr.mid_point_in_us = - (max_frame_duration_in_ns + - min_frame_duration_in_ns) / 2000; + return v_total; } -static void calc_v_total_from_duration(struct dc_stream_state *stream, - unsigned int duration_in_ns, int *v_total_nominal) -{ - *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( - duration_in_ns) * stream->timing.pix_clk_khz), - stream->timing.h_total), 1000000); -} - -static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, - struct dc_stream_state *stream, - unsigned int index, int *v_total) +static void update_v_total_for_static_ramp( + struct core_freesync *core_freesync, + const struct dc_stream_state *stream, + struct mod_vrr_params *in_out_vrr) { - unsigned int frame_duration = 0; - - struct gradual_static_ramp *static_ramp_variables = - &core_freesync->map[index].state.static_ramp; + unsigned int v_total = 0; + unsigned int current_duration_in_us = + calc_duration_in_us_from_v_total( + stream, in_out_vrr, + in_out_vrr->adjust.v_total_max); + unsigned int target_duration_in_us = + calc_duration_in_us_from_refresh_in_uhz( + in_out_vrr->fixed.target_refresh_in_uhz); + bool ramp_direction_is_up = (current_duration_in_us > + target_duration_in_us) ? true : false; /* Calc ratio between new and current frame duration with 3 digit */ unsigned int frame_duration_ratio = div64_u64(1000000, (1000 + div64_u64(((unsigned long long)( STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * - static_ramp_variables->ramp_current_frame_duration_in_ns), - 1000000000))); + current_duration_in_us), + 1000000))); - /* Calculate delta between new and current frame duration in ns */ + /* Calculate delta between new and current frame duration in us */ unsigned int frame_duration_delta = div64_u64(((unsigned long long)( - static_ramp_variables->ramp_current_frame_duration_in_ns) * + current_duration_in_us) * (1000 - frame_duration_ratio)), 1000); /* Adjust frame duration delta based on ratio between current and * standard frame duration (frame duration at 60 Hz refresh rate). */ unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( - frame_duration_delta) * static_ramp_variables-> - ramp_current_frame_duration_in_ns), 16666666); + frame_duration_delta) * current_duration_in_us), 16666); /* Going to a higher refresh rate (lower frame duration) */ - if (static_ramp_variables->ramp_direction_is_up) { + if (ramp_direction_is_up) { /* reduce frame duration */ - static_ramp_variables->ramp_current_frame_duration_in_ns -= - ramp_rate_interpolated; - - /* min frame duration */ - frame_duration = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - core_freesync->map[index].state. - nominal_refresh_rate_in_micro_hz))); + current_duration_in_us -= ramp_rate_interpolated; /* adjust for frame duration below min */ - if (static_ramp_variables->ramp_current_frame_duration_in_ns <= - frame_duration) { - - static_ramp_variables->ramp_is_active = false; - static_ramp_variables-> - ramp_current_frame_duration_in_ns = - frame_duration; + if (current_duration_in_us <= target_duration_in_us) { + in_out_vrr->fixed.ramping_active = false; + in_out_vrr->fixed.ramping_done = true; + current_duration_in_us = + calc_duration_in_us_from_refresh_in_uhz( + in_out_vrr->fixed.target_refresh_in_uhz); } /* Going to a lower refresh rate (larger frame duration) */ } else { /* increase frame duration */ - static_ramp_variables->ramp_current_frame_duration_in_ns += - ramp_rate_interpolated; - - /* max frame duration */ - frame_duration = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - core_freesync->map[index].caps->min_refresh_in_micro_hz))); + current_duration_in_us += ramp_rate_interpolated; /* adjust for frame duration above max */ - if (static_ramp_variables->ramp_current_frame_duration_in_ns >= - frame_duration) { - - static_ramp_variables->ramp_is_active = false; - static_ramp_variables-> - ramp_current_frame_duration_in_ns = - frame_duration; + if (current_duration_in_us >= target_duration_in_us) { + in_out_vrr->fixed.ramping_active = false; + in_out_vrr->fixed.ramping_done = true; + current_duration_in_us = + calc_duration_in_us_from_refresh_in_uhz( + in_out_vrr->fixed.target_refresh_in_uhz); } } - calc_v_total_from_duration(stream, static_ramp_variables-> - ramp_current_frame_duration_in_ns, v_total); -} + v_total = div64_u64(div64_u64(((unsigned long long)( + current_duration_in_us) * stream->timing.pix_clk_khz), + stream->timing.h_total), 1000); -static void reset_freesync_state_variables(struct freesync_state* state) -{ - state->static_ramp.ramp_is_active = false; - if (state->nominal_refresh_rate_in_micro_hz) - state->static_ramp.ramp_current_frame_duration_in_ns = - ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - state->nominal_refresh_rate_in_micro_hz))); - - state->btr.btr_active = false; - state->btr.frame_counter = 0; - state->btr.frames_to_insert = 0; - state->btr.inserted_frame_duration_in_us = 0; - state->btr.program_btr = false; - - state->fixed_refresh.fixed_active = false; - state->fixed_refresh.program_fixed = false; + in_out_vrr->adjust.v_total_min = v_total; + in_out_vrr->adjust.v_total_max = v_total; } -/* - * Sets freesync mode on a stream depending on current freesync state. - */ -static bool set_freesync_on_streams(struct core_freesync *core_freesync, - struct dc_stream_state **streams, int num_streams) -{ - int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; - unsigned int stream_idx, map_index = 0; - struct freesync_state *state; - if (num_streams == 0 || streams == NULL || num_streams > 1) - return false; +static void apply_below_the_range(struct core_freesync *core_freesync, + const struct dc_stream_state *stream, + unsigned int last_render_time_in_us, + struct mod_vrr_params *in_out_vrr) +{ + unsigned int inserted_frame_duration_in_us = 0; + unsigned int mid_point_frames_ceil = 0; + unsigned int mid_point_frames_floor = 0; + unsigned int frame_time_in_us = 0; + unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; + unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; + unsigned int frames_to_insert = 0; + unsigned int min_frame_duration_in_ns = 0; + unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; - for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { + min_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + in_out_vrr->max_refresh_in_uhz))); - map_index = map_index_from_stream(core_freesync, - streams[stream_idx]); + /* Program BTR */ + if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) { + /* Exit Below the Range */ + if (in_out_vrr->btr.btr_active) { + in_out_vrr->btr.frame_counter = 0; + in_out_vrr->btr.btr_active = false; - state = &core_freesync->map[map_index].state; + /* Exit Fixed Refresh mode */ + } else if (in_out_vrr->fixed.fixed_active) { - if (core_freesync->map[map_index].caps->supported) { + in_out_vrr->fixed.frame_counter++; - /* Fullscreen has the topmost priority. If the - * fullscreen bit is set, we are in a fullscreen - * application where it should not matter if it is - * static screen. We should not check the static_screen - * or video bit. - * - * Special cases of fullscreen include btr and fixed - * refresh. We program btr on every flip and involves - * programming full range right before the last inserted frame. - * However, we do not want to program the full freesync range - * when fixed refresh is active, because we only program - * that logic once and this will override it. - */ - if (core_freesync->map[map_index].user_enable. - enable_for_gaming == true && - state->fullscreen == true && - state->fixed_refresh.fixed_active == false) { - /* Enable freesync */ - - v_total_min = state->freesync_range.vmin; - v_total_max = state->freesync_range.vmax; - - /* Update the freesync context for the stream */ - update_stream_freesync_context(core_freesync, - streams[stream_idx]); - - adjust_vmin_vmax(core_freesync, streams, - num_streams, map_index, - v_total_min, - v_total_max); - - return true; - - } else if (core_freesync->map[map_index].user_enable. - enable_for_video && state->video == true) { - /* Enable 48Hz feature */ - - calc_v_total_from_duration(streams[stream_idx], - state->time.update_duration_in_ns, - &v_total_nominal); - - /* Program only if v_total_nominal is in range*/ - if (v_total_nominal >= - streams[stream_idx]->timing.v_total) { - - /* Update the freesync context for - * the stream - */ - update_stream_freesync_context( - core_freesync, - streams[stream_idx]); - - adjust_vmin_vmax( - core_freesync, streams, - num_streams, map_index, - v_total_nominal, - v_total_nominal); - } - return true; - - } else { - /* Disable freesync */ - v_total_nominal = streams[stream_idx]-> - timing.v_total; - - /* Update the freesync context for - * the stream - */ - update_stream_freesync_context( - core_freesync, - streams[stream_idx]); - - adjust_vmin_vmax(core_freesync, streams, - num_streams, map_index, - v_total_nominal, - v_total_nominal); - - /* Reset the cached variables */ - reset_freesync_state_variables(state); - - return true; + if (in_out_vrr->fixed.frame_counter > + FIXED_REFRESH_EXIT_FRAME_COUNT) { + in_out_vrr->fixed.frame_counter = 0; + in_out_vrr->fixed.fixed_active = false; } - } else { - /* Disable freesync */ - v_total_nominal = streams[stream_idx]-> - timing.v_total; - /* - * we have to reset drr always even sink does - * not support freesync because a former stream has - * be programmed - */ - adjust_vmin_vmax(core_freesync, streams, - num_streams, map_index, - v_total_nominal, - v_total_nominal); - /* Reset the cached variables */ - reset_freesync_state_variables(state); } + } else if (last_render_time_in_us > max_render_time_in_us) { + /* Enter Below the Range */ + if (!in_out_vrr->btr.btr_active && + in_out_vrr->btr.btr_enabled) { + in_out_vrr->btr.btr_active = true; - } - - return false; -} + /* Enter Fixed Refresh mode */ + } else if (!in_out_vrr->fixed.fixed_active && + !in_out_vrr->btr.btr_enabled) { + in_out_vrr->fixed.frame_counter++; -static void set_static_ramp_variables(struct core_freesync *core_freesync, - unsigned int index, bool enable_static_screen) -{ - unsigned int frame_duration = 0; - unsigned int nominal_refresh_rate = core_freesync->map[index].state. - nominal_refresh_rate_in_micro_hz; - unsigned int min_refresh_rate= core_freesync->map[index].caps-> - min_refresh_in_micro_hz; - struct gradual_static_ramp *static_ramp_variables = - &core_freesync->map[index].state.static_ramp; - - /* If we are ENABLING static screen, refresh rate should go DOWN. - * If we are DISABLING static screen, refresh rate should go UP. - */ - if (enable_static_screen) - static_ramp_variables->ramp_direction_is_up = false; - else - static_ramp_variables->ramp_direction_is_up = true; - - /* If ramp is not active, set initial frame duration depending on - * whether we are enabling/disabling static screen mode. If the ramp is - * already active, ramp should continue in the opposite direction - * starting with the current frame duration - */ - if (!static_ramp_variables->ramp_is_active) { - if (enable_static_screen == true) { - /* Going to lower refresh rate, so start from max - * refresh rate (min frame duration) - */ - frame_duration = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - nominal_refresh_rate))); - } else { - /* Going to higher refresh rate, so start from min - * refresh rate (max frame duration) - */ - frame_duration = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - min_refresh_rate))); + if (in_out_vrr->fixed.frame_counter > + FIXED_REFRESH_ENTER_FRAME_COUNT) { + in_out_vrr->fixed.frame_counter = 0; + in_out_vrr->fixed.fixed_active = true; + } } - static_ramp_variables-> - ramp_current_frame_duration_in_ns = frame_duration; - - static_ramp_variables->ramp_is_active = true; } -} - -void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams) -{ - unsigned int index, v_total, inserted_frame_v_total = 0; - unsigned int min_frame_duration_in_ns, vmax, vmin = 0; - struct freesync_state *state; - struct core_freesync *core_freesync = NULL; - struct dc_static_screen_events triggers = {0}; - - if (mod_freesync == NULL) - return; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - if (core_freesync->num_entities == 0) - return; - - index = map_index_from_stream(core_freesync, - streams[0]); - - if (core_freesync->map[index].caps->supported == false) - return; - - state = &core_freesync->map[index].state; - - /* Below the Range Logic */ + /* BTR set to "not active" so disengage */ + if (!in_out_vrr->btr.btr_active) { + in_out_vrr->btr.btr_active = false; + in_out_vrr->btr.inserted_duration_in_us = 0; + in_out_vrr->btr.frames_to_insert = 0; + in_out_vrr->btr.frame_counter = 0; - /* Only execute if in fullscreen mode */ - if (state->fullscreen == true && - core_freesync->map[index].user_enable.enable_for_gaming && - core_freesync->map[index].caps->btr_supported && - state->btr.btr_active) { + /* Restore FreeSync */ + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh(stream, + in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + calc_v_total_from_refresh(stream, + in_out_vrr->min_refresh_in_uhz); + /* BTR set to "active" so engage */ + } else { - /* TODO: pass in flag for Pre-DCE12 ASIC - * in order for frame variable duration to take affect, - * it needs to be done one VSYNC early, which is at - * frameCounter == 1. - * For DCE12 and newer updates to V_TOTAL_MIN/MAX - * will take affect on current frame + /* Calculate number of midPoint frames that could fit within + * the render time interval- take ceil of this value */ - if (state->btr.frames_to_insert == state->btr.frame_counter) { - - min_frame_duration_in_ns = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - state->nominal_refresh_rate_in_micro_hz))); - - vmin = state->freesync_range.vmin; - - inserted_frame_v_total = vmin; - - if (min_frame_duration_in_ns / 1000) - inserted_frame_v_total = - state->btr.inserted_frame_duration_in_us * - vmin / (min_frame_duration_in_ns / 1000); - - /* Set length of inserted frames as v_total_max*/ - vmax = inserted_frame_v_total; - vmin = inserted_frame_v_total; + mid_point_frames_ceil = (last_render_time_in_us + + in_out_vrr->btr.mid_point_in_us - 1) / + in_out_vrr->btr.mid_point_in_us; - /* Program V_TOTAL */ - adjust_vmin_vmax(core_freesync, streams, - num_streams, index, - vmin, vmax); + if (mid_point_frames_ceil > 0) { + frame_time_in_us = last_render_time_in_us / + mid_point_frames_ceil; + delta_from_mid_point_in_us_1 = + (in_out_vrr->btr.mid_point_in_us > + frame_time_in_us) ? + (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) : + (frame_time_in_us - in_out_vrr->btr.mid_point_in_us); } - if (state->btr.frame_counter > 0) - state->btr.frame_counter--; - - /* Restore FreeSync */ - if (state->btr.frame_counter == 0) - set_freesync_on_streams(core_freesync, streams, num_streams); - } - - /* If in fullscreen freesync mode or in video, do not program - * static screen ramp values - */ - if (state->fullscreen == true || state->video == true) { - - state->static_ramp.ramp_is_active = false; - - return; - } - - /* Gradual Static Screen Ramping Logic */ + /* Calculate number of midPoint frames that could fit within + * the render time interval- take floor of this value + */ + mid_point_frames_floor = last_render_time_in_us / + in_out_vrr->btr.mid_point_in_us; - /* Execute if ramp is active and user enabled freesync static screen*/ - if (state->static_ramp.ramp_is_active && - core_freesync->map[index].user_enable.enable_for_static) { + if (mid_point_frames_floor > 0) { - calc_v_total_for_static_ramp(core_freesync, streams[0], - index, &v_total); + frame_time_in_us = last_render_time_in_us / + mid_point_frames_floor; + delta_from_mid_point_in_us_2 = + (in_out_vrr->btr.mid_point_in_us > + frame_time_in_us) ? + (in_out_vrr->btr.mid_point_in_us - frame_time_in_us) : + (frame_time_in_us - in_out_vrr->btr.mid_point_in_us); + } - /* Update the freesync context for the stream */ - update_stream_freesync_context(core_freesync, streams[0]); + /* Choose number of frames to insert based on how close it + * can get to the mid point of the variable range. + */ + if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) + frames_to_insert = mid_point_frames_ceil; + else + frames_to_insert = mid_point_frames_floor; - /* Program static screen ramp values */ - adjust_vmin_vmax(core_freesync, streams, - num_streams, index, - v_total, - v_total); + /* Either we've calculated the number of frames to insert, + * or we need to insert min duration frames + */ + if (frames_to_insert > 0) + inserted_frame_duration_in_us = last_render_time_in_us / + frames_to_insert; - triggers.overlay_update = true; - triggers.surface_update = true; + if (inserted_frame_duration_in_us < + (1000000 / in_out_vrr->max_refresh_in_uhz)) + inserted_frame_duration_in_us = + (1000000 / in_out_vrr->max_refresh_in_uhz); - dc_stream_set_static_screen_events(core_freesync->dc, streams, - num_streams, &triggers); + /* Cache the calculated variables */ + in_out_vrr->btr.inserted_duration_in_us = + inserted_frame_duration_in_us; + in_out_vrr->btr.frames_to_insert = frames_to_insert; + in_out_vrr->btr.frame_counter = frames_to_insert; } } -void mod_freesync_update_state(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - struct mod_freesync_params *freesync_params) +static void apply_fixed_refresh(struct core_freesync *core_freesync, + const struct dc_stream_state *stream, + unsigned int last_render_time_in_us, + struct mod_vrr_params *in_out_vrr) { - bool freesync_program_required = false; - unsigned int stream_index; - struct freesync_state *state; - struct core_freesync *core_freesync = NULL; - struct dc_static_screen_events triggers = {0}; + bool update = false; + unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; - if (mod_freesync == NULL) - return; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - - if (core_freesync->num_entities == 0) - return; - - for(stream_index = 0; stream_index < num_streams; stream_index++) { - - unsigned int map_index = map_index_from_stream(core_freesync, - streams[stream_index]); - - bool is_embedded = dc_is_embedded_signal( - streams[stream_index]->sink->sink_signal); - - struct freesync_registry_options *opts = &core_freesync->opts; - - state = &core_freesync->map[map_index].state; + if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) { + /* Exit Fixed Refresh mode */ + if (in_out_vrr->fixed.fixed_active) { + in_out_vrr->fixed.frame_counter++; - switch (freesync_params->state){ - case FREESYNC_STATE_FULLSCREEN: - state->fullscreen = freesync_params->enable; - freesync_program_required = true; - state->windowed_fullscreen = - freesync_params->windowed_fullscreen; - break; - case FREESYNC_STATE_STATIC_SCREEN: - /* Static screen ramp is disabled by default, but can - * be enabled through regkey. - */ - if ((is_embedded && opts->drr_internal_supported) || - (!is_embedded && opts->drr_external_supported)) - - if (state->static_screen != - freesync_params->enable) { - - /* Change the state flag */ - state->static_screen = - freesync_params->enable; - - /* Update static screen ramp */ - set_static_ramp_variables(core_freesync, - map_index, - freesync_params->enable); - } - /* We program the ramp starting next VUpdate */ - break; - case FREESYNC_STATE_VIDEO: - /* Change core variables only if there is a change*/ - if(freesync_params->update_duration_in_ns != - state->time.update_duration_in_ns) { - - state->video = freesync_params->enable; - state->time.update_duration_in_ns = - freesync_params->update_duration_in_ns; - - freesync_program_required = true; + if (in_out_vrr->fixed.frame_counter > + FIXED_REFRESH_EXIT_FRAME_COUNT) { + in_out_vrr->fixed.frame_counter = 0; + in_out_vrr->fixed.fixed_active = false; + in_out_vrr->fixed.target_refresh_in_uhz = 0; + update = true; } - break; - case FREESYNC_STATE_NONE: - /* handle here to avoid warning */ - break; } - } - - /* Update mask */ - triggers.overlay_update = true; - triggers.surface_update = true; - - dc_stream_set_static_screen_events(core_freesync->dc, streams, - num_streams, &triggers); - - if (freesync_program_required) - /* Program freesync according to current state*/ - set_freesync_on_streams(core_freesync, streams, num_streams); -} - - -bool mod_freesync_get_state(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - struct mod_freesync_params *freesync_params) -{ - unsigned int index = 0; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; + } else if (last_render_time_in_us > max_render_time_in_us) { + /* Enter Fixed Refresh mode */ + if (!in_out_vrr->fixed.fixed_active) { + in_out_vrr->fixed.frame_counter++; - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - if (core_freesync->map[index].state.fullscreen) { - freesync_params->state = FREESYNC_STATE_FULLSCREEN; - freesync_params->enable = true; - } else if (core_freesync->map[index].state.static_screen) { - freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; - freesync_params->enable = true; - } else if (core_freesync->map[index].state.video) { - freesync_params->state = FREESYNC_STATE_VIDEO; - freesync_params->enable = true; - } else { - freesync_params->state = FREESYNC_STATE_NONE; - freesync_params->enable = false; + if (in_out_vrr->fixed.frame_counter > + FIXED_REFRESH_ENTER_FRAME_COUNT) { + in_out_vrr->fixed.frame_counter = 0; + in_out_vrr->fixed.fixed_active = true; + in_out_vrr->fixed.target_refresh_in_uhz = + in_out_vrr->max_refresh_in_uhz; + update = true; + } + } } - freesync_params->update_duration_in_ns = - core_freesync->map[index].state.time.update_duration_in_ns; - - freesync_params->windowed_fullscreen = - core_freesync->map[index].state.windowed_fullscreen; - - return true; -} - -bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - struct mod_freesync_user_enable *user_enable) -{ - unsigned int stream_index, map_index; - int persistent_data = 0; - struct persistent_data_flag flag; - struct dc *dc = NULL; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - dc = core_freesync->dc; - - flag.save_per_edid = true; - flag.save_per_link = false; - - for(stream_index = 0; stream_index < num_streams; - stream_index++){ - - map_index = map_index_from_stream(core_freesync, - streams[stream_index]); - - core_freesync->map[map_index].user_enable = *user_enable; - - /* Write persistent data in registry*/ - if (core_freesync->map[map_index].user_enable. - enable_for_gaming) - persistent_data = persistent_data | 1; - if (core_freesync->map[map_index].user_enable. - enable_for_static) - persistent_data = persistent_data | 2; - if (core_freesync->map[map_index].user_enable. - enable_for_video) - persistent_data = persistent_data | 4; - - dm_write_persistent_data(dc->ctx, - streams[stream_index]->sink, - FREESYNC_REGISTRY_NAME, - "userenable", - &persistent_data, - sizeof(int), - &flag); + if (update) { + if (in_out_vrr->fixed.fixed_active) { + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh( + stream, in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + in_out_vrr->adjust.v_total_min; + } else { + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh(stream, + in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + calc_v_total_from_refresh(stream, + in_out_vrr->min_refresh_in_uhz); + } } - - set_freesync_on_streams(core_freesync, streams, num_streams); - - return true; } -bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - struct mod_freesync_user_enable *user_enable) -{ - unsigned int index = 0; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - *user_enable = core_freesync->map[index].user_enable; - - return true; -} - -bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - bool *is_ramp_active) -{ - unsigned int index = 0; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - *is_ramp_active = - core_freesync->map[index].state.static_ramp.ramp_is_active; - - return true; -} - -bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, - struct dc_stream_state *streams, - unsigned int min_refresh, - unsigned int max_refresh, - struct mod_freesync_caps *caps) +static bool vrr_settings_require_update(struct core_freesync *core_freesync, + struct mod_freesync_config *in_config, + unsigned int min_refresh_in_uhz, + unsigned int max_refresh_in_uhz, + struct mod_vrr_params *in_vrr) { - unsigned int index = 0; - struct core_freesync *core_freesync; - struct freesync_state *state; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, streams); - state = &core_freesync->map[index].state; - - if (max_refresh == 0) - max_refresh = state->nominal_refresh_rate_in_micro_hz; - - if (min_refresh == 0) { - /* Restore defaults */ - calc_freesync_range(core_freesync, streams, state, - core_freesync->map[index].caps-> - min_refresh_in_micro_hz, - state->nominal_refresh_rate_in_micro_hz); - } else { - calc_freesync_range(core_freesync, streams, - state, - min_refresh, - max_refresh); - - /* Program vtotal min/max */ - adjust_vmin_vmax(core_freesync, &streams, 1, index, - state->freesync_range.vmin, - state->freesync_range.vmax); - } - - if (min_refresh != 0 && - dc_is_embedded_signal(streams->sink->sink_signal) && - (max_refresh - min_refresh >= 10000000)) { - caps->supported = true; - caps->min_refresh_in_micro_hz = min_refresh; - caps->max_refresh_in_micro_hz = max_refresh; + if (in_vrr->state != in_config->state) { + return true; + } else if (in_vrr->state == VRR_STATE_ACTIVE_FIXED && + in_vrr->fixed.target_refresh_in_uhz != + in_config->min_refresh_in_uhz) { + return true; + } else if (in_vrr->min_refresh_in_uhz != min_refresh_in_uhz) { + return true; + } else if (in_vrr->max_refresh_in_uhz != max_refresh_in_uhz) { + return true; } - /* Update the stream */ - update_stream(core_freesync, streams); - - return true; -} - -bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - unsigned int *min_refresh, - unsigned int *max_refresh) -{ - unsigned int index = 0; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - *min_refresh = - core_freesync->map[index].state.freesync_range.min_refresh; - *max_refresh = - core_freesync->map[index].state.freesync_range.max_refresh; - - return true; + return false; } bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, + const struct dc_stream_state *stream, unsigned int *vmin, unsigned int *vmax) { - unsigned int index = 0; - struct core_freesync *core_freesync = NULL; - - if (mod_freesync == NULL) - return false; - - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); - - *vmin = - core_freesync->map[index].state.freesync_range.vmin; - *vmax = - core_freesync->map[index].state.freesync_range.vmax; + *vmin = stream->adjust.v_total_min; + *vmax = stream->adjust.v_total_max; return true; } @@ -1189,7 +459,6 @@ bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, unsigned int *nom_v_pos, unsigned int *v_pos) { - unsigned int index = 0; struct core_freesync *core_freesync = NULL; struct crtc_position position; @@ -1197,7 +466,6 @@ bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, return false; core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - index = map_index_from_stream(core_freesync, stream); if (dc_stream_get_crtc_position(core_freesync->dc, &stream, 1, &position.vertical_count, @@ -1212,310 +480,363 @@ bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, return false; } -void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams) +void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) { - unsigned int stream_index, map_index; - struct freesync_state *state; - struct core_freesync *core_freesync = NULL; - struct dc_static_screen_events triggers = {0}; - unsigned long long temp = 0; + /* SPD info packet for FreeSync */ + unsigned char checksum = 0; + unsigned int idx, payload_size = 0; - if (mod_freesync == NULL) + /* Check if Freesync is supported. Return if false. If true, + * set the corresponding bit in the info packet + */ + if (!vrr->supported || !vrr->send_vsif) return; - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + if (dc_is_hdmi_signal(stream->signal)) { - for (stream_index = 0; stream_index < num_streams; stream_index++) { - map_index = map_index_from_stream(core_freesync, - streams[stream_index]); + /* HEADER */ - state = &core_freesync->map[map_index].state; + /* HB0 = Packet Type = 0x83 (Source Product + * Descriptor InfoFrame) + */ + infopacket->hb0 = DC_HDMI_INFOFRAME_TYPE_SPD; - /* Update the field rate for new timing */ - temp = streams[stream_index]->timing.pix_clk_khz; - temp *= 1000ULL * 1000ULL * 1000ULL; - temp = div_u64(temp, - streams[stream_index]->timing.h_total); - temp = div_u64(temp, - streams[stream_index]->timing.v_total); - state->nominal_refresh_rate_in_micro_hz = - (unsigned int) temp; + /* HB1 = Version = 0x01 */ + infopacket->hb1 = 0x01; - if (core_freesync->map[map_index].caps->supported) { + /* HB2 = [Bits 7:5 = 0] [Bits 4:0 = Length = 0x08] */ + infopacket->hb2 = 0x08; - /* Update the stream */ - update_stream(core_freesync, streams[stream_index]); + payload_size = 0x08; - /* Calculate vmin/vmax and refresh rate for - * current mode - */ - calc_freesync_range(core_freesync, *streams, state, - core_freesync->map[map_index].caps-> - min_refresh_in_micro_hz, - state->nominal_refresh_rate_in_micro_hz); - - /* Update mask */ - triggers.overlay_update = true; - triggers.surface_update = true; - - dc_stream_set_static_screen_events(core_freesync->dc, - streams, num_streams, - &triggers); - } - } + } else if (dc_is_dp_signal(stream->signal)) { - /* Program freesync according to current state*/ - set_freesync_on_streams(core_freesync, streams, num_streams); -} + /* HEADER */ -/* Add the timestamps to the cache and determine whether BTR programming - * is required, depending on the times calculated - */ -static void update_timestamps(struct core_freesync *core_freesync, - const struct dc_stream_state *stream, unsigned int map_index, - unsigned int last_render_time_in_us) -{ - struct freesync_state *state = &core_freesync->map[map_index].state; + /* HB0 = Secondary-data Packet ID = 0 - Only non-zero + * when used to associate audio related info packets + */ + infopacket->hb0 = 0x00; - state->time.render_times[state->time.render_times_index] = - last_render_time_in_us; - state->time.render_times_index++; + /* HB1 = Packet Type = 0x83 (Source Product + * Descriptor InfoFrame) + */ + infopacket->hb1 = DC_HDMI_INFOFRAME_TYPE_SPD; - if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) - state->time.render_times_index = 0; + /* HB2 = [Bits 7:0 = Least significant eight bits - + * For INFOFRAME, the value must be 1Bh] + */ + infopacket->hb2 = 0x1B; - if (last_render_time_in_us + BTR_EXIT_MARGIN < - state->time.max_render_time_in_us) { + /* HB3 = [Bits 7:2 = INFOFRAME SDP Version Number = 0x1] + * [Bits 1:0 = Most significant two bits = 0x00] + */ + infopacket->hb3 = 0x04; - /* Exit Below the Range */ - if (state->btr.btr_active) { + payload_size = 0x1B; + } - state->btr.program_btr = true; - state->btr.btr_active = false; - state->btr.frame_counter = 0; + /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ + infopacket->sb[1] = 0x1A; - /* Exit Fixed Refresh mode */ - } else if (state->fixed_refresh.fixed_active) { + /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ + infopacket->sb[2] = 0x00; - state->fixed_refresh.frame_counter++; + /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ + infopacket->sb[3] = 0x00; - if (state->fixed_refresh.frame_counter > - FIXED_REFRESH_EXIT_FRAME_COUNT) { - state->fixed_refresh.frame_counter = 0; - state->fixed_refresh.program_fixed = true; - state->fixed_refresh.fixed_active = false; - } - } + /* PB4 = Reserved */ - } else if (last_render_time_in_us > state->time.max_render_time_in_us) { + /* PB5 = Reserved */ - /* Enter Below the Range */ - if (!state->btr.btr_active && - core_freesync->map[map_index].caps->btr_supported) { + /* PB6 = [Bits 7:3 = Reserved] */ - state->btr.program_btr = true; - state->btr.btr_active = true; + /* PB6 = [Bit 0 = FreeSync Supported] */ + if (vrr->state != VRR_STATE_UNSUPPORTED) + infopacket->sb[6] |= 0x01; - /* Enter Fixed Refresh mode */ - } else if (!state->fixed_refresh.fixed_active && - !core_freesync->map[map_index].caps->btr_supported) { + /* PB6 = [Bit 1 = FreeSync Enabled] */ + if (vrr->state != VRR_STATE_DISABLED && + vrr->state != VRR_STATE_UNSUPPORTED) + infopacket->sb[6] |= 0x02; - state->fixed_refresh.frame_counter++; + /* PB6 = [Bit 2 = FreeSync Active] */ + if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || + vrr->state == VRR_STATE_ACTIVE_FIXED) + infopacket->sb[6] |= 0x04; - if (state->fixed_refresh.frame_counter > - FIXED_REFRESH_ENTER_FRAME_COUNT) { - state->fixed_refresh.frame_counter = 0; - state->fixed_refresh.program_fixed = true; - state->fixed_refresh.fixed_active = true; - } - } - } + /* PB7 = FreeSync Minimum refresh rate (Hz) */ + infopacket->sb[7] = (unsigned char)(vrr->min_refresh_in_uhz / 1000000); - /* When Below the Range is active, must react on every frame */ - if (state->btr.btr_active) - state->btr.program_btr = true; -} + /* PB8 = FreeSync Maximum refresh rate (Hz) + * Note: We should never go above the field rate of the mode timing set. + */ + infopacket->sb[8] = (unsigned char)(vrr->max_refresh_in_uhz / 1000000); -static void apply_below_the_range(struct core_freesync *core_freesync, - struct dc_stream_state *stream, unsigned int map_index, - unsigned int last_render_time_in_us) -{ - unsigned int inserted_frame_duration_in_us = 0; - unsigned int mid_point_frames_ceil = 0; - unsigned int mid_point_frames_floor = 0; - unsigned int frame_time_in_us = 0; - unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; - unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; - unsigned int frames_to_insert = 0; - unsigned int min_frame_duration_in_ns = 0; - struct freesync_state *state = &core_freesync->map[map_index].state; + /* PB9 - PB27 = Reserved */ - if (!state->btr.program_btr) - return; + /* Calculate checksum */ + checksum += infopacket->hb0; + checksum += infopacket->hb1; + checksum += infopacket->hb2; + checksum += infopacket->hb3; - state->btr.program_btr = false; + for (idx = 1; idx <= payload_size; idx++) + checksum += infopacket->sb[idx]; - min_frame_duration_in_ns = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - state->nominal_refresh_rate_in_micro_hz))); + /* PB0 = Checksum (one byte complement) */ + infopacket->sb[0] = (unsigned char)(0x100 - checksum); - /* Program BTR */ + infopacket->valid = true; +} - /* BTR set to "not active" so disengage */ - if (!state->btr.btr_active) +void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + struct mod_freesync_config *in_config, + struct mod_vrr_params *in_out_vrr) +{ + struct core_freesync *core_freesync = NULL; + unsigned long long nominal_field_rate_in_uhz = 0; + unsigned int refresh_range = 0; + unsigned int min_refresh_in_uhz = 0; + unsigned int max_refresh_in_uhz = 0; - /* Restore FreeSync */ - set_freesync_on_streams(core_freesync, &stream, 1); + if (mod_freesync == NULL) + return; - /* BTR set to "active" so engage */ - else { + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - /* Calculate number of midPoint frames that could fit within - * the render time interval- take ceil of this value - */ - mid_point_frames_ceil = (last_render_time_in_us + - state->btr.mid_point_in_us- 1) / - state->btr.mid_point_in_us; + /* Calculate nominal field rate for stream */ + nominal_field_rate_in_uhz = + mod_freesync_calc_nominal_field_rate(stream); - if (mid_point_frames_ceil > 0) { + min_refresh_in_uhz = in_config->min_refresh_in_uhz; + max_refresh_in_uhz = in_config->max_refresh_in_uhz; - frame_time_in_us = last_render_time_in_us / - mid_point_frames_ceil; - delta_from_mid_point_in_us_1 = - (state->btr.mid_point_in_us > - frame_time_in_us) ? - (state->btr.mid_point_in_us - frame_time_in_us): - (frame_time_in_us - state->btr.mid_point_in_us); - } + // Don't allow min > max + if (min_refresh_in_uhz > max_refresh_in_uhz) + min_refresh_in_uhz = max_refresh_in_uhz; - /* Calculate number of midPoint frames that could fit within - * the render time interval- take floor of this value - */ - mid_point_frames_floor = last_render_time_in_us / - state->btr.mid_point_in_us; + // Full range may be larger than current video timing, so cap at nominal + if (max_refresh_in_uhz > nominal_field_rate_in_uhz) + max_refresh_in_uhz = nominal_field_rate_in_uhz; - if (mid_point_frames_floor > 0) { + // Full range may be larger than current video timing, so cap at nominal + if (min_refresh_in_uhz > nominal_field_rate_in_uhz) + min_refresh_in_uhz = nominal_field_rate_in_uhz; - frame_time_in_us = last_render_time_in_us / - mid_point_frames_floor; - delta_from_mid_point_in_us_2 = - (state->btr.mid_point_in_us > - frame_time_in_us) ? - (state->btr.mid_point_in_us - frame_time_in_us): - (frame_time_in_us - state->btr.mid_point_in_us); - } + if (!vrr_settings_require_update(core_freesync, + in_config, min_refresh_in_uhz, max_refresh_in_uhz, + in_out_vrr)) + return; - /* Choose number of frames to insert based on how close it - * can get to the mid point of the variable range. - */ - if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) - frames_to_insert = mid_point_frames_ceil; - else - frames_to_insert = mid_point_frames_floor; + in_out_vrr->state = in_config->state; + in_out_vrr->send_vsif = in_config->vsif_supported; - /* Either we've calculated the number of frames to insert, - * or we need to insert min duration frames - */ - if (frames_to_insert > 0) - inserted_frame_duration_in_us = last_render_time_in_us / - frames_to_insert; + if (in_config->state == VRR_STATE_UNSUPPORTED) { + in_out_vrr->state = VRR_STATE_UNSUPPORTED; + in_out_vrr->supported = false; + in_out_vrr->adjust.v_total_min = stream->timing.v_total; + in_out_vrr->adjust.v_total_max = stream->timing.v_total; - if (inserted_frame_duration_in_us < - state->time.min_render_time_in_us) + return; - inserted_frame_duration_in_us = - state->time.min_render_time_in_us; + } else { + in_out_vrr->min_refresh_in_uhz = min_refresh_in_uhz; + in_out_vrr->max_duration_in_us = + calc_duration_in_us_from_refresh_in_uhz( + min_refresh_in_uhz); - /* Cache the calculated variables */ - state->btr.inserted_frame_duration_in_us = - inserted_frame_duration_in_us; - state->btr.frames_to_insert = frames_to_insert; - state->btr.frame_counter = frames_to_insert; + in_out_vrr->max_refresh_in_uhz = max_refresh_in_uhz; + in_out_vrr->min_duration_in_us = + calc_duration_in_us_from_refresh_in_uhz( + max_refresh_in_uhz); + + refresh_range = in_out_vrr->max_refresh_in_uhz - + in_out_vrr->min_refresh_in_uhz; + in_out_vrr->supported = true; + } + + in_out_vrr->fixed.ramping_active = in_config->ramping; + + in_out_vrr->btr.btr_enabled = in_config->btr; + if (in_out_vrr->max_refresh_in_uhz < + 2 * in_out_vrr->min_refresh_in_uhz) + in_out_vrr->btr.btr_enabled = false; + in_out_vrr->btr.btr_active = false; + in_out_vrr->btr.inserted_duration_in_us = 0; + in_out_vrr->btr.frames_to_insert = 0; + in_out_vrr->btr.frame_counter = 0; + in_out_vrr->btr.mid_point_in_us = + in_out_vrr->min_duration_in_us + + (in_out_vrr->max_duration_in_us - + in_out_vrr->min_duration_in_us) / 2; + + if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) { + in_out_vrr->adjust.v_total_min = stream->timing.v_total; + in_out_vrr->adjust.v_total_max = stream->timing.v_total; + } else if (in_out_vrr->state == VRR_STATE_DISABLED) { + in_out_vrr->adjust.v_total_min = stream->timing.v_total; + in_out_vrr->adjust.v_total_max = stream->timing.v_total; + } else if (in_out_vrr->state == VRR_STATE_INACTIVE) { + in_out_vrr->adjust.v_total_min = stream->timing.v_total; + in_out_vrr->adjust.v_total_max = stream->timing.v_total; + } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && + refresh_range >= MIN_REFRESH_RANGE_IN_US) { + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh(stream, + in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + calc_v_total_from_refresh(stream, + in_out_vrr->min_refresh_in_uhz); + } else if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED) { + in_out_vrr->fixed.target_refresh_in_uhz = + in_out_vrr->min_refresh_in_uhz; + if (in_out_vrr->fixed.ramping_active && + in_out_vrr->fixed.fixed_active) { + /* Do not update vtotals if ramping is already active + * in order to continue ramp from current refresh. + */ + in_out_vrr->fixed.fixed_active = true; + } else { + in_out_vrr->fixed.fixed_active = true; + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh(stream, + in_out_vrr->fixed.target_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + in_out_vrr->adjust.v_total_min; + } + } else { + in_out_vrr->state = VRR_STATE_INACTIVE; + in_out_vrr->adjust.v_total_min = stream->timing.v_total; + in_out_vrr->adjust.v_total_max = stream->timing.v_total; } } -static void apply_fixed_refresh(struct core_freesync *core_freesync, - struct dc_stream_state *stream, unsigned int map_index) +void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, + const struct dc_plane_state *plane, + const struct dc_stream_state *stream, + unsigned int curr_time_stamp_in_us, + struct mod_vrr_params *in_out_vrr) { - unsigned int vmin = 0, vmax = 0; - struct freesync_state *state = &core_freesync->map[map_index].state; + struct core_freesync *core_freesync = NULL; + unsigned int last_render_time_in_us = 0; + unsigned int average_render_time_in_us = 0; - if (!state->fixed_refresh.program_fixed) + if (mod_freesync == NULL) return; - state->fixed_refresh.program_fixed = false; + core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); + + if (in_out_vrr->supported && + in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) { + unsigned int i = 0; + unsigned int oldest_index = plane->time.index + 1; - /* Program Fixed Refresh */ + if (oldest_index >= DC_PLANE_UPDATE_TIMES_MAX) + oldest_index = 0; - /* Fixed Refresh set to "not active" so disengage */ - if (!state->fixed_refresh.fixed_active) { - set_freesync_on_streams(core_freesync, &stream, 1); + last_render_time_in_us = curr_time_stamp_in_us - + plane->time.prev_update_time_in_us; - /* Fixed Refresh set to "active" so engage (fix to max) */ - } else { + // Sum off all entries except oldest one + for (i = 0; i < DC_PLANE_UPDATE_TIMES_MAX; i++) { + average_render_time_in_us += + plane->time.time_elapsed_in_us[i]; + } + average_render_time_in_us -= + plane->time.time_elapsed_in_us[oldest_index]; + + // Add render time for current flip + average_render_time_in_us += last_render_time_in_us; + average_render_time_in_us /= DC_PLANE_UPDATE_TIMES_MAX; + + if (in_out_vrr->btr.btr_enabled) { + apply_below_the_range(core_freesync, + stream, + last_render_time_in_us, + in_out_vrr); + } else { + apply_fixed_refresh(core_freesync, + stream, + last_render_time_in_us, + in_out_vrr); + } - vmin = state->freesync_range.vmin; - vmax = vmin; - adjust_vmin_vmax(core_freesync, &stream, map_index, - 1, vmin, vmax); } } -void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - unsigned int curr_time_stamp_in_us) +void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + struct mod_vrr_params *in_out_vrr) { - unsigned int stream_index, map_index, last_render_time_in_us = 0; struct core_freesync *core_freesync = NULL; - if (mod_freesync == NULL) + if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) return; core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - for (stream_index = 0; stream_index < num_streams; stream_index++) { - - map_index = map_index_from_stream(core_freesync, - streams[stream_index]); + if (in_out_vrr->supported == false) + return; - if (core_freesync->map[map_index].caps->supported) { + /* Below the Range Logic */ - last_render_time_in_us = curr_time_stamp_in_us - - core_freesync->map[map_index].state.time. - prev_time_stamp_in_us; + /* Only execute if in fullscreen mode */ + if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && + in_out_vrr->btr.btr_active) { + /* TODO: pass in flag for Pre-DCE12 ASIC + * in order for frame variable duration to take affect, + * it needs to be done one VSYNC early, which is at + * frameCounter == 1. + * For DCE12 and newer updates to V_TOTAL_MIN/MAX + * will take affect on current frame + */ + if (in_out_vrr->btr.frames_to_insert == + in_out_vrr->btr.frame_counter) { + in_out_vrr->adjust.v_total_min = + calc_v_total_from_duration(stream, + in_out_vrr, + in_out_vrr->btr.inserted_duration_in_us); + in_out_vrr->adjust.v_total_max = + in_out_vrr->adjust.v_total_min; + } - /* Add the timestamps to the cache and determine - * whether BTR program is required - */ - update_timestamps(core_freesync, streams[stream_index], - map_index, last_render_time_in_us); - - if (core_freesync->map[map_index].state.fullscreen && - core_freesync->map[map_index].user_enable. - enable_for_gaming) { - - if (core_freesync->map[map_index].caps->btr_supported) { - - apply_below_the_range(core_freesync, - streams[stream_index], map_index, - last_render_time_in_us); - } else { - apply_fixed_refresh(core_freesync, - streams[stream_index], map_index); - } - } + if (in_out_vrr->btr.frame_counter > 0) + in_out_vrr->btr.frame_counter--; - core_freesync->map[map_index].state.time. - prev_time_stamp_in_us = curr_time_stamp_in_us; + /* Restore FreeSync */ + if (in_out_vrr->btr.frame_counter == 0) { + in_out_vrr->adjust.v_total_min = + calc_v_total_from_refresh(stream, + in_out_vrr->max_refresh_in_uhz); + in_out_vrr->adjust.v_total_max = + calc_v_total_from_refresh(stream, + in_out_vrr->min_refresh_in_uhz); } + } + + /* If in fullscreen freesync mode or in video, do not program + * static screen ramp values + */ + if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE) + in_out_vrr->fixed.ramping_active = false; + /* Gradual Static Screen Ramping Logic */ + /* Execute if ramp is active and user enabled freesync static screen*/ + if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED && + in_out_vrr->fixed.ramping_active) { + update_v_total_for_static_ramp( + core_freesync, stream, in_out_vrr); } } void mod_freesync_get_settings(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, + const struct mod_vrr_params *vrr, unsigned int *v_total_min, unsigned int *v_total_max, unsigned int *event_triggers, unsigned int *window_min, unsigned int *window_max, @@ -1523,7 +844,6 @@ void mod_freesync_get_settings(struct mod_freesync *mod_freesync, unsigned int *inserted_frames, unsigned int *inserted_duration_in_us) { - unsigned int stream_index, map_index; struct core_freesync *core_freesync = NULL; if (mod_freesync == NULL) @@ -1531,25 +851,111 @@ void mod_freesync_get_settings(struct mod_freesync *mod_freesync, core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - for (stream_index = 0; stream_index < num_streams; stream_index++) { - - map_index = map_index_from_stream(core_freesync, - streams[stream_index]); - - if (core_freesync->map[map_index].caps->supported) { - struct freesync_state state = - core_freesync->map[map_index].state; - *v_total_min = state.vmin; - *v_total_max = state.vmax; - *event_triggers = 0; - *window_min = state.time.min_window; - *window_max = state.time.max_window; - *lfc_mid_point_in_us = state.btr.mid_point_in_us; - *inserted_frames = state.btr.frames_to_insert; - *inserted_duration_in_us = - state.btr.inserted_frame_duration_in_us; - } - + if (vrr->supported) { + *v_total_min = vrr->adjust.v_total_min; + *v_total_max = vrr->adjust.v_total_max; + *event_triggers = 0; + *lfc_mid_point_in_us = vrr->btr.mid_point_in_us; + *inserted_frames = vrr->btr.frames_to_insert; + *inserted_duration_in_us = vrr->btr.inserted_duration_in_us; } } +unsigned long long mod_freesync_calc_nominal_field_rate( + const struct dc_stream_state *stream) +{ + unsigned long long nominal_field_rate_in_uhz = 0; + + /* Calculate nominal field rate for stream */ + nominal_field_rate_in_uhz = stream->timing.pix_clk_khz; + nominal_field_rate_in_uhz *= 1000ULL * 1000ULL * 1000ULL; + nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, + stream->timing.h_total); + nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, + stream->timing.v_total); + + return nominal_field_rate_in_uhz; +} + +bool mod_freesync_is_valid_range(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + uint32_t min_refresh_cap_in_uhz, + uint32_t max_refresh_cap_in_uhz, + uint32_t min_refresh_request_in_uhz, + uint32_t max_refresh_request_in_uhz) +{ + /* Calculate nominal field rate for stream */ + unsigned long long nominal_field_rate_in_uhz = + mod_freesync_calc_nominal_field_rate(stream); + + /* Typically nominal refresh calculated can have some fractional part. + * Allow for some rounding error of actual video timing by taking floor + * of caps and request. Round the nominal refresh rate. + * + * Dividing will convert everything to units in Hz although input + * variable name is in uHz! + * + * Also note, this takes care of rounding error on the nominal refresh + * so by rounding error we only expect it to be off by a small amount, + * such as < 0.1 Hz. i.e. 143.9xxx or 144.1xxx. + * + * Example 1. Caps Min = 40 Hz, Max = 144 Hz + * Request Min = 40 Hz, Max = 144 Hz + * Nominal = 143.5x Hz rounded to 144 Hz + * This function should allow this as valid request + * + * Example 2. Caps Min = 40 Hz, Max = 144 Hz + * Request Min = 40 Hz, Max = 144 Hz + * Nominal = 144.4x Hz rounded to 144 Hz + * This function should allow this as valid request + * + * Example 3. Caps Min = 40 Hz, Max = 144 Hz + * Request Min = 40 Hz, Max = 144 Hz + * Nominal = 120.xx Hz rounded to 120 Hz + * This function should return NOT valid since the requested + * max is greater than current timing's nominal + * + * Example 4. Caps Min = 40 Hz, Max = 120 Hz + * Request Min = 40 Hz, Max = 120 Hz + * Nominal = 144.xx Hz rounded to 144 Hz + * This function should return NOT valid since the nominal + * is greater than the capability's max refresh + */ + nominal_field_rate_in_uhz = + div_u64(nominal_field_rate_in_uhz + 500000, 1000000); + min_refresh_cap_in_uhz /= 1000000; + max_refresh_cap_in_uhz /= 1000000; + min_refresh_request_in_uhz /= 1000000; + max_refresh_request_in_uhz /= 1000000; + + // Check nominal is within range + if (nominal_field_rate_in_uhz > max_refresh_cap_in_uhz || + nominal_field_rate_in_uhz < min_refresh_cap_in_uhz) + return false; + + // If nominal is less than max, limit the max allowed refresh rate + if (nominal_field_rate_in_uhz < max_refresh_cap_in_uhz) + max_refresh_cap_in_uhz = nominal_field_rate_in_uhz; + + // Don't allow min > max + if (min_refresh_request_in_uhz > max_refresh_request_in_uhz) + return false; + + // Check min is within range + if (min_refresh_request_in_uhz > max_refresh_cap_in_uhz || + min_refresh_request_in_uhz < min_refresh_cap_in_uhz) + return false; + + // Check max is within range + if (max_refresh_request_in_uhz > max_refresh_cap_in_uhz || + max_refresh_request_in_uhz < min_refresh_cap_in_uhz) + return false; + + // For variable range, check for at least 10 Hz range + if ((max_refresh_request_in_uhz != min_refresh_request_in_uhz) && + (max_refresh_request_in_uhz - min_refresh_request_in_uhz < 10)) + return false; + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h index f083e1619dbe..a0f32cde721c 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -56,96 +56,74 @@ #include "dm_services.h" -struct mod_freesync *mod_freesync_create(struct dc *dc); -void mod_freesync_destroy(struct mod_freesync *mod_freesync); - +// Access structures struct mod_freesync { int dummy; }; -enum mod_freesync_state { - FREESYNC_STATE_NONE, - FREESYNC_STATE_FULLSCREEN, - FREESYNC_STATE_STATIC_SCREEN, - FREESYNC_STATE_VIDEO -}; - -enum mod_freesync_user_enable_mask { - FREESYNC_USER_ENABLE_STATIC = 0x1, - FREESYNC_USER_ENABLE_VIDEO = 0x2, - FREESYNC_USER_ENABLE_GAMING = 0x4 -}; - -struct mod_freesync_user_enable { - bool enable_for_static; - bool enable_for_video; - bool enable_for_gaming; -}; - +// TODO: References to this should be removed struct mod_freesync_caps { bool supported; unsigned int min_refresh_in_micro_hz; unsigned int max_refresh_in_micro_hz; - - bool btr_supported; }; -struct mod_freesync_params { - enum mod_freesync_state state; - bool enable; - unsigned int update_duration_in_ns; - bool windowed_fullscreen; +enum mod_vrr_state { + VRR_STATE_UNSUPPORTED = 0, + VRR_STATE_DISABLED, + VRR_STATE_INACTIVE, + VRR_STATE_ACTIVE_VARIABLE, + VRR_STATE_ACTIVE_FIXED }; -/* - * Add stream to be tracked by module - */ -bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, struct mod_freesync_caps *caps); +struct mod_freesync_config { + enum mod_vrr_state state; + bool vsif_supported; + bool ramping; + bool btr; + unsigned int min_refresh_in_uhz; + unsigned int max_refresh_in_uhz; +}; -/* - * Remove stream to be tracked by module - */ -bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream); +struct mod_vrr_params_btr { + bool btr_enabled; + bool btr_active; + uint32_t mid_point_in_us; + uint32_t inserted_duration_in_us; + uint32_t frames_to_insert; + uint32_t frame_counter; +}; -/* - * Update the freesync state flags for each display and program - * freesync accordingly - */ -void mod_freesync_update_state(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - struct mod_freesync_params *freesync_params); +struct mod_vrr_params_fixed_refresh { + bool fixed_active; + bool ramping_active; + bool ramping_done; + uint32_t target_refresh_in_uhz; + uint32_t frame_counter; +}; -bool mod_freesync_get_state(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - struct mod_freesync_params *freesync_params); +struct mod_vrr_params { + bool supported; + bool send_vsif; + enum mod_vrr_state state; -bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - struct mod_freesync_user_enable *user_enable); + uint32_t min_refresh_in_uhz; + uint32_t max_duration_in_us; + uint32_t max_refresh_in_uhz; + uint32_t min_duration_in_us; -bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - struct mod_freesync_user_enable *user_enable); + struct dc_crtc_timing_adjust adjust; -bool mod_freesync_get_static_ramp_active(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - bool *is_ramp_active); + struct mod_vrr_params_fixed_refresh fixed; -bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync, - struct dc_stream_state *streams, - unsigned int min_refresh, - unsigned int max_refresh, - struct mod_freesync_caps *caps); + struct mod_vrr_params_btr btr; +}; -bool mod_freesync_get_min_max(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, - unsigned int *min_refresh, - unsigned int *max_refresh); +struct mod_freesync *mod_freesync_create(struct dc *dc); +void mod_freesync_destroy(struct mod_freesync *mod_freesync); bool mod_freesync_get_vmin_vmax(struct mod_freesync *mod_freesync, - struct dc_stream_state *stream, + const struct dc_stream_state *stream, unsigned int *vmin, unsigned int *vmax); @@ -154,18 +132,8 @@ bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, unsigned int *nom_v_pos, unsigned int *v_pos); -void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams); - -void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams); - -void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, - unsigned int curr_time_stamp); - void mod_freesync_get_settings(struct mod_freesync *mod_freesync, - struct dc_stream_state **streams, int num_streams, + const struct mod_vrr_params *vrr, unsigned int *v_total_min, unsigned int *v_total_max, unsigned int *event_triggers, unsigned int *window_min, unsigned int *window_max, @@ -173,4 +141,34 @@ void mod_freesync_get_settings(struct mod_freesync *mod_freesync, unsigned int *inserted_frames, unsigned int *inserted_duration_in_us); +void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket); + +void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + struct mod_freesync_config *in_config, + struct mod_vrr_params *in_out_vrr); + +void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, + const struct dc_plane_state *plane, + const struct dc_stream_state *stream, + unsigned int curr_time_stamp_in_us, + struct mod_vrr_params *in_out_vrr); + +void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + struct mod_vrr_params *in_out_vrr); + +unsigned long long mod_freesync_calc_nominal_field_rate( + const struct dc_stream_state *stream); + +bool mod_freesync_is_valid_range(struct mod_freesync *mod_freesync, + const struct dc_stream_state *stream, + uint32_t min_refresh_cap_in_uhz, + uint32_t max_refresh_cap_in_uhz, + uint32_t min_refresh_request_in_uhz, + uint32_t max_refresh_request_in_uhz); + #endif diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h new file mode 100644 index 000000000000..786b34380f85 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef MOD_INFO_PACKET_H_ +#define MOD_INFO_PACKET_H_ + +struct info_packet_inputs { + const struct dc_stream_state *pStream; +}; + +struct info_packets { + struct dc_info_packet *pVscInfoPacket; +}; + +void mod_build_infopackets(struct info_packet_inputs *inputs, + struct info_packets *info_packets); + +#endif diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/Makefile b/drivers/gpu/drm/amd/display/modules/info_packet/Makefile new file mode 100644 index 000000000000..4c382d728536 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/info_packet/Makefile @@ -0,0 +1,31 @@ +# +# Copyright 2017 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# +# Makefile for the 'info_packet' sub-module of DAL. +# + +INFO_PACKET = info_packet.o + +AMD_DAL_INFO_PACKET = $(addprefix $(AMDDALPATH)/modules/info_packet/,$(INFO_PACKET)) +#$(info ************ DAL INFO_PACKET MODULE MAKEFILE ************) + +AMD_DISPLAY_FILES += $(AMD_DAL_INFO_PACKET) diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c new file mode 100644 index 000000000000..52378fc69079 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -0,0 +1,279 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "mod_info_packet.h" +#include "core_types.h" + +enum ColorimetryRGBDP { + ColorimetryRGB_DP_sRGB = 0, + ColorimetryRGB_DP_AdobeRGB = 3, + ColorimetryRGB_DP_P3 = 4, + ColorimetryRGB_DP_CustomColorProfile = 5, + ColorimetryRGB_DP_ITU_R_BT2020RGB = 6, +}; +enum ColorimetryYCCDP { + ColorimetryYCC_DP_ITU601 = 0, + ColorimetryYCC_DP_ITU709 = 1, + ColorimetryYCC_DP_AdobeYCC = 5, + ColorimetryYCC_DP_ITU2020YCC = 6, + ColorimetryYCC_DP_ITU2020YCbCr = 7, +}; + +static void mod_build_vsc_infopacket(const struct dc_stream_state *stream, + struct dc_info_packet *info_packet) +{ + unsigned int vscPacketRevision = 0; + unsigned int i; + unsigned int pixelEncoding = 0; + unsigned int colorimetryFormat = 0; + + if (stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE && stream->view_format != VIEW_3D_FORMAT_NONE) + vscPacketRevision = 1; + + /*VSC packet set to 2 when DP revision >= 1.2*/ + if (stream->psr_version != 0) + vscPacketRevision = 2; + + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + vscPacketRevision = 5; + + /* VSC packet not needed based on the features + * supported by this DP display + */ + if (vscPacketRevision == 0) + return; + + if (vscPacketRevision == 0x2) { + /* Secondary-data Packet ID = 0*/ + info_packet->hb0 = 0x00; + /* 07h - Packet Type Value indicating Video + * Stream Configuration packet + */ + info_packet->hb1 = 0x07; + /* 02h = VSC SDP supporting 3D stereo and PSR + * (applies to eDP v1.3 or higher). + */ + info_packet->hb2 = 0x02; + /* 08h = VSC packet supporting 3D stereo + PSR + * (HB2 = 02h). + */ + info_packet->hb3 = 0x08; + + for (i = 0; i < 28; i++) + info_packet->sb[i] = 0; + + info_packet->valid = true; + } + + if (vscPacketRevision == 0x1) { + + info_packet->hb0 = 0x00; // Secondary-data Packet ID = 0 + info_packet->hb1 = 0x07; // 07h = Packet Type Value indicating Video Stream Configuration packet + info_packet->hb2 = 0x01; // 01h = Revision number. VSC SDP supporting 3D stereo only + info_packet->hb3 = 0x01; // 01h = VSC SDP supporting 3D stereo only (HB2 = 01h). + + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_INBAND_FA) + info_packet->sb[0] = 0x1; + + info_packet->valid = true; + } + + /* 05h = VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/Colorimetry Format indication. + * Added in DP1.3, a DP Source device is allowed to indicate the pixel encoding/colorimetry + * format to the DP Sink device with VSC SDP only when the DP Sink device supports it + * (i.e., VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED bit in the DPRX_FEATURE_ENUMERATION_LIST + * register (DPCD Address 02210h, bit 3) is set to 1). + * (Requires VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED bit set to 1 in DPCD 02210h. This + * DPCD register is exposed in the new Extended Receiver Capability field for DPCD Rev. 1.4 + * (and higher). When MISC1. bit 6. is Set to 1, a Source device uses a VSC SDP to indicate + * the Pixel Encoding/Colorimetry Format and that a Sink device must ignore MISC1, bit 7, and + * MISC0, bits 7:1 (MISC1, bit 7. and MISC0, bits 7:1 become “don’t care”).) + */ + if (vscPacketRevision == 0x5) { + /* Secondary-data Packet ID = 0 */ + info_packet->hb0 = 0x00; + /* 07h - Packet Type Value indicating Video Stream Configuration packet */ + info_packet->hb1 = 0x07; + /* 05h = VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/Colorimetry Format indication. */ + info_packet->hb2 = 0x05; + /* 13h = VSC SDP supporting 3D stereo, + PSR2, + Pixel Encoding/Colorimetry Format indication (HB2 = 05h). */ + info_packet->hb3 = 0x13; + + info_packet->valid = true; + + /* Set VSC SDP fields for pixel encoding and colorimetry format from DP 1.3 specs + * Data Bytes DB 18~16 + * Bits 3:0 (Colorimetry Format) | Bits 7:4 (Pixel Encoding) + * ---------------------------------------------------------------------------------------------------- + * 0x0 = sRGB | 0 = RGB + * 0x1 = RGB Wide Gamut Fixed Point + * 0x2 = RGB Wide Gamut Floating Point + * 0x3 = AdobeRGB + * 0x4 = DCI-P3 + * 0x5 = CustomColorProfile + * (others reserved) + * ---------------------------------------------------------------------------------------------------- + * 0x0 = ITU-R BT.601 | 1 = YCbCr444 + * 0x1 = ITU-R BT.709 + * 0x2 = xvYCC601 + * 0x3 = xvYCC709 + * 0x4 = sYCC601 + * 0x5 = AdobeYCC601 + * 0x6 = ITU-R BT.2020 Y'cC'bcC'rc + * 0x7 = ITU-R BT.2020 Y'C'bC'r + * (others reserved) + * ---------------------------------------------------------------------------------------------------- + * 0x0 = ITU-R BT.601 | 2 = YCbCr422 + * 0x1 = ITU-R BT.709 + * 0x2 = xvYCC601 + * 0x3 = xvYCC709 + * 0x4 = sYCC601 + * 0x5 = AdobeYCC601 + * 0x6 = ITU-R BT.2020 Y'cC'bcC'rc + * 0x7 = ITU-R BT.2020 Y'C'bC'r + * (others reserved) + * ---------------------------------------------------------------------------------------------------- + * 0x0 = ITU-R BT.601 | 3 = YCbCr420 + * 0x1 = ITU-R BT.709 + * 0x2 = xvYCC601 + * 0x3 = xvYCC709 + * 0x4 = sYCC601 + * 0x5 = AdobeYCC601 + * 0x6 = ITU-R BT.2020 Y'cC'bcC'rc + * 0x7 = ITU-R BT.2020 Y'C'bC'r + * (others reserved) + * ---------------------------------------------------------------------------------------------------- + * 0x0 =DICOM Part14 Grayscale | 4 = Yonly + * Display Function + * (others reserved) + */ + + /* Set Pixel Encoding */ + switch (stream->timing.pixel_encoding) { + case PIXEL_ENCODING_RGB: + pixelEncoding = 0x0; /* RGB = 0h */ + break; + case PIXEL_ENCODING_YCBCR444: + pixelEncoding = 0x1; /* YCbCr444 = 1h */ + break; + case PIXEL_ENCODING_YCBCR422: + pixelEncoding = 0x2; /* YCbCr422 = 2h */ + break; + case PIXEL_ENCODING_YCBCR420: + pixelEncoding = 0x3; /* YCbCr420 = 3h */ + break; + default: + pixelEncoding = 0x0; /* default RGB = 0h */ + break; + } + + /* Set Colorimetry format based on pixel encoding */ + switch (stream->timing.pixel_encoding) { + case PIXEL_ENCODING_RGB: + if ((stream->output_color_space == COLOR_SPACE_SRGB) || + (stream->output_color_space == COLOR_SPACE_SRGB_LIMITED)) + colorimetryFormat = ColorimetryRGB_DP_sRGB; + else if (stream->output_color_space == COLOR_SPACE_ADOBERGB) + colorimetryFormat = ColorimetryRGB_DP_AdobeRGB; + else if ((stream->output_color_space == COLOR_SPACE_2020_RGB_FULLRANGE) || + (stream->output_color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE)) + colorimetryFormat = ColorimetryRGB_DP_ITU_R_BT2020RGB; + break; + + case PIXEL_ENCODING_YCBCR444: + case PIXEL_ENCODING_YCBCR422: + case PIXEL_ENCODING_YCBCR420: + /* Note: xvYCC probably not supported correctly here on DP since colorspace translation + * loses distinction between BT601 vs xvYCC601 in translation + */ + if (stream->output_color_space == COLOR_SPACE_YCBCR601) + colorimetryFormat = ColorimetryYCC_DP_ITU601; + else if (stream->output_color_space == COLOR_SPACE_YCBCR709) + colorimetryFormat = ColorimetryYCC_DP_ITU709; + else if (stream->output_color_space == COLOR_SPACE_ADOBERGB) + colorimetryFormat = ColorimetryYCC_DP_AdobeYCC; + else if (stream->output_color_space == COLOR_SPACE_2020_YCBCR) + colorimetryFormat = ColorimetryYCC_DP_ITU2020YCbCr; + break; + + default: + colorimetryFormat = ColorimetryRGB_DP_sRGB; + break; + } + + info_packet->sb[16] = (pixelEncoding << 4) | colorimetryFormat; + + /* Set color depth */ + switch (stream->timing.display_color_depth) { + case COLOR_DEPTH_666: + /* NOTE: This is actually not valid for YCbCr pixel encoding to have 6 bpc + * as of DP1.4 spec, but value of 0 probably reserved here for potential future use. + */ + info_packet->sb[17] = 0; + break; + case COLOR_DEPTH_888: + info_packet->sb[17] = 1; + break; + case COLOR_DEPTH_101010: + info_packet->sb[17] = 2; + break; + case COLOR_DEPTH_121212: + info_packet->sb[17] = 3; + break; + /*case COLOR_DEPTH_141414: -- NO SUCH FORMAT IN DP SPEC */ + case COLOR_DEPTH_161616: + info_packet->sb[17] = 4; + break; + default: + info_packet->sb[17] = 0; + break; + } + + /* all YCbCr are always limited range */ + if ((stream->output_color_space == COLOR_SPACE_SRGB_LIMITED) || + (stream->output_color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE) || + (pixelEncoding != 0x0)) { + info_packet->sb[17] |= 0x80; /* DB17 bit 7 set to 1 for CEA timing. */ + } + + /* Content Type (Bits 2:0) + * 0 = Not defined. + * 1 = Graphics. + * 2 = Photo. + * 3 = Video. + * 4 = Game. + */ + info_packet->sb[18] = 0; + } + +} + +void mod_build_infopackets(struct info_packet_inputs *inputs, + struct info_packets *info_packets) +{ + if (info_packets->pVscInfoPacket != NULL) + mod_build_vsc_infopacket(inputs->pStream, info_packets->pVscInfoPacket); +} + |