From 7a366fa8360a48b1d22e40bf170c21e9fb1be2ee Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 31 Jul 2015 22:03:33 -0400 Subject: amd/dal: Display Service The display service maintains display state, implements driver feature policy, builds parameters for HW programming, and call HWSS to program HW. SW Layer /===============================================================\ | Display Timing Mode Asic | | Service Service Manager Capability | | | | Display Topology Display Link Adapter | | Path Manager Capability Service Service | | Service | |---------------------------------------------------------------| | GPIO IRQ I2cAux HW BIOS | | Service Manager Sequencer Parser | | | | Connector Encoder Audio GPU Controller | \===============================================================/ HW Layer Signed-off-by: Harry Wentland --- drivers/gpu/drm/amd/dal/Makefile | 5 +- drivers/gpu/drm/amd/dal/display_service/Makefile | 14 + .../drm/amd/dal/display_service/adjustment_api.c | 292 +++ .../drm/amd/dal/display_service/adjustment_api.h | 64 + .../amd/dal/display_service/adjustment_container.c | 591 +++++ .../amd/dal/display_service/adjustment_container.h | 207 ++ .../display_service/adjustment_types_internal.h | 198 ++ .../amd/dal/display_service/backlight_adj_group.c | 500 ++++ .../amd/dal/display_service/backlight_adj_group.h | 97 + .../amd/dal/display_service/color_temperature.c | 212 ++ .../amd/dal/display_service/color_temperature.h | 56 + .../drm/amd/dal/display_service/display_service.c | 631 +++++ .../drm/amd/dal/display_service/display_service.h | 30 + .../drm/amd/dal/display_service/ds_calculation.c | 288 +++ .../drm/amd/dal/display_service/ds_calculation.h | 48 + .../gpu/drm/amd/dal/display_service/ds_dispatch.h | 137 + .../dal/display_service/ds_dispatch_adjustment.c | 1128 +++++++++ .../dal/display_service/ds_dispatch_info_frame.c | 957 +++++++ .../dal/display_service/ds_dispatch_mode_setting.c | 2609 ++++++++++++++++++++ .../amd/dal/display_service/ds_dispatch_planes.c | 133 + .../gpu/drm/amd/dal/display_service/ds_overlay.c | 202 ++ .../drm/amd/dal/display_service/ds_translation.c | 560 +++++ .../drm/amd/dal/display_service/ds_translation.h | 99 + .../gpu/drm/amd/dal/display_service/gamma_lut.c | 391 +++ .../gpu/drm/amd/dal/display_service/gamma_lut.h | 89 + .../gpu/drm/amd/dal/display_service/gamut_space.c | 1140 +++++++++ .../gpu/drm/amd/dal/display_service/gamut_space.h | 270 ++ .../amd/dal/display_service/grph_colors_group.c | 1326 ++++++++++ .../amd/dal/display_service/grph_colors_group.h | 124 + .../drm/amd/dal/display_service/path_mode_set.c | 220 ++ .../dal/display_service/path_mode_set_with_data.c | 308 +++ .../dal/display_service/path_mode_set_with_data.h | 134 + .../drm/amd/dal/display_service/scaler_adj_group.c | 944 +++++++ .../drm/amd/dal/display_service/scaler_adj_group.h | 57 + .../drm/amd/dal/display_service/set_mode_params.c | 822 ++++++ .../drm/amd/dal/display_service/single_adj_group.c | 447 ++++ .../drm/amd/dal/display_service/single_adj_group.h | 75 + .../gpu/drm/amd/dal/include/adjustment_interface.h | 230 ++ .../amd/dal/include/display_service_interface.h | 165 ++ .../gpu/drm/amd/dal/include/overlay_interface.h | 137 + drivers/gpu/drm/amd/dal/include/overlay_types.h | 164 ++ 41 files changed, 16099 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/amd/dal/display_service/Makefile create mode 100644 drivers/gpu/drm/amd/dal/display_service/adjustment_api.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/adjustment_api.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/adjustment_container.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/adjustment_container.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/adjustment_types_internal.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/color_temperature.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/color_temperature.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/display_service.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/display_service.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_calculation.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_calculation.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_dispatch.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_dispatch_adjustment.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_dispatch_info_frame.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_dispatch_mode_setting.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_dispatch_planes.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_overlay.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_translation.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/ds_translation.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/gamma_lut.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/gamma_lut.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/gamut_space.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/gamut_space.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/grph_colors_group.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/grph_colors_group.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/path_mode_set.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.h create mode 100644 drivers/gpu/drm/amd/dal/display_service/set_mode_params.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/single_adj_group.c create mode 100644 drivers/gpu/drm/amd/dal/display_service/single_adj_group.h create mode 100644 drivers/gpu/drm/amd/dal/include/adjustment_interface.h create mode 100644 drivers/gpu/drm/amd/dal/include/display_service_interface.h create mode 100644 drivers/gpu/drm/amd/dal/include/overlay_interface.h create mode 100644 drivers/gpu/drm/amd/dal/include/overlay_types.h diff --git a/drivers/gpu/drm/amd/dal/Makefile b/drivers/gpu/drm/amd/dal/Makefile index b697398d7e4f..b95ba1e51f55 100644 --- a/drivers/gpu/drm/amd/dal/Makefile +++ b/drivers/gpu/drm/amd/dal/Makefile @@ -8,8 +8,9 @@ AMDDALPATH = $(RELATIVE_AMD_DAL_PATH) subdir-ccflags-y += -I$(AMDDALPATH)/ -I$(AMDDALPATH)/include -DDAL_CZ_BRINGUP DAL_LIBS = adapter amdgpu_dm audio asic_capability basics bios connector \ - controller dcs display_path encoder gpio gpu hw_sequencer i2caux irq \ - link_service mode_manager timing_service topology + controller dcs display_service display_path encoder gpio gpu \ + hw_sequencer i2caux irq link_service mode_manager timing_service \ + topology AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/,$(DAL_LIBS))) diff --git a/drivers/gpu/drm/amd/dal/display_service/Makefile b/drivers/gpu/drm/amd/dal/display_service/Makefile new file mode 100644 index 000000000000..8482bdf5b8ca --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the 'display_service' sub-component of DAL. +# It provides the control and status of availible display services. + +DS = display_service.o path_mode_set.o set_mode_params.o \ + ds_dispatch_adjustment.o ds_overlay.o ds_calculation.o grph_colors_group.o \ + adjustment_container.o ds_dispatch_info_frame.o \ + ds_dispatch_mode_setting.o ds_dispatch_planes.o ds_translation.o adjustment_api.o \ + scaler_adj_group.o backlight_adj_group.o single_adj_group.o \ + gamut_space.o color_temperature.o path_mode_set_with_data.o gamma_lut.o + +AMD_DAL_DS = $(addprefix $(AMDDALPATH)/display_service/,$(DS)) + +AMD_DAL_FILES += $(AMD_DAL_DS) diff --git a/drivers/gpu/drm/amd/dal/display_service/adjustment_api.c b/drivers/gpu/drm/amd/dal/display_service/adjustment_api.c new file mode 100644 index 000000000000..4ceab23383a4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/adjustment_api.c @@ -0,0 +1,292 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/adjustment_interface.h" +#include "include/display_path_interface.h" +#include "display_service/adjustment_types_internal.h" + +#include "adjustment_api.h" + +static const struct range_adjustment_api default_adj_range_dfp_table[] = { + { ADJ_ID_SATURATION, 100, 0, 200, 1, {0} }, + { ADJ_ID_UNDERSCAN, 0, 0, 15, 1, {0} }, + { ADJ_ID_BIT_DEPTH_REDUCTION, 1, 0, 23, 1, {0} }, + { ADJ_ID_BACKLIGHT, 255, 0, 255, 1, {0} }, + { ADJ_ID_BRIGHTNESS, 0, -100, 100, 1, {0} }, + { ADJ_ID_CONTRAST, 100, 0, 200, 1, {0} }, + { ADJ_ID_HUE, 0, -30, 30, 1, {0} }, + { ADJ_ID_TEMPERATURE, 6500, 4000, 10000, 100, {0} }, + { ADJ_ID_TEMPERATURE_SOURCE, 2, 1, 2, 1, {0} }, + { ADJ_ID_NOMINAL_RANGE_RGB_LIMITED, 0, 0, 1, 1, {0} }, +}; + +static const struct range_adjustment_api default_adj_range_crt_table[] = { + { ADJ_ID_SATURATION, 100, 0, 200, 1, {0} }, + { ADJ_ID_BRIGHTNESS, 0, -100, 100, 1, {0} }, + { ADJ_ID_CONTRAST, 100, 0, 200, 1, {0} }, + { ADJ_ID_HUE, 0, -30, 30, 1, {0} }, + { ADJ_ID_TEMPERATURE, 6500, 4000, 10000, 100, {0} }, + { ADJ_ID_TEMPERATURE_SOURCE, 2, 1, 2, 1, {0} }, + { ADJ_ID_NOMINAL_RANGE_RGB_LIMITED, 0, 0, 1, 1, {0} }, + { ADJ_ID_BACKLIGHT, 255, 0, 255, 1, {0} }, +}; + +static const struct range_adjustment_api default_adj_range_lcd_table[] = { + { ADJ_ID_SATURATION, 100, 0, 200, 1, {0} }, + { ADJ_ID_UNDERSCAN, 0, 0, 10, 1, {0} }, + { ADJ_ID_BIT_DEPTH_REDUCTION, 1, 0, 23, 1, {0} }, + { ADJ_ID_BACKLIGHT, 255, 0, 255, 1, {0} }, + { ADJ_ID_BRIGHTNESS, 0, -100, 100, 1, {0} }, + { ADJ_ID_CONTRAST, 100, 0, 200, 1, {0} }, + { ADJ_ID_HUE, 0, -30, 30, 1, {0} }, + { ADJ_ID_TEMPERATURE, 6500, 4000, 10000, 100, {0} }, + { ADJ_ID_TEMPERATURE_SOURCE, 2, 1, 2, 1, {0} }, + { ADJ_ID_NOMINAL_RANGE_RGB_LIMITED, 0, 0, 1, 1, {0} }, + + + +}; + +static void destruct(struct adjustment_api *adj_api) +{ + if (adj_api->range_table) { + dal_free(adj_api->range_table); + adj_api->range_table = NULL; + } +} + +static bool build_default_adj_table(struct adjustment_api *adj_api) +{ + uint32_t i; + uint32_t range_alloc_size = 0; + const struct range_adjustment_api *tmp_range_table = NULL; + + switch (adj_api->adj_category) { + case CAT_CRT: + adj_api->range_table_size = sizeof(default_adj_range_crt_table) + / sizeof(default_adj_range_crt_table[0]); + tmp_range_table = default_adj_range_crt_table; + break; + case CAT_DFP: + adj_api->range_table_size = sizeof(default_adj_range_dfp_table) + / sizeof(default_adj_range_dfp_table[0]); + tmp_range_table = default_adj_range_dfp_table; + break; + case CAT_LCD: + adj_api->range_table_size = sizeof(default_adj_range_lcd_table) + / sizeof(default_adj_range_lcd_table[0]); + tmp_range_table = default_adj_range_lcd_table; + break; + default: + break; + } + if (adj_api->range_table_size > 0) + range_alloc_size = adj_api->range_table_size * + sizeof(struct range_adjustment_api); + if (range_alloc_size > 0 && tmp_range_table != NULL) { + adj_api->range_table = dal_alloc(range_alloc_size); + if (adj_api->range_table) { + for (i = 0; i < adj_api->range_table_size; i++) { + dal_memmove( + &adj_api->range_table[i], + &tmp_range_table[i], + sizeof(struct range_adjustment_api)); + adj_api->range_table[i].flag.bits. + def_from_driver = 1; + /* not to do read config from reg key*/ + } + } + if (adj_api->range_table == NULL) { + destruct(adj_api); + return false; + } + + return true; + } + /* to do vector table in furture*/ + return false; +} + +void dal_adj_api_get_api_flag( + struct adjustment_api *adj_api, + enum adjustment_id adj_id, + union adjustment_api_flag *flag) +{ + uint32_t i; + + if (adj_api->range_table) { + for (i = 0; i < adj_api->range_table_size; i++) { + if (adj_api->range_table[i].adj_id == adj_id) { + *flag = adj_api->range_table[i].flag; + break; + } + } + } +} + +bool dal_adj_api_get_range_adj_data( + struct adjustment_api *adj_api, + enum adjustment_id adj_id, + struct adjustment_info *adj_info) +{ + uint32_t i; + + if (adj_api->range_table) { + for (i = 0; i < adj_api->range_table_size; i++) { + if (adj_api->range_table[i].adj_id == adj_id) { + adj_info->adj_data.ranged.def = + adj_api->range_table[i].def; + adj_info->adj_data.ranged.min = + adj_api->range_table[i].min; + adj_info->adj_data.ranged.max = + adj_api->range_table[i].max; + adj_info->adj_data.ranged.step = + adj_api->range_table[i].step; + return true; + } + } + } + return false; +} +static bool construct( + struct adjustment_api *adj_api, + enum adjustment_category category) +{ + if (category == CAT_INVALID) + return false; + adj_api->adj_category = category; + adj_api->range_table_size = 0; + adj_api->bit_vector_table_size = 0; + adj_api->range_table = NULL; + + return true; +} + +static bool parent_api_construct(struct adjustment_parent_api *parent_api) +{ + parent_api->api_crt = NULL; + parent_api->api_dfp = NULL; + parent_api->api_lcd = NULL; + return true; +} + +static void parent_api_destruct(struct adjustment_parent_api **parent_api) +{ + dal_adj_api_destroy(&(*parent_api)->api_crt); + dal_adj_api_destroy(&(*parent_api)->api_dfp); + dal_adj_api_destroy(&(*parent_api)->api_lcd); +} + +struct adjustment_api *dal_adj_api_create(enum adjustment_category category) +{ + struct adjustment_api *adj_api; + + adj_api = dal_alloc(sizeof(*adj_api)); + + if (!adj_api) + return NULL; + if (construct(adj_api, category)) + return adj_api; + + dal_free(adj_api); + BREAK_TO_DEBUGGER(); + return NULL; +} + +struct adjustment_parent_api *dal_adj_parent_api_create() +{ + struct adjustment_parent_api *parent_api; + + parent_api = dal_alloc(sizeof(*parent_api)); + if (!parent_api) + return NULL; + if (parent_api_construct(parent_api)) + return parent_api; + + dal_free(parent_api); + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dal_adj_parent_api_destroy(struct adjustment_parent_api **parent_api) +{ + if (!parent_api || !*parent_api) { + BREAK_TO_DEBUGGER(); + return; + } + parent_api_destruct(parent_api); + dal_free(*parent_api); + *parent_api = NULL; +} + +void dal_adj_api_destroy(struct adjustment_api **adj_api) +{ + if (!adj_api || !*adj_api) { + BREAK_TO_DEBUGGER(); + return; + } + destruct(*adj_api); + dal_free(*adj_api); + *adj_api = NULL; +} + +bool dal_adj_parent_api_build_child_objs(struct adjustment_parent_api *adj_api) +{ + adj_api->api_crt = dal_adj_api_create(CAT_CRT); + if (!adj_api->api_crt || !build_default_adj_table(adj_api->api_crt)) + return false; + + adj_api->api_dfp = dal_adj_api_create(CAT_DFP); + if (!adj_api->api_dfp || !build_default_adj_table(adj_api->api_dfp)) + return false; + + adj_api->api_lcd = dal_adj_api_create(CAT_LCD); + if (!adj_api->api_lcd || !build_default_adj_table(adj_api->api_lcd)) + return false; + return true; +} + +struct adjustment_api *dal_adj_parent_api_what_is_the_target_obj( + struct adjustment_parent_api *adj_api, + enum signal_type signal) +{ + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_SINGLE_LINK1: + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_WIRELESS: + return adj_api->api_dfp; + case SIGNAL_TYPE_EDP: + return adj_api->api_lcd; + case SIGNAL_TYPE_RGB: + return adj_api->api_crt; + default: + return NULL; + } +} diff --git a/drivers/gpu/drm/amd/dal/display_service/adjustment_api.h b/drivers/gpu/drm/amd/dal/display_service/adjustment_api.h new file mode 100644 index 000000000000..6a1337891dfd --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/adjustment_api.h @@ -0,0 +1,64 @@ +/* + * Copyright 2012-15 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 __DAL_ADJUSTMENT_API_H__ +#define __DAL_ADJUSTMENT_API_H__ + +#include "include/adjustment_types.h" +#include "include/signal_types.h" + +struct adjustment_api { + enum adjustment_category adj_category; + uint32_t range_table_size; + uint32_t bit_vector_table_size; + struct range_adjustment_api *range_table; +}; + +struct adjustment_parent_api { + struct adjustment_api *api_crt; + struct adjustment_api *api_dfp; + struct adjustment_api *api_lcd; +}; + +struct adjustment_parent_api *dal_adj_parent_api_create(void); + +void dal_adj_parent_api_destroy(struct adjustment_parent_api **parent_api); + +struct adjustment_api *dal_adj_parent_api_what_is_the_target_obj( + struct adjustment_parent_api *adj_api, + enum signal_type signal); + +struct adjustment_api *dal_adj_api_create(enum adjustment_category category); + +bool dal_adj_parent_api_build_child_objs(struct adjustment_parent_api *adj_api); + +void dal_adj_api_destroy(struct adjustment_api **adj_api); + +bool dal_adj_api_get_range_adj_data( + struct adjustment_api *adj_api, + enum adjustment_id adj_id, + struct adjustment_info *adj_info); + +#endif /* __DAL_ADJUSTMENT_API_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/adjustment_container.c b/drivers/gpu/drm/amd/dal/display_service/adjustment_container.c new file mode 100644 index 000000000000..41409b144639 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/adjustment_container.c @@ -0,0 +1,591 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/display_path_interface.h" +#include "include/dcs_interface.h" +#include "include/fixed31_32.h" + +#include "adjustment_container.h" + +/*no used for now +static struct adj_container *get_adj_container_for_path(void) +{ + return NULL; +} +*/ +void dal_adj_container_update_display_cap( + struct adj_container *container, + struct display_path *display_path) +{ + struct vendor_product_id_info vendor_info = {0}; + struct cea861_support cea861_support = { 0 }; + union cea_video_capability_data_block video_cap = { {0} }; + struct dcs *dcs = dal_display_path_get_dcs(display_path); + + dal_dcs_get_vendor_product_id_info(dcs, &vendor_info); + if (!((container->ctx.edid_signature.manufacturer_id == + vendor_info.manufacturer_id) && (container->ctx. + edid_signature.product_id == vendor_info.product_id))) { + /*new edid,update required */ + container->ctx.update_required = true; + container->ctx.edid_signature = vendor_info; + dal_dcs_get_display_characteristics( + dcs, + &container->ctx.display_characteristics); + /* CEA861 support block from edid */ + + if (dal_dcs_get_cea861_support(dcs, &cea861_support)) { + container->ctx.cea861_support = cea861_support; + container->ctx.valid.CEA861_SUPPORT = true; + } + /* get CEA861 vcblock from edid*/ + if (dal_dcs_get_cea_video_capability_data_block( + dcs, &video_cap)) { + container->ctx.vcblock = video_cap; + container->ctx.valid.VCBLOCK = true; + } + } +} + +bool dal_adj_container_get_default_underscan_allow( + struct adj_container *container) +{ + return container->ctx.flags.NO_DEFAULT_UNDERSCAN; +} + +void dal_adj_container_set_default_underscan_allow( + struct adj_container *container, + bool restricted) +{ + container->ctx.flags.NO_DEFAULT_UNDERSCAN = restricted; +} + +struct adj_container *dal_adj_container_create() +{ + + struct adj_container *container = dal_alloc(sizeof(*container)); + + if (!container) { + dal_free(container); + BREAK_TO_DEBUGGER(); + return NULL; + } + + return container; +} + +void dal_adj_container_destroy(struct adj_container **container) +{ + if (!container || !*container) { + BREAK_TO_DEBUGGER(); + return; + } + dal_free(*container); + *container = NULL; +} + +bool dal_adj_container_get_scan_type( + const struct adj_container *container, + enum scanning_type *scanning_type) +{ + if (container->ctx.valid.SCAN_TYPE) { + *scanning_type = container->ctx.scan_type; + return true; + } + return false; +} + +bool dal_adj_container_get_display_content_capability( + const struct adj_container *container, + union display_content_support *support) +{ + if (container->ctx.valid.DISP_CONTENT_SUPPORT) { + *support = container->ctx.disp_content_support; + return true; + } + return false; +} + +bool dal_adj_container_get_adjustment_val( + const struct adj_container *adj_container, + enum adjustment_id adj_id, + uint32_t *val) +{ + /* TODO: implementation of adjustment */ + return false; +} + + +void dal_adj_info_set_clear(struct adj_info_set *adj_info_set) +{ + dal_memset( + adj_info_set->adj_info_array, + 0, + MAX_ADJUSTMENT_NUM * sizeof(struct adjustment_info)); +} + +void dal_adj_info_set_add_adj_info( + struct adj_info_set *adj_info_set, + struct adjustment_info *adj_info) +{ + adj_info_set->adj_info_array[adj_info->adj_id] = *adj_info; + adj_info_set->adj_info_array[adj_info->adj_id].adj_state = + ADJUSTMENT_STATE_VALID; +} + +static void copy_contents_from( + struct adj_info_set *adj_info_set, + const struct adj_info_set *adj_info_set_src) +{ + uint32_t i; + + for (i = 0; i < MAX_ADJUSTMENT_NUM; i++) + adj_info_set->adj_info_array[i] = + adj_info_set_src->adj_info_array[i]; +} + +struct adjustment_info *dal_adj_info_set_get_adj_info( + struct adj_info_set *adj_info_set, + enum adjustment_id adj_id) +{ + if (adj_id <= ADJ_ID_END && adj_id != ADJ_ID_INVALID) { + if (adj_info_set->adj_info_array[adj_id].adj_state != + ADJUSTMENT_STATE_INVALID) + return &adj_info_set->adj_info_array[adj_id]; + } + return NULL; +} + +const struct mode_info *dal_adj_container_get_mode_info( + struct adj_container *container) +{ + return &container->ctx.mode_info; +} + +const struct view *dal_adj_container_get_view( + struct adj_container *container) +{ + return &container->ctx.view; +} + +void dal_adj_container_update_timing_mode( + struct adj_container *container, + const struct mode_info *mode_info, + const struct view *view) +{ + if (mode_info && ((container->ctx.mode_info.field_rate != mode_info-> + field_rate) || (container->ctx.mode_info.pixel_width != + mode_info->pixel_width) || (container->ctx.mode_info. + pixel_height != mode_info->pixel_height) || + (container->ctx.view.height != view->height) || + (container->ctx.view.width != + view->width))) { + + container->ctx.view = *view; + container->ctx.mode_info = *mode_info; + container->ctx.update_required = true; + } +} + +bool dal_adj_container_get_cea861_support( + const struct adj_container *container, + struct cea861_support *cea861_support) +{ + if (container->ctx.cea861_support.revision) { + *cea861_support = container->ctx.cea861_support; + return true; + } + return false; +} + +bool dal_adj_container_get_cea_video_cap_data_block( + const struct adj_container *container, + union cea_video_capability_data_block *vcblock) +{ + if (container->ctx.valid.VCBLOCK) { + *vcblock = container->ctx.vcblock; + return true; + } + return false; +} + +void dal_adj_container_update_color_space( + struct adj_container *container, + enum ds_color_space color_space) +{ + container->ctx.color_space = color_space; + container->ctx.valid.COLOR_SPACE = true; +} + +void dal_adj_container_update_signal_type( + struct adj_container *container, + enum signal_type signal_type) +{ + container->ctx.signal_type = signal_type; + container->ctx.valid.SIGNAL_TYPE = true; +} + +enum signal_type dal_adj_container_get_signal_type( + struct adj_container *container) +{ + if (container->ctx.valid.SIGNAL_TYPE) + return container->ctx.signal_type; + return SIGNAL_TYPE_NONE; +} + +enum ds_color_space dal_adj_container_get_color_space( + const struct adj_container *container) +{ + if (container->ctx.valid.COLOR_SPACE) + return container->ctx.color_space; + return DS_COLOR_SPACE_UNKNOWN; +} + +const struct display_characteristics *dal_adj_container_get_disp_character( + struct adj_container *container) +{ + if (container->ctx.valid.DISPLAY_CHARACTERISTICS) + return &container->ctx.display_characteristics; + + return NULL; +} + +void dal_adj_container_copy_contents_from( + struct adj_container *container, + struct adj_container *container_src) +{ + copy_contents_from( + &container->adj_info_set, + &container_src->adj_info_set); + container->ctx = container_src->ctx; +} + +bool dal_adj_container_commit_adj( + struct adj_container *container, + enum adjustment_id adj_id) +{ + struct adjustment_info *info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, adj_id); + if (info) { + info->adj_state = ADJUSTMENT_STATE_COMMITTED_TO_HW; + return true; + } + return false; +} + +bool dal_adj_container_is_adjustment_committed( + struct adj_container *container, + enum adjustment_id adj_id) +{ + struct adjustment_info *info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, adj_id); + if (info) + return (info->adj_state == ADJUSTMENT_STATE_COMMITTED_TO_HW); + + return false; +} + +bool dal_adj_info_set_get_adj_val( + struct adj_info_set *adj_info_set, + enum adjustment_id adj_id, + int32_t *val) +{ + struct adjustment_info *info = dal_adj_info_set_get_adj_info( + adj_info_set, adj_id); + if (info && val) { + *val = info->adj_data.ranged.cur; + return true; + } + return false; +} + +bool dal_adj_info_set_update_cur_value( + struct adj_info_set *adj_info_set, + enum adjustment_id adj_id, + int32_t val) +{ + struct adjustment_info *info = dal_adj_info_set_get_adj_info( + adj_info_set, adj_id); + if (info) { + info->adj_data.ranged.cur = val; + info->adj_state = ADJUSTMENT_STATE_REQUESTED; + return true; + } + return false; +} + +bool dal_adj_container_is_update_required(struct adj_container *adj_container) +{ + return adj_container->ctx.update_required; +} + +void dal_adj_container_updated(struct adj_container *adj_container) +{ + adj_container->ctx.update_required = false; +} + +bool dal_adj_container_validate_regamma( + struct adj_container *adj_container, + const struct ds_regamma_lut *data) +{ + uint32_t i; + + struct fixed31_32 a0; + struct fixed31_32 a1; + struct fixed31_32 a2; + struct fixed31_32 a3; + struct fixed31_32 gamma; + struct fixed31_32 divide; + struct fixed31_32 discrete_increment; + struct fixed31_32 linear_end; + struct fixed31_32 exp_start; + bool ret = true; + + if (data->flags.bits.GAMMA_RAMP_ARRAY == 0) { + for (i = 0; i < COEFF_RANGE && ret ; i++) { + if (data->coeff.coeff_a0[i] == 0 || + data->coeff.coeff_a1[i] == 0 || + data->coeff.coeff_a2[i] == 0 || + data->coeff.coeff_a3[i] == 0 || + data->coeff.gamma[i] == 0) { + ret = false; + break; + } + + a0 = dal_fixed31_32_from_int( + (int64_t)data->coeff.coeff_a0[i]); + a1 = dal_fixed31_32_from_int( + (int64_t)data->coeff.coeff_a1[i]); + a2 = dal_fixed31_32_from_int( + (int64_t)data->coeff.coeff_a2[i]); + a3 = dal_fixed31_32_from_int( + (int64_t)data->coeff.coeff_a3[i]); + gamma = dal_fixed31_32_from_int( + (int64_t)data->coeff.gamma[i]); + + a0 = dal_fixed31_32_div_int( + a0, ADJUST_DIVIDER_2); + a1 = dal_fixed31_32_div_int( + a1, ADJUST_DIVIDER_1); + a2 = dal_fixed31_32_div_int( + a2, ADJUST_DIVIDER_1); + a3 = dal_fixed31_32_div_int( + a3, ADJUST_DIVIDER_1); + gamma = dal_fixed31_32_div_int( + gamma, ADJUST_DIVIDER_1); + + divide = dal_fixed31_32_one; + discrete_increment = dal_fixed31_32_one; + + if (dal_fixed31_32_lt( + dal_fixed31_32_from_int( + (int64_t)REGAMMA_CONSTANT_1), + dal_fixed31_32_div(divide, a0))) + discrete_increment = dal_fixed31_32_div_int( + discrete_increment, 8192); + else if (dal_fixed31_32_lt( + dal_fixed31_32_from_int( + (int64_t)REGAMMA_CONSTANT_2), + dal_fixed31_32_div(divide, a0))) + discrete_increment = dal_fixed31_32_div_int( + discrete_increment, 4096); + else if (dal_fixed31_32_lt( + dal_fixed31_32_from_int( + (int64_t)REGAMMA_CONSTANT_3), + dal_fixed31_32_div(divide, a0))) + discrete_increment = dal_fixed31_32_div_int( + discrete_increment, 2048); + else + discrete_increment = dal_fixed31_32_div_int( + discrete_increment, 1024); + + linear_end = dal_fixed31_32_mul( + dal_fixed31_32_sub( + a0, discrete_increment), a1); + + discrete_increment = dal_fixed31_32_one; + exp_start = dal_fixed31_32_div( + discrete_increment, gamma); + exp_start = dal_fixed31_32_pow(a0, exp_start); + + exp_start = dal_fixed31_32_mul( + (dal_fixed31_32_add( + discrete_increment, a3)), + exp_start); + + exp_start = dal_fixed31_32_sub( + exp_start, a2); + + if (dal_fixed31_32_lt( + exp_start, linear_end)) { + ret = false; + break; + } + } + } else { + for (i = 1; i < REGAMMA_VALUE ; i++) { + if (data->gamma.gamma[i] < data->gamma.gamma[i-1]) { + ret = false; + break; + } + if (data->gamma.gamma[i+256] < + data->gamma.gamma[i-1+256]) { + ret = false; + break; + } + if (data->gamma.gamma[i+512] < + data->gamma.gamma[i-1+512]) { + ret = false; + break; + } + } + } + return ret; +} + +bool dal_adj_container_set_regamma( + struct adj_container *adj_container, + const struct ds_regamma_lut *regamma) +{ + if (!dal_adj_container_validate_regamma( + adj_container, regamma)) + return false; + + adj_container->ctx.valid.REGAMMA = true; + adj_container->ctx.regamma = *regamma; + + return true; +} + +const struct ds_regamma_lut *dal_adj_container_get_regamma( + struct adj_container *adj_container) +{ + + if (adj_container->ctx.valid.REGAMMA == true) + return &adj_container->ctx.regamma; + return NULL; +} + +bool dal_adj_container_get_gamut( + struct adj_container *adj_container, + enum adjustment_id adj_id, + struct gamut_data *data) +{ + bool ret = false; + + switch (adj_id) { + case ADJ_ID_GAMUT_SOURCE_GRPH: + if (adj_container->ctx.valid.GAMUT_SRC_GRPH == true) { + *data = adj_container->ctx.gamut_src_grph; + ret = true; + } + break; + case ADJ_ID_GAMUT_SOURCE_OVL: + if (adj_container->ctx.valid.GAMUT_SRC_OVL == true) { + *data = adj_container->ctx.gamut_src_ovl; + ret = true; + } + break; + case ADJ_ID_GAMUT_DESTINATION: + if (adj_container->ctx.valid.GAMUT_DST == true) { + *data = adj_container->ctx.gamut_dst; + ret = true; + } + break; + default: + break; + } + return ret; +} + +bool dal_adj_container_validate_gamut( + struct adj_container *adj_container, + struct gamut_data *data) +{ + if (data->option.bits.CUSTOM_GAMUT_SPACE == 1) { + if (data->gamut.custom.red_x == 0 || + data->gamut.custom.red_y == 0 || + data->gamut.custom.green_x == 0 || + data->gamut.custom.green_y == 0 || + data->gamut.custom.blue_x == 0 || + data->gamut.custom.blue_y == 0) + return false; + } else + if (data->gamut.predefined.u32all == 0) + return false; + + if (data->option.bits.CUSTOM_WHITE_POINT == 1) { + if (data->white_point.custom.white_x == 0 || + data->white_point.custom.white_y == 0) + return false; + } else + if (data->white_point.predefined.u32all == 0) + return false; + + return true; +} + +bool dal_adj_container_update_gamut( + struct adj_container *adj_container, + enum adjustment_id adj_id, + struct gamut_data *data) +{ + if (!dal_adj_container_validate_gamut( + adj_container, data)) + return false; + + switch (adj_id) { + case ADJ_ID_GAMUT_SOURCE_GRPH: + adj_container->ctx.gamut_src_grph = *data; + adj_container->ctx.valid.GAMUT_SRC_GRPH = true; + break; + case ADJ_ID_GAMUT_SOURCE_OVL: + adj_container->ctx.gamut_src_ovl = *data; + adj_container->ctx.valid.GAMUT_SRC_OVL = true; + break; + case ADJ_ID_GAMUT_DESTINATION: + adj_container->ctx.gamut_dst = *data; + adj_container->ctx.valid.GAMUT_DST = true; + break; + default: + adj_container->ctx.valid.GAMUT_SRC_GRPH = false; + adj_container->ctx.valid.GAMUT_SRC_OVL = false; + adj_container->ctx.valid.GAMUT_DST = false; + break; + } + return true; +} + +bool dal_adj_container_get_regamma_copy( + struct adj_container *adj_container, + struct ds_regamma_lut *regamma) +{ + if (adj_container->ctx.valid.REGAMMA == true) { + *regamma = adj_container->ctx.regamma; + return true; + } + return false; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/adjustment_container.h b/drivers/gpu/drm/amd/dal/display_service/adjustment_container.h new file mode 100644 index 000000000000..5bc12aca54ab --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/adjustment_container.h @@ -0,0 +1,207 @@ +/* + * Copyright 2012-15 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 __DAL_ADJUSTMENT_CONTAINER_H__ +#define __DAL_ADJUSTMENT_CONTAINER_H__ + +#include "include/adjustment_types.h" +#include "include/dcs_types.h" +#include "include/timing_service_types.h" +#include "include/set_mode_types.h" +#include "include/signal_types.h" +#include "display_service/adjustment_types_internal.h" + +#define ADJUST_DIVIDER_1 1000 +#define ADJUST_DIVIDER_2 10000000 +#define REGAMMA_CONSTANT_1 256 +#define REGAMMA_CONSTANT_2 128 +#define REGAMMA_CONSTANT_3 64 + +struct adj_info_set { + struct adjustment_info adj_info_array[MAX_ADJUSTMENT_NUM]; +}; + +struct adj_container { + /* to-do,not 100% be ported */ + struct adj_info_set adj_info_set; + + struct { + bool update_required; + struct mode_info mode_info; + struct view view; + struct cea861_support cea861_support; + struct vendor_product_id_info edid_signature; + struct display_characteristics display_characteristics; + union cea_video_capability_data_block vcblock; + enum ds_color_space color_space; + enum signal_type signal_type; + enum scanning_type scan_type; + union display_content_support disp_content_support; + struct gamut_data gamut_src_grph; + struct gamut_data gamut_src_ovl; + struct gamut_data gamut_dst; + struct ds_regamma_lut regamma; + + struct { + bool DISPLAY_CHARACTERISTICS:1; + bool COLOR_SPACE:1; + bool VCBLOCK:1; + bool CEA861_SUPPORT:1; + bool SIGNAL_TYPE:1; + bool SCAN_TYPE:1; + bool GAMUT_SRC_GRPH:1; + bool GAMUT_SRC_OVL:1; + bool GAMUT_DST:1; + bool REGAMMA:1; + bool DISP_CONTENT_SUPPORT:1; + } valid; + struct { + bool NO_DEFAULT_UNDERSCAN:1; + } flags; + } ctx; +}; + +union display_content_support; + +bool dal_adj_container_get_default_underscan_allow( + struct adj_container *container); + +void dal_adj_container_update_display_cap( + struct adj_container *container, + struct display_path *display_path); + +void dal_adj_container_set_default_underscan_allow( + struct adj_container *container, + bool restricted); + +void dal_adj_container_destroy(struct adj_container **container); + +const struct display_characteristics *dal_adj_container_get_disp_character( + struct adj_container *container); + +enum ds_color_space dal_adj_container_get_color_space( + const struct adj_container *container); + +enum signal_type dal_adj_container_get_signal_type( + struct adj_container *container); + +void dal_adj_container_update_color_space( + struct adj_container *container, + enum ds_color_space color_space); + +void dal_adj_container_update_signal_type( + struct adj_container *container, + enum signal_type signal_type); + +bool dal_adj_container_get_cea861_support( + const struct adj_container *container, + struct cea861_support *cea861_support); + +bool dal_adj_container_get_cea_video_cap_data_block( + const struct adj_container *container, + union cea_video_capability_data_block *vcblock); + +const struct mode_info *dal_adj_container_get_mode_info( + struct adj_container *container); + +const struct view *dal_adj_container_get_view( + struct adj_container *container); + +bool dal_adj_container_is_adjustment_committed( + struct adj_container *container, + enum adjustment_id adj_id); + +bool dal_adj_container_commit_adj( + struct adj_container *container, + enum adjustment_id adj_id); + +void dal_adj_container_updated(struct adj_container *adj_container); + +struct adj_container *dal_adj_container_create(void); + +struct adjustment_info *dal_adj_info_set_get_adj_info( + struct adj_info_set *adj_info_set, + enum adjustment_id adj_id); + +bool dal_adj_container_get_scan_type( + const struct adj_container *adj_container, + enum scanning_type *scanning_type); + +/* TODO: implementation of adjustment */ +bool dal_adj_container_get_display_content_capability( + const struct adj_container *adj_container, + union display_content_support *support); + +/* TODO: implementation of adjustment */ +bool dal_adj_container_get_adjustment_val( + const struct adj_container *adj_container, + enum adjustment_id adj_id, + uint32_t *val); + +bool dal_adj_info_set_update_cur_value( + struct adj_info_set *adj_info_set, + enum adjustment_id adj_id, + int32_t val); + +bool dal_adj_container_is_update_required( + struct adj_container *adj_container); + +void dal_adj_info_set_add_adj_info( + struct adj_info_set *adj_info_set, + struct adjustment_info *adj_info); + +void dal_adj_info_set_clear(struct adj_info_set *adj_info_set); + +void dal_adj_container_update_timing_mode( + struct adj_container *container, + const struct mode_info *mode_info, + const struct view *view); + +bool dal_adj_container_set_regamma( + struct adj_container *adj_container, + const struct ds_regamma_lut *regamma); + +bool dal_adj_container_get_gamut( + struct adj_container *adj_container, + enum adjustment_id adj_id, + struct gamut_data *data); + +const struct ds_regamma_lut *dal_adj_container_get_regamma( + struct adj_container *adj_container); + +bool dal_adj_container_validate_gamut( + struct adj_container *adj_container, + struct gamut_data *data); + +bool dal_adj_container_update_gamut( + struct adj_container *adj_container, + enum adjustment_id adj_id, + struct gamut_data *data); + +bool dal_adj_container_get_regamma_copy( + struct adj_container *adj_container, + struct ds_regamma_lut *regamma); + +#endif /* __DAL_ADJUSTMENT_CONTAINER_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/adjustment_types_internal.h b/drivers/gpu/drm/amd/dal/display_service/adjustment_types_internal.h new file mode 100644 index 000000000000..31180d049e97 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/adjustment_types_internal.h @@ -0,0 +1,198 @@ +/* + * Copyright 2012-15 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 __DAL_ADJUSTMENT_TYPES_INTERNAL_H__ +#define __DAL_ADJUSTMENT_TYPES_INTERNAL_H__ + +#include "include/adjustment_types.h" +#include "include/set_mode_types.h" +#include "include/hw_sequencer_types.h" +#include "include/timing_service_types.h" + +struct underscan_adjustment_group { + enum adjustment_id id_overscan; + enum adjustment_id id_underscan; + enum adjustment_id id_underscan_auto; + enum adjustment_id id_multi_media_pass_thru; + enum adjustment_id id_tv_underscan; + enum adjustment_id id_requested; + + union adjustment_property prop_overscan; + union adjustment_property prop_underscan; + union adjustment_property prop_underscan_auto; + union adjustment_property prop_multi_media_pass_thru; + union adjustment_property prop_tv_underscan; + + int32_t requested_value; + int32_t current_overscan; + int32_t current_percent_x; + int32_t current_percent_y; + int32_t current_underscan_auto; + int32_t current_multi_media_pass_thru; + struct ds_underscan_desc current_underscan_desc; +}; + +struct timing_info_parameter { + struct hw_crtc_timing timing; + uint32_t dst_width; + uint32_t dst_height; +}; +/* Display content type as flag */ +enum display_content_type { + DISPLAY_CONTENT_TYPE_NO_DATA = 0, + DISPLAY_CONTENT_TYPE_GRAPHICS = 1, + DISPLAY_CONTENT_TYPE_PHOTO = 2, + DISPLAY_CONTENT_TYPE_CINEMA = 4, + DISPLAY_CONTENT_TYPE_GAME = 8 +}; + +union display_content_support { + uint32_t raw; + struct { + uint32_t VALID_CONTENT_TYPE:1; + uint32_t GAME_CONTENT:1; + uint32_t CINEMA_CONTENT:1; + uint32_t PHOTO_CONTENT:1; + uint32_t GRAPHICS_CONTENT:1; + uint32_t RESERVED:27; + } bits; +}; + +/* to-do regkey if android supported*/ +union adjustment_api_flag { + uint32_t value; + struct { + uint32_t def_from_driver:1; + uint32_t reserved:31; + } bits; +}; + +struct range_adjustment_api { + enum adjustment_id adj_id; + int32_t def; + int32_t min; + int32_t max; + int32_t step; + union adjustment_api_flag flag; +}; + +union ds_scaler_flags { + uint32_t val; + struct { + uint32_t VALID_DS_MODE:1; + uint32_t IS_FOR_SET_MODE:1; + uint32_t IS_TV:1; + uint32_t IS_UNDERSCAN_DESC:1; + uint32_t RESERVED28:28; + } bits; +}; + +struct ds_adjustment_scaler { + uint32_t display_index; + enum adjustment_id adjust_id; + int32_t value; + enum timing_standard timing_standard; + enum timing_source timing_source; + struct ds_underscan_desc underscan_desc; + union ds_scaler_flags flags; +}; + +struct ds_adjustment_status { + uint32_t val; + struct { + uint32_t SET_TO_DEFAULT:1; + uint32_t SET_FROM_EXTERNAL:1; + uint32_t SET_TO_HARDWARE:1; + uint32_t RESERVED:29; + } bits; +}; + +enum ds_underscan_type { + DS_UNDERSCAN_TYPE_PERCENT, + DS_UNDERSCAN_TYPE_DIMENTIONS, +}; + +struct ds_underscan_data { + uint32_t position_x; + uint32_t position_y; + uint32_t width; + uint32_t height; +}; + +struct ds_underscan_percent { + uint32_t percent_x; + uint32_t percent_y; + uint32_t old_dst_x; + uint32_t old_dst_y; +}; + +struct ds_underscan_data_parameter { + struct ds_underscan_data data; + uint32_t modified_boarder_x; + uint32_t modified_boarder_y; +}; + +struct ds_underscan_parameter { + enum ds_underscan_type type; + union { + struct ds_underscan_data_parameter dimentions; + struct ds_underscan_percent percent; + } data; +}; + +enum ds_bit_depth_reduction { + DS_BIT_DEPTH_REDUCTION_DISABLE = 0, + DS_BIT_DEPTH_REDUCTION_DRIVER_DEFAULT, + DS_BIT_DEPTH_REDUCTION_FM6, + DS_BIT_DEPTH_REDUCTION_FM8, + DS_BIT_DEPTH_REDUCTION_FM10, + DS_BIT_DEPTH_REDUCTION_DITH6, + DS_BIT_DEPTH_REDUCTION_DITH8, + DS_BIT_DEPTH_REDUCTION_DITH10, + DS_BIT_DEPTH_REDUCTION_DITH6_NO_FRAME_RAND, + DS_BIT_DEPTH_REDUCTION_DITH8_NO_FRAME_RAND, + DS_BIT_DEPTH_REDUCTION_DITH10_NO_FRAME_RAND, + DS_BIT_DEPTH_REDUCTION_TRUN6, + DS_BIT_DEPTH_REDUCTION_TRUN8, + DS_BIT_DEPTH_REDUCTION_TRUN10, + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8, + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH6, + DS_BIT_DEPTH_REDUCTION_TRUN10_FM8, + DS_BIT_DEPTH_REDUCTION_TRUN10_FM6, + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8_FM6, + DS_BIT_DEPTH_REDUCTION_DITH10_FM8, + DS_BIT_DEPTH_REDUCTION_DITH10_FM6, + DS_BIT_DEPTH_REDUCTION_TRUN8_DITH6, + DS_BIT_DEPTH_REDUCTION_TRUN8_FM6, + DS_BIT_DEPTH_REDUCTION_DITH8_FM6, /* =23 */ + + DS_BIT_DEPTH_REDUCTION_MAX = DS_BIT_DEPTH_REDUCTION_DITH8_FM6 +}; + +enum color_temperature_source { + COLOR_TEMPERATURE_SOURCE_EDID = 1, + COLOR_TEMPERATURE_SOURCE_USER +}; + +#endif /* __DAL_ADJUSTMENT_TYPES_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.c b/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.c new file mode 100644 index 000000000000..0952ebd5f013 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.c @@ -0,0 +1,500 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/topology_mgr_interface.h" +#include "include/display_path_interface.h" +#include "include/display_service_types.h" +#include "include/hw_adjustment_types.h" +#include "include/adjustment_interface.h" +#include "include/logger_interface.h" +#include "include/hw_adjustment_set.h" + +#include "ds_dispatch.h" +#include "adjustment_container.h" +#include "backlight_adj_group.h" + +static uint32_t adj_id_to_cache_index( + enum adjustment_id adj_id) +{ + if (adj_id == ADJ_ID_BACKLIGHT) + return BI_ADJ_INDEX_BACKLIGHT; + else if (adj_id == ADJ_ID_BACKLIGHT_OPTIMIZATION) + return BI_ADJ_INDEX_BACKLIGHT_OPTIMIZATION; + + return -1; +} + +bool dal_backlight_adj_group_add_adj_to_post_mode_set( + struct backlight_adj_group *backlight_adj, + uint32_t value, + struct hw_adjustment_set *set) +{ + struct hw_adjustment_value *adj_value = NULL; + + if (set->backlight != NULL) { + dal_logger_write( + backlight_adj->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "HW Backlight adjustment is NULL"); + return false; + } + adj_value = dal_alloc(sizeof(*adj_value)); + + if (!adj_value) + return false; + + adj_value->i_value = value; + set->backlight = adj_value; + + return true; + +} +bool dal_backlight_adj_group_include_backlight_opt_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + uint32_t value, + struct hw_adjustment_set *set) +{ + uint32_t count = 0; + + switch (value) { + case DS_BACKLIGHT_OPTIMIZATION_DISABLE: + { + uint32_t backlight; + + if (dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT, + false, + &backlight)) { + if (dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + backlight, + set)) + count++; + } + if (dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + 0, + set)) + count++; + } + break; + case DS_BACKLIGHT_OPTIMIZATION_DESKTOP: + case DS_BACKLIGHT_OPTIMIZATION_DYNAMIC: + { + uint32_t backlight; + + if (dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT, + false, + &backlight)) { + if (dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + backlight, + set)) + count++; + } + + } + break; + case DS_BACKLIGHT_OPTIMIZATION_DIMMED: + { + struct panel_backlight_boundaries boundaries = {0}; + + if (dal_adapter_service_get_panel_backlight_boundaries( + backlight_adj->as, + &boundaries)) { + if (dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + boundaries.min_signal_level, + set)) + count++; + } + if (dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + 0, + set)) + count++; + } + break; + default: + break; + } + if (count > 0) + return true; + else + return false; +} + +bool dal_backlight_adj_group_include_post_set_mode_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + struct ds_adj_id_value adj, + struct hw_adjustment_set *set) +{ + uint32_t index = adj_id_to_cache_index(adj.adj_id); + uint32_t opt_adjustment = 0; + bool result = false; + uint32_t value = backlight_adj->cache[index].value; + + if (index >= NUM_OF_BACKLIGHT_ADJUSTMENTS) + return false; + + if (!backlight_adj->cache[index].pending) + return false; + + + if (adj.adj_id != ADJ_ID_BACKLIGHT_OPTIMIZATION) { + if (!dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT_OPTIMIZATION, + true, + &opt_adjustment)) + return false; + } + + switch (adj.adj_id) { + case ADJ_ID_BACKLIGHT: + { + if (opt_adjustment != DS_BACKLIGHT_OPTIMIZATION_DIMMED) + result = + dal_backlight_adj_group_add_adj_to_post_mode_set( + backlight_adj, + value, + set); + } + break; + case ADJ_ID_BACKLIGHT_OPTIMIZATION: + { + result = + dal_backlight_adj_group_include_backlight_opt_adj( + backlight_adj, + disp_path, + value, + set); + } + break; + default: + break; + } + + if (backlight_adj->cache[index].pending_sw_commit) { + uint32_t display_index = + dal_display_path_get_display_index(disp_path); + struct adj_container *adj_container = + dal_ds_dispatch_get_adj_container_for_path( + backlight_adj->ds, display_index); + const struct adjustment_info *adj_info = NULL; + + bool commit = (adj_container != NULL); + + if (commit) { + adj_info = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + adj.adj_id); + + commit = (adj_info != NULL); + } + if (commit) + commit = dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, + adj.adj_id, value); + + if (commit) + commit = dal_adj_container_commit_adj( + adj_container, adj.adj_id); + } + backlight_adj->cache[index].pending = false; + backlight_adj->cache[index].pending_sw_commit = false; + return result; +} + + +enum ds_return dal_backlight_adj_group_set_adjustment( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value) +{ + enum ds_return result = DS_SUCCESS; + uint32_t display_index = + dal_display_path_get_display_index(disp_path); + uint32_t adj_index = adj_id_to_cache_index(adj_id); + + struct adj_container *adj_container = + dal_ds_dispatch_get_adj_container_for_path( + backlight_adj->ds, + display_index); + const struct adjustment_info *adj_info = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, adj_id); + uint32_t opt_adj = 0; + + if (adj_container == NULL || + adj_index >= NUM_OF_BACKLIGHT_ADJUSTMENTS) + return DS_ERROR; + + + if (adj_info == NULL) { + + struct adjustment_info default_adj_info; + + if (dal_ds_dispatch_get_adjustment_info( + backlight_adj->ds, + display_index, + adj_id, + &default_adj_info) != DS_SUCCESS) + return DS_ERROR; + + if (value < default_adj_info.adj_data.ranged.min || + value > default_adj_info.adj_data.ranged.max) + return DS_ERROR; + backlight_adj->cache[adj_index].pending = true; + backlight_adj->cache[adj_index].pending_sw_commit = true; + backlight_adj->cache[adj_index].value = value; + return DS_SUCCESS; + } + if (value < adj_info->adj_data.ranged.min || + value > adj_info->adj_data.ranged.max) + return DS_ERROR; + + if (!dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, adj_id, value)) + return DS_ERROR; + + if (adj_id != ADJ_ID_BACKLIGHT_OPTIMIZATION) { + if (!dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT_OPTIMIZATION, + true, &opt_adj)) + return DS_ERROR; + } + + if (dal_display_path_is_acquired(disp_path) && + dal_tm_is_hw_state_valid(backlight_adj->tm)) { + + switch (adj_id) { + case ADJ_ID_BACKLIGHT: + { + if (opt_adj != DS_BACKLIGHT_OPTIMIZATION_DIMMED) + result = + dal_backlight_adj_group_set_backlight_adj( + backlight_adj, + disp_path, + value); + } + break; + case ADJ_ID_BACKLIGHT_OPTIMIZATION: + { + result = + dal_backlight_adj_group_set_backlight_optimization_adj( + backlight_adj, + disp_path, + value); + } + break; + default: + result = DS_ERROR; + break; + } + backlight_adj->cache[adj_index].pending = false; + backlight_adj->cache[adj_index].pending_sw_commit = false; + } else { + backlight_adj->cache[adj_index].pending = true; + backlight_adj->cache[adj_index].pending_sw_commit = false; + backlight_adj->cache[adj_index].value = value; + } + if (result == DS_SUCCESS) + dal_adj_container_commit_adj(adj_container, adj_id); + return result; +} + +bool dal_backlight_adj_group_get_current_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + bool allow_default, + uint32_t *value) +{ + uint32_t index = adj_id_to_cache_index(adj_id); + + if (backlight_adj->cache[index].pending) { + *value = backlight_adj->cache[index].value; + return true; + } else if (dal_ds_dispatch_get_adjustment_value( + backlight_adj->ds, + disp_path, + adj_id, + allow_default, + value) == DS_SUCCESS) + return true; + + return false; +} + +enum ds_return dal_backlight_adj_group_set_backlight_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + uint32_t value) +{ + enum ds_return result = DS_ERROR; + struct hw_adjustment_value hw_adj_value; + + hw_adj_value.ui_value = value; + if (dal_hw_sequencer_set_backlight_adjustment( + backlight_adj->hws, + disp_path, + &hw_adj_value) == HWSS_RESULT_OK) + result = DS_SUCCESS; + + return result; +} + +enum ds_return dal_backlight_adj_group_set_backlight_optimization_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + uint32_t value) +{ + switch (value) { + case DS_BACKLIGHT_OPTIMIZATION_DISABLE: + { + uint32_t backlight; + + if (dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT, + false, + &backlight)) { + + if (dal_backlight_adj_group_set_backlight_adj( + backlight_adj, + disp_path, + backlight) != DS_SUCCESS) + return DS_ERROR; + } + } + break; + case DS_BACKLIGHT_OPTIMIZATION_DESKTOP: + case DS_BACKLIGHT_OPTIMIZATION_DYNAMIC: + { + uint32_t backlight; + + if (dal_backlight_adj_group_get_current_adj( + backlight_adj, + disp_path, + ADJ_ID_BACKLIGHT, + false, + &backlight)) { + if (dal_backlight_adj_group_set_backlight_adj( + backlight_adj, + disp_path, + backlight) != DS_SUCCESS) + return DS_ERROR; + } + + } + break; + case DS_BACKLIGHT_OPTIMIZATION_DIMMED: + { + struct panel_backlight_boundaries boundaries = {0}; + uint32_t backlight; + + if (!dal_adapter_service_get_panel_backlight_boundaries( + backlight_adj->as, + &boundaries)) + return DS_ERROR; + + backlight = boundaries.min_signal_level; + if (dal_backlight_adj_group_set_backlight_adj( + backlight_adj, + disp_path, + backlight) != DS_SUCCESS) + return DS_ERROR; + + } + break; + default: + return DS_ERROR; + } + return DS_SUCCESS; + +} + +static bool backlight_adj_group_construct( + struct backlight_adj_group *backlight_adj, + struct backlight_adj_group_init_data *init_data) +{ + if (!init_data) + return false; + + backlight_adj->ds = init_data->ds; + backlight_adj->as = init_data->as; + backlight_adj->hws = init_data->hws; + backlight_adj->tm = init_data->tm; + backlight_adj->dal_context = init_data->dal_context; + + return true; +} + +struct backlight_adj_group *dal_backlight_adj_group_create( + struct backlight_adj_group_init_data *init_data) +{ + struct backlight_adj_group *backlight_adj = NULL; + + backlight_adj = dal_alloc(sizeof(*backlight_adj)); + + if (!backlight_adj) + return NULL; + + if (backlight_adj_group_construct(backlight_adj, init_data)) + return backlight_adj; + + dal_free(backlight_adj); + + return NULL; +} + +static void destruct( + struct backlight_adj_group *backlight_adj) +{ +} + +void dal_backlight_adj_group_destroy( + struct backlight_adj_group **backlight_adj) +{ + if (backlight_adj == NULL || *backlight_adj == NULL) + return; + destruct(*backlight_adj); + dal_free(*backlight_adj); + *backlight_adj = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.h b/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.h new file mode 100644 index 000000000000..ff23040e8425 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/backlight_adj_group.h @@ -0,0 +1,97 @@ +/* + * Copyright 2012-15 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 __DAL_BACKLIGHT_ADJ_GROUP_H__ +#define __DAL_BACKLIGHT_ADJ_GROUP_H__ + +/* Include */ +#include "include/adjustment_types.h" + + +#define NUM_OF_BACKLIGHT_ADJUSTMENTS 4 + +struct adj_catch { + uint32_t value; + bool pending; + bool pending_sw_commit; +}; +enum bi_adj_index { + BI_ADJ_INDEX_BACKLIGHT = 0, + BI_ADJ_INDEX_BACKLIGHT_OPTIMIZATION +}; + +struct backlight_adj_group { + struct ds_dispatch *ds; + struct topology_mgr *tm; + struct hw_sequencer *hws; + struct adapter_service *as; + struct dal_context *dal_context; + struct adj_catch cache[NUM_OF_BACKLIGHT_ADJUSTMENTS]; +}; + +struct backlight_adj_group_init_data { + struct ds_dispatch *ds; + struct topology_mgr *tm; + struct hw_sequencer *hws; + struct adapter_service *as; + struct dal_context *dal_context; +}; + +struct backlight_adj_group *dal_backlight_adj_group_create( + struct backlight_adj_group_init_data *init_data); + +void dal_backlight_adj_group_destroy( + struct backlight_adj_group **backlight_adj); + +bool dal_backlight_adj_group_get_current_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + bool allow_default, + uint32_t *value); + +enum ds_return dal_backlight_adj_group_set_backlight_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + uint32_t value); + +enum ds_return dal_backlight_adj_group_set_backlight_optimization_adj( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + uint32_t value); + + +bool dal_backlight_adj_group_add_adj_to_post_mode_set( + struct backlight_adj_group *backlight_adj, + uint32_t value, + struct hw_adjustment_set *set); + +enum ds_return dal_backlight_adj_group_set_adjustment( + struct backlight_adj_group *backlight_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value); + +#endif /*__DAL_BACKLIGHT_ADJ_GROUP_H__*/ diff --git a/drivers/gpu/drm/amd/dal/display_service/color_temperature.c b/drivers/gpu/drm/amd/dal/display_service/color_temperature.c new file mode 100644 index 000000000000..39b92193635e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/color_temperature.c @@ -0,0 +1,212 @@ +/* + * Copyright 2015 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 "dal_services.h" + +#include "color_temperature.h" + + +static const struct white_point_entry white_point_table[] = { +/*001*/ { 1000, 6499, 3474 }, +/*002*/ { 1100, 6361, 3594 }, +/*003*/ { 1200, 6226, 3703 }, +/*004*/ { 1300, 6095, 3801 }, +/*005*/ { 1400, 5966, 3887 }, +/*006*/ { 1500, 5841, 3962 }, +/*007*/ { 1600, 5720, 4025 }, +/*008*/ { 1700, 5601, 4076 }, +/*009*/ { 1800, 5486, 4118 }, +/*010*/ { 1900, 5375, 4150 }, +/*011*/ { 2000, 5267, 4173 }, +/*012*/ { 2100, 5162, 4188 }, +/*013*/ { 2200, 5062, 4196 }, +/*014*/ { 2300, 4965, 4198 }, +/*015*/ { 2400, 4872, 4194 }, +/*016*/ { 2500, 4782, 4186 }, +/*017*/ { 2600, 4696, 4173 }, +/*018*/ { 2700, 4614, 4158 }, +/*019*/ { 2800, 4535, 4139 }, +/*020*/ { 2900, 4460, 4118 }, +/*021*/ { 3000, 4388, 4095 }, +/*022*/ { 3100, 4320, 4070 }, +/*023*/ { 3200, 4254, 4044 }, +/*024*/ { 3300, 4192, 4018 }, +/*025*/ { 3400, 4132, 3990 }, +/*026*/ { 3500, 4075, 3962 }, +/*027*/ { 3600, 4021, 3934 }, +/*028*/ { 3700, 3969, 3905 }, +/*029*/ { 3800, 3919, 3877 }, +/*030*/ { 3900, 3872, 3849 }, +/*031*/ { 4000, 3827, 3820 }, +/*032*/ { 4100, 3784, 3793 }, +/*033*/ { 4200, 3743, 3765 }, +/*034*/ { 4300, 3704, 3738 }, +/*035*/ { 4400, 3666, 3711 }, +/*036*/ { 4500, 3631, 3685 }, +/*037*/ { 4600, 3596, 3659 }, +/*038*/ { 4700, 3563, 3634 }, +/*039*/ { 4800, 3532, 3609 }, +/*040*/ { 4900, 3502, 3585 }, +/*041*/ { 5000, 3473, 3561 }, +/*042*/ { 5100, 3446, 3538 }, +/*043*/ { 5200, 3419, 3516 }, +/*044*/ { 5300, 3394, 3494 }, +/*045*/ { 5400, 3369, 3472 }, +/*046*/ { 5500, 3346, 3451 }, +/*047*/ { 5600, 3323, 3431 }, +/*048*/ { 5700, 3302, 3411 }, +/*049*/ { 5800, 3281, 3392 }, +/*050*/ { 5900, 3261, 3373 }, +/*051*/ { 6000, 3242, 3355 }, +/*052*/ { 6100, 3223, 3337 }, +/*053*/ { 6200, 3205, 3319 }, +/*054*/ { 6300, 3188, 3302 }, + +/*055*/ { 6400, 3161, 3296 }, +/*056*/ { 6500, 3127, 3290 }, +/*057*/ { 6600, 3126, 3264 }, +/*058*/ { 6700, 3125, 3238 }, +/*059*/ { 6800, 3110, 3224 }, +/*060*/ { 6900, 3097, 3209 }, +/*061*/ { 7000, 3083, 3195 }, +/*062*/ { 7100, 3070, 3181 }, +/*063*/ { 7200, 3058, 3168 }, +/*064*/ { 7300, 3045, 3154 }, +/*065*/ { 7400, 3034, 3142 }, +/*066*/ { 7500, 3022, 3129 }, +/*067*/ { 7600, 3011, 3117 }, +/*068*/ { 7700, 3000, 3105 }, +/*069*/ { 7800, 2990, 3094 }, +/*070*/ { 7900, 2980, 3082 }, +/*071*/ { 8000, 2970, 3071 }, +/*072*/ { 8100, 2961, 3061 }, +/*073*/ { 8200, 2952, 3050 }, +/*074*/ { 8300, 2943, 3040 }, +/*075*/ { 8400, 2934, 3030 }, +/*076*/ { 8500, 2926, 3020 }, +/*077*/ { 8600, 2917, 3011 }, +/*078*/ { 8700, 2910, 3001 }, +/*079*/ { 8800, 2902, 2992 }, +/*080*/ { 8900, 2894, 2983 }, +/*081*/ { 9000, 2887, 2975 }, +/*082*/ { 9100, 2880, 2966 }, +/*083*/ { 9200, 2873, 2958 }, +/*084*/ { 9300, 2866, 2950 }, +/*085*/ { 9400, 2860, 2942 }, +/*086*/ { 9500, 2853, 2934 }, +/*087*/ { 9600, 2847, 2927 }, +/*088*/ { 9700, 2841, 2919 }, +/*089*/ { 9800, 2835, 2912 }, +/*090*/ { 9900, 2829, 2905 }, +/*091*/ { 10000, 2824, 2898 }, +}; + +bool dal_color_temperature_find_white_point( + int32_t request_temperature, + struct white_point_data *data) +{ + bool ret = false; + struct white_point_entry entry; + + if (request_temperature > 0) { + ret = + dal_color_temperature_search_white_point_table( + request_temperature, + &entry); + if (ret == true) { + data->white_x = entry.dx; + data->white_y = entry.dy; + } + } + return ret; +} + +bool dal_color_temperature_search_white_point_table( + uint32_t temp_to_find, + struct white_point_entry *entry) +{ + bool ret = false; + const struct white_point_entry *p; + uint32_t const_size; + + const_size = ARRAY_SIZE(white_point_table); + + for (p = white_point_table; p < &white_point_table[const_size]; p++) { + if (p->temperature == temp_to_find) { + *entry = *p; + ret = true; + break; + } + } + return ret; +} + +bool dal_color_temperature_find_color_temperature( + struct white_point_data *data, + int32_t *temperature, + bool *exact_match) +{ + bool ret = false; + const struct white_point_entry *p; + const struct white_point_entry *next; + const struct white_point_entry *foundx = NULL; + uint32_t const_size; + + const_size = ARRAY_SIZE(white_point_table); + + for (p = white_point_table; p < &white_point_table[const_size]; p++) { + if (p->dx == data->white_x && p->dy == data->white_y) { + *temperature = p->temperature; + *exact_match = true; + ret = true; + break; + } + } + + if (ret == false) { + for (p = white_point_table; + p < &white_point_table[const_size]; p++) { + next = p + 1; + if (p->dx >= data->white_x && + next->dx <= data->white_x) { + foundx = p; + *exact_match = false; + ret = true; + break; + } + if (foundx != NULL) + *temperature = foundx->temperature; + } + + } + if (ret == false) { + /* no such color temperature */ + /* give default and investigate because + * it would be annoying for CCC */ + *temperature = 6500; + ret = true; + } + return ret; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/color_temperature.h b/drivers/gpu/drm/amd/dal/display_service/color_temperature.h new file mode 100644 index 000000000000..03e9e49b6143 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/color_temperature.h @@ -0,0 +1,56 @@ +/* + * Copyright 2015 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 __DAL_COLOR_TEMPERATURE_H__ +#define __DAL_COLOR_TEMPERATURE_H__ + +/* Include */ +#include "include/adjustment_types.h" + +struct white_point_data { + uint32_t white_x; + uint32_t white_y; +}; + +struct white_point_entry { + int32_t temperature; + uint32_t dx; + uint32_t dy; +}; + +bool dal_color_temperature_find_white_point( + int32_t request_temperature, + struct white_point_data *data); + +bool dal_color_temperature_find_color_temperature( + struct white_point_data *data, + int32_t *temperature, + bool *exact_match); + +bool dal_color_temperature_search_white_point_table( + uint32_t temp_to_find, + struct white_point_entry *entry); + +#endif /*__DAL_COLOR_TEMPERATURE_H__*/ diff --git a/drivers/gpu/drm/amd/dal/display_service/display_service.c b/drivers/gpu/drm/amd/dal/display_service/display_service.c new file mode 100644 index 000000000000..0fac5410bb09 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/display_service.c @@ -0,0 +1,631 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/display_service_interface.h" +#include "include/topology_mgr_interface.h" +#include "include/set_mode_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/link_service_interface.h" +#include "include/adjustment_interface.h" +#include "include/display_path_interface.h" + +#include "display_service.h" +#include "ds_dispatch.h" +#include "path_mode_set_with_data.h" + +struct display_service { + struct ds_dispatch *ds_dispatch; +}; + +/* + * Local function declaration + */ +/* Initialize display service */ +static bool ds_construct( + struct display_service *ds, const struct ds_init_data *data); + +/* + * dal_display_service_create + * + * Create display service + */ +struct display_service *dal_display_service_create(struct ds_init_data *data) +{ + struct display_service *ds; + + ds = dal_alloc(sizeof(struct display_service)); + + if (ds == NULL) + return NULL; + + if (ds_construct(ds, data)) + return ds; + + dal_free(ds); + BREAK_TO_DEBUGGER(); + + return NULL; +} + +/* + * dal_display_service_destroy + * + * Destroy display service + */ +void dal_display_service_destroy(struct display_service **ds) +{ + if (!ds || !*ds) { + BREAK_TO_DEBUGGER(); + return; + } + dal_ds_dispatch_cleanup_adjustment((*ds)->ds_dispatch); + dal_ds_dispatch_destroy(&(*ds)->ds_dispatch); + + dal_free(*ds); + *ds = NULL; +} + +struct ds_dispatch *dal_display_service_get_adjustment_interface( + struct display_service *ds) +{ + return ds->ds_dispatch; +} + +struct ds_overlay *dal_display_service_get_overlay_interface( + struct display_service *ds) +{ + /*TODO: add implementation*/ + return NULL; +} + +struct ds_dispatch *dal_display_service_get_set_mode_interface( + struct display_service *ds) +{ + if (ds == NULL) + return NULL; + + return ds->ds_dispatch; +} + +struct ds_dispatch *dal_display_service_get_reset_mode_interface( + struct display_service *ds) +{ + if (ds == NULL) + return NULL; + + return ds->ds_dispatch; +} + +struct ds_synchronization *dal_display_service_get_synchronization_interface( + struct display_service *ds) +{ + /*TODO: add implementation*/ + return NULL; +} + +enum ds_return dal_display_service_notify_v_sync_int_state( + struct display_service *ds, + uint32_t display_index, + bool maintain_v_sync_phase) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_target_power_control( + struct display_service *ds, + uint32_t display_index, + bool power_on) +{ + /* Handles DPMS states of all displaypaths + * by event and Manages the DPMS state of + * given DisplayPath by powerOn + */ + /*TODO: Depends on AdjustmentContainer.cpp, should + * be uncommented once adjustmentContainer is + * implemented. + * ManageDPMSState(displayPathIndex, powerOn);*/ + + enum hwss_result hwss_ret = HWSS_RESULT_OK; + struct hw_path_mode hw_path_mode; + struct display_path *display_path = NULL; + + if (dal_tm_is_hw_state_valid(ds->ds_dispatch->tm)) { + uint32_t link_cnt = 0; + int32_t i = 0; + struct active_path_data *path_data = + dal_pms_with_data_get_path_data_for_display_index( + ds->ds_dispatch->set, + display_index); + + if (!path_data) + return DS_ERROR; + + path_data->flags.bits.TURN_OFF_BACK_END_AND_RX = + power_on ? 0 : 1; + + /* Create hw_path_mode for this display path*/ + if (!dal_ds_dispatch_build_hw_path_mode_for_adjustment( + ds->ds_dispatch, + &hw_path_mode, + display_index, + NULL)) + /* DisplayIndex requested not in + * currently active PathModeSet, + * therefore invalid parameter + */ + return DS_ERROR; + + /*Signal EventID_DisplayPhyAccessBegin + * event to allow access to Display output HW + */ + /*TODO: Event eventPhyAccessBegin( + * EventID_DisplayPhyAccessBegin); + * getEM()->SendEvent(this, &eventPhyAccessBegin); + */ + + display_path = dal_tm_display_index_to_display_path( + ds->ds_dispatch->tm, + display_index); + link_cnt = dal_display_path_get_number_of_links( + display_path); + + if (power_on) { + /* Call EnableAllowSelfRefresh + * when not resuming from S3 or S4. */ + /*TODO: Remove the below + * comments once stutter mode + * is implemented. + if (dal_tm_get_current_power_state( + ds->ds_dispatch->tm) == + VIDEO_POWER_ON && + dal_tm_get_previous_power_state( + ds->ds_dispatch->tm) > + VIDEO_POWER_ON && + dal_tm_get_previous_power_state( + ds->ds_dispatch->tm) < + VIDEO_POWER_SHUTDOWN) + hwss_ret = + dal_hw_sequencer_enable_allow_self_refresh( + &hw_path_mode, + false); + */ + /* turn off DPMS light sleep - power on memories*/ + /*TODO: Mainly used for DCE 10 & DCE11 + * dal_tm_toggle_dpms_light_sleep(false); + */ + + /* move stream to Enabled from Power Off state*/ + for (i = 0; i < link_cnt; i++) { + + struct link_service *ls = + dal_display_path_get_link_config_interface( + display_path, i); + + ASSERT_CRITICAL(ls != NULL); + dal_ls_power_on_stream( + ls, + display_index, + &hw_path_mode); + } + + + + if (dal_adapter_service_is_feature_supported( + FEATURE_DPMS_AUDIO_ENDPOINT_CONTROL)) + /* Enable audio device*/ + dal_hw_sequencer_enable_azalia_audio_jack_presence( + ds->ds_dispatch->hwss, + display_path); + else + dal_hw_sequencer_mute_audio_endpoint( + ds->ds_dispatch->hwss, + display_path, + false); + + /* move stream from Enabled to Active*/ + for (i = 0; i < link_cnt; i++) { + + struct link_service *ls = + dal_display_path_get_link_config_interface( + display_path, i); + + ASSERT_CRITICAL(ls != NULL); + + dal_ls_unblank_stream( + ls, + display_index, + &hw_path_mode); + } + + dal_hw_sequencer_mute_audio_endpoint( + ds->ds_dispatch->hwss, + display_path, + false); + + /* Make sure internal states are updated properly*/ + path_data->display_state.OUTPUT_ENABLED = 1; + path_data->display_state.OUTPUT_BLANKED = 0; + + /* + * Re-enable PSR if display is not in blanked state + */ + if (dal_display_path_is_source_blanked(display_path) && + dal_display_path_is_psr_supported(display_path)) + dal_hw_sequencer_psr_enable( + ds->ds_dispatch->hwss, + display_path); + + /* Use m_numDisplaysConnected to mark the + * end of wake-up/resume by setting TM's + * current power state to On + */ + /*TODO: Depends on AdjustmentContainer.cpp, should be + * uncommented once AdjustmentContainer is implemented + * if (m_numDisplaysDPMSOn == m_numDisplaysConnected) + { + getTM()->SetCurrentPowerState(VideoPowerOn); + }*/ + } else { + /* disable PSR*/ + if (dal_display_path_is_psr_supported(display_path)) + dal_hw_sequencer_psr_disable( + ds->ds_dispatch->hwss, + display_path); + + /* move stream from Active to Enable state*/ + for (i = link_cnt - 1; i >= 0; i--) { + + struct link_service *ls = + dal_display_path_get_link_config_interface( + display_path, i); + + ASSERT_CRITICAL(ls != NULL); + dal_ls_blank_stream( + ls, + display_index, + &hw_path_mode); + dal_hw_sequencer_mute_audio_endpoint( + ds->ds_dispatch->hwss, + hw_path_mode.display_path, + true); + } + + /* move stream from Enable to Power Off state*/ + for (i = link_cnt - 1; i >= 0; i--) { + + struct link_service *ls = + dal_display_path_get_link_config_interface( + display_path, i); + + ASSERT_CRITICAL(ls != NULL); + + dal_ls_power_off_stream( + ls, + display_index, + &hw_path_mode); + } + + /* blank CRTC and enable stutter + * mode allow_self_Refresh*/ + /*TODO: Remove comments after implementing + * stutter mode + */ + /* hwssRet = getHWSS()-> + * EnableAllowSelfRefresh( + * &hwPathMode, + * true); + + force SW controlled memories + into light sleep state + getTM()->ToggleDPMSLightSleep(true); + */ + /* Make sure internal states + * are updated properly*/ + path_data->display_state.OUTPUT_ENABLED = 0; + path_data->display_state.OUTPUT_BLANKED = 1; + } + + /*TODO: PPLib Notification + Logical state of display might + change so we need to update PPlib cache + m_pDSDispatch-> + NotifySingleDisplayConfig( + displayPathIndex, + true); + + //Signal EventID_DisplayPhyAccessEnd + event to inform that Display + output configuration has changed + Event eventPhyAccessEnd( + EventID_DisplayPhyAccessEnd); + getEM()->SendEvent( + this, &eventPhyAccessEnd);*/ + } + + /*TODO: PPLib Notification + NotifyETW(DAL_NOTIFYPPLIBSCREENSTATUSCHANGE_ENTER); + + notify PPLib only once - + when first display DPMS on or + last display DPMS off, pplib will program + NBMCU based on this. + if (powerOn && m_numDisplaysDPMSOn == 1) + { + getEC()->NotifyScreenStatusChange(true); + } + else if (!powerOn && m_numDisplaysDPMSOn == 0) + { + getEC()->NotifyScreenStatusChange(false); + //Clear m_numDisplaysConnected + m_numDisplaysConnected = 0; + } + + NotifyETW(DAL_NOTIFYPPLIBSCREENSTATUSCHANGE_EXIT); + */ + /* No adjustments expected be allocated + * no need to destroy adjustments + */ + return (hwss_ret == HWSS_RESULT_OK ? DS_SUCCESS : DS_ERROR); +} + +enum ds_return dal_display_service_power_down_active_hw( + struct display_service *ds, + enum dal_video_power_state state) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_mem_request_control( + struct display_service *ds, + uint32_t display_index, + bool blank) +{ + if (dal_tm_is_hw_state_valid(ds->ds_dispatch->tm)) { + struct display_path *display_path; + enum signal_type signal; + struct hw_path_mode mode; + + if (!dal_ds_dispatch_build_hw_path_mode_for_adjustment( + ds->ds_dispatch, &mode, display_index, NULL)) + return DS_ERROR; + + display_path = dal_tm_create_resource_context_for_display_index( + ds->ds_dispatch->tm, display_index); + + signal = dal_display_path_get_query_signal( + display_path, SINK_LINK_INDEX); + + dal_tm_destroy_resource_context_for_display_path( + ds->ds_dispatch->tm, display_path); + + if (!blank) { + dal_hw_sequencer_enable_memory_requests( + ds->ds_dispatch->hwss, + &mode); + + if (signal == SIGNAL_TYPE_WIRELESS) + dal_hw_sequencer_enable_wireless_idle_detection + (ds->ds_dispatch->hwss, true); + } else { + dal_hw_sequencer_disable_memory_requests( + ds->ds_dispatch->hwss, + &mode); + if (signal == SIGNAL_TYPE_WIRELESS) + dal_hw_sequencer_enable_wireless_idle_detection + (ds->ds_dispatch->hwss, false); + } + } + + return DS_SUCCESS; +} + +enum ds_return dal_display_service_set_multimedia_pass_through_mode( + struct display_service *ds, + uint32_t display_index, + bool passThrough) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_set_palette( + struct display_service *ds, + uint32_t display_index, + const struct ds_devclut *palette, + const uint32_t start, + const uint32_t length) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_apply_pix_clk_range( + struct display_service *ds, + uint32_t display_index, + struct pixel_clock_safe_range *range) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_get_safe_pix_clk( + struct display_service *ds, + uint32_t display_index, + uint32_t *pix_clk_khz) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_apply_refreshrate_adjustment( + struct display_service *ds, + uint32_t display_index, + enum ds_refreshrate_adjust_action action, + struct ds_refreshrate *refreshrate) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_pre_ddc( + struct display_service *ds, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_post_ddc( + struct display_service *ds, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_backlight_control( + struct display_service *ds, + uint32_t display_index, + bool enable) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_get_backlight_user_level( + struct display_service *ds, + uint32_t display_index, + uint32_t *level) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_get_backlight_effective_level( + struct display_service *ds, + uint32_t display_index, + uint32_t *level) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_enable_hpd( + struct display_service *ds, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_disable_hpd( + struct display_service *ds, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_get_min_mem_channels( + struct display_service *ds, + const struct path_mode_set *path_mode_set, + uint32_t mem_channels_num, + uint32_t *min_mem_channels_num) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_enable_advanced_request( + struct display_service *ds, + bool enable) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +/*Audio related*/ + +enum ds_return dal_display_service_enable_audio_endpoint( + struct display_service *ds, + uint32_t display_index, + bool enable) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_display_service_mute_audio_endpoint( + struct display_service *ds, + uint32_t display_index, + bool mute) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +bool dal_display_service_calc_view_port_for_wide_display( + struct display_service *ds, + uint32_t display_index, + const struct ds_view_port *set_view_port, + struct ds_get_view_port *get_view_port) +{ + /*TODO: add implementation*/ + return false; +} + +/* + * Local function definition + */ + +/* + * ds_construct + * + * Initialize display service + */ +static bool ds_construct( + struct display_service *ds, const struct ds_init_data *data) +{ + struct ds_dispatch_init_data dispatch_data; + + if (data == NULL) + return false; + + dispatch_data.dal_context = data->dal_context; + dispatch_data.as = data->as; + dispatch_data.hwss = data->hwss; + dispatch_data.tm = data->tm; + dispatch_data.ts = data->ts; + + ds->ds_dispatch = dal_ds_dispatch_create(&dispatch_data); + + if (ds->ds_dispatch == NULL) + return false; + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/display_service.h b/drivers/gpu/drm/amd/dal/display_service/display_service.h new file mode 100644 index 000000000000..7b0596eb37b7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/display_service.h @@ -0,0 +1,30 @@ +/* + * Copyright 2012-15 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 __DAL_DISPLAY_SERVICE_H__ +#define __DAL_DISPLAY_SERVICE_H__ + + +#endif diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_calculation.c b/drivers/gpu/drm/amd/dal/display_service/ds_calculation.c new file mode 100644 index 000000000000..cc26248c1efc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_calculation.c @@ -0,0 +1,288 @@ + +/* + * Copyright 2012-15 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 */ +#include "dal_services.h" + +/*#include "include/set_mode_interface.h" +#include "include/hw_path_mode_set_interface.h" +#include "include/topology_mgr_interface.h" +#include "include/dcs_interface.h" +#include "include/link_service_interface.h"*/ +#include "include/display_path_interface.h" +#include "include/display_service_types.h" +#include "include/hw_sequencer_types.h" +#include "include/timing_service_types.h" +#include "include/dcs_types.h" + +#include "ds_calculation.h" + +/* + * Public function definition + */ + +/* Adjust timing to the timing limits. */ +void dal_ds_calculation_tuneup_timing( + struct hw_crtc_timing *timing, + const struct timing_limits *timing_limits) +{ + uint32_t cur_pixel_clock; + uint32_t new_pixel_clock; + uint32_t new_h_total; + + if ((timing_limits == NULL) || + (timing == NULL) || + (timing->h_total == 0) || + (timing->v_total == 0)) { + return; + } + + /* Perform all calculations in DS internal units [kHz] */ + cur_pixel_clock = timing->pixel_clock; + new_h_total = timing->h_total; + + /* Set pixel clock in the middle of the range. + * Upper layer should provide a valid range. */ + new_pixel_clock = + (timing_limits->max_pixel_clock_in_khz + + timing_limits->min_pixel_clock_in_khz) / + 2; + + if ((new_pixel_clock > 0) && + (new_pixel_clock != cur_pixel_clock)) { + uint32_t v_h_total = timing->h_total * timing->v_total; + uint32_t refresh_rate = + ((timing->pixel_clock * 1000) + (v_h_total / 2)) / + v_h_total; + + /* Pixel clock adjusted to fit in the range; adjust HTOTAL to + * preserve refresh rate. + * + * SS = Sync Start + * SE = Sync End + * SW = Sync Width + * HA = Horizontal Active + * HT = Horizontal Total + * HBS = Horizontal Blank Start + * HBE = Horizontal Blank End + * FP = FrontPorch (affected by changing Horizontal Total) + * + * SS. .SE + * ______ ________________ ______ + * ___| |______|xxxxxxxxxxxxxxxx|______| |___ + * | | | | | + * |<-SW->| |<------HA------>| | + * | | | | + * |<-----------------HT---------------->| + * | | | | + * |--------------HBS------------>| | + * |-----HBE---->| | | + * | | + * |<-FP->| + * 32 164 2084 2184 + * + * Assuming there is no pixel repetition, register programming + * will look like this: + * + * CRTC_H_TOTAL = HTotal + * CRTC_H_SYNC_X_START = 0 + * CRTC_H_SYNC_X_END = HSyncWidth + * CRTC_H_BANK_START = HTotal - (HSyncStart - HActive) + * CRTC_H_BANK_END = HTotal - HSyncStart + * + * pixelClock = HTotal * VTotal * refRate => + * HTotal = pixelClock / VTotal / refRate + * + * newFrontPorch = newHTtotal - blankStart = + * (newPixelClock/VTotal/refRate) - blankStart */ + + int32_t cur_front_porch = + timing->h_sync_start - timing->h_addressable; + int32_t blank_start = + timing->h_total - cur_front_porch; + int32_t new_front_porch = + (new_pixel_clock * + PIXEL_CLOCK_MULTIPLIER / + timing->v_total / + refresh_rate) - + blank_start; + + /* New HTOTAL differs from current one by front porch + * difference */ + new_h_total += new_front_porch - cur_front_porch; + timing->pixel_clock = new_pixel_clock; + timing->h_total = new_h_total; + } +} + +/* Setup ranged timing parameters for features such as DRR or PSR. */ +void dal_ds_calculation_setup_ranged_timing( + struct hw_crtc_timing *timing, + struct display_path *display_path, + struct ranged_timing_preference_flags flags) +{ + uint64_t fps_in_micro_hz = 0; + bool drr_supported = false; + bool psr_supported = false; + bool vce_supported = false; + struct drr_config drr_config; + uint32_t v_h_total; + struct hw_ranged_timing *rt; + + if ((display_path == NULL) || (timing == NULL)) + return; + + /* Temporary pointer to ranged timing we want to build */ + rt = &timing->ranged_timing; + + /* Initialize flags to program both static screen event triggers and + * dynamic refresh rate. + * Originally, the purpose of these flags is to prevent DRR from being + * enabled if PSR was supported. But in current implementation, PSR may + * be used in static screen, while DRR can still be enabled during non- + * static state. Because of this, we always need prepare ranged timing + * for both features. When we do actual programming, we may set flags + * to enable the feature we actually want at the current state. */ + rt->control.program_static_screen_mask = false; + rt->control.program_dynamic_refresh_rate = false; + rt->control.force_disable_drr = false; + + /* 1. Check for features that require Static Screen Detection */ + v_h_total = timing->h_total * timing->v_total; + if (v_h_total != 0) { + fps_in_micro_hz = timing->pixel_clock * 1000; + fps_in_micro_hz *= 1000000; + fps_in_micro_hz = div_u64(fps_in_micro_hz, v_h_total); + } + + dal_display_path_get_drr_config(display_path, &drr_config); + + if (dal_display_path_is_psr_supported(display_path)) { + /* Check if PSR is supported. PSR requires static screen + * detection enabled in HW. */ + psr_supported = true; + + /* If PSR is enabled, DRR should be force disabled + * unless we override later by forced flags. */ + rt->control.force_disable_drr = true; + } else if (dal_display_path_get_config_signal( + display_path, SINK_LINK_INDEX) == + SIGNAL_TYPE_WIRELESS) { + /* Check if this display is a Wireless Display by checking + * the sink signal type. If this is a Wireless Display, static + * screen detection may be enabled for power saving. */ + vce_supported = true; + } else if (dal_display_path_is_drr_supported(display_path)) { + /* The check for DRR supported returns true means it satisfied: + * 1. EDID reported DRR capability and Stream supports DRR or + * 2. Forced capability through runtime parameter or + * 3. Forced capability through VBIOS */ + drr_supported = true; + } + + /* 2. Apply some override flags */ + if (flags.bits.force_disable_drr == 1) { + /* Setup ranged timing parameters to force disable DRR. + * This is typically set if PSR is being enabled and we want + * to force disable DRR. */ + rt->control.force_disable_drr = true; + } else if (flags.bits.prefer_enable_drr == 1) { + /* Preference flag to enable DRR with higher priority. For + * static screen power saving use case, DRR is usually not + * enabled if PSR is also supported in the system. But in this + * case it may be OS telling us to disable power feature for + * screen active. Then we want to use DRR for matching render + * and refresh rates. */ + rt->control.force_disable_drr = false; + } + + /* 3. If any feature is supported on the display, enable static screen + * detection and prepare ranged timing. */ + if (drr_supported || psr_supported || vce_supported) { + struct static_screen_events ss_events; + + rt->control.program_static_screen_mask = true; + + /* Initialize to the VTOTAL value. This means refresh rate will + * be constant. */ + rt->vertical_total_min = timing->v_total; + rt->vertical_total_max = timing->v_total; + + /* Prevent divide by zero. We always want to build DRR settings + * if possible, even if not for static screen purpose. It could + * still be used for 48 Hz feature. */ + if (drr_config.min_fps_in_microhz != 0) { + timing->ranged_timing.control.program_dynamic_refresh_rate + = true; + + /* If DRR is supported, update DRR parameters with min + * and max VTOTAL values to define the refresh rate + * range. */ + rt->vertical_total_min = timing->v_total; + rt->vertical_total_max = + div_u64((timing->v_total * fps_in_micro_hz), + drr_config.min_fps_in_microhz); + + rt->control.force_lock_on_event = + drr_config.force_lock_on_event; + rt->control.lock_to_master_vsync = + drr_config.lock_to_master_vsync; + } + + dal_display_path_get_static_screen_triggers( + display_path, &ss_events); + + rt->control.event_mask.u_all = 0; + rt->control.event_mask.bits.FRAME_START = + ss_events.bits.FRAME_START; + rt->control.event_mask.bits.CURSOR_MOVE = + ss_events.bits.CURSOR_MOVE; + rt->control.event_mask.bits.MEM_WRITE = + ss_events.bits.MEM_WRITE; + rt->control.event_mask.bits.MEM_REGION0_WRITE = + ss_events.bits.MEM_REGION0_WRITE; + rt->control.event_mask.bits.MEM_REGION1_WRITE = + ss_events.bits.MEM_REGION1_WRITE; + rt->control.event_mask.bits.MEM_REGION2_WRITE = + ss_events.bits.MEM_REGION2_WRITE; + rt->control.event_mask.bits.MEM_REGION3_WRITE = + ss_events.bits.MEM_REGION3_WRITE; + rt->control.event_mask.bits.GFX_UPDATE = + ss_events.bits.GFX_UPDATE; + rt->control.event_mask.bits.INVALIDATE_FBC_SURFACE = + ss_events.bits.INVALIDATE_FBC_SURFACE; + rt->control.event_mask.bits.REG_PENDING_UPDATE = + ss_events.bits.REG_PENDING_UPDATE; + rt->control.event_mask.bits.CRTC_TRIG_A = + ss_events.bits.CRTC_TRIG_A; + rt->control.event_mask.bits.CRTC_TRIG_B = + ss_events.bits.CRTC_TRIG_B; + rt->control.event_mask.bits.READBACK_NOMINAL_VERTICAL = + ss_events.bits.READBACK_NOMINAL_VERTICAL; + rt->control.event_mask.bits.READBACK_DYNAMIC_VERTICAL = + ss_events.bits.READBACK_DYNAMIC_VERTICAL; + } +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_calculation.h b/drivers/gpu/drm/amd/dal/display_service/ds_calculation.h new file mode 100644 index 000000000000..5d3b4845ee69 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_calculation.h @@ -0,0 +1,48 @@ +/* + * Copyright 2012-15 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 __DAL_DS_CALCULATION_H__ +#define __DAL_DS_CALCULATION_H__ + +/* Includes */ +/* None */ + +struct hw_crtc_timing; +struct timing_limits; +struct ranged_timing_preference_flags; +struct display_path; + +/* Adjust timing to the timing limits. */ +void dal_ds_calculation_tuneup_timing( + struct hw_crtc_timing *timing, + const struct timing_limits *timing_limits); + +/* Setup ranged timing parameters for features such as DRR or PSR. */ +void dal_ds_calculation_setup_ranged_timing( + struct hw_crtc_timing *timing, + struct display_path *display_path, + struct ranged_timing_preference_flags flags); + +#endif /* __DAL_DS_CALCULATION_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_dispatch.h b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch.h new file mode 100644 index 000000000000..4cf6b5e8e59e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch.h @@ -0,0 +1,137 @@ +/* + * Copyright 2012-15 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 __DAL_DS_DISPATCH_H__ +#define __DAL_DS_DISPATCH_H__ + +#include "include/adjustment_types.h" + +#include "path_mode_set_with_data.h" + +#define MAX_NUM_SUPPORT_SIGNAL (DS_SIGNAL_TYPE_END - DS_SIGNAL_TYPE_BEGIN + 1) +#define REGAMMA_COEFF_A0 31308 +#define REGAMMA_COEFF_A1 12920 +#define REGAMMA_COEFF_A2 55 +#define REGAMMA_COEFF_A3 55 +#define REGAMMA_COEFF_GAMMA 2400 + +/* TODO: remove this once defined */ +struct hw_get_viewport_x_adjustments; +struct topology_mgr; +struct hw_sequencer; +struct adapter_service; +struct hw_path_mode; + +enum ds_signal_type { + DS_SIGNAL_TYPE_CRT = 0, /* the first */ + DS_SIGNAL_TYPE_DISCRETEVGA, + DS_SIGNAL_TYPE_DFP, + DS_SIGNAL_TYPE_LVDS, + DS_SIGNAL_TYPE_HDMI, + DS_SIGNAL_TYPE_DP, + DS_SIGNAL_TYPE_EDP, + DS_SIGNAL_TYPE_CF, + DS_SIGNAL_TYPE_WIRELESS, /* the last */ + DS_SIGNAL_TYPE_UNKNOWN, + + DS_SIGNAL_TYPE_BEGIN = DS_SIGNAL_TYPE_CRT, + DS_SIGNAL_TYPE_END = DS_SIGNAL_TYPE_WIRELESS +}; +/* Purpose to build the path set for */ +enum build_path_set_reason { + BUILD_PATH_SET_REASON_SET_MODE = 0, + BUILD_PATH_SET_REASON_WATERMARKS, + BUILD_PATH_SET_REASON_VALIDATE, + BUILD_PATH_SET_REASON_SET_ADJUSTMENT, + BUILD_PATH_SET_REASON_GET_ACTIVE_PATHS, + BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN +}; + +struct adj_global_info { + enum adjustment_id adj_id; + enum adjustment_data_type adj_data_type; + union adjustment_property adj_prop; + bool display_is_supported[MAX_NUM_SUPPORT_SIGNAL]; +}; + +/* Display service dispatch init data */ +struct ds_dispatch_init_data { + struct dal_context *dal_context; + struct hw_sequencer *hwss; + struct topology_mgr *tm; + struct adapter_service *as; + struct timing_service *ts; +}; + +/* Display service dispatch */ +struct ds_dispatch { + struct dal_context *dal_context; + struct path_mode_set_with_data *set; + struct topology_mgr *tm; + struct hw_sequencer *hwss; + struct adapter_service *as; + struct timing_service *ts; + struct adj_container **applicable_adj; + struct adjustment_parent_api *default_adjustments; + + /* Temporary storage for Path Mode Set validation. */ + struct path_mode path_modes[MAX_COFUNC_PATH]; + uint32_t disp_path_num; + struct backlight_adj_group *backlight_adj; + struct single_adj_group *single_adj; + struct grph_colors_group *grph_colors_adj; + struct grph_gamma_lut_group *grph_gamma_adj; +}; + + +/* + * DS dispatch functions + */ + +/* Create DS dispatch */ +struct ds_dispatch *dal_ds_dispatch_create( + const struct ds_dispatch_init_data *data); + +/* Destroy DS dispatch */ +void dal_ds_dispatch_destroy(struct ds_dispatch **ds_dispatch); + +/* Set up info frames */ +void dal_ds_dispatch_setup_info_frame( + struct ds_dispatch *ds_dispatch, + const struct path_mode *mode, + struct hw_path_mode *hw_mode); + +/* Check if gamut needs reprogramming */ +bool dal_ds_dispatch_is_gamut_change_required( + struct ds_dispatch *ds_dispatch, + enum pixel_encoding pixel_encoding, + enum pixel_format pixel_format, + uint32_t disp_index); + +/* Return active path modes */ +struct path_mode_set_with_data *dal_ds_dispatch_get_active_pms_with_data( + struct ds_dispatch *ds_dispatch); + +#endif /* __DAL_DS_DISPATCH_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_adjustment.c b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_adjustment.c new file mode 100644 index 000000000000..6210302e74ce --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_adjustment.c @@ -0,0 +1,1128 @@ +/* + * Copyright 2012-15 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 "dal_services.h" + +#include "include/adjustment_interface.h" +#include "include/display_path_interface.h" +#include "include/signal_types.h" +#include "include/dcs_interface.h" +#include "include/topology_mgr_interface.h" +#include "include/display_service_interface.h" +#include "include/set_mode_interface.h" +#include "include/logger_interface.h" +#include "include/fixed31_32.h" + +#include "ds_dispatch.h" +#include "adjustment_container.h" +#include "scaler_adj_group.h" +#include "adjustment_api.h" +#include "backlight_adj_group.h" +#include "single_adj_group.h" +#include "grph_colors_group.h" +#include "gamut_space.h" +#include "gamma_lut.h" +#include "path_mode_set_with_data.h" + +#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL( \ + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, "Display Service:%s\n", __func__) + +/* NOTE make sure to update CURRENT_ADJUSTMENT_NUM when updating this array */ +static const struct adj_global_info adj_global_info_array[CURRENT_ADJUSTMENT_NUM] = { + { ADJ_ID_SATURATION, ADJ_RANGED, { 0x140 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_BIT_DEPTH_REDUCTION, ADJ_RANGED, { 0x14A }, { 1, 1, 1, 1, 1, 1, 1, 0, 0 } }, + { ADJ_ID_UNDERSCAN, ADJ_RANGED, { 0x145 }, { 0, 0, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_UNDERSCAN_TYPE, ADJ_RANGED, { 0x101 }, { 0, 0, 1, 0, 1, 0, 0, 0, 1 } }, + { ADJ_ID_BACKLIGHT, ADJ_RANGED, { 0x1a0 }, { 1, 1, 1, 1, 1, 1, 1, 0, 0 } }, + { ADJ_ID_CONTRAST, ADJ_RANGED, { 0x160 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_BRIGHTNESS, ADJ_RANGED, { 0x140 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_HUE, ADJ_RANGED, { 0x140 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_TEMPERATURE, ADJ_RANGED, { 0x140 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_TEMPERATURE_SOURCE, ADJ_RANGED, { 0x142 }, { 1, 1, 1, 1, 0, 1, 1, 0, 1 } }, + { ADJ_ID_NOMINAL_RANGE_RGB_LIMITED, ADJ_RANGED, { 0x141 }, { 1, 1, 1, 1, 1, 1, 1, 0, 1 } }, + { ADJ_ID_GAMMA_RAMP, ADJ_LUT, { 0x108 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1 } } +}; + +static void build_adj_container_for_path(struct ds_dispatch *ds, + struct display_path *display_path); + +static enum ds_signal_type get_ds_signal_from_display_path( + struct ds_dispatch *ds, + struct display_path *display_path, + uint32_t idx); + +/*get info from global table adj_global_info_array*/ +static enum ds_return get_adj_type( + struct ds_dispatch *ds, + enum adjustment_id adj_id, + enum adjustment_data_type *type) +{ + uint32_t i = 0; + + if (adj_id < ADJ_ID_BEGIN || ADJ_ID_END < adj_id) + return DS_ERROR; + for (i = 0; i < CURRENT_ADJUSTMENT_NUM; i++) { + if (adj_global_info_array[i].adj_id == adj_id) { + *type = adj_global_info_array[i].adj_data_type; + return DS_SUCCESS; + } + } + return DS_ERROR; +} + +static enum ds_return get_adj_info_from_defaults( + struct ds_dispatch *ds, + uint32_t disp_index, + struct display_path *path, + enum adjustment_id adjust_id, + struct adjustment_info *adj_info) +{ + struct adjustment_api *api = NULL; + enum signal_type signal; + union cea_video_capability_data_block video_cap = { {0} }; + struct dcs *dcs = dal_display_path_get_dcs(path); + + dal_dcs_get_cea_video_capability_data_block(dcs, &video_cap); + signal = dal_display_path_get_query_signal(path, SINK_LINK_INDEX); + + api = dal_adj_parent_api_what_is_the_target_obj( + ds->default_adjustments, + signal); + if (!api) + return DS_ERROR; + get_adj_type( + ds, + adjust_id, + &adj_info->adj_data_type); + + if (adj_info->adj_data_type == ADJ_RANGED) { + /*default value from table*/ + if (!dal_adj_api_get_range_adj_data( + api, adjust_id, adj_info)) + return DS_ERROR; + /*then override it by signal types and other requests*/ + if (adjust_id == ADJ_ID_UNDERSCAN) { + if (signal == SIGNAL_TYPE_DVI_SINGLE_LINK || + signal == SIGNAL_TYPE_DVI_SINGLE_LINK1 || + signal == SIGNAL_TYPE_DVI_DUAL_LINK || + signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_WIRELESS) { + /*reviewers:this parts i changed a little bit + * compare with dal2*/ + /*we set 0 underscan for non-HDMI or S_CE1 is + * 1,S_CE1 indicates monitor will underscan + * automaticlly*/ + if (video_cap.bits.S_CE1 || signal != + SIGNAL_TYPE_HDMI_TYPE_A) + adj_info->adj_data.ranged.def = 0; + } + } + + } + return DS_SUCCESS; +} + +static bool is_underscan_supported( + struct ds_dispatch *ds, + struct display_path *path, + enum dcs_edid_connector_type connector_type, + enum ds_signal_type ds_signal + ) +{ + bool supported = true; + /* This call will check if underscan requirements are met. + Currently, this is checking to see if the display engine + clock is high enough to support underscan.*/ + if (!dal_adapter_service_is_meet_underscan_req(ds->as)) + supported = false; + /* Checks if underscan is for HDMI only */ + else if (dal_adapter_service_underscan_for_hdmi_only(ds->as)) { + connector_type = dal_dcs_get_connector_type( + dal_display_path_get_dcs(path)); + + if (!((connector_type == EDID_CONNECTOR_HDMIA) || + (dal_adapter_service_is_feature_supported( + FEATURE_INSTANT_UP_SCALE_DOWN_SCALE) && + (ds_signal == DS_SIGNAL_TYPE_EDP)))) + supported = false; + + } else if (!dal_adapter_service_is_feature_supported( + FEATURE_INSTANT_UP_SCALE_DOWN_SCALE)) { + if (ds_signal == DS_SIGNAL_TYPE_EDP) + supported = false; + } else + supported = true; + return supported; +} + +static bool is_adjustment_supported( + struct ds_dispatch *ds, + struct display_path *path, + enum adjustment_id adjust_id) +{ + bool supported = true; + uint32_t display_index; + uint32_t index = 0; + enum ds_signal_type ds_signal; + enum dcs_edid_connector_type connector_type; + + if (!path) { + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "display path is NULL"); + return false; + } + display_index = dal_tm_display_path_to_display_index(ds->tm, path); + ds_signal = get_ds_signal_from_display_path(ds, path, display_index); + connector_type = + dal_dcs_get_connector_type( + dal_display_path_get_dcs(path)); + if (ds_signal == DS_SIGNAL_TYPE_UNKNOWN) { + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "DS_SIGNAL_TYPE_UNKNOWN"); + return false; + } + for (index = 0; index < CURRENT_ADJUSTMENT_NUM; index++) { + if (adjust_id == adj_global_info_array[index].adj_id) { + if (false == adj_global_info_array[index]. + display_is_supported[ds_signal]) + return false; + else { + if ((adjust_id == ADJ_ID_UNDERSCAN) || + (adjust_id == ADJ_ID_UNDERSCAN_TYPE)) + supported = + is_underscan_supported( + ds, + path, + connector_type, + ds_signal); + break; + } + } + } + return supported; +} + +static enum ds_return get_adjustment_info( + struct ds_dispatch *ds, + struct display_path *disp_path, + enum adjustment_id adjust_id, + bool fallback_to_default, + struct adjustment_info *adj_info) +{ + uint32_t display_index = + dal_display_path_get_display_index(disp_path); + struct adj_container *adj_container = + dal_ds_dispatch_get_adj_container_for_path(ds, display_index); + const struct adjustment_info *cont_info = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, adjust_id); + + if (disp_path == NULL || adj_info == NULL || + !is_adjustment_supported(ds, disp_path, adjust_id)) + return DS_ERROR; + + if (!adj_container) + return DS_ERROR; + + if (cont_info != NULL) + *adj_info = *cont_info; + else if (fallback_to_default) + return get_adj_info_from_defaults( + ds, + display_index, + disp_path, + adjust_id, + adj_info); + else + return DS_ERROR; + + return DS_SUCCESS; +} + +enum ds_return dal_ds_dispatch_get_adjustment_info( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + struct adjustment_info *adj_info) +{ + struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds->tm, display_index); + return get_adjustment_info( + ds, + display_path, + adjust_id, + true, + adj_info); +} + +static enum ds_return get_adj_property( + struct ds_dispatch *ds, + uint32_t disp_index, + enum adjustment_id adjust_id, + union adjustment_property *adj_property) +{ + enum ds_return result = DS_ERROR; + uint32_t i = 0; + + if (disp_index >= dal_tm_get_num_display_paths(ds->tm, false)) + return DS_ERROR; + for (i = 0; i < CURRENT_ADJUSTMENT_NUM; i++) { + if (adj_global_info_array[i].adj_id == adjust_id) { + *adj_property = adj_global_info_array[i].adj_prop; + result = DS_SUCCESS; + break; + } + } + return result; +} +/* no used for now +static void update_adj_container_use_edid( + struct ds_dispatch *ds, + struct display_path *display_path) +{ + +}*/ + +static void build_gamut_adj_for_path( + struct ds_dispatch *ds, + uint32_t disp_index, + struct adj_container *adj_container, + struct display_path *display_path) +{ + struct gamut_data gamut_source_grph; + struct gamut_data gamut_destination; + struct ds_regamma_lut *regamma = NULL; + + dal_memset(&gamut_source_grph, 0, sizeof(gamut_source_grph)); + dal_memset(&gamut_destination, 0, sizeof(gamut_destination)); + + /* Gamut source grph */ + dal_gamut_space_setup_default_gamut( + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_source_grph, + true, + true); + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_source_grph); + + /* Gamut Destination */ + dal_gamut_space_setup_default_gamut( + ADJ_ID_GAMUT_DESTINATION, + &gamut_destination, + true, + true); + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &gamut_destination); + + regamma = dal_alloc(sizeof(*regamma)); + if (!dal_gamut_space_setup_predefined_regamma_coefficients( + &gamut_destination, regamma)) + dal_ds_dispatch_setup_default_regamma( + ds, regamma); + + dal_adj_container_set_regamma( + adj_container, + regamma); + + dal_free(regamma); + regamma = NULL; + +} + +void dal_ds_dispatch_setup_default_regamma( + struct ds_dispatch *ds, + struct ds_regamma_lut *regamma) +{ + uint32_t i; + + regamma->flags.u32all = 0; + regamma->flags.bits.COEFF_FROM_USER = 1; + + for (i = 0 ; i < COEFF_RANGE ; i++) { + regamma->coeff.coeff_a0[i] = REGAMMA_COEFF_A0; + regamma->coeff.coeff_a1[i] = REGAMMA_COEFF_A1; + regamma->coeff.coeff_a2[i] = REGAMMA_COEFF_A2; + regamma->coeff.coeff_a3[i] = REGAMMA_COEFF_A3; + regamma->coeff.gamma[i] = REGAMMA_COEFF_GAMMA; + } + +} + +enum ds_return dal_ds_dispatch_get_adjustment_current_value( + struct ds_dispatch *ds, + struct adj_container *container, + struct adjustment_info *info, + enum adjustment_id id, + bool fall_back_to_default) +{ + if (info) + if (info->adj_data_type == ADJ_RANGED) + info->adj_data.ranged.cur = info->adj_data.ranged.def; + if (id == ADJ_ID_UNDERSCAN || id == ADJ_ID_UNDERSCAN_TYPE) + if (container && + dal_adj_container_get_default_underscan_allow( + container)) + info->adj_data.ranged.cur = 0; + return DS_SUCCESS; +} + +enum ds_return dal_ds_dispatch_get_adjustment_value( + struct ds_dispatch *ds, + struct display_path *disp_path, + enum adjustment_id adj_id, + bool fall_back_to_default, + int32_t *value) +{ + uint32_t display_index; + struct adj_container *adj_container; + struct adjustment_info adj_info; + + if (!disp_path) + return DS_ERROR; + + if (!is_adjustment_supported(ds, disp_path, adj_id)) + return DS_ERROR; + + display_index = dal_display_path_get_display_index( + disp_path); + adj_container = dal_ds_dispatch_get_adj_container_for_path( + ds, display_index); + + if (DS_SUCCESS != get_adjustment_info(ds, + disp_path, adj_id, + fall_back_to_default, &adj_info)) + return DS_ERROR; + + *value = adj_info.adj_data.ranged.cur; + + if (adj_id == ADJ_ID_UNDERSCAN || adj_id == ADJ_ID_UNDERSCAN_TYPE) + if (adj_container && + dal_adj_container_get_default_underscan_allow( + adj_container)) + *value = 0; + if (adj_info.adj_data_type == ADJ_RANGED) + adj_info.adj_data.ranged.cur = *value; + else if (adj_info.adj_data_type == ADJ_BITVECTOR) + adj_info.adj_data.bit_vector.current_supported = *value; + + return DS_SUCCESS; +} + +void dal_ds_dispatch_update_adj_container_for_path_with_edid( + struct ds_dispatch *ds, + struct display_path *path) +{ + uint32_t index = dal_display_path_get_display_index(path); + struct adj_container *container; + + if (!path) + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "display_path is unknown"); + container = dal_ds_dispatch_get_adj_container_for_path(ds, index); + dal_adj_container_update_display_cap(container, path); + dal_adj_container_update_signal_type( + container, + dal_display_path_get_query_signal(path, SINK_LINK_INDEX)); + build_adj_container_for_path(ds, path); +} + +void dal_ds_dispatch_update_adj_container_for_path_with_mode_info( + struct ds_dispatch *ds, + struct display_path *display_path, + const struct path_mode *path_mode) +{ + uint32_t index = dal_display_path_get_display_index(display_path); + struct adj_container *container; + + if (!display_path) + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "display_path is unknown"); + container = dal_ds_dispatch_get_adj_container_for_path(ds, index); + if (container) { + dal_adj_container_update_timing_mode( + container, + &path_mode->mode_timing->mode_info, + &path_mode->view); + dal_ds_dispatch_update_adj_container_for_path_with_edid( + ds, + display_path); + } + +} + +static enum ds_signal_type get_ds_signal_from_display_path( + struct ds_dispatch *ds, + struct display_path *display_path, + uint32_t idx) +{ enum ds_signal_type ds_signal = DS_SIGNAL_TYPE_CRT; + enum signal_type signal = dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_SINGLE_LINK1: + case SIGNAL_TYPE_DVI_DUAL_LINK: + ds_signal = DS_SIGNAL_TYPE_DFP; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + ds_signal = DS_SIGNAL_TYPE_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + ds_signal = DS_SIGNAL_TYPE_DP; + break; + case SIGNAL_TYPE_EDP: + ds_signal = DS_SIGNAL_TYPE_EDP; + break; + case SIGNAL_TYPE_RGB: + if (dal_dcs_is_non_continous_frequency( + dal_display_path_get_dcs(display_path))) + ds_signal = DS_SIGNAL_TYPE_DISCRETEVGA; + else + ds_signal = DS_SIGNAL_TYPE_CRT; + break; + case SIGNAL_TYPE_MVPU_A: + case SIGNAL_TYPE_MVPU_B: + case SIGNAL_TYPE_MVPU_AB: + ds_signal = DS_SIGNAL_TYPE_CF; + break; + case SIGNAL_TYPE_WIRELESS: + ds_signal = DS_SIGNAL_TYPE_WIRELESS; + break; + default: + ds_signal = DS_SIGNAL_TYPE_UNKNOWN; + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "dignal type is unknown"); + break; + } + return ds_signal; +} + +struct adj_container *dal_ds_dispatch_get_adj_container_for_path( + const struct ds_dispatch *ds, + uint32_t display_index) +{ + + if (display_index < ds->disp_path_num) + return ds->applicable_adj[display_index]; + return NULL; +} + +bool dal_ds_dispatch_initialize_adjustment(struct ds_dispatch *ds) +{ + uint32_t i; + uint32_t num; + + /* TODO unnecesary init_data. can just pass ds */ + struct backlight_adj_group_init_data backlight_init_data; + struct single_adj_group_init_data single_init_data; + struct grph_colors_group_init_data colors_init_data; + struct grph_gamma_lut_group_init_data gamma_init_data; + + ds->disp_path_num = dal_tm_get_num_display_paths(ds->tm, false); + num = ds->disp_path_num; + + if (num == 0) + return false; + + ds->applicable_adj = dal_alloc((sizeof(*ds->applicable_adj) * num)); + if (ds->applicable_adj == NULL) + return false; + for (i = 0; i < num; i++) { + ds->applicable_adj[i] = dal_adj_container_create(); + if (!ds->applicable_adj[i]) { + dal_logger_write( + ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "initilize_adjustment has error"); + dal_adj_container_destroy(ds->applicable_adj); + return false; + } + } + + ds->default_adjustments = dal_adj_parent_api_create(); + if (!ds->default_adjustments) + return false; + dal_adj_parent_api_build_child_objs(ds->default_adjustments); + + backlight_init_data.ds = ds; + backlight_init_data.as = ds->as; + backlight_init_data.hws = ds->hwss; + backlight_init_data.tm = ds->tm; + backlight_init_data.dal_context = ds->dal_context; + ds->backlight_adj = dal_backlight_adj_group_create( + &backlight_init_data); + if (!ds->backlight_adj) { + dal_ds_dispatch_cleanup_adjustment(ds); + return false; + } + single_init_data.ds = ds; + single_init_data.hws = ds->hwss; + single_init_data.tm = ds->tm; + single_init_data.dal_context = ds->dal_context; + ds->single_adj = dal_single_adj_group_create(&single_init_data); + + if (!ds->single_adj) { + dal_ds_dispatch_cleanup_adjustment(ds); + return false; + } + colors_init_data.ds = ds; + colors_init_data.hws = ds->hwss; + colors_init_data.dal_context = ds->dal_context; + ds->grph_colors_adj = + dal_grph_colors_group_create(&colors_init_data); + if (!ds->grph_colors_adj) { + dal_ds_dispatch_cleanup_adjustment(ds); + return false; + } + + gamma_init_data.ds = ds; + gamma_init_data.hws = ds->hwss; + gamma_init_data.dal_context = ds->dal_context; + ds->grph_gamma_adj = dal_gamma_adj_group_create(&gamma_init_data); + if (!ds->grph_gamma_adj) { + dal_ds_dispatch_cleanup_adjustment(ds); + return false; + } + + return true; +} + +void dal_ds_dispatch_cleanup_adjustment(struct ds_dispatch *ds) +{ + uint32_t i; + uint32_t num; + + num = ds->disp_path_num; + + for (i = 0; i < num; i++) + dal_adj_container_destroy(ds->applicable_adj + i); + dal_free(ds->applicable_adj); + + if (ds->default_adjustments) + dal_adj_parent_api_destroy(&ds->default_adjustments); + + if (ds->backlight_adj != NULL) + dal_backlight_adj_group_destroy(&ds->backlight_adj); + + if (ds->single_adj != NULL) + dal_single_adj_group_destroy(&ds->single_adj); + + if (ds->grph_colors_adj != NULL) + dal_grph_colors_adj_group_destroy(&ds->grph_colors_adj); + + if (ds->grph_gamma_adj != NULL) + dal_grph_gamma_adj_group_destroy(&ds->grph_gamma_adj); + +} + +bool dal_ds_dispatch_build_post_set_mode_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *display_path, + struct hw_adjustment_set *set) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_dispatch_apply_scaling( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct adj_container *container, + enum build_path_set_reason reason, + struct hw_path_mode *hw_path_mode) +{ + enum adjustment_id adj_id = ADJ_ID_UNDERSCAN; + struct ds_adjustment_scaler scaler; + const struct adjustment_info *info; + struct adj_container *set; + + if (!hw_path_mode || !container) + return false; + info = dal_adj_info_set_get_adj_info(&container->adj_info_set, adj_id); + + if (!info) + return false; + if (reason == BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN && + hw_path_mode->action == HW_PATH_ACTION_SET) { + set = dal_ds_dispatch_get_adj_container_for_path( + ds, + mode->display_path_index); + dal_adj_info_set_update_cur_value( + &set->adj_info_set, adj_id, 0); + } + if (!dal_scaler_adj_group_build_scaler_parameter( + mode, + container, + reason, + adj_id, + info->adj_data.ranged.cur, + NULL, + hw_path_mode->display_path, + &scaler)) + return false; + return dal_scaler_adj_group_apply_scaling( + &scaler, + container, + reason, + hw_path_mode); +} + +bool dal_ds_dispatch_is_adjustment_supported( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id) +{ + struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds->tm, display_index); + return is_adjustment_supported(ds, display_path, adjust_id); +} + +enum ds_return dal_ds_dispatch_get_property( + struct ds_dispatch *adj, + uint32_t display_index, + enum adjustment_id adjust_id, + union adjustment_property *property) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_set_adjustment( + struct ds_dispatch *ds, + const uint32_t display_index, + enum adjustment_id adjust_id, + int32_t value) +{ + enum ds_return result = DS_ERROR; + struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds->tm, display_index); + if (display_path == NULL || !is_adjustment_supported( + ds, display_path, adjust_id)) { + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "display path is NULL"); + return DS_ERROR; + } + switch (adjust_id) { + case ADJ_ID_SATURATION: + result = dal_grph_colors_group_set_adjustment( + ds->grph_colors_adj, + display_path, + adjust_id, + value); + break; + + case ADJ_ID_UNDERSCAN: + case ADJ_ID_UNDERSCAN_TYPE: + result = dal_scaler_adj_group_set_adjustment( + ds, + display_index, + display_path, + adjust_id, + value); + break; + + case ADJ_ID_BACKLIGHT: + result = dal_backlight_adj_group_set_adjustment( + ds->backlight_adj, + display_path, + adjust_id, + value); + break; + case ADJ_ID_BIT_DEPTH_REDUCTION: + result = dal_single_adj_group_set_adjustment( + ds->single_adj, + display_path, + adjust_id, + value); + break; + default: + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "set_adjustment failed"); + result = DS_ERROR; + } + return result; +} + +enum ds_return dal_ds_dispatch_set_gamma_adjustment(struct ds_dispatch *ds, + uint32_t display_index, enum adjustment_id adjust_id, + const struct raw_gamma_ramp *gamma) { + struct adj_container *adj_container = NULL; + const struct adjustment_info *cont_info = NULL; + const struct ds_regamma_lut *regumma_lut = NULL; + struct display_path *disp_path; + const struct path_mode *disp_path_mode; + + if (ds == NULL) + return DS_ERROR; + + disp_path = dal_tm_display_index_to_display_path( + ds->tm, display_index); + + disp_path_mode = dal_pms_with_data_get_path_mode_for_display_index( + ds->set, + display_index); + + if (disp_path == NULL || disp_path_mode == NULL) + return DS_ERROR; + + adj_container = dal_ds_dispatch_get_adj_container_for_path(ds, + display_index); + + if (adj_container == NULL) + return DS_ERROR; + + cont_info = dal_adj_info_set_get_adj_info(&adj_container->adj_info_set, + adjust_id); + + if (cont_info == NULL) + return DS_ERROR; + + if (disp_path == NULL || cont_info == NULL || + !is_adjustment_supported(ds, disp_path, adjust_id)) + return DS_ERROR; + + if (!is_adjustment_supported(ds, disp_path, adjust_id)) + return DS_ERROR; + + regumma_lut = dal_adj_container_get_regamma( + adj_container); + + if (regumma_lut == NULL) + return DS_ERROR; + + if (dal_grph_gamma_lut_set_adjustment(ds, disp_path, disp_path_mode, + adjust_id, gamma, regumma_lut)) + return DS_SUCCESS; + + return DS_ERROR; +} + +const struct raw_gamma_ramp *dal_ds_dispatch_get_current_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id) +{ + /*TODO: add implementation*/ + return NULL; +} + +const struct raw_gamma_ramp *dal_ds_dispatch_get_default_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id) +{ + /*TODO: add implementation*/ + return NULL; +} + +enum ds_return dal_ds_dispatch_set_current_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + const struct raw_gamma_ramp *gamma) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_set_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + const struct raw_gamma_ramp *gamma) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +bool dal_ds_dispatch_get_underscan_info( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_info *info) +{ + bool result = false; + struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds->tm, display_index); + const struct path_mode *path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + ds->set, display_index); + if (!display_path || !path_mode) + return false; + /*TODO: add implementation* + * result = scaler_adj->get_underscan_info();*/ + return result; +} + +bool dal_ds_dispatch_get_underscan_mode( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_desc *desc) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_dispatch_set_underscan_mode( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_desc *desc) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_dispatch_setup_overlay( + struct ds_dispatch *adj, + uint32_t display_index, + struct overlay_data *data) +{ + /*TODO: add implementation*/ + return false; +} + +void dal_ds_dispatch_set_applicable_adj( + struct ds_dispatch *adj, + uint32_t display_index, + const struct adj_container *applicable) +{ + /*TODO: add implementation*/ +} + +enum ds_return dal_ds_dispatch_set_color_gamut( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_set_gamut_data *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_get_color_gamut( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_gamut_reference_data *ref, + struct ds_get_gamut_data *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_get_color_gamut_info( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_gamut_reference_data *ref, + struct ds_gamut_info *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_get_regamma_lut( + struct ds_dispatch *adj, + uint32_t display_index, + struct ds_regamma_lut *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_set_regamma_lut( + struct ds_dispatch *adj, + uint32_t display_index, + struct ds_regamma_lut *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_set_info_packets( + struct ds_dispatch *adj, + uint32_t display_index, + const struct info_frame *info_frames) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_dispatch_get_info_packets( + struct ds_dispatch *adj, + uint32_t display_index, + struct info_frame *info_frames) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +static void build_adj_container_for_path( + struct ds_dispatch *ds, + struct display_path *display_path) +{ + uint32_t index = 0; + uint32_t i = 0; + + struct adjustment_info info; + struct adj_container *container = NULL; + + dal_memset(&info, 0, sizeof(struct adjustment_info)); + index = dal_display_path_get_display_index(display_path); + if (!display_path) + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "display_path is unknown"); + container = dal_ds_dispatch_get_adj_container_for_path(ds, index); + + if (!container) + return; + + if (!dal_adj_container_is_update_required(container)) + return; + + dal_adj_info_set_clear(&container->adj_info_set); + + build_gamut_adj_for_path(ds, index, container, display_path); + + for (i = ADJ_ID_BEGIN; i < ADJ_ID_END; i++) { + enum adjustment_id adj_id = i; + + if (is_adjustment_supported(ds, display_path, adj_id)) { + if (DS_SUCCESS != get_adj_info_from_defaults( + ds, + index, + display_path, + adj_id, + &info)) + continue; + if (DS_SUCCESS != get_adj_property( + ds, + index, + adj_id, + &info.adj_prop)) + continue; + info.adj_id = adj_id; + info.adj_state = ADJUSTMENT_STATE_VALID; + dal_adj_info_set_add_adj_info( + &container->adj_info_set, + &info); + if (ADJ_RANGED == info.adj_data_type) { + dal_ds_dispatch_get_adjustment_current_value( + ds, + container, + &info, + adj_id, + true); + dal_adj_info_set_update_cur_value( + &container->adj_info_set, + adj_id, + info.adj_data.ranged.cur); + } + + } + } + + dal_adj_container_updated(container); +} + +bool dal_ds_dispatch_include_adjustment( + struct ds_dispatch *ds, + struct display_path *disp_path, + struct ds_adj_id_value adj, + struct hw_adjustment_set *set) +{ + bool ret = false; + + if (adj.adj_id == ADJ_ID_BIT_DEPTH_REDUCTION) + ret = dal_single_adj_group_include_adjustment( + ds->single_adj, + disp_path, adj, set); + return ret; +} + +bool dal_ds_dispatch_build_include_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *display_path, + struct hw_path_mode *hw_mode, + struct hw_adjustment_set *set) +{ + /* TODO implement build_include_adjustments */ + return false; +} + +bool dal_ds_dispatch_build_color_control_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *disp_path, + struct hw_adjustment_set *set) +{ + if (!ds->grph_colors_adj || !disp_path) + return false; + + return dal_grph_colors_group_build_color_control_adj( + ds->grph_colors_adj, + mode, + disp_path, + set); +} + +void dal_ds_dispatch_update_adj_container_for_path_with_color_space( + struct ds_dispatch *ds, + uint32_t display_index, + enum ds_color_space color_space) +{ + struct adj_container *adj_container; + + adj_container = dal_ds_dispatch_get_adj_container_for_path( + ds, display_index); + + if (adj_container != NULL) + dal_adj_container_update_color_space( + adj_container, + color_space); +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_info_frame.c b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_info_frame.c new file mode 100644 index 000000000000..8adff1106d98 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_info_frame.c @@ -0,0 +1,957 @@ +/* + * Copyright 2015 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 "dal_services.h" + +#include "include/adapter_service_interface.h" +#include "include/set_mode_types.h" +#include "include/hw_sequencer_types.h" +#include "include/dcs_types.h" +#include "include/logger_interface.h" +#include "include/adjustment_interface.h" +#include "include/display_path_interface.h" +#include "include/timing_service_interface.h" +#include "include/topology_mgr_interface.h" + +#include "ds_dispatch.h" +#include "adjustment_container.h" +#include "grph_colors_group.h" +#include "adjustment_types_internal.h" +#include "ds_translation.h" + +/* + * build_itc_cn0_cn1_flags + * + * @brief + * build ITC, CN0 & CN1 flags + * + * ITC flag value comes from AVI Info Frame (GetAdjustmentVal) + * If ITC flag is enabled, itc is set to 1 + * Support for CNC content is retrieved from CEA Block-Byte 8 + * (GetCeaVendorSpecificDataBlock) + * If the content being sent is photo, cinema, or game, and the EDID does + * not have the corresponding CNC flag set, ITC has to be 0 and (CN1, CN0) + * have to be (0, 0) in order to meet HDMI compliance. + * + * @param + * const struct adj_container *adj_container + * - [in] adjustment container + * const uint32_t disp_index - [in] display index + * bool *itc - [out] ITC flag + * uint8_t *cn0_cn1 - [out] CN0 CN1 flags + * + * @return + * void + */ +static void build_itc_cn0_cn1_flags( + const struct adj_container *adj_container, bool *itc, + uint8_t *cn0_cn1) +{ + uint32_t itc_flag = 0; + uint32_t cn0_cn1_temp = 0; + enum display_content_type display_content; + union display_content_support support; + + /* Initialize output values */ + *itc = true; + *cn0_cn1 = 0; + + /* Check what kind of content display could support */ + if (adj_container == NULL + || !dal_adj_container_get_display_content_capability( + adj_container, &support)) + return; + + if (!dal_adj_container_get_adjustment_val(adj_container, + ADJ_ID_ITC_ENABLE, &itc_flag)) { + /* This case is valid for: + * 1) non HDMI */ + *itc = false; + return; + } + + if (!dal_adj_container_get_adjustment_val(adj_container, + ADJ_ID_CNC_CONTENT, &cn0_cn1_temp)) + /* This case is valid for: + * 1) non HDMI */ + return; + + *itc = itc_flag > 0; + + if (itc_flag == 0) { + *cn0_cn1 = 0; + return; + } + + /* Get the requested content type from adjustment value */ + dal_ds_translation_translate_content_type(cn0_cn1_temp, + &display_content); + + if (!support.bits.VALID_CONTENT_TYPE) + /* We are dealing with HDMI version < 1.4 where CNC flags were + * not defined (i.e. these fields are reserved and are set to 0) + * In this case, we are allowed to support ITC if user wants it + * (CN1, CN0) are set to (0, 0) since they are reserved fields + * in HDMI version < 1.4 */ + *cn0_cn1 = 0; + else { + /* We are dealing with HDMI version 1.4 or later where + * CNC flags can be read from the vendor specific data block */ + if (display_content == DISPLAY_CONTENT_TYPE_GRAPHICS) { + /* In this case, ITC = 1 or ITC = 0. */ + if (support.bits.GRAPHICS_CONTENT == 1) + *cn0_cn1 = 0; + } else if (display_content == DISPLAY_CONTENT_TYPE_PHOTO) { + if (support.bits.PHOTO_CONTENT == 1) + *cn0_cn1 = 1; + else { + /* To pass HDMI compliance test, + * ITC = 0, and (CN1, CN0) = (0, 0) */ + *cn0_cn1 = 0; + *itc = 0; + } + } else if (display_content == DISPLAY_CONTENT_TYPE_CINEMA) { + if (support.bits.CINEMA_CONTENT == 1) + *cn0_cn1 = 2; + else { + /* To pass HDMI compliance test, + *ITC = 0, and (CN1, CN0) = (0, 0) */ + *cn0_cn1 = 0; + *itc = 0; + } + } else if (display_content == DISPLAY_CONTENT_TYPE_GAME) { + if (support.bits.GAME_CONTENT == 1) + *cn0_cn1 = 3; + else { + /* To pass HDMI compliance test, + * ITC = 0, and (CN1, CN0) = (0, 0) */ + *cn0_cn1 = 0; + *itc = 0; + } + } + } +} + +/* + * ds_prepare_avi_info_frame + * + * @brief + * Build AVI info frame + * + * @param + * const struct path_mode *mode - [in] path mode + * const struct display_path *disp_path - [in] display path + * const struct hw_overscan overscan - [in] overscan config + * const struct hw_scale_options underscan_rule -[in] scale options + * struct hw_info_packet *info_packet - [out] info frame packet being set + * + * @return + * void + */ +static void prepare_avi_info_frame(struct ds_dispatch *ds_dispatch, + const struct path_mode *mode, + const struct display_path *disp_path, + const struct overscan_info overscan, + const enum hw_scale_options underscan_rule, + struct hw_info_packet *info_packet) +{ + struct adj_container *adj_container = NULL; + enum ds_color_space color_space = DS_COLOR_SPACE_UNKNOWN; + bool extended_colorimetry = false; + struct info_frame info_frame = { {0} }; + uint32_t pixel_encoding = 0; + enum scanning_type scan_type = SCANNING_TYPE_NODATA; + enum aspect_ratio aspect = ASPECT_RATIO_NO_DATA; + bool itc = false; + uint8_t cn0_cn1 = 0; + union cea_video_capability_data_block video_cap; + bool video_cap_support = false; + uint32_t hw_pixel_repetition = 0; + uint8_t *check_sum = NULL; + uint8_t byte_index = 0; + + if (mode == NULL || info_packet == NULL || mode->mode_timing == NULL + || disp_path == NULL) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Invalid parameters"); + return; + } + + /* TODO: Adjustment implementation. */ + adj_container = dal_ds_dispatch_get_adj_container_for_path( + ds_dispatch, mode->display_path_index); + + /* TODO: Verify adjustment container + if (adj_container == NULL) { + dal_logger_write(ds_dispatch->dal_context, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Invalid adjustment container"); + return; + } + */ + + color_space = dal_grph_colors_group_get_color_space( + ds_dispatch->grph_colors_adj, + &mode->mode_timing->crtc_timing, + disp_path, + adj_container); + + /* TODO: Verify colors group + if (color_space == DS_COLOR_SPACE_UNKNOWN) { + dal_logger_write(ds_dispatch->dal_context, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Invalid color space"); + return; + } + */ + + extended_colorimetry = dal_ds_dispatch_is_gamut_change_required( + ds_dispatch, + mode->mode_timing->crtc_timing.pixel_encoding, + mode->pixel_format, mode->display_path_index); + + /* Initialize header */ + info_frame.avi_info_packet.info_packet.bits.header.info_frame_type = + INFO_FRAME_AVI; + /* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall + * not be used in HDMI 2.0 (Section 10.1) */ + info_frame.avi_info_packet.info_packet.bits.header.version = + INFO_FRAME_VERSION_2; + info_frame.avi_info_packet.info_packet.bits.header.length = + INFO_FRAME_SIZE_AVI; + + /* IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built + * according to HDMI 2.0 spec (Section 10.1) + * Add "case PixelEncoding_YCbCr420: pixelEncoding = 3; break;" + * when YCbCr 4:2:0 is supported by DAL hardware. */ + switch (mode->mode_timing->crtc_timing.pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + pixel_encoding = 1; + break; + + case PIXEL_ENCODING_YCBCR444: + pixel_encoding = 2; + break; + + case PIXEL_ENCODING_RGB: + default: + pixel_encoding = 0; + } + + /* Y0_Y1_Y2 : The pixel encoding */ + /* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */ + info_frame.avi_info_packet.info_packet.bits.Y0_Y1_Y2 = pixel_encoding; + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, + "====AVIInfoFrame pixEnc (%d) %s===", + info_frame.avi_info_packet.info_packet.bits.Y0_Y1_Y2, + pixel_encoding == 1 ? "YCbCr422" : + pixel_encoding == 2 ? "YCbCr444" : "RGB"); + + /* A0 = 1 Active Format Information valid */ + info_frame.avi_info_packet.info_packet.bits.A0 = ACTIVE_FORMAT_VALID; + + /* B0, B1 = 3; Bar info data is valid */ + info_frame.avi_info_packet.info_packet.bits.B0_B1 = BAR_INFO_BOTH_VALID; + + info_frame.avi_info_packet.info_packet.bits.SC0_SC1 = + PICTURE_SCALING_UNIFORM; + + /* S0, S1 : Underscan / Overscan */ + + if (dal_adj_container_get_scan_type(adj_container, &scan_type)) + info_frame.avi_info_packet.info_packet.bits.S0_S1 = scan_type; + else + info_frame.avi_info_packet.info_packet.bits.S0_S1 = + underscan_rule; + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "scanType (%d) %s", + info_frame.avi_info_packet.info_packet.bits.S0_S1, + info_frame.avi_info_packet.info_packet.bits.S0_S1 == 1 ? + "Overscan" : + info_frame.avi_info_packet.info_packet.bits.S0_S1 == 2 ? + "Underscan" : "Unknown"); + + /* C0, C1 : Colorimetry */ + if (color_space == DS_COLOR_SPACE_YPBPR709) + info_frame.avi_info_packet.info_packet.bits.C0_C1 = + COLORIMETRY_ITU709; + else if (color_space == DS_COLOR_SPACE_YPBPR601) + info_frame.avi_info_packet.info_packet.bits.C0_C1 = + COLORIMETRY_ITU601; + else + info_frame.avi_info_packet.info_packet.bits.C0_C1 = + COLORIMETRY_NO_DATA; + + /* EC2,EC1,EC0 - Valid only if C0,C1 = 1,1 */ + + if (extended_colorimetry) { + /* GBD enabled, we need update EC0~EC2, + * also change C0, C1 to 1, 1. */ + if (info_frame.avi_info_packet.info_packet.bits.C0_C1 + == COLORIMETRY_ITU601) + info_frame.avi_info_packet.info_packet.bits.EC0_EC2 = + COLORIMETRY_EX_XVYCC601; + else if (info_frame.avi_info_packet.info_packet.bits.C0_C1 + == COLORIMETRY_ITU709) + info_frame.avi_info_packet.info_packet.bits.EC0_EC2 = + COLORIMETRY_EX_XVYCC709; + + info_frame.avi_info_packet.info_packet.bits.C0_C1 = + COLORIMETRY_EXTENDED; + } + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "colometry (%d) %s", + info_frame.avi_info_packet.info_packet.bits.C0_C1, + info_frame.avi_info_packet.info_packet.bits.C0_C1 == 1 ? + "ITU601" : + info_frame.avi_info_packet.info_packet.bits.C0_C1 == 2 ? + "ITU709" : + info_frame.avi_info_packet.info_packet.bits.C0_C1 == 3 ? + "Underscan" : "Unknown"); + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "excolometry (%d) %s", + info_frame.avi_info_packet.info_packet.bits.EC0_EC2, + info_frame.avi_info_packet.info_packet.bits.EC0_EC2 + == 1 ? "xvYCC601" : + info_frame.avi_info_packet.info_packet.bits.EC0_EC2 + == 2 ? "xvYCC709" : "not supported"); + + /* Get aspect ratio from timing service */ + aspect = dal_timing_service_get_aspect_ratio_for_timing(ds_dispatch->ts, + &mode->mode_timing->crtc_timing); + + switch (aspect) { + case ASPECT_RATIO_4_3: + case ASPECT_RATIO_16_9: + info_frame.avi_info_packet.info_packet.bits.M0_M1 = aspect; + break; + + case ASPECT_RATIO_NO_DATA: + default: + info_frame.avi_info_packet.info_packet.bits.M0_M1 = 0; + } + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "aspect ratio (%d) %s", + info_frame.avi_info_packet.info_packet.bits.M0_M1, + info_frame.avi_info_packet.info_packet.bits.M0_M1 == 1 ? + "4_3" : + info_frame.avi_info_packet.info_packet.bits.M0_M1 == 2 ? + "16_9" : "unknown"); + + /* Active Format Aspect ratio - same as Picture Aspect Ratio. */ + info_frame.avi_info_packet.info_packet.bits.R0_R3 = + ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE; + + /* Check for ITC Flag and CNC content Value from AVI Info Packet + * the method always set the required values */ + build_itc_cn0_cn1_flags(adj_container, &itc, &cn0_cn1); + + if (itc) { + info_frame.avi_info_packet.info_packet.bits.ITC = 1; + info_frame.avi_info_packet.info_packet.bits.CN0_CN1 = cn0_cn1; + } + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "Itc %d CNC %d", + info_frame.avi_info_packet.info_packet.bits.ITC, + info_frame.avi_info_packet.info_packet.bits.CN0_CN1); + + video_cap_support = + dal_adj_container_get_cea_video_cap_data_block( + adj_container, &video_cap); + + if (video_cap_support && video_cap.bits.QS == 1) { + if (color_space == DS_COLOR_SPACE_SRGB_FULLRANGE) + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 = + RGB_QUANTIZATION_FULL_RANGE; + else if (color_space == DS_COLOR_SPACE_SRGB_LIMITEDRANGE) + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 = + RGB_QUANTIZATION_LIMITED_RANGE; + else + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 = + RGB_QUANTIZATION_DEFAULT_RANGE; + } else + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 = + RGB_QUANTIZATION_DEFAULT_RANGE; + + /* TODO : We should handle YCC quantization, + * but we do not have matrix calculation */ + if (video_cap_support && video_cap.bits.QY == 1) + info_frame.avi_info_packet.info_packet.bits.YQ0_YQ1 = + RGB_QUANTIZATION_LIMITED_RANGE; + else + info_frame.avi_info_packet.info_packet.bits.YQ0_YQ1 = + RGB_QUANTIZATION_LIMITED_RANGE; + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "RGB quantization %d %s", + info_frame.avi_info_packet.info_packet.bits.Q0_Q1, + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 == 2 ? + "full rgb" : + info_frame.avi_info_packet.info_packet.bits.Q0_Q1 == 1 ? + "lim rgb" : "default"); + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "YCC quantization %d %s", + info_frame.avi_info_packet.info_packet.bits.YQ0_YQ1, + info_frame.avi_info_packet.info_packet.bits.YQ0_YQ1 == + 0 ? "lim ycc" : + info_frame.avi_info_packet.info_packet.bits.YQ0_YQ1 == + 1 ? "full ycc" : "reserved"); + + /* VIC */ + info_frame.avi_info_packet.info_packet.bits.VIC0_VIC7 = + mode->mode_timing->crtc_timing.vic; + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "VIC0_VIC7 %d", + info_frame.avi_info_packet.info_packet.bits.VIC0_VIC7); + + hw_pixel_repetition = + mode->mode_timing->crtc_timing.flags.PIXEL_REPETITION + == 0 ? 1 : + mode->mode_timing->crtc_timing.flags.PIXEL_REPETITION; + /* pixel repetition + * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel + * repetition start from 1 */ + info_frame.avi_info_packet.info_packet.bits.PR0_PR3 = + hw_pixel_repetition - 1; + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "Pixel repetition %d", + info_frame.avi_info_packet.info_packet.bits.PR0_PR3); + + /* Bar Info + * barTop: Line Number of End of Top Bar. + * barBottom: Line Number of Start of Bottom Bar. + * barLeft: Pixel Number of End of Left Bar. + * barRight: Pixel Number of Start of Right Bar. */ + info_frame.avi_info_packet.info_packet.bits.bar_top = + (uint16_t) overscan.top; + info_frame.avi_info_packet.info_packet.bits.bar_bottom = + (uint16_t) (mode->mode_timing->crtc_timing.v_border_top + - overscan.bottom + 1); + info_frame.avi_info_packet.info_packet.bits.bar_left = + (uint16_t) overscan.left; + info_frame.avi_info_packet.info_packet.bits.bar_right = + (uint16_t) (mode->mode_timing->crtc_timing.h_total + - overscan.right + 1); + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, + "top %d, bottom %d, left %d, right %d", + info_frame.avi_info_packet.info_packet.bits.bar_top, + info_frame.avi_info_packet.info_packet.bits.bar_bottom, + info_frame.avi_info_packet.info_packet.bits.bar_left, + info_frame.avi_info_packet.info_packet.bits.bar_right); + + /* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */ + check_sum = + &info_frame.avi_info_packet.info_packet.packet_raw_data.sb[0]; + *check_sum = INFO_FRAME_AVI + INFO_FRAME_SIZE_AVI + + INFO_FRAME_VERSION_2; + + for (byte_index = 1; byte_index <= INFO_FRAME_SIZE_AVI; byte_index++) + *check_sum += info_frame.avi_info_packet.info_packet. + packet_raw_data.sb[byte_index]; + + /* one byte complement */ + *check_sum = (uint8_t) (0x100 - *check_sum); + + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_INFO_PACKETS, + LOG_MINOR_INFO_PACKETS_HDMI, "===check sum %x===", + (uint32_t) *check_sum); + + /* Store in hw_path_mode */ + info_packet->hb0 = + info_frame.avi_info_packet.info_packet.packet_raw_data.hb0; + info_packet->hb1 = + info_frame.avi_info_packet.info_packet.packet_raw_data.hb1; + info_packet->hb2 = + info_frame.avi_info_packet.info_packet.packet_raw_data.hb2; + + for (byte_index = 0; byte_index < sizeof(info_packet->sb); byte_index++) + info_packet->sb[byte_index] = info_frame.avi_info_packet. + info_packet.packet_raw_data.sb[byte_index]; + + info_packet->valid = true; +} + +/* + * prepare_vendor_info_packet + * + * @brief + * Build vendor info frame + * + * Currently only thing we put in VSIF, is 3D stereo support + * + * @param + * mode - [in] path mode which contains data may need to construct VSIF + * pPacket - [out] output structre where to store VSIF + * + * @return + * void + */ +static void prepare_vendor_info_packet(const struct path_mode *mode, + struct hw_info_packet *info_packet) +{ + uint32_t length = 0; + bool hdmi_vic_mode = false; + uint8_t checksum = 0; + uint32_t i = 0; + enum timing_3d_format format; + + ASSERT_CRITICAL(mode != NULL); + ASSERT_CRITICAL(info_packet != NULL); + + format = dal_ds_translation_get_active_timing_3d_format( + mode->mode_timing->crtc_timing.timing_3d_format, + mode->view_3d_format); + + /* Can be different depending on packet content */ + length = 5; + + if (mode->mode_timing->crtc_timing.hdmi_vic != 0 && + mode->view.width >= 3840 && + mode->view.height == 2160) + hdmi_vic_mode = true; + + /* According to HDMI 1.4a CTS, VSIF should be sent + * for both 3D stereo and HDMI VIC modes. + * For all other modes, there is no VSIF sent. */ + + if (format == TIMING_3D_FORMAT_NONE && !hdmi_vic_mode) + return; + + /* 24bit IEEE Registration identifier (0x000c03). LSB first. */ + info_packet->sb[1] = 0x03; + info_packet->sb[2] = 0x0C; + info_packet->sb[3] = 0x00; + + /*PB4: 5 lower bytes = 0 (reserved). 3 higher bits = HDMI_Video_Format. + * The value for HDMI_Video_Format are: + * 0x0 (0b000) - No additional HDMI video format is presented in this + * packet + * 0x1 (0b001) - Extended resolution format present. 1 byte of HDMI_VIC + * parameter follows + * 0x2 (0b010) - 3D format indication present. 3D_Structure and + * potentially 3D_Ext_Data follows + * 0x3..0x7 (0b011..0b111) - reserved for future use */ + if (format != TIMING_3D_FORMAT_NONE) + info_packet->sb[4] = (2 << 5); + else if (hdmi_vic_mode) + info_packet->sb[4] = (1 << 5); + + /* PB5: If PB4 claims 3D timing (HDMI_Video_Format = 0x2): + * 4 lower bites = 0 (reserved). 4 higher bits = 3D_Structure. + * The value for 3D_Structure are: + * 0x0 - Frame Packing + * 0x1 - Field Alternative + * 0x2 - Line Alternative + * 0x3 - Side-by-Side (full) + * 0x4 - L + depth + * 0x5 - L + depth + graphics + graphics-depth + * 0x6 - Top-and-Bottom + * 0x7 - Reserved for future use + * 0x8 - Side-by-Side (Half) + * 0x9..0xE - Reserved for future use + * 0xF - Not used */ + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + info_packet->sb[5] = (0x0 << 4); + break; + + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + case TIMING_3D_FORMAT_SBS_SW_PACKED: + info_packet->sb[5] = (0x8 << 4); + length = 6; + break; + + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + case TIMING_3D_FORMAT_TB_SW_PACKED: + info_packet->sb[5] = (0x6 << 4); + break; + + default: + break; + } + + /*PB5: If PB4 is set to 0x1 (extended resolution format) + * fill PB5 with the correct HDMI VIC code */ + if (hdmi_vic_mode) + info_packet->sb[5] = + (uint8_t) (mode->mode_timing->crtc_timing.hdmi_vic); + + /* Header */ + info_packet->hb0 = 0x81; /* VSIF packet type. */ + info_packet->hb1 = 0x01; /* Version */ + + /* 4 lower bits = Length, 4 higher bits = 0 (reserved) */ + info_packet->hb2 = (uint8_t) (length); + + /* Calculate checksum */ + checksum = 0; + checksum += info_packet->hb0; + checksum += info_packet->hb1; + checksum += info_packet->hb2; + + for (i = 1; i <= length; i++) + checksum += info_packet->sb[i]; + + info_packet->sb[0] = (uint8_t) (0x100 - checksum); + + info_packet->valid = true; +} + +/* + * prepare_default_gamut_packet + * + * @brief + * Build default gamut packet + * + * @param + * mode - [in] path mode that contains the gamut change flag + * + * @return + * True if packet is buit. False otherwise. + */ +static bool prepare_default_gamut_packet(struct ds_dispatch *ds_dispatch, + const struct path_mode *mode, + struct hw_info_packet *info_packet) +{ + bool result = false; + uint8_t base = 0; /* GBD profile 0 */ + uint32_t xgdb_color_precision = 0; + uint32_t xgdb_color_space = 0; + uint32_t xgdb_min_red_data = 0; + uint32_t xgdb_max_red_data = 0; + uint32_t xgdb_min_green_data = 0; + uint32_t xgdb_max_green_data = 0; + uint32_t xgdb_min_blue_data = 0; + uint32_t xgdb_max_blue_data = 0; + + if (mode == NULL || mode->mode_timing == NULL) + return result; + + if (!dal_ds_dispatch_is_gamut_change_required( + ds_dispatch, + mode->mode_timing->crtc_timing.pixel_encoding, + mode->pixel_format, mode->display_path_index)) + return result; + + /* uint32_t xgdb_format_Flag = 1; */ + + /* 0->8-bit precision + * 1->10-bit precision + * 2->12-bit precision */ + xgdb_color_precision = 2; + xgdb_color_space = 2; + xgdb_min_red_data = 0x84f; + xgdb_max_red_data = 0x7ff; + xgdb_min_green_data = 0x84f; + xgdb_max_green_data = 0x7ff; + xgdb_min_blue_data = 0x84f; + xgdb_max_blue_data = 0x7ff; + + /* Header */ + info_packet->hb0 = 0x0A; /* Gamut packed type. */ + info_packet->hb1 = 0x81; /* HDMI spec Rev1.3 page 78. + hb2 = 0x31; /* page 78 //0x80 bit is for stop GBD use. */ + + /* translate to Fixed point format first: + * 1 signed bit, 2 bits integer, 9 bits fraction. */ + + /* Min_Red_H */ + info_packet->sb[base + 1] |= (xgdb_min_red_data & 0xff0) >> 4; + + /* Min_Red_L|MaxRed_H */ + info_packet->sb[base + 2] |= (xgdb_min_red_data & 0x00f) << 4; + info_packet->sb[base + 2] |= (xgdb_max_red_data & 0xf00) >> 8; + info_packet->sb[base + 3] |= (xgdb_max_red_data & 0x0ff); /*Max_Red_L*/ + + /* Min_Green_H */ + info_packet->sb[base + 4] |= (xgdb_min_green_data & 0xff0) >> 4; + + /* Min_Green_L|MaxGreen_H */ + info_packet->sb[base + 5] |= (xgdb_min_green_data & 0x00f) << 4; + info_packet->sb[base + 5] |= (xgdb_max_green_data & 0xf00) >> 8; + + /* Max_Green_L */ + info_packet->sb[base + 6] |= (xgdb_max_green_data & 0x0ff); + + /* Min_Blue_H */ + info_packet->sb[base + 7] |= (xgdb_min_blue_data & 0xff0) >> 4; + + /* Min_Blue_L|MaxBlue_H */ + info_packet->sb[base + 8] |= (xgdb_min_blue_data & 0x00f) << 4; + info_packet->sb[base + 8] |= (xgdb_max_blue_data & 0xf00) >> 8; + + /* Max_Blue_L */ + info_packet->sb[base + 9] |= (xgdb_max_blue_data & 0x0ff); + + info_packet->sb[base + 0] |= 0x80; /* RANGE FORMAT */ + info_packet->sb[base + 0] |= (xgdb_color_precision) << 0x3; + info_packet->sb[base + 0] |= (xgdb_color_space & 0x3); + + info_packet->valid = true; + result = true; + + return result; + +} + +/* + * ds_prepare_video_stream_configuration_packet + * + * @brief + * Build video stream configuration packet + * + * @param + * + * @return + * void + */ +static void prepare_video_stream_configuration_packet( + struct ds_dispatch *ds_dispatch, + const struct path_mode *mode, + struct hw_info_packet *info_packet) +{ + bool psr_panel_support = false; + enum timing_3d_format format; + + /* Check for invalid input parameters */ + if (mode == NULL || info_packet == NULL) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Invalid parameters"); + return; + } + + /* Check if timing is 3D format. If so, VSC packet should be updated to + * indicate to Sink we are outputting 3D. */ + format = dal_ds_translation_get_active_timing_3d_format( + mode->mode_timing->crtc_timing.timing_3d_format, + mode->view_3d_format); + + /* Check if PSR is supported. If so, we need to enable and send the VSC + * packet to allow DMCU to update PSR specific fields. */ + if (dal_adapter_service_is_feature_supported(FEATURE_PSR_ENABLE)) { + struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds_dispatch->tm, + mode->display_path_index); + + if ((display_path != NULL) && + (dal_display_path_is_psr_supported( + display_path))) + psr_panel_support = true; + } + + /* Packet Header */ + /* Secondary data packet ID byte 0 = 0h */ + info_packet->hb0 = 0x00; + /* Secondary data packet ID byte 0 = 07h */ + info_packet->hb1 = 0x07; + + if (psr_panel_support) { + /* If PSR panel is supported, VSC packet header should be + * initialized with packet revision 2 set in byte 2 */ + info_packet->hb2 = 0x02; + + /* ===========================================================| + * PSR uses 7 Bytes. We need to set 8 Bytes here since 3D + * Stereo is occupying the first Byte in the VSC packet. + * + * ===========================================================| + * Byte 0 is set by driver for 3D Stereo purposes. + * ----------------------------------------------- + * + * Byte 0 - Used by 3D Stereo. See (VSC Payload (1 byte) + * From DP1.2 spec) below for usage. + * + * ===========================================================| + * Bytes 1-7 are set by DMCU for PSR purposes. + * However, driver must enable and send the VSC packet by + * setting up the packet header. DMCU will only fill in the PSR + * specific fields in the VSC packet and must not modify + * non-PSR related fields. + * ------------------------------------------------------------ + * + * Byte 1 - Used by DMCU to indicate action to panel. + * 1. PSR active (bit 0 - 0x1) + * 2. RFB update (bit 1 - 0x2) + * 3. CRC update (bit 2 - 0x4) + * Bytes 2-3- Used by DMCU for CRC by sending value of + * FMT_CRC_SIG_RED_GREEN:FMT_CRC_SIG_RED. + * Bytes 4-5- Used by DMCU for CRC by sending value of + * FMT_CRC_SIG_RED_GREEN:FMT_CRC_SIG_GREEN. + * Bytes 6-7- Used by DMCU for CRC by sending value of + * FMT_CRC_SIG_BLUE_CONTROL:FMT_CRC_SIG_BLUE. + * + * ==========================================================*/ + + /* Bits 4:0 = Number of valid data bytes = 08h, + * Bits 7:5 = RESERVED (all 0's)*/ + info_packet->hb3 = 0x08; + info_packet->valid = true; + } else { + /* Bits 4:0 = Revision Number = 01h, + * Bits 7:5 = RESERVED (all 0's) */ + info_packet->hb2 = 0x01; + /* Bits 4:0 = Number of valid data bytes = 01h + * Bits 7:5 = RESERVED (all 0's) */ + info_packet->hb3 = 0x01; + } + + /* Set VSC data fields for 3D Stereo if supported */ + if (format != TIMING_3D_FORMAT_NONE) { + /* VSC Payload (1 byte) From DP1.2 spec + * + * Bits 3:0 | Bits 7:4 + * (Stereo Interface | (Stereo Interface + * Method Code) | Method Specific Parameter) + * ------------------------------------------------------------ + * 0 = Non Stereo Video | Must be set to 0x0 + * ------------------------------------------------------------ + * 1 = Frame/Field Sequential| 0x0: L + R view indication + * | based on MISC1 bit 2:1 + * | 0x1: Right when Stereo + * | Signal = 1 + * | 0x2: Left when Stereo + * | Signal = 1 + * | (others reserved) + * ------------------------------------------------------------ + * 2 = Stacked Frame | 0x0: Left view is on top and + * | right view on bottom + * | (others reserved) + * ------------------------------------------------------------ + * 3 = Pixel Interleaved | 0x0: horiz interleaved, right + * | view pixels on even lines + * | 0x1: horiz interleaved, right + * | view pixels on odd lines + * | 0x2: checker board, start with + * | left view pixel + * | 0x3: vertical interleaved, + * | start w/ left view pixels + * | 0x4: vertical interleaved, + * | start w/ right view pixels + * | (others reserved) + * ------------------------------------------------------------ + * 4 = Side-by-side | 0x0: left half represents left + * | eye view + * | 0x1: left half represents right + * | eye view + * ==========================================================*/ + + switch (format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + /* Stacked Frame */ + info_packet->sb[0] = 0x02; + info_packet->valid = true; + break; + + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + case TIMING_3D_FORMAT_INBAND_FA: + /* Frame/Field Sequential, + * L + R view indication based on MISC1 bit 2:1 */ + info_packet->sb[0] = 0x01; + info_packet->valid = true; + break; + + default: + break; + } + } +} + +/* + * dal_ds_dispatch_setup_info_frame + * + * @brief + * Set up info frames + * + * @param + * path_mode *mode - [in] path mode + * hw_path_mode *hw_mode - [out] HW path mode whose info frame would be set + * + * @return + * void + */ +void dal_ds_dispatch_setup_info_frame(struct ds_dispatch *ds_dispatch, + const struct path_mode *mode, struct hw_path_mode *hw_mode) +{ + enum signal_type signal = SIGNAL_TYPE_NONE; + + /* default all packets to invalid */ + hw_mode->info_frame.avi_info_packet.valid = false; + hw_mode->info_frame.gamut_packet.valid = false; + hw_mode->info_frame.vendor_info_packet.valid = false; + hw_mode->info_frame.spd_packet.valid = false; + hw_mode->info_frame.vsc_packet.valid = false; + + signal = dal_display_path_get_config_signal(hw_mode->display_path, + SINK_LINK_INDEX); + + /* HDMi and DP have different info packets*/ + if (dal_is_hdmi_signal(signal)) { + prepare_avi_info_frame(ds_dispatch, mode, hw_mode->display_path, + hw_mode->mode.overscan, + hw_mode->mode.underscan_rule, + &hw_mode->info_frame.avi_info_packet); + prepare_vendor_info_packet(mode, + &hw_mode->info_frame.vendor_info_packet); + prepare_default_gamut_packet(ds_dispatch, mode, + &hw_mode->info_frame.gamut_packet); + } else if (dal_is_dp_signal(signal)) + prepare_video_stream_configuration_packet(ds_dispatch, + mode, + &hw_mode->info_frame.vsc_packet); +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_mode_setting.c b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_mode_setting.c new file mode 100644 index 000000000000..709587820b6c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_mode_setting.c @@ -0,0 +1,2609 @@ +/* + * Copyright 2015 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 */ +#include "dal_services.h" + +#include "include/set_mode_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/hw_path_mode_set_interface.h" +#include "include/display_path_interface.h" +#include "include/topology_mgr_interface.h" +#include "include/dcs_interface.h" +#include "include/link_service_interface.h" +#include "include/mode_query_interface.h" +#include "include/logger_interface.h" +#include "include/adjustment_interface.h" +#include "include/hw_adjustment_set.h" + +#include "ds_dispatch.h" +#include "ds_calculation.h" +#include "grph_colors_group.h" +#include "path_mode_set_with_data.h" + +/****************************************************************************** + * Local constants. + *****************************************************************************/ +static const uint32_t DVI_10BIT_THRESHOLD_RATE_IN_KHZ = 50000; + + +/****************************************************************************** + * Local functions prototypes. + *****************************************************************************/ + +/* Initialise DS dispatch */ +static bool ds_dispatch_construct( + struct ds_dispatch *ds, + const struct ds_dispatch_init_data *data); + +/* Build HW path mode from path mode */ +static bool build_hw_path_mode( + struct ds_dispatch *ds, + const struct path_mode *const mode, + struct hw_path_mode *const hw_mode, + enum build_path_set_reason reason, + struct adjustment_params *params); + +/* Program HW layer through HWSS */ +static bool program_hw( + struct ds_dispatch *ds, + bool enable_display); + +/* Update notification and flags after set mode */ +static void post_mode_change_update( + struct ds_dispatch *ds); + +/* Disable output */ +static void disable_output( + struct ds_dispatch *ds, + struct hw_path_mode_set *hw_mode_set); + +/* Enable output */ +static void enable_output( + struct ds_dispatch *ds, + struct hw_path_mode_set *hw_mode_set); + +/* Convert path mode into HW path mode */ +static void hw_mode_info_from_path_mode( + const struct ds_dispatch *ds_dispatch, + struct hw_mode_info *info, + struct display_path *disp_path, + const struct path_mode *mode, + enum build_path_set_reason reason); + +/* Tune up timing */ +static void tune_up_timing( + struct ds_dispatch *ds, + struct display_path *display_path, + struct hw_path_mode *hw_mode); + +/* Build a set of adjustments */ +static bool build_adjustment_set( + struct ds_dispatch *ds, + struct hw_path_mode *hw_mode, + const struct path_mode *mode, + struct display_path *display_path, + enum build_path_set_reason reason); + +/* Set up additional parameters for HW path mode */ +static void setup_additional_parameters( + const struct path_mode *mode, + struct hw_path_mode *hw_mode); + +/* Set dithering */ +static void set_dithering_options( + const struct ds_dispatch *ds_dispatch, + struct hw_mode_info *info, + const struct path_mode *mode, + struct display_path *disp_path); + +/* Helper to convert path mode into hw path mode */ +static void convert_mode_info( + const struct ds_dispatch *ds_dispatch, + struct hw_mode_info *info, + struct display_path *disp_path, + const struct path_mode *mode, + enum build_path_set_reason reason); + +static enum timing_3d_format get_active_timing_3d_format( + enum timing_3d_format timing_3d_format, + enum view_3d_format view_3d_format); + +/* Convert CRTC timing into HW format */ +static void hw_crtc_timing_from_crtc_timing( + struct hw_crtc_timing *hw_timing, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format, + enum signal_type signal); + +/* Setup HW stereo mixer parameters */ +static void setup_hw_stereo_mixer_params( + struct hw_mode_info *info, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format); + +static enum view_3d_format timing_3d_format_to_view_3d_format( + enum timing_3d_format timing_3d_format); + +static bool validate_stereo_3d_format( + struct display_path *display_path, + const struct crtc_timing *crtc_timing, + enum view_3d_format view_3d_format); + +static void enable_gtc_embedding( + struct ds_dispatch *ds_dispatch, + struct hw_path_mode_set *hw_mode_set); + +static void destroy_adjustment_set( + struct hw_adjustment_set **set); + +static void send_wireless_setmode_end_event( + struct ds_dispatch *ds_dispatch, + const struct path_mode_set *path_mode_set); +/****************************************************************************** + * Public function implementation. + *****************************************************************************/ + +/* Perform static validation of Mode for this path (without taking into + * account other active displays). + * Static means considering only capabilities of Links and GOs on the path + * (no video memory bandwidth calculations). + * The functions is used to filter out modes which this path can't support. */ +bool dal_ds_dispatch_is_valid_mode_timing_for_display( + const struct ds_dispatch *ds_dispatch, + uint32_t display_path_index, + enum validation_method method, + const struct mode_timing *mode_timing) +{ + struct hw_path_mode hw_path_mode = { 0 }; + struct link_validation_flags flags = { 0 }; + + struct display_path *display_path; + + enum view_3d_format view_3d_format; + enum signal_type asic_signal; + + bool result = true; + + if (!mode_timing) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Invalid Input\n", __func__); + return false; + } + + /* Validate mandatory pixel encoding */ + + if (mode_timing->crtc_timing.pixel_encoding == + PIXEL_ENCODING_UNDEFINED) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Pixel Encoding is not defined\n", + __func__); + return false; + } + + /* Validate mandatory color depth */ + + if (mode_timing->crtc_timing.display_color_depth == + DISPLAY_COLOR_DEPTH_UNDEFINED) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Color Depth is not defined\n", __func__); + return false; + } + + /* Choose action based on validation method */ + + switch (method) { + case VALIDATION_METHOD_STATIC: + hw_path_mode.action = HW_PATH_ACTION_STATIC_VALIDATE; + break; + case VALIDATION_METHOD_DYNAMIC: + hw_path_mode.action = HW_PATH_ACTION_EXISTING; + flags.DYNAMIC_VALIDATION = 1; + break; + default: + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Invalid Validation Method\n", __func__); + return false; + } + + /* Fill up the rest of data, and do validation */ + + display_path = dal_tm_create_resource_context_for_display_index( + ds_dispatch->tm, display_path_index); + + if (!display_path) { + dal_logger_write(ds_dispatch->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Failed to create temporary clone path!\n", + __func__); + return false; + } + + hw_path_mode.display_path = display_path; + hw_path_mode.mode.scaling_info.src.width = + mode_timing->mode_info.pixel_width; + hw_path_mode.mode.scaling_info.src.height = + mode_timing->mode_info.pixel_height; + hw_path_mode.mode.scaling_info.dst.width = + mode_timing->crtc_timing.h_addressable; + hw_path_mode.mode.scaling_info.dst.height = + mode_timing->crtc_timing.v_addressable; + hw_path_mode.mode.refresh_rate = + mode_timing->mode_info.field_rate; + + /* This is validation out of "display view" context. + * Therefore we assume 3D view_3d_format from timing only */ + + view_3d_format = timing_3d_format_to_view_3d_format( + mode_timing->crtc_timing.timing_3d_format); + + asic_signal = dal_display_path_get_query_signal( + display_path, ASIC_LINK_INDEX); + + hw_crtc_timing_from_crtc_timing( + &hw_path_mode.mode.timing, + &mode_timing->crtc_timing, + view_3d_format, + asic_signal); + + setup_hw_stereo_mixer_params( + &hw_path_mode.mode, + &mode_timing->crtc_timing, + view_3d_format); + + if (result) { + result = validate_stereo_3d_format( + display_path, + &mode_timing->crtc_timing, + view_3d_format); + } + + /* Validate the static capabilities of this pathmode */ + + result = result && (HWSS_RESULT_OK == + dal_hw_sequencer_validate_display_path_mode( + ds_dispatch->hwss, &hw_path_mode)); + + /* Bandwidth validation on each LINK (not to confuse with + * Video Memory Bandwidth validation, which is done on a SET of + * path modes when cofunctionality is validated). */ + + if (result) { + uint32_t link_count = + dal_display_path_get_number_of_links(display_path); + + uint32_t i; + + /* TEMPORARY CHECK */ + if (!dal_display_path_get_link_query_interface(display_path, 0)) + link_count = 0; + + for (i = 0; i != link_count; ++i) { + struct link_service *ls = + dal_display_path_get_link_query_interface( + display_path, i); + + if (!dal_ls_validate_mode_timing( + ls, display_path_index, + &hw_path_mode.mode.timing, flags)) { + result = false; + dal_logger_write( + ds_dispatch->dal_context->logger, + LOG_MAJOR_DISPLAY_SERVICE, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: Timing validation failed in Link Service!\n", + __func__); + break; + } + } + } + + dal_tm_destroy_resource_context_for_display_path(ds_dispatch->tm, + display_path); + + return result; +} + +void dal_ds_dispatch_pin_active_path_modes( + struct ds_dispatch *ds_dispatch, + void *param, + uint32_t display_index, + void (*func)(void *, const struct path_mode *)) +{ + uint32_t i; + uint32_t count; + const struct path_mode *existing_mode; + + dal_memset(ds_dispatch->path_modes, 0, sizeof(ds_dispatch->path_modes)); + + count = dal_pms_with_data_get_path_mode_num(ds_dispatch->set); + + /* Step 1. Copy all existing modes into the validation list, except + * passed display_path index + */ + for (i = 0; i < count; i++) { + existing_mode = + dal_pms_with_data_get_path_mode_at_index( + ds_dispatch->set, i); + if (existing_mode->display_path_index == display_index) + continue; + func(param, existing_mode); + } +} + +static struct rect get_viewport( + struct rect *src_rect, + struct rect *clip_rect, + struct rect *dest_rect) +{ + struct rect viewport = {0}; + + viewport.x = src_rect->x + (clip_rect->x - dest_rect->x) * + src_rect->width / dest_rect->width; + viewport.width = clip_rect->width * src_rect->width / dest_rect->width; + viewport.y = src_rect->y + (clip_rect->y - dest_rect->y) * + src_rect->height / dest_rect->height; + viewport.height = + clip_rect->height * src_rect->height / dest_rect->height; + + return viewport; +} + +static struct overscan_info get_overscan( + const struct view *os_resolution, + const struct rect *clip_rect) +{ + struct overscan_info overscan = {0}; + + if (os_resolution == NULL || clip_rect == NULL) + return overscan; + + overscan.left = clip_rect->x; + overscan.top = clip_rect->y; + + overscan.right = + os_resolution->width - clip_rect->width - overscan.left; + overscan.bottom = + os_resolution->height - clip_rect->height - overscan.top; + + if ((overscan.right + overscan.left > os_resolution->width) || + (overscan.top + overscan.bottom > os_resolution->height)) + BREAK_TO_DEBUGGER(); + + return overscan; +} + +DAL_VECTOR_AT_INDEX(plane_configs, struct plane_config *) + +static void cal_scaling_overscan_params( + const struct path_mode *mode, + enum scaling_transformation scl_type, + struct hw_mode_info *info, + struct vector *plane_configs) +{ + uint32_t screen_w; + uint32_t screen_h; + uint32_t bound_w; + uint32_t bound_h; + uint32_t i; + struct view os_resolution; + + uint32_t planes_num = dal_vector_get_count(plane_configs); + + /*PlaneConfig used instead of PlaneAttributes since only + *planeConfig can be an indexable array for getBoundingClipRect + */ + + if (planes_num == 0) { + BREAK_TO_DEBUGGER(); + return; + } + + os_resolution = mode->view; + screen_w = info->timing.h_addressable; + screen_h = info->timing.v_addressable; + + /* Here we build a bounding rectangle for the destination rectangles + * provided by OS. As the name implies all of the destination + * rectangles are bound inside this rectangle. It is necessary as + * destination rectangles do not necessarily coincide with the + * display timing. The bounding rectangle is used to calculate a common + * scaling ratio (CSR) for all surfaces which when multiplied by scaling + * ration to get source rect scaled onto the dest rect will give + * us the true scaling ratio. This is done in a single step so no actual + * CSR is calculated. + */ + + bound_w = os_resolution.width; + bound_h = os_resolution.height; + + if (bound_w == 0 || bound_h == 0) { + BREAK_TO_DEBUGGER(); + return; + } + + /*TODO: rotation + * Fixed31_32 h_sr + * Fixed31_32 v_sr + *ScalingTransformation_PreserveAspectRatioScale + */ + for (i = 0; i < planes_num; i++) { + + struct view dest; + struct rect clip_rect; + struct rect dst_rect; + struct rect src_rect; + struct rect viewport; + struct overscan_info overscan; + struct plane_config *plane_config = + plane_configs_vector_at_index(plane_configs, i); + + struct fixed31_32 v_sr; + struct fixed31_32 h_sr; + + dal_memset(&clip_rect, 0, sizeof(clip_rect)); + dal_memset(&dst_rect, 0, sizeof(dst_rect)); + dal_memset(&src_rect, 0, sizeof(src_rect)); + + { + /* this is the else case when rotation is not enabled + * TODO: add rotation case above */ + + src_rect = plane_config->attributes.src_rect; + dst_rect = plane_config->attributes.dst_rect; + clip_rect = plane_config->attributes.clip_rect; + } + + if (dst_rect.height == 0 || dst_rect.width == 0) { + BREAK_TO_DEBUGGER(); + return; + } + + /** + * Viewport does not need to be scaled to screen resolution + * Underscan and destination scale with screen resolution + */ + viewport = get_viewport(&src_rect, &clip_rect, &dst_rect); + overscan = get_overscan(&os_resolution, &clip_rect); + + h_sr = dal_fixed31_32_from_int(src_rect.width); + v_sr = dal_fixed31_32_from_int(src_rect.height); + + /* TODO: add stereo 3d h_sr and v_sr translation */ + + if (scl_type == SCALING_TRANSFORMATION_FULL_SCREEN_SCALE) { + overscan.left = overscan.left * screen_w / bound_w; + overscan.right = overscan.right * screen_w / bound_w; + /*h_sr *= screen_w; + h_sr /= bound_w;*/ + overscan.top = overscan.top * screen_h / bound_h; + overscan.bottom = overscan.bottom * screen_h / bound_h; + /*v_sr *= screen_h; + v_sr /= bound_h;*/ + + /*The way scaling calculation works here is by + * calculating overscan Whatever remains is the timing + * destination + */ + dest.width = + screen_w - overscan.left - overscan.right; + dest.height = + screen_h - overscan.top - overscan.bottom; + } else if (scl_type == + SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE) { + /* To preserve aspect ratio without cutting parts of the + * surface we must use a common scaling ratio for both + * width and height. This ratio is the largest of the + * two. + */ + if (screen_w * bound_h < screen_h * bound_w) { + /* width needs less upscaling/more downscaling + */ + overscan.left = overscan.left * screen_w + / bound_w; + overscan.right = overscan.right * screen_w + / bound_w; + /* Uf = Ui*SR + (timingH - bound_h/SR)/2 where + * SR is ratio: bound_w/timingW */ + overscan.top = + (2 * overscan.top * screen_w + + screen_h * bound_w - + bound_h * screen_w) / + bound_w / 2; + overscan.bottom = + (2 * overscan.bottom * screen_w + + screen_h * bound_w - + bound_h * screen_w) / + bound_w / 2; + + h_sr = dal_fixed31_32_mul_int(h_sr, bound_w); + h_sr = dal_fixed31_32_div_int(h_sr, screen_w); + v_sr = dal_fixed31_32_mul_int(v_sr, bound_w); + v_sr = dal_fixed31_32_div_int(v_sr, screen_w); + } else { + /* height needs less upscaling/more downscaling + */ + overscan.top = overscan.top * screen_h + / bound_h; + overscan.bottom = overscan.bottom * screen_h + / bound_h; + + /* Uf = Ui*SR + (timingW - bound_w/SR)/2 where + * SR is ratio: bound_h/timingH */ + overscan.left = + (2 * overscan.left * screen_h + + screen_w * bound_h - + bound_w * screen_h) / + bound_h / 2; + overscan.right = + (2 * overscan.right * screen_h + + screen_w * bound_h - + bound_w * screen_h) / + bound_h / 2; + + h_sr = dal_fixed31_32_mul_int(h_sr, bound_h); + h_sr = dal_fixed31_32_div_int(h_sr, screen_h); + v_sr = dal_fixed31_32_mul_int(v_sr, bound_h); + v_sr = dal_fixed31_32_div_int(v_sr, screen_h); + } + + dest.width = screen_w - overscan.left - overscan.right; + dest.height = screen_h - overscan.top - overscan.bottom; + } else if (scl_type == SCALING_TRANSFORMATION_CENTER_TIMING) { + /*All we do in this case is calculate how much more + * overscan is needed to center the bounding rectangle + */ + ASSERT((screen_w >= bound_w) && (screen_h >= bound_h)); + + overscan.left += (screen_w - bound_w) / 2; + overscan.right += + screen_w - bound_w - (screen_w - bound_w) / 2; + overscan.top += (screen_h - bound_h) / 2; + overscan.bottom += + screen_h - bound_h - (screen_h - bound_h) / 2; + + dest.width = + plane_config->attributes.clip_rect.width; + dest.height = + plane_config->attributes.clip_rect.height; + } else if (scl_type == SCALING_TRANSFORMATION_IDENTITY) { + /*do nothing for identity*/ + dest.width = + plane_config->attributes.clip_rect.width; + dest.height = + plane_config->attributes.clip_rect.height; + } else { + /*error unsupported scalingTransformation*/ + BREAK_TO_DEBUGGER(); + return; + } + + /* Division last to minimize truncation error */ + h_sr = dal_fixed31_32_div_int(h_sr, dst_rect.width); + v_sr = dal_fixed31_32_div_int(v_sr, dst_rect.height); + + /*Add timing overscan to finalize overscan calculation*/ + overscan.left += info->timing.h_overscan_left; + overscan.right += info->timing.h_overscan_right; + overscan.top += info->timing.v_overscan_top; + overscan.bottom += info->timing.v_overscan_bottom; + + if (dest.height == 0 || dest.width == 0) + BREAK_TO_DEBUGGER(); + + plane_config->mp_scaling_data.overscan = overscan; + + if (plane_config->mp_scaling_data.viewport.x == 0 && + plane_config->mp_scaling_data.viewport.y == 0 && + plane_config->mp_scaling_data.viewport.width == 0 && + plane_config->mp_scaling_data.viewport.height == 0) + plane_config->mp_scaling_data.viewport = viewport; + + plane_config->mp_scaling_data.dst_res = dest; + plane_config->mp_scaling_data.ratios.horz = h_sr; + plane_config->mp_scaling_data.ratios.vert = v_sr; + plane_config->mp_scaling_data.ratios.horz_c = h_sr; + plane_config->mp_scaling_data.ratios.vert_c = v_sr; + + /* TODO: add MPO horzc and vertc preparation w amd w/o rotation + */ + + /* DM always passes requested num of taps even if + * scaling is not required + */ + if (viewport.width == dest.width + && viewport.height == dest.height) { + plane_config->attributes.scaling_quality. + h_taps = 1; + plane_config->attributes.scaling_quality. + v_taps = 1; + } + + if (viewport.width == 2 * dest.width + && (plane_config->config.dal_pixel_format == + PIXEL_FORMAT_420BPP12 + || plane_config->config.rotation == + PLANE_ROTATION_ANGLE_90 + || plane_config->config.rotation == + PLANE_ROTATION_ANGLE_270)) + plane_config->attributes.scaling_quality.h_taps_c = 1; + + if (viewport.height == 2 * dest.height && + (plane_config->config.dal_pixel_format == + PIXEL_FORMAT_420BPP12 || + plane_config->config.rotation == + PLANE_ROTATION_ANGLE_0 || + plane_config->config.rotation == + PLANE_ROTATION_ANGLE_180)) + plane_config->attributes.scaling_quality. + v_taps_c = 1; + + + /*Graphics don't need to match mmd scaling*/ + if (plane_config->config.dal_pixel_format <= + PIXEL_FORMAT_GRPH_END) { + plane_config->attributes.scaling_quality. + h_taps = TAP_VALUE_INVALID; + plane_config->attributes.scaling_quality.v_taps = + TAP_VALUE_INVALID; + plane_config->attributes.scaling_quality.h_taps_c = + TAP_VALUE_INVALID; + plane_config->attributes.scaling_quality.v_taps_c = + TAP_VALUE_INVALID; + } + } +} + +/*build scaler overscan parameters for new_mode*/ +static void build_scaling_params( + const struct ds_dispatch *ds, + const struct path_mode *mode, + enum scaling_transformation scl_type, + struct hw_mode_info *info) +{ + struct display_path *disp_path = NULL; + struct vector *plane_configs = NULL; + + disp_path = dal_tm_display_index_to_display_path( + ds->tm, mode->display_path_index); + + plane_configs = + dal_pms_with_data_get_plane_configs( + ds->set, + mode->display_path_index); + + cal_scaling_overscan_params( + mode, + scl_type, + info, + plane_configs); +} + +static bool is_timing_change_required( + const struct path_mode *existing_mode, + const struct path_mode *new_mode) +{ + struct mode_timing old_mt = *existing_mode->mode_timing; + struct mode_timing new_mt = *new_mode->mode_timing; + + /* + * TODO: (see DAL2's DSDispatch::isTimingChangeRequired for reference) + * - stutter mode + * - Stereo 3D format + */ + + /* pixel format change affect FMT and color space, not timing */ + old_mt.crtc_timing.pixel_encoding = PIXEL_ENCODING_UNDEFINED; + new_mt.crtc_timing.pixel_encoding = PIXEL_ENCODING_UNDEFINED; + + /* ignore timing standard if all fields are equal */ + old_mt.crtc_timing.timing_standard = TIMING_STANDARD_UNDEFINED; + new_mt.crtc_timing.timing_standard = TIMING_STANDARD_UNDEFINED; + + /* TODO - we ignore stereo 3D until we implement it */ + old_mt.crtc_timing.timing_3d_format = TIMING_3D_FORMAT_NONE; + new_mt.crtc_timing.timing_3d_format = TIMING_3D_FORMAT_NONE; + + /* ignore vic and hdmi_vic */ + old_mt.crtc_timing.vic = 0; + new_mt.crtc_timing.vic = 0; + + old_mt.crtc_timing.hdmi_vic = 0; + new_mt.crtc_timing.hdmi_vic = 0; + + /* compare pixel clock within 10kHz */ + old_mt.crtc_timing.pix_clk_khz /= 10; + new_mt.crtc_timing.pix_clk_khz /= 10; + + + if (dal_memcmp(&old_mt, &new_mt, sizeof(struct mode_timing))) + return true; + + /* TODO check if adjusted HW mode timing changed */ + + return false; +} + +/* + * dal_set_mode_interface_set_mode + * + * Set mode on a set of display paths + */ +enum ds_return dal_ds_dispatch_set_mode( + struct ds_dispatch *ds_dispatch, + const struct path_mode_set *path_mode_set) +{ + enum ds_return result = DS_ERROR; + + uint32_t path_mode_num; + uint32_t i; + + if (!ds_dispatch || !path_mode_set) { + BREAK_TO_DEBUGGER(); + return DS_ERROR; + } + + if (!dal_tm_is_hw_state_valid(ds_dispatch->tm)) { + /* TODO: optimize display programming */ + + /* HW transition from VBIOS-controlled to driver */ + dal_tm_enable_accelerated_mode(ds_dispatch->tm); + } + + path_mode_num = dal_pms_get_path_mode_num(path_mode_set); + + /* TODO: Reset mode? */ + + for (i = 0; i < path_mode_num; i++) { + bool timing_changed = false; + bool view_res_changed = false; + bool new_path = false; + bool path_acquired = false; + /* TODO bool audio_bandwidth_changed = true;*/ + + const struct path_mode *mode = + dal_pms_get_path_mode_at_index(path_mode_set, i); + + uint32_t disp_index = mode->display_path_index; + + const struct path_mode *existing_mode = + dal_pms_with_data_get_path_mode_for_display_index( + ds_dispatch->set, disp_index); + + struct active_path_data *existing_data = + dal_pms_with_data_get_path_data_for_display_index( + ds_dispatch->set, disp_index); + + + struct active_path_data data_copy; + + /* Copy existing path data */ + if (existing_data) { + dal_memmove(&data_copy, existing_data, + sizeof(struct active_path_data)); + + existing_data = &data_copy; + } + + /* TODO : fight -Wframe-larger-than caused by + * too large struct hw_path_mode allocated on stack + if (existing_mode) { + struct hw_path_mode old = { 0 }; + struct hw_path_mode new = { 0 }; + + build_hw_path_mode( + ds_dispatch, existing_mode, &old, + BUILD_PATH_SET_REASON_GET_ACTIVE_PATHS, NULL); + + build_hw_path_mode( + ds_dispatch, new_mode, &new, + BUILD_PATH_SET_REASON_GET_ACTIVE_PATHS, NULL); + + audio_bandwidth_changed = + dal_hw_sequencer_has_audio_bandwidth_changed( + ds_dispatch->hwss, &old, &new); + }*/ + + /* TODO: Handle gamut pack change*/ + + /* TODO: Handle stereo 3D change*/ + + /* Update existing path mode */ + if (existing_mode) { + /* TODO: Handle flags for existing path*/ + + if (is_timing_change_required( + existing_mode, mode)) + timing_changed = true; + + if (existing_mode->view.height != mode->view.height || + existing_mode->view.width != mode->view.width) + view_res_changed = true; + + /* remove existing path */ + dal_pms_with_data_remove_path_mode_for_display_index( + ds_dispatch->set, disp_index); + } else { + /* Enabling new path */ + path_acquired = + dal_tm_acquire_display_path( + ds_dispatch->tm, disp_index); + + if (!path_acquired) { + BREAK_TO_DEBUGGER(); + continue; + } + + timing_changed = true; + view_res_changed = true; + new_path = true; + } + + /* Add the path mode to the current active path mode set*/ + if (dal_pms_with_data_add_path_mode_with_data( + ds_dispatch->set, mode, existing_data)) + result = DS_SUCCESS; + else + BREAK_TO_DEBUGGER(); + + { + struct plane_config pc; + + dal_memset(&pc, 0, sizeof(pc)); + + pc.attributes.dst_rect.height = mode->view.height; + pc.attributes.dst_rect.width = mode->view.width; + pc.attributes.dst_rect.x = 0; + pc.attributes.dst_rect.y = 0; + pc.attributes.clip_rect = pc.attributes.dst_rect; + pc.attributes.src_rect = pc.attributes.dst_rect; + + /* TODO: add stereo 3d translation here */ + dal_pms_with_data_add_plane_configs( + ds_dispatch->set, + mode->display_path_index, + &pc, + 1); + } + + /* Setup active data flags for pre-mode change*/ + if (result == DS_SUCCESS) { + struct active_path_data *data = + dal_pms_with_data_get_path_data_for_display_index( + ds_dispatch->set, disp_index); + + data->flags.all = 0; + data->flags.bits.ENABLE_HW = new_path; + data->flags.bits.REPROGRAM_HW = !new_path; + data->flags.bits.SYNC_TIMING_SERVER = (i == 0); + data->flags.bits.POST_ACTION_DISPLAY_ON = true; + data->flags.bits.TIMING_CHANGED = timing_changed; + data->flags.bits.VIEW_RES_CHANGED = view_res_changed; + + /* TODO: use real value */ + data->flags.bits.PENDING_DISPLAY_STEREO = false; + data->flags.bits.PIXEL_ENCODING_CHANGED = false; + data->flags.bits.GAMUT_CHANGED = false; + data->flags.bits.AUDIO_BANDWIDTH_CHANGED = false; + + /* TODO: adjustment */ + /*build scaler overscan parameters for new_mode, + * plane_configs is not added yet*/ + /*build_scaler_overscan_params(ds_dispatch, mode);*/ + } + } + + /* Program HW*/ + if (result == DS_SUCCESS) { + bool enable_display = + !dal_pms_is_display_power_off_required(path_mode_set); + + result = (program_hw(ds_dispatch, enable_display) + ? DS_SUCCESS : DS_ERROR); + } + + if (result == DS_SUCCESS) { + /* TODO: Post-set mode for 3D, overlay, and embedded */ + send_wireless_setmode_end_event(ds_dispatch, path_mode_set); + + } + /* Post-set mode. Update notification and data flags */ + post_mode_change_update(ds_dispatch); + + return result; +} + +/* + * dal_ds_dispatch_build_hw_path_mode_for_adjustment + * + * Create HW path mode specifically for adjustment or generic control request + * on an active display + */ +bool dal_ds_dispatch_build_hw_path_mode_for_adjustment( + struct ds_dispatch *ds_dispatch, + struct hw_path_mode *mode, + uint32_t disp_index, + struct adjustment_params *params) +{ + const struct path_mode *path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + ds_dispatch->set, disp_index); + + if (mode != NULL && path_mode != NULL) + return build_hw_path_mode( + ds_dispatch, + path_mode, + mode, + BUILD_PATH_SET_REASON_SET_ADJUSTMENT, + params); + + return false; + +} + +bool dal_ds_dispatch_build_hw_path_set_for_adjustment( + struct ds_dispatch *ds, + struct hw_path_mode_set *hw_pms, + struct adjustment_params *params) +{ + + uint32_t path_num = dal_pms_with_data_get_path_mode_num(ds->set); + + if (hw_pms != NULL) + return dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index(ds->set, 0), + hw_pms, + BUILD_PATH_SET_REASON_SET_ADJUSTMENT, + params); + return false; + +} + +/* + * dal_ds_dispatch_create + * + * Create DS dispatch + */ +struct ds_dispatch *dal_ds_dispatch_create( + const struct ds_dispatch_init_data *data) +{ + struct ds_dispatch *ds_dispatch = + dal_alloc(sizeof(struct ds_dispatch)); + + if (!ds_dispatch) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (ds_dispatch_construct(ds_dispatch, data)) + return ds_dispatch; + + BREAK_TO_DEBUGGER(); + + dal_free(ds_dispatch); + + return NULL; +} + +static void destruct(struct ds_dispatch *ds_dispatch) +{ + dal_pms_with_data_destroy(&ds_dispatch->set); +} + +/* + * dal_ds_dispatch_destroy + * + * Destroy DS dispatch + */ +void dal_ds_dispatch_destroy( + struct ds_dispatch **ds_dispatch) +{ + if (!ds_dispatch || !*ds_dispatch) { + BREAK_TO_DEBUGGER(); + return; + } + + destruct(*ds_dispatch); + + dal_free(*ds_dispatch); + + *ds_dispatch = NULL; +} + +struct set_mode_params *dal_ds_dispatch_create_resource_context( + const struct ds_dispatch *ds_dispatch, + const uint32_t display_idx[], + uint32_t idx_num) +{ + struct set_mode_params *sm_params; + struct set_mode_params_init_data init_data; + + init_data.hws = ds_dispatch->hwss; + init_data.ctx = ds_dispatch->dal_context; + init_data.tm = ds_dispatch->tm; + + sm_params = dal_set_mode_params_create(&init_data); + if (!sm_params) + return NULL; + + if (dal_set_mode_params_init_with_topology( + sm_params, + display_idx, + idx_num)) + return sm_params; + + dal_set_mode_params_destroy(&sm_params); + return NULL; +} + +/* +* do_reset_mode +* +* Reset mode on a set of display paths to make the paths be properly released +* +* If successful, returns DS_SUCCESS. Any other value indicates a failure. +*/ +enum ds_return do_reset_mode( + struct ds_dispatch *ds_dispatch, + const uint32_t displays_num, + const uint32_t *display_index, + bool bypass_reset_vcc) +{ + enum ds_return ret = DS_SUCCESS; + bool do_hw_programming = false; + uint32_t i; + + if (displays_num == 0) + return ret; + + /* TODO: Reset workstation stereo */ + + for (i = 0; i < displays_num; i++) { + uint32_t index = display_index[i]; + const struct path_mode *path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + ds_dispatch->set, + index); + struct active_path_data *path_data = + dal_pms_with_data_get_path_data_for_display_index( + ds_dispatch->set, + index); + + /* If we have at least one PathMode to reset - this loop + * considered successful and we will reprogram HW for relevant + * paths. */ + if (path_mode != NULL) { + if (path_data != NULL) { + /* save the AUDIO_BANDWIDTH_CHANGED value which + * could have been set to true in reset_mode + * this also serves to reset the + * DISPLAY_PATH_INVALID flag + */ + bool audio_bandwidth_changed = + path_data->flags.bits. + AUDIO_BANDWIDTH_CHANGED; + path_data->flags.all = 0; + path_data->flags.bits.DISABLE_HW = true; + path_data->flags.bits.KEEP_VCC_ON_DISABLE_HW = + bypass_reset_vcc; + path_data->flags.bits.AUDIO_BANDWIDTH_CHANGED = + audio_bandwidth_changed; + } + + /* TODO: Reset synchronization state whatever it was - + * since we are going to reprogram this display path + * TODO: Reset display stereo */ + do_hw_programming = true; + } + } + + if (do_hw_programming) + ret = (program_hw(ds_dispatch, false) ? + DS_SUCCESS : DS_ERROR); + + post_mode_change_update(ds_dispatch); + + return ret; +} + +/* + * dal_ds_dispatch_reset_mode + * + * Reset mode on a set of display paths to make the paths to be properly + * released. + * + * If successful, returns DS_SUCCESS. Any other value indicates a failure. + */ +enum ds_return dal_ds_dispatch_reset_mode( + struct ds_dispatch *ds_dispatch, + const uint32_t displays_num, + const uint32_t *display_indexes) +{ + enum ds_return result = DS_SUCCESS; + + /* uint32_t ur_sent_num = 0; + * bool update_ur = false; + */ + uint32_t i; + + if (!ds_dispatch || !display_indexes) + return DS_ERROR; + + /* We never want to call ResetMode before enabling accelerated mode. + * Doing so can power down a display path in the incorrect order, + * resulting in a hang in VBIOS table. + * The reason is because driver logic state of the display paths may not + * match what VBIOS has enabled. + * So we must always power down the hw blocks in a specific sequence. + */ + if (!dal_tm_is_hw_state_valid(ds_dispatch->tm)) { + /* enableAcceleratedMode will do two things: + * 1. call TopologyManager::EnableAcceleratedMode to power down + * all hw blocks + * 2. mark all displays as invalid + */ + /* TODO: accelerated mode */ + } + + /* TODO: finish implementation */ + + for (i = 0; i < displays_num; ++i) { + struct active_path_data *path_data; + /*struct display_path *display_path = + dal_tm_display_index_to_display_path( + ds_dispatch->tm, + display_indexes[i]); + + struct audio *audio = dal_display_path_get_audio( + display_path, + ASIC_LINK_INDEX); + + if ((audio != NULL) && (ur_sent_num == 0)) + update_ur = true; + */ + /* Disable Mirabilis and send UR once + * In dal_hw_sequencer_enable_audio_channel_split_mapping, + * there is a check that the display path has a valid audio + * object before attempting to send an UR to audio driver. So, + * we send UR on the first display path that has a valid audio + * object. + */ + /* TODO: implement + dal_hw_sequencer_enable_audio_channel_split_mapping( + display_path, 0, false, update_ur); + */ + + path_data = + dal_pms_with_data_get_path_data_for_display_index( + ds_dispatch->set, + display_indexes[i]); + + if (path_data) + path_data->flags.bits.AUDIO_BANDWIDTH_CHANGED = true; + } + + result = do_reset_mode( + ds_dispatch, displays_num, display_indexes, false); + + /* TODO: clear previous bandwidth validation on current mode. */ + + return result; +} + + +/* dal_ds_dispatch_get_active_path_mode + * + * Return active path modes + */ +struct path_mode_set_with_data *dal_ds_dispatch_get_active_pms_with_data( + struct ds_dispatch *ds_dispatch) +{ + return ds_dispatch->set; +} + + +enum ds_return dal_ds_dispatch_pre_adapter_clock_change( + struct ds_dispatch *ds) +{ + uint32_t path_num = dal_pms_with_data_get_path_mode_num(ds->set); + + struct hw_path_mode_set *hw_mode_set = dal_hw_path_mode_set_create(); + + if (!hw_mode_set) { + BREAK_TO_DEBUGGER(); + return DS_ERROR; + } + + /* Convert path mode set into HW path mode set for HWSS */ + if (!dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index(ds->set, 0), + hw_mode_set, + BUILD_PATH_SET_REASON_WATERMARKS, + NULL)) { + return DS_ERROR; + } + + if (dal_hw_path_mode_set_get_paths_number(hw_mode_set)) + dal_hw_sequencer_set_safe_displaymark(ds->hwss, hw_mode_set); + + dal_ds_dispatch_destroy_hw_path_set(hw_mode_set); + + return DS_SUCCESS; +} + +enum ds_return dal_ds_dispatch_post_adapter_clock_change( + struct ds_dispatch *ds) +{ + uint32_t path_num = dal_pms_with_data_get_path_mode_num(ds->set); + + struct hw_path_mode_set *hw_mode_set = dal_hw_path_mode_set_create(); + + if (!hw_mode_set) { + BREAK_TO_DEBUGGER(); + return DS_ERROR; + } + + /* Convert path mode set into HW path mode set for HWSS */ + if (!dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index(ds->set, 0), + hw_mode_set, + BUILD_PATH_SET_REASON_WATERMARKS, + NULL)) { + return DS_ERROR; + } + + if (dal_hw_path_mode_set_get_paths_number(hw_mode_set)) + dal_hw_sequencer_set_displaymark(ds->hwss, hw_mode_set); + + dal_ds_dispatch_destroy_hw_path_set(hw_mode_set); + + return DS_SUCCESS; +} + +/* + * Local function definition + */ + +/* Initialize DS dispatch */ +static bool ds_dispatch_construct( + struct ds_dispatch *ds, + const struct ds_dispatch_init_data *data) +{ + if (!data) { + BREAK_TO_DEBUGGER(); + return false; + } + + ds->dal_context = data->dal_context; + ds->as = data->as; + ds->hwss = data->hwss; + ds->tm = data->tm; + ds->ts = data->ts; + + ds->set = dal_pms_with_data_create(); + if (!ds->set) { + BREAK_TO_DEBUGGER(); + return false; + } + if (!dal_ds_dispatch_initialize_adjustment(ds)) { + dal_pms_with_data_destroy(&ds->set); + return false; + } + return true; +} + +/* + * program_hw + * + * Program HW layer through HWSS. + */ +static bool program_hw( + struct ds_dispatch *ds, + bool enable_display) +{ + uint32_t path_num = dal_pms_with_data_get_path_mode_num(ds->set); + + struct hw_path_mode_set *hw_mode_set = dal_hw_path_mode_set_create(); + + bool result = false; + + if (!hw_mode_set) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* Convert path mode set into HW path mode set for HWSS */ + result = dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index(ds->set, 0), + hw_mode_set, + BUILD_PATH_SET_REASON_SET_MODE, + NULL); + + /* TODO: Apply synchronization */ + + /* HW programming */ + if (result) { + /* Disable output*/ + disable_output(ds, hw_mode_set); + + /* Set Mode */ + if (dal_hw_sequencer_set_mode(ds->hwss, hw_mode_set) != + HWSS_RESULT_OK) + result = false; + + /* Enable output */ + if (enable_display) + enable_output(ds, hw_mode_set); + + } + + /* TODO: update ISR and DRR setup */ + + dal_ds_dispatch_destroy_hw_path_set(hw_mode_set); + + return result; +} + +/* + * dal_ds_dispatch_build_hw_path_set + * + * Build a set of HW path modes from the given paths + */ +bool dal_ds_dispatch_build_hw_path_set( + struct ds_dispatch *ds, + const uint32_t num, + const struct path_mode *const mode, + struct hw_path_mode_set *const hw_mode_set, + enum build_path_set_reason reason, + struct adjustment_params *params) +{ + bool result = true; + + uint32_t adj_counter = 0; + uint32_t i; + + if (num == 0 || !mode) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* Build HW path mode for every path */ + for (i = 0; i < num; i++) { + struct hw_path_mode hw_path_mode = { 0 }; + + if (!build_hw_path_mode( + ds, &mode[i], &hw_path_mode, reason, params)) { + result = false; + break; + } + + if (params && params->affected_path == + hw_path_mode.display_path) + adj_counter++; + + if (reason == BUILD_PATH_SET_REASON_SET_MODE) + if (!dal_hw_path_mode_set_add( + hw_mode_set, &hw_path_mode, NULL)) { + result = false; + break; + } + } + + /* Only allow one adjustment per path each time */ + if (reason == BUILD_PATH_SET_REASON_SET_ADJUSTMENT && adj_counter > 1) + result = false; + + /* TODO: Free all adjustment if error occurs */ + + if (!result) { + uint32_t j; + struct hw_path_mode *mode; + + for (j = 0; j < i; j++) { + mode = dal_hw_path_mode_set_get_path_by_index( + hw_mode_set, j); + + if (mode != NULL && mode->adjustment_set != NULL) + destroy_adjustment_set(&mode->adjustment_set); + } + } + + return result; +} + +/** + * Builds HW Path mode with adjustments applied according to the parameters. + * As part of the building procedure, will apply relevant adjustments, + * only skipping adjustments that specified as skip_adjustments. + * + * NOTE: This function resets output parameter hw_mode. + * + * \param [in] mode: path Mode from which to build hw Path mode. + * \param [out] hw_mode: HW Path mode to fill. + * \param [in] reason: The reason for this request. + * \param [in] skip_adjustments: Adjustments which should NOT be applied as + * part of building hw Path set. + * + * \return + * true: HW Path mode was successfully built + * false: otherwise + */ +static bool build_hw_path_mode( + struct ds_dispatch *ds, + const struct path_mode *const mode, + struct hw_path_mode *const hw_mode, + enum build_path_set_reason reason, + struct adjustment_params *skip_adjustments) +{ + struct active_path_data *data = NULL; + struct display_path *disp_path = NULL; + bool adjustment_done = false; + + disp_path = dal_tm_display_index_to_display_path( + ds->tm, + mode->display_path_index); + + data = dal_pms_with_data_get_path_data_for_display_index( + ds->set, + mode->display_path_index); + + /*associate hw_info->plane_configs with ds_dispatch->set-> + *plane_configs + */ + hw_mode->plane_configs = + dal_pms_with_data_get_plane_configs( + ds->set, + mode->display_path_index); + + if (!disp_path) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* Setup HW action flags */ + if (data) { + hw_mode->action_flags.TIMING_CHANGED = + data->flags.bits.TIMING_CHANGED; + hw_mode->action_flags.PIXEL_ENCODING_CHANGED = + data->flags.bits.PIXEL_ENCODING_CHANGED; + hw_mode->action_flags.RESYNC_PATH = + data->flags.bits.RESYNC_HW; + hw_mode->action_flags.GAMUT_CHANGED = + data->flags.bits.GAMUT_CHANGED; + hw_mode->action_flags.TURN_OFF_VCC = true; + + /* TODO: Do not turn off VCC as optimization*/ + + /* TODO: Implement viewport_adjustment + if (reason == BUILD_PATH_SET_REASON_SET_MODE) + hw_mode->mode.view_port_adjustments = + &data->viewport_adjustment; + */ + + if (data->flags.bits.DISABLE_HW) + hw_mode->action = HW_PATH_ACTION_RESET; + else if (data->flags.bits.ENABLE_HW || + data->flags.bits.REPROGRAM_HW) + hw_mode->action = HW_PATH_ACTION_SET; + else if (data->flags.bits.EXISTING) + hw_mode->action = HW_PATH_ACTION_EXISTING; + else + BREAK_TO_DEBUGGER(); + + /* TODO: adjustment for down scaling */ + } else { + ASSERT(reason == BUILD_PATH_SET_REASON_VALIDATE); + ASSERT(reason == BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN); + + hw_mode->action = HW_PATH_ACTION_SET; + } + + /* Setup initial HW path mode */ + hw_mode->display_path = disp_path; + + hw_mode_info_from_path_mode( + ds, &hw_mode->mode, disp_path, mode, reason); + + setup_additional_parameters(mode, hw_mode); + + if (skip_adjustments && skip_adjustments->affected_path == disp_path) { + switch (skip_adjustments->action) { + case ADJUSTMENT_ACTION_VALIDATE: + hw_mode->action = HW_PATH_ACTION_SET; + break; + case ADJUSTMENT_ACTION_SET_ADJUSTMENT: + hw_mode->action = HW_PATH_ACTION_SET_ADJUSTMENT; + break; + default: + break; + } + + /* TODO: build calculated adjustments */ + } else { + build_adjustment_set(ds, hw_mode, mode, disp_path, reason); + + adjustment_done = true; + } + + tune_up_timing(ds, disp_path, hw_mode); + + if (data && adjustment_done) + dal_ds_dispatch_setup_info_frame(ds, mode, hw_mode); + + return true; +} + +/* + * dal_ds_dispatch_destroy_hw_path_set + * + * Destroy a set of HW path + */ +void dal_ds_dispatch_destroy_hw_path_set( + struct hw_path_mode_set *hw_mode_set) +{ + uint32_t i; + uint32_t num; + struct hw_path_mode *mode; + + if (!hw_mode_set) { + BREAK_TO_DEBUGGER(); + return; + } + + num = dal_hw_path_mode_set_get_paths_number(hw_mode_set); + + for (i = 0; i < num; i++) { + mode = dal_hw_path_mode_set_get_path_by_index( + hw_mode_set, i); + + if (mode != NULL && mode->adjustment_set != NULL) + destroy_adjustment_set(&mode->adjustment_set); + } + + dal_hw_path_mode_set_destroy(&hw_mode_set); +} + +/* + * disable_output + * + * Disable output + */ +static void disable_output( + struct ds_dispatch *ds, + struct hw_path_mode_set *hw_mode_set) +{ + uint32_t i; + uint32_t j; + uint32_t num = dal_pms_with_data_get_path_mode_num(ds->set); + + for (i = 0; i < num; i++) { + struct link_service *link = NULL; + struct hw_path_mode *hw_mode = + dal_hw_path_mode_set_get_path_by_index( + hw_mode_set, i); + + const struct path_mode *mode = + dal_pms_with_data_get_path_mode_at_index(ds->set, i); + + struct active_path_data *data = + dal_pms_with_data_get_path_data_at_index(ds->set, i); + + struct display_path *disp_path = + dal_tm_display_index_to_display_path( + ds->tm, mode->display_path_index); + + uint32_t link_count = + dal_display_path_get_number_of_links(disp_path); + + bool disable_required = data->flags.bits.DISABLE_HW; + + bool safe_mode_change = data->flags.bits.TIMING_CHANGED; + + if (!dal_display_path_is_target_powered_off(disp_path) && + safe_mode_change) + data->flags.bits.POST_ACTION_DISPLAY_ON = true; + + /* display currently disabled, don't need to disable output */ + if (data->flags.bits.ENABLE_HW) + continue; + + if (disable_required || safe_mode_change) { + for (j = link_count; j > 0; j--) { + enum signal_type signal; + + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j - 1); + + dal_ls_blank_stream( + link, + mode->display_path_index, + hw_mode); + + dal_hw_sequencer_mute_audio_endpoint( + ds->hwss, + hw_mode->display_path, + true); + + signal = dal_display_path_get_query_signal( + disp_path, j - 1); + + if (signal == SIGNAL_TYPE_WIRELESS) + dal_hw_sequencer_enable_wireless_idle_detection + (ds->hwss, false); + } + } + + + if (disable_required) { + for (j = link_count; j > 0; j--) { + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j - 1); + + /* TODO: Disable audio jack presence */ + + dal_ls_disable_stream( + link, + mode->display_path_index, + hw_mode); + } + + data->display_state.OUTPUT_ENABLED = 0; + data->display_state.OUTPUT_BLANKED = 1; + } else if (safe_mode_change) { + for (j = link_count; j > 0; j--) { + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j - 1); + + /* TODO: Disable audio jack presence */ + + dal_ls_pre_mode_change( + link, + mode->display_path_index, + hw_mode); + } + + data->display_state.OUTPUT_BLANKED = 1; + } + + link = dal_display_path_get_link_config_interface( + disp_path, ASIC_LINK_INDEX); + + dal_ls_get_current_link_setting( + link, &hw_mode->link_settings); + } +} + +/* + * enable_output + * + * Enable output + */ +static void enable_output( + struct ds_dispatch *ds, + struct hw_path_mode_set *hw_mode_set) +{ + uint32_t turned_on_displays = 0; + + uint32_t i; + uint32_t num = dal_pms_with_data_get_path_mode_num(ds->set); + + for (i = 0; i != num; ++i) { + struct display_path *disp_path; + + uint32_t link_count; + uint32_t j; + + struct hw_path_mode *hw_mode = + dal_hw_path_mode_set_get_path_by_index( + hw_mode_set, + i); + const struct path_mode *mode = + dal_pms_with_data_get_path_mode_at_index(ds->set, i); + struct active_path_data *data = + dal_pms_with_data_get_path_data_at_index(ds->set, i); + + if (data->flags.bits.DISABLE_HW || + !data->flags.bits.POST_ACTION_DISPLAY_ON || + data->flags.bits.SKIP_ENABLE) { + continue; + } + + disp_path = dal_tm_display_index_to_display_path( + ds->tm, mode->display_path_index); + + link_count = dal_display_path_get_number_of_links(disp_path); + + if (!data->display_state.OUTPUT_ENABLED) { + for (j = 0; j < link_count; j++) { + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j); + + dal_ls_enable_stream( + link, + mode->display_path_index, + hw_mode); + } + } else if (data->display_state.OUTPUT_BLANKED) { + for (j = 0; j < link_count; j++) { + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j); + + dal_ls_post_mode_change( + link, + mode->display_path_index, + hw_mode); + } + } else { + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, ASIC_LINK_INDEX); + + dal_hw_sequencer_update_info_packets(ds->hwss, hw_mode); + + dal_ls_update_stream_features(link, hw_mode); + } + + if (!data->display_state.OUTPUT_ENABLED || + data->display_state.OUTPUT_BLANKED) { + for (j = 0; j < link_count; j++) { + + struct link_service *link = + dal_display_path_get_link_config_interface( + disp_path, j); + + dal_ls_unblank_stream( + link, + mode->display_path_index, + hw_mode); + } + + turned_on_displays |= + (1 << dal_display_path_get_display_index( + disp_path)); + + data->display_state.OUTPUT_BLANKED = 0; + data->display_state.OUTPUT_ENABLED = 1; + } + + data->flags.bits.POST_ACTION_DISPLAY_ON = false; + } + + /* Enable GTC embedding on audio stream if GTC feature isn't disabled */ + if (!dal_adapter_service_is_feature_supported( + FEATURE_DISABLE_DP_GTC_SYNC)) + enable_gtc_embedding(ds, hw_mode_set); + + /* After GTC enabled (or skipped if not needed) + * we could "hotplug" audio device */ + for (i = 0; i != num; ++i) { + const struct path_mode *mode = + dal_pms_with_data_get_path_mode_at_index(ds->set, i); + + if (turned_on_displays & (1 << mode->display_path_index)) + dal_hw_sequencer_enable_azalia_audio_jack_presence( + ds->hwss, + dal_tm_display_index_to_display_path( + ds->tm, mode->display_path_index)); + } + + for (i = 0; i != num; ++i) { + const struct path_mode *mode = + dal_pms_with_data_get_path_mode_at_index(ds->set, i); + + if (turned_on_displays & (1 << mode->display_path_index)) + dal_hw_sequencer_mute_audio_endpoint( + ds->hwss, + dal_tm_display_index_to_display_path( + ds->tm, mode->display_path_index), + false); + } +} + +/* + * post_mode_change_update + * + * Update notification and flags after set mode + */ +static void post_mode_change_update( + struct ds_dispatch *ds) +{ + uint32_t i; + + for (i = dal_pms_with_data_get_path_mode_num(ds->set); i > 0; i--) { + struct active_path_data *data = + dal_pms_with_data_get_path_data_at_index( + ds->set, i - 1); + + const struct path_mode *mode = + dal_pms_with_data_get_path_mode_at_index( + ds->set, + i - 1); + + ASSERT(data); + ASSERT(mode); + + if (data->flags.bits.DISABLE_HW) { + dal_tm_release_display_path( + ds->tm, mode->display_path_index); + dal_pms_with_data_remove_path_mode_at_index( + ds->set, + i - 1); + } else { + /* Need to restore this value after reset */ + bool display_on = + data->flags.bits.POST_ACTION_DISPLAY_ON; + + /* Reset flags */ + data->flags.bits.RESYNC_HW = false; + data->flags.bits.SYNC_TIMING_SERVER = false; + data->flags.bits.NO_DEFAULT_DOWN_SCALING = false; + + if (data->flags.bits.ENABLE_HW || + data->flags.bits.REPROGRAM_HW) { + data->flags.all = 0; + data->flags.bits.EXISTING = true; + } + + data->flags.bits.POST_ACTION_DISPLAY_ON = display_on; + } + } + + dal_tm_force_update_scratch_active_and_requested(ds->tm); +} + + +/* + * hw_mode_info_from_path_mode + * + * Convert path mode into HW path mode + */ +static void hw_mode_info_from_path_mode( + const struct ds_dispatch *ds_dispatch, + struct hw_mode_info *info, + struct display_path *display_path, + const struct path_mode *mode, + enum build_path_set_reason reason) +{ + convert_mode_info(ds_dispatch, info, display_path, mode, reason); + + /* TODO: Overlay implementation */ + + /* TODO: Display state container implementation */ + /* TODO: Set color space */ + + /* TODO: Set scaling info. Function for this should be ported and + * following 4 assignment should be removed */ + + info->color_space = dal_grph_colors_group_get_color_space( + ds_dispatch->grph_colors_adj, + &mode->mode_timing->crtc_timing, + display_path, + dal_ds_dispatch_get_adj_container_for_path( + ds_dispatch, + mode->display_path_index)); + + info->scaling_info.dst.height = + mode->mode_timing->mode_info.pixel_height; + info->scaling_info.dst.width = + mode->mode_timing->mode_info.pixel_width; + info->scaling_info.src = info->view; + + info->scaling_info.signal = + dal_display_path_get_config_signal( + display_path, SINK_LINK_INDEX); + + set_dithering_options(ds_dispatch, info, mode, display_path); +} + +/* + * update_ranged_timing_feature_preferences + * + * When there is a change in ranged timing feature preference, flags need to be + * updated. This may be a transition between VSYNC phase requirement where we + * are switching from programming ranged timing parameters from supporting + * DRR -> PSR. This may also be for test interface to force DRR disabled, in + * which case a flag should indicate ranged timing registers be programmed 0. + */ +static void update_ranged_timing_feature_preferences( + struct ds_dispatch *ds, + uint32_t display_path_index, + struct ranged_timing_preference_flags pref_flags) +{ + struct active_path_data *path_data = + dal_pms_with_data_get_path_data_for_display_index( + ds->set, + display_path_index); + + if (path_data != NULL) + path_data->ranged_timing_pref_flags.u32all = pref_flags.u32all; +} + +/* + * + * tune_up_timing + * Tune up timing + */ +static void tune_up_timing( + struct ds_dispatch *ds, + struct display_path *display_path, + struct hw_path_mode *hw_mode) +{ + struct timing_limits timing_limits; + + if (dal_dcs_get_timing_limits(dal_display_path_get_dcs(display_path), + &timing_limits)) { + struct pixel_clock_safe_range pixel_clock_safe_range; + struct ranged_timing_preference_flags pref_flags; + struct active_path_data *path_data; + uint32_t display_index = dal_display_path_get_display_index( + display_path); + + /* Get requested limits */ + if (dal_display_path_get_pixel_clock_safe_range( + display_path, + &pixel_clock_safe_range)) { + if (timing_limits.min_pixel_clock_in_khz < + pixel_clock_safe_range.min_frequency) { + timing_limits.min_pixel_clock_in_khz = + pixel_clock_safe_range.min_frequency; + } + if (timing_limits.max_pixel_clock_in_khz > + pixel_clock_safe_range.max_frequency) { + timing_limits.max_pixel_clock_in_khz = + pixel_clock_safe_range.max_frequency; + } + } else { + /* Not a safe pixel clock, clear max pixel clock and + * min pixel clock. tune_up_timing function will not + * tune timing for safe pixel clock feature, but may + * still need to tune timing for DRR feature. */ + timing_limits.min_pixel_clock_in_khz = 0; + timing_limits.max_pixel_clock_in_khz = 0; + } + + /* During reset of path, reset ranged timing flags. */ + pref_flags.u32all = 0; + if (hw_mode->action == HW_PATH_ACTION_RESET) { + update_ranged_timing_feature_preferences( + ds, + display_index, + pref_flags); + } + + /* Use flags from active_path_data if exists, otherwise flags + * will not be set and default behaviour will be used while + * building ranged timing. */ + path_data = + dal_pms_with_data_get_path_data_for_display_index( + ds->set, + display_index); + if (path_data != NULL) { + pref_flags.u32all = + path_data->ranged_timing_pref_flags.u32all; + } + + dal_ds_calculation_setup_ranged_timing( + &hw_mode->mode.timing, + display_path, + pref_flags); + dal_ds_calculation_tuneup_timing( + &hw_mode->mode.timing, + &timing_limits); + } +} + +/* + * build_adjustment_set + * + * Build a set of adjustments + */ +static bool build_adjustment_set( + struct ds_dispatch *ds, + struct hw_path_mode *hw_mode, + const struct path_mode *mode, + struct display_path *display_path, + enum build_path_set_reason reason) +{ + struct adj_container *container; + struct hw_adjustment_set *set = NULL; + + hw_mode->adjustment_set = NULL; + dal_ds_dispatch_update_adj_container_for_path_with_mode_info( + ds, + display_path, + mode); + container = dal_ds_dispatch_get_adj_container_for_path( + ds, + mode->display_path_index); + dal_ds_dispatch_apply_scaling( + ds, + mode, + container, + reason, + hw_mode); + + if (reason == BUILD_PATH_SET_REASON_SET_MODE) { + set = dal_alloc(sizeof(*set)); + if (set == NULL) + return false; + dal_ds_dispatch_build_include_adj( + ds, + mode, + display_path, + hw_mode, + set); + if (hw_mode->action == HW_PATH_ACTION_SET) + dal_ds_dispatch_build_post_set_mode_adj( + ds, + mode, + display_path, + set); + dal_ds_dispatch_build_color_control_adj( + ds, + mode, + display_path, + set); + } + hw_mode->adjustment_set = set; + + return true; +} + +/* + * setup_additional_parameters + * + * Set up additional parameters for HW path mode + */ +static void setup_additional_parameters( + const struct path_mode *mode, + struct hw_path_mode *hw_mode) +{ + hw_mode->mode.ds_info.original_timing = hw_mode->mode.timing; + hw_mode->mode.ds_info.DISPLAY_PREFERED_MODE = + mode->mode_timing->mode_info.flags.PREFERRED; + hw_mode->mode.underscan_rule = HW_SCALE_OPTION_UNKNOWN; +} + +/* + * set_dithering_options + * + * Set dithering + */ +static void set_dithering_options( + const struct ds_dispatch *ds_dispatch, + struct hw_mode_info *info, + const struct path_mode *mode, + struct display_path *display_path) +{ + enum signal_type signal; + + /* Check for dithering restrictions + * 1. only digital, except LVDS/eDP which is handled by VBIOS + * 2. since surface output stream is always in 10bpc, + * dithering is only required for 6 and 8 bpc + * 3. dithering can be applied only for RGB and YCbCr444 + * 4. packed pixel formats can't use dithering */ + + if ((mode->mode_timing->crtc_timing.pixel_encoding != + PIXEL_ENCODING_YCBCR422) && + (mode->mode_timing->crtc_timing.display_color_depth < + DISPLAY_COLOR_DEPTH_101010) && + (dal_dcs_get_enabled_packed_pixel_format( + dal_display_path_get_dcs(display_path)) == + DCS_PACKED_PIXEL_FORMAT_NOT_PACKED)) + info->dithering = HW_DITHERING_OPTION_ENABLE; + else + info->dithering = HW_DITHERING_OPTION_SKIP_PROGRAMMING; + + + signal = dal_display_path_get_config_signal( + display_path, ASIC_LINK_INDEX); + + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + if (dal_adapter_service_is_feature_supported( + FEATURE_TMDS_DISABLE_DITHERING)) + info->dithering = HW_DITHERING_OPTION_DISABLE; + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + if (dal_adapter_service_is_feature_supported( + FEATURE_DP_DISABLE_DITHERING)) + info->dithering = HW_DITHERING_OPTION_DISABLE; + break; + case SIGNAL_TYPE_DVO: + case SIGNAL_TYPE_DVO24: + /* do nothing since dithering option is already set to enable */ + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + if ((get_active_timing_3d_format( + mode->mode_timing->crtc_timing.timing_3d_format, + mode->view_3d_format) == + TIMING_3D_FORMAT_SW_FRAME_PACKING) || + dal_adapter_service_is_feature_supported( + FEATURE_HDMI_DISABLE_DITHERING)) + info->dithering = HW_DITHERING_OPTION_DISABLE; + break; + case SIGNAL_TYPE_WIRELESS: + /* Enabling dithering will affect VCE bitrate management + * due to randomness of pixel data, so we should disable it */ + info->dithering = HW_DITHERING_OPTION_DISABLE; + break; + case SIGNAL_TYPE_LVDS: + case SIGNAL_TYPE_EDP: + if (dal_adapter_service_is_feature_supported( + FEATURE_EMBEDDED_DISABLE_DITHERING)) + info->dithering = HW_DITHERING_OPTION_SKIP_PROGRAMMING; + break; + default: + /* Dithering should be applied (usually due to incompatible + * or unsupported signal). + * Analog signal does not support dithering, and, + * since LVDS/eDP are handled by VBIOS, + * driver should not touch Formatter at all. + * Therefore, skip programming. */ + info->dithering = HW_DITHERING_OPTION_SKIP_PROGRAMMING; + break; + } +} + +static void patch_hw_view_for_3d( + struct view *view, + const struct crtc_timing *crtc_timing, + enum view_3d_format view_3d_format) +{ + if (get_active_timing_3d_format( + crtc_timing->timing_3d_format, view_3d_format) == + TIMING_3D_FORMAT_SW_FRAME_PACKING) { + ASSERT(view->height == crtc_timing->v_addressable); + view->height = + crtc_timing->v_total + crtc_timing->v_addressable; + } +} + +/* + * convert_mode_info + * + * Helper to convert path mode into hw path mode + */ +static void convert_mode_info( + const struct ds_dispatch *ds, + struct hw_mode_info *info, + struct display_path *display_path, + const struct path_mode *mode, + enum build_path_set_reason reason) +{ + enum signal_type asic_signal; + enum scaling_transformation scl_type; + + info->view.height = mode->view.height; + info->view.width = mode->view.width; + + patch_hw_view_for_3d( + &info->view, + &mode->mode_timing->crtc_timing, + mode->view_3d_format); + + info->refresh_rate = mode->mode_timing->mode_info.field_rate; + + info->pixel_format = mode->pixel_format; + + info->tiling_mode = mode->tiling_mode; + + info->is_tiling_rotated = mode->is_tiling_rotated; + info->rotation = mode->rotation_angle; + + /* temporary */ + info->ds_info.cea_vic = mode->mode_timing->crtc_timing.vic; + + /* should be updated by wrapping function */ + info->color_space = HW_COLOR_SPACE_UNKNOWN; + + asic_signal = dal_display_path_get_config_signal( + display_path, ASIC_LINK_INDEX); + + hw_crtc_timing_from_crtc_timing( + &info->timing, + &mode->mode_timing->crtc_timing, + mode->view_3d_format, + asic_signal); + + setup_hw_stereo_mixer_params( + info, + &mode->mode_timing->crtc_timing, + mode->view_3d_format); + + scl_type = mode->scaling; + + /* TODO: add DTO timing processing and scl_type change */ + + /*build scaler overscan parameters for new_mode*/ + build_scaling_params(ds, mode, scl_type, info); +} + +static enum timing_3d_format get_active_timing_3d_format( + enum timing_3d_format timing_3d_format, + enum view_3d_format view_3d_format) +{ + return (view_3d_format != timing_3d_format_to_view_3d_format( + timing_3d_format)) ? TIMING_3D_FORMAT_NONE : timing_3d_format; +} + +/* + * hw_crtc_timing_from_crtc_timing + * + * Convert CRTC timing into HW format + */ +static void hw_crtc_timing_from_crtc_timing( + struct hw_crtc_timing *hw_timing, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format, + enum signal_type signal) +{ + uint32_t pixel_repetition = timing->flags.PIXEL_REPETITION == 0 ? + 1 : timing->flags.PIXEL_REPETITION; + + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch - timing->flags.INTERLACE; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + + enum timing_3d_format timing_3d_format; + + hw_timing->h_total = timing->h_total / pixel_repetition; + hw_timing->h_addressable = timing->h_addressable / pixel_repetition; + hw_timing->h_overscan_left = timing->h_border_left / pixel_repetition; + hw_timing->h_overscan_right = timing->h_border_right / pixel_repetition; + hw_timing->h_sync_start = (timing->h_addressable + hsync_offset) / + pixel_repetition; + hw_timing->h_sync_width = timing->h_sync_width / pixel_repetition; + + hw_timing->v_total = timing->v_total; + hw_timing->v_addressable = timing->v_addressable; + hw_timing->v_overscan_top = timing->v_border_top; + hw_timing->v_overscan_bottom = timing->v_border_bottom; + hw_timing->v_sync_start = timing->v_addressable + vsync_offset; + hw_timing->v_sync_width = timing->v_sync_width; + + hw_timing->pixel_clock = timing->pix_clk_khz; + + hw_timing->flags.INTERLACED = timing->flags.INTERLACE; + hw_timing->flags.DOUBLESCAN = timing->flags.DOUBLESCAN; + hw_timing->flags.PIXEL_REPETITION = pixel_repetition; + hw_timing->flags.HSYNC_POSITIVE_POLARITY = + timing->flags.HSYNC_POSITIVE_POLARITY; + hw_timing->flags.VSYNC_POSITIVE_POLARITY = + timing->flags.VSYNC_POSITIVE_POLARITY; + hw_timing->flags.RIGHT_EYE_3D_POLARITY = + timing->flags.RIGHT_EYE_3D_POLARITY; + hw_timing->flags.PACK_3D_FRAME = false; + hw_timing->flags.HIGH_COLOR_DL_MODE = false; + hw_timing->flags.Y_ONLY = timing->flags.YONLY; + + /* Note: converting between two different enums directly */ + hw_timing->flags.COLOR_DEPTH = timing->display_color_depth; + hw_timing->flags.PIXEL_ENCODING = timing->pixel_encoding; + hw_timing->timing_standard = timing->timing_standard; + + if (signal == SIGNAL_TYPE_DVI_DUAL_LINK && + timing->display_color_depth >= DISPLAY_COLOR_DEPTH_101010) { + if (hw_timing->pixel_clock > DVI_10BIT_THRESHOLD_RATE_IN_KHZ) + hw_timing->pixel_clock *= 2; + + hw_timing->flags.HIGH_COLOR_DL_MODE = true; + } + + /* Adjust HW timing for 3D Frame Packing format, + * according to HDMI specs: + * 1. 3D pixel clock frequency is x2 of 2D pixel clock frequency + * 2. 3D vertical total lines is x2 of 2D vertical total lines + * 3. 3D horizontal total pixels is equal to 2D horizontal total pixels + */ + + timing_3d_format = get_active_timing_3d_format( + timing->timing_3d_format, view_3d_format); + + switch (timing_3d_format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + /* HW will adjust image size. + * Here we need only adjust pixel clock */ + hw_timing->pixel_clock *= 2; + hw_timing->flags.PACK_3D_FRAME = true; + break; + case TIMING_3D_FORMAT_SW_FRAME_PACKING: { + /* HW does not support frame packing. + * Need to adjust image and pixel clock */ + uint32_t blank_region = + hw_timing->v_total - hw_timing->v_addressable; + + hw_timing->v_total *= 2; + hw_timing->v_addressable = hw_timing->v_total - blank_region; + hw_timing->v_sync_start = + hw_timing->v_addressable + vsync_offset; + hw_timing->pixel_clock *= 2; + break; + } + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + /* When we try to set frame packing with a HDMI display + * which is connected via active DP-HDMI dongle, + * we want to use HDMI frame packing, + * so the pixel clock is doubled. */ + hw_timing->pixel_clock *= 2; + break; + default: + break; + } + + /* + * Initialize ranged_timing for DRR/Freesync + */ + hw_timing->ranged_timing.vertical_total_min = 0; + hw_timing->ranged_timing.vertical_total_max = 0; + hw_timing->ranged_timing.control.force_lock_on_event = 0; + hw_timing->ranged_timing.control.lock_to_master_vsync = 0; +} + +/* + * setup_hw_stereo_mixer_params + * + * Setup HW stereo mixer parameters + */ +static void setup_hw_stereo_mixer_params( + struct hw_mode_info *info, + const struct crtc_timing *crtc_timing, + enum view_3d_format view_3d_format) +{ + enum timing_3d_format timing_3d_format = + get_active_timing_3d_format( + crtc_timing->timing_3d_format, view_3d_format); + + switch (timing_3d_format) { + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + info->stereo_format = HW_STEREO_FORMAT_ROW_INTERLEAVED; + info->stereo_mixer_params.sub_sampling = + crtc_timing->flags.SUB_SAMPLE_3D; + break; + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + info->stereo_format = HW_STEREO_FORMAT_COLUMN_INTERLEAVED; + info->stereo_mixer_params.sub_sampling = + crtc_timing->flags.SUB_SAMPLE_3D; + break; + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + info->stereo_format = HW_STEREO_FORMAT_CHECKER_BOARD; + info->stereo_mixer_params.sub_sampling = + crtc_timing->flags.SUB_SAMPLE_3D; + break; + default: + info->stereo_format = HW_STEREO_FORMAT_NONE; + break; + } + + /* If we have SBS_AppPacked OR TB_AppPacked timing on Frame Sequential + * view format, then we know that single pipe is enabled and hence we + * store the format/mode based on the timing. */ + if (view_3d_format == VIEW_3D_FORMAT_FRAME_SEQUENTIAL) { + switch (timing_3d_format) { + case TIMING_3D_FORMAT_SIDE_BY_SIDE: + info->stereo_format = HW_STEREO_FORMAT_SIDE_BY_SIDE; + info->stereo_mixer_params.single_pipe = true; + break; + case TIMING_3D_FORMAT_TOP_AND_BOTTOM: + info->stereo_format = HW_STEREO_FORMAT_TOP_AND_BOTTOM; + info->stereo_mixer_params.single_pipe = true; + break; + default: + break; + } + } /* if() */ +} + +static enum view_3d_format timing_3d_format_to_view_3d_format( + enum timing_3d_format timing_3d_format) +{ + switch (timing_3d_format) { + case TIMING_3D_FORMAT_SIDEBAND_FA: + case TIMING_3D_FORMAT_INBAND_FA: + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + case TIMING_3D_FORMAT_FRAME_ALTERNATE: + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + return VIEW_3D_FORMAT_FRAME_SEQUENTIAL; + case TIMING_3D_FORMAT_SBS_SW_PACKED: + return VIEW_3D_FORMAT_SIDE_BY_SIDE; + case TIMING_3D_FORMAT_TB_SW_PACKED: + return VIEW_3D_FORMAT_TOP_AND_BOTTOM; + default: + return VIEW_3D_FORMAT_NONE; + } +} + +static bool validate_stereo_3d_format( + struct display_path *display_path, + const struct crtc_timing *crtc_timing, + enum view_3d_format view_3d_format) +{ + enum timing_3d_format timing_3d_format = get_active_timing_3d_format( + crtc_timing->timing_3d_format, view_3d_format); + + enum signal_type signal = dal_display_path_get_query_signal( + display_path, SINK_LINK_INDEX); + + switch (timing_3d_format) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + /* Frame packing is defined only by DP and HDMI specs */ + return dal_is_hdmi_signal(signal) || dal_is_dp_signal(signal); + case TIMING_3D_FORMAT_SBS_SW_PACKED: + case TIMING_3D_FORMAT_TB_SW_PACKED: + /* Driver supports only HDMI signaling for these formats */ + return dal_is_hdmi_signal(signal); + default: + return true; + } +} + +static void enable_gtc_embedding( + struct ds_dispatch *ds_dispatch, + struct hw_path_mode_set *hw_mode_set) +{ + /* TODO implement */ +} + + +/* + * is_gamut_change_required + * + * @brief + * Check if gamut needs reprogramming + * + * @param + * enum pixel_encoding pixel_encoding: [in] pixel encoding + * enum pixel_format pixel_format: [in] pixel format + * uint32_t disp_index: [in] display index + * + * @return + * True if gamut needs to be reprogrammed, false otherwise + */ +bool dal_ds_dispatch_is_gamut_change_required( + struct ds_dispatch *ds_dispatch, + enum pixel_encoding pixel_encoding, + enum pixel_format pixel_format, + uint32_t disp_index) +{ + /* TODO: Implement adjustment */ + return false; +} + +static void destruct_adjustment_set(struct hw_adjustment_set *set) +{ + if (set->backlight != NULL) + dal_free(set->backlight); + if (set->bit_depth != NULL) + dal_free(set->bit_depth); + if (set->coherent != NULL) + dal_free(set->coherent); + if (set->color_control != NULL) + dal_free(set->color_control); + if (set->composite_sync != NULL) + dal_free(set->composite_sync); + if (set->deflicker_filter != NULL) + dal_free(set->deflicker_filter); + if (set->gamma_ramp != NULL) + dal_free(set->gamma_ramp); + if (set->h_sync != NULL) + dal_free(set->h_sync); + if (set->v_sync != NULL) + dal_free(set->v_sync); + if (set->vb_level != NULL) + dal_free(set->vb_level); +} + +static void destroy_adjustment_set( + struct hw_adjustment_set **set) +{ + if (set == NULL || *set == NULL) + return; + destruct_adjustment_set(*set); + dal_free(*set); + *set = NULL; +} + +static void send_wireless_setmode_end_event( + struct ds_dispatch *ds, + const struct path_mode_set *path_mode_set) +{ + uint32_t i; + uint32_t path_mode_num = dal_pms_get_path_mode_num(path_mode_set); + + for (i = 0; i < path_mode_num; i++) { + const struct path_mode *path_mode_in = + dal_pms_get_path_mode_at_index( + path_mode_set, i); + uint32_t disp_index = path_mode_in->display_path_index; + + struct display_path *disp_path = NULL; + enum signal_type signal; + + disp_path = dal_tm_display_index_to_display_path( + ds->tm, + disp_index); + + signal = dal_display_path_get_active_signal( + disp_path, SINK_LINK_INDEX); + + if (SIGNAL_TYPE_WIRELESS == signal) { + + const struct mode_timing *mode_timing = + path_mode_in->mode_timing; + + const struct crtc_timing *crtc_timing = + &mode_timing->crtc_timing; + + uint32_t h_active = crtc_timing->h_addressable + + crtc_timing->h_border_left + + crtc_timing->h_border_right; + + uint32_t v_active = crtc_timing->v_addressable + + crtc_timing->v_border_top + + crtc_timing->v_border_bottom; + + + dal_notify_setmode_complete( + ds->dal_context, + crtc_timing->h_total, + crtc_timing->v_total, + h_active, + v_active, + crtc_timing->pix_clk_khz); + } + } +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_planes.c b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_planes.c new file mode 100644 index 000000000000..920a2b287cf0 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_dispatch_planes.c @@ -0,0 +1,133 @@ +/* + * Copyright 2015 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 */ +#include "dal_services.h" + +#include "include/set_mode_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/hw_path_mode_set_interface.h" +#include "include/topology_mgr_interface.h" + +#include "ds_dispatch.h" + + +enum ds_return dal_ds_dispatch_setup_plane_configurations( + struct ds_dispatch *ds, + uint32_t display_index, + uint32_t planes_num, + const struct plane_config *configs) +{ + enum ds_return result = DS_SUCCESS; + uint32_t path_num = dal_pms_with_data_get_path_mode_num(ds->set); + struct hw_path_mode_set *hw_mode_set = dal_hw_path_mode_set_create(); + + if (!hw_mode_set) { + BREAK_TO_DEBUGGER(); + return DS_ERROR; + } + + /* Convert path mode set into HW path mode set for HWSS + * This is needed for dal_hw_sequencer_prepare_to_release_planes + * to get hw_mode_set*/ + if (false == dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index( + ds->set, + 0), + hw_mode_set, + BUILD_PATH_SET_REASON_SET_MODE, + NULL)) { + /* error */ + result = DS_ERROR; + } + + /* HW programming */ + if (DS_SUCCESS == result) { + /* clean-up part */ + dal_hw_sequencer_prepare_to_release_planes( + ds->hwss, + hw_mode_set, + display_index); + dal_tm_release_plane_resources( + ds->tm, + display_index); + dal_pms_with_data_clear_plane_configs(ds->set, display_index); + + dal_tm_acquire_plane_resources( + ds->tm, + display_index, + planes_num, + configs); + + dal_pms_with_data_add_plane_configs( + ds->set, + display_index, + configs, + planes_num); + + /*Because configs may be update, hw_mode_set needs + *to be re-built*/ + if (false == dal_ds_dispatch_build_hw_path_set( + ds, + path_num, + dal_pms_with_data_get_path_mode_at_index( + ds->set, + 0), + hw_mode_set, + BUILD_PATH_SET_REASON_GET_ACTIVE_PATHS, + NULL)) { + /* error */ + result = DS_ERROR; + } + + if (DS_SUCCESS == result) + if (dal_hw_sequencer_set_plane_config( + ds->hwss, + hw_mode_set, + display_index) != HWSS_RESULT_OK) + result = DS_ERROR; + } + + dal_ds_dispatch_destroy_hw_path_set(hw_mode_set); + + return result; +} + +bool dal_ds_dispatch_validate_plane_configurations( + struct ds_dispatch *ds, + uint32_t num_planes, + const struct plane_config *pl_configs, + bool *supported) +{ + uint32_t i; + + /* For now, we just return true for all planes */ + for (i = 0; i < num_planes; i++) + supported[i] = true; + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_overlay.c b/drivers/gpu/drm/amd/dal/display_service/ds_overlay.c new file mode 100644 index 000000000000..6b5aa11d88b0 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_overlay.c @@ -0,0 +1,202 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/overlay_interface.h" + + +bool dal_ds_overlay_is_active( + struct ds_overlay *ovl, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return false; +} + +uint32_t dal_ds_overlay_get_controller_handle( + struct ds_overlay *ovl, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return 0; +} + +enum ds_return dal_ds_overlay_alloc( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index, + struct view *view, + struct overlay_data *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_overlay_validate( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index, + struct view *view, + struct overlay_data *data) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_overlay_free( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_overlay_get_info( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_color_space *color_space, + enum overlay_backend_bpp *backend_bpp, + enum overlay_alloc_option *alloc_option, + enum overlay_format *surface_format) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_overlay_set_otm( + struct ds_overlay *ovl, + uint32_t display_index, + const struct path_mode *current_path_mode) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +enum ds_return dal_ds_overlay_reset_otm( + struct ds_overlay *ovl, + uint32_t display_index, + struct path_mode **saved_path_mode) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +/**is in overlay theater mode*/ +bool dal_ds_overlay_is_in_otm( + struct ds_overlay *ovl, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return false; +} + +void dal_ds_overlay_set_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + const struct overlay_color_matrix *matrix) +{ + /*TODO: add implementation*/ +} + +void dal_ds_overlay_reset_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_csc_matrix_type type) +{ + /*TODO: add implementation*/ +} + +const struct overlay_color_matrix *dal_ds_overlay_get_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_csc_matrix_type type) +{ + /*TODO: add implementation*/ + return NULL; +} + +bool dal_ds_overlay_set_color_space( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_color_space space) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_overlay_get_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index, + enum display_pixel_encoding *pixel_encoding) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_overlay_set_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index, + enum display_pixel_encoding pixel_encoding) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_ds_overlay_reset_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index) +{ + /*TODO: add implementation*/ + return false; +} + +/*After Set Overlay Theatre Mode (OTM) on a display path, + * saving the passed setting of Gpu scaling option for later restore*/ +enum ds_return dal_ds_overlay_save_gpu_scaling_before_otm( + struct ds_overlay *ovl, + uint32_t display_index, + int32_t timing_sel_before_otm) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +/* After reset Overlay Theatre Mode (OTM) on a display path, + * returning the previous Gpu scaling option by SetOverlayTheatreMode*/ +enum ds_return dal_ds_overlay_get_gpu_scaling_before_otm( + struct ds_overlay *ovl, + uint32_t display_index, + int32_t *timing_sel_before_otm) +{ + /*TODO: add implementation*/ + return DS_ERROR; +} + +uint32_t dal_ds_overlay_get_num_of_allowed(struct ds_overlay *ovl) +{ + /*TODO: add implementation*/ + return 0; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_translation.c b/drivers/gpu/drm/amd/dal/display_service/ds_translation.c new file mode 100644 index 000000000000..5bc50040d6b5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_translation.c @@ -0,0 +1,560 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/hw_sequencer_types.h" + +#include "ds_translation.h" +#include "adjustment_types_internal.h" + +/* + * ds_translation_translate_content_type + * + * Translate adjustment value into supported display content type + * + * uint32_t val - adjustment value + * union display_content_type *support - supported display content type + * + * return void + */ +void dal_ds_translation_translate_content_type(uint32_t val, + enum display_content_type *support) +{ + switch (val) { + case DISPLAY_CONTENT_TYPE_GRAPHICS: + *support = DISPLAY_CONTENT_TYPE_GRAPHICS; + break; + + case DISPLAY_CONTENT_TYPE_PHOTO: + *support = DISPLAY_CONTENT_TYPE_PHOTO; + break; + + case DISPLAY_CONTENT_TYPE_CINEMA: + *support = DISPLAY_CONTENT_TYPE_CINEMA; + break; + + case DISPLAY_CONTENT_TYPE_GAME: + *support = DISPLAY_CONTENT_TYPE_GAME; + break; + + default: + *support = DISPLAY_CONTENT_TYPE_NO_DATA; + } +} + +/* Translate 3D format */ +enum timing_3d_format dal_ds_translation_get_active_timing_3d_format( + enum timing_3d_format timing_format, + enum view_3d_format view_format) +{ + enum view_3d_format target_format = + dal_ds_translation_3d_format_timing_to_view( + timing_format); + + if (target_format != view_format) + return TIMING_3D_FORMAT_NONE; + + return timing_format; +} + +/* Translate timing 3D format to view 3D format*/ +enum view_3d_format dal_ds_translation_3d_format_timing_to_view( + enum timing_3d_format timing_format) +{ + enum view_3d_format view_format = VIEW_3D_FORMAT_NONE; + + switch (timing_format) { + case TIMING_3D_FORMAT_SIDEBAND_FA: + case TIMING_3D_FORMAT_INBAND_FA: + case TIMING_3D_FORMAT_FRAME_ALTERNATE: + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + view_format = VIEW_3D_FORMAT_FRAME_SEQUENTIAL; + break; + + case TIMING_3D_FORMAT_SBS_SW_PACKED: + view_format = VIEW_3D_FORMAT_SIDE_BY_SIDE; + break; + + case TIMING_3D_FORMAT_TB_SW_PACKED: + view_format = VIEW_3D_FORMAT_TOP_AND_BOTTOM; + break; + + default: + break; + } + + return view_format; +} + +/* + * dal_ds_translation_patch_hw_view_for_3d + * + * Adjust HW view for 3D Frame Packing format (according to HDMI spec). + * This is relevant only 3D timing packed by SW. + * Also assumes if 3D timing packed by SW, then no scaling available. + * 1. NewVTotal = OldVTotal * 2 + * 2. NewVBlank = OldVBlank (reminder: VBlank includes borders) + * 3. NewView = NewVTotal - NewVBlank + * + */ +void dal_ds_translation_patch_hw_view_for_3d( + struct view *view, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format) +{ + + /* Adjust HW view for 3D Frame Packing format (according to HDMI spec) + * 1. NewVTotal = OldVTotal * 2 + * 2. NewVBlank = OldVBlank (reminder: VBlank includes borders) + * 3. NewView = NewVTotal - NewVBlank + */ + if (dal_ds_translation_get_active_timing_3d_format( + timing->timing_3d_format, + view_3d_format) == TIMING_3D_FORMAT_SW_FRAME_PACKING) { + uint32_t blank_region = timing->v_total - + timing->v_addressable; + ASSERT(view->height == timing->v_addressable); + view->height = (timing->v_total * 2) - blank_region; + } +} + +#define DVI_10_BIT_TRESHOLD_RATE_IN_KHZ 50000 + +/* + * dal_ds_translation_hw_crtc_timing_from_crtc_timing + * + * Converts SW layer timing structure into HW layer timing structure. + * additional input parameters maybe present for proper conversion. + * + */ +void dal_ds_translation_hw_crtc_timing_from_crtc_timing( + struct hw_crtc_timing *hw_timing, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format, + enum signal_type signal) +{ + enum timing_3d_format timing_3d_format; + uint32_t pixel_repetition = + timing->flags.PIXEL_REPETITION == 0 ? + 1 : timing->flags.PIXEL_REPETITION; + + /* HW expects FrontPorch - 1 for interlaced modes */ + uint32_t vsync_offset = timing->v_border_bottom + + (timing->v_front_porch - timing->flags.INTERLACE); + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + + hw_timing->h_total = timing->h_total / pixel_repetition; + hw_timing->h_addressable = timing->h_addressable / pixel_repetition; + hw_timing->h_overscan_left = timing->h_border_left / pixel_repetition; + hw_timing->h_overscan_right = timing->h_border_right / pixel_repetition; + hw_timing->h_sync_start = (timing->h_addressable + hsync_offset) / + pixel_repetition; + hw_timing->h_sync_width = timing->h_sync_width / pixel_repetition; + + hw_timing->v_total = timing->v_total; + hw_timing->v_addressable = timing->v_addressable; + hw_timing->v_overscan_top = timing->v_border_top; + hw_timing->v_overscan_bottom = timing->v_border_bottom; + hw_timing->v_sync_width = timing->v_sync_width; + hw_timing->v_sync_start = timing->v_addressable + vsync_offset; + + hw_timing->pixel_clock = timing->pix_clk_khz; + + /* flags */ + hw_timing->flags.INTERLACED = timing->flags.INTERLACE; + hw_timing->flags.DOUBLESCAN = timing->flags.DOUBLESCAN; + hw_timing->flags.PIXEL_REPETITION = pixel_repetition; + hw_timing->flags.HSYNC_POSITIVE_POLARITY = + timing->flags.HSYNC_POSITIVE_POLARITY; + hw_timing->flags.VSYNC_POSITIVE_POLARITY = + timing->flags.VSYNC_POSITIVE_POLARITY; + hw_timing->flags.RIGHT_EYE_3D_POLARITY = + timing->flags.RIGHT_EYE_3D_POLARITY; + hw_timing->flags.PACK_3D_FRAME = false; + hw_timing->flags.HIGH_COLOR_DL_MODE = false; + hw_timing->flags.Y_ONLY = timing->flags.YONLY; + + /* below work only because HW def is clone of TS def. + * need translation to make this robust */ + hw_timing->flags.COLOR_DEPTH = + (enum hw_color_depth) timing->display_color_depth; + hw_timing->flags.PIXEL_ENCODING = + (enum hw_pixel_encoding) timing->pixel_encoding; + hw_timing->timing_standard = + (enum hw_timing_standard) timing->timing_standard; + + /* Adjust HW timing for DVI DualLink 10bit. For low clocks we do not + * double pixel rate */ + if (signal == SIGNAL_TYPE_DVI_DUAL_LINK && + timing->display_color_depth >= DISPLAY_COLOR_DEPTH_101010) { + if (hw_timing->pixel_clock > DVI_10_BIT_TRESHOLD_RATE_IN_KHZ) + hw_timing->pixel_clock *= 2; + + hw_timing->flags.HIGH_COLOR_DL_MODE = true; + } + + /* Adjust HW timing for 3D Frame Packing format (according to HDMI spec) + * 1. 3D pixel clock frequency is x2 of 2D pixel clock frequency + * 2. 3D vertical total line is x2 of 2D vertical total line + * 3. 3D horizontal total pixel is equal to 2D horizontal total pixel + */ + timing_3d_format = dal_ds_translation_get_active_timing_3d_format( + timing->timing_3d_format, + view_3d_format); + switch (timing_3d_format) { + /* HW will adjust image size. Here we need only adjust pixel clock. + * Otherwise we need to do it as following: */ + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + hw_timing->pixel_clock *= 2; + hw_timing->flags.PACK_3D_FRAME = true; + break; + + /* HW does not support frame packing. Need to adjust image and + * pixel clock + * 1. NewVTotal = OldVTotal * 2 + * 2. NewVBlank = OldVBlank (reminder: VBlank includes borders) + * 2. NewVSyncOffset = OldVSyncOffset + * 4. NewVAddressable = NewVTotal - NewVBlank + * 5. NewVSyncStart = NewVAddressable - NewVSyncOffset + */ + case TIMING_3D_FORMAT_SW_FRAME_PACKING: { + uint32_t blank_region = hw_timing->v_total - + hw_timing->v_addressable; + hw_timing->v_total *= 2; + hw_timing->v_addressable = hw_timing->v_total - + blank_region; + hw_timing->v_sync_start = hw_timing->v_addressable + + vsync_offset; + hw_timing->pixel_clock *= 2; + + break; + } + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + /* When we try to set frame packing with a HDMI display that is + * connected via active DP-HDMI dongle, we want to use HDMI + * frame packing, so the pixel clock is doubled */ + hw_timing->pixel_clock *= 2; + break; + + default: + break; + } +} + +/* +* dal_ds_translation_setup_hw_stereo_mixer_params +* +* Setups Stereo Mixer parameters for HW sequencer +* +*/ +void dal_ds_translation_setup_hw_stereo_mixer_params( + struct hw_mode_info *hw_mode, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format) +{ + enum timing_3d_format timing_3d_format = + dal_ds_translation_get_active_timing_3d_format( + timing->timing_3d_format, view_3d_format); + + switch (timing_3d_format) { + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + hw_mode->stereo_format = HW_STEREO_FORMAT_ROW_INTERLEAVED; + hw_mode->stereo_mixer_params.sub_sampling = + timing->flags.SUB_SAMPLE_3D; + break; + + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + hw_mode->stereo_format = HW_STEREO_FORMAT_COLUMN_INTERLEAVED; + hw_mode->stereo_mixer_params.sub_sampling = + timing->flags.SUB_SAMPLE_3D; + break; + + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + hw_mode->stereo_format = HW_STEREO_FORMAT_CHECKER_BOARD; + hw_mode->stereo_mixer_params.sub_sampling = + timing->flags.SUB_SAMPLE_3D; + break; + + default: + hw_mode->stereo_format = HW_STEREO_FORMAT_NONE; + break; + } +} + +enum hw_color_space dal_ds_translation_hw_color_space_from_color_space( + enum ds_color_space color_space) +{ + enum hw_color_space hw_color_space; + + switch (color_space) { + case DS_COLOR_SPACE_SRGB_FULLRANGE: + hw_color_space = HW_COLOR_SPACE_SRGB_FULL_RANGE; + break; + case DS_COLOR_SPACE_SRGB_LIMITEDRANGE: + hw_color_space = HW_COLOR_SPACE_SRGB_LIMITED_RANGE; + break; + case DS_COLOR_SPACE_YPBPR601: + hw_color_space = HW_COLOR_SPACE_YPBPR601; + break; + case DS_COLOR_SPACE_YPBPR709: + hw_color_space = HW_COLOR_SPACE_YPBPR709; + break; + case DS_COLOR_SPACE_YCBCR601: + hw_color_space = HW_COLOR_SPACE_YCBCR601; + break; + case DS_COLOR_SPACE_YCBCR709: + hw_color_space = HW_COLOR_SPACE_YCBCR709; + break; + case DS_COLOR_SPACE_NMVPU_SUPERAA: + hw_color_space = HW_COLOR_SPACE_NMVPU_SUPERAA; + break; + default: + hw_color_space = HW_COLOR_SPACE_UNKNOWN; + break; + } + return hw_color_space; +} + +enum ds_color_space dal_ds_translation_color_space_from_hw_color_space( + enum hw_color_space hw_color_space) +{ + enum ds_color_space color_space; + + switch (hw_color_space) { + case HW_COLOR_SPACE_SRGB_FULL_RANGE: + color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; + break; + case HW_COLOR_SPACE_SRGB_LIMITED_RANGE: + color_space = DS_COLOR_SPACE_SRGB_LIMITEDRANGE; + break; + case HW_COLOR_SPACE_YPBPR601: + color_space = DS_COLOR_SPACE_YPBPR601; + break; + case HW_COLOR_SPACE_YPBPR709: + color_space = DS_COLOR_SPACE_YPBPR709; + break; + case HW_COLOR_SPACE_YCBCR601: + color_space = DS_COLOR_SPACE_YCBCR601; + break; + case HW_COLOR_SPACE_YCBCR709: + color_space = DS_COLOR_SPACE_YCBCR709; + break; + case HW_COLOR_SPACE_NMVPU_SUPERAA: + color_space = DS_COLOR_SPACE_NMVPU_SUPERAA; + break; + default: + color_space = DS_COLOR_SPACE_UNKNOWN; + break; + } + return color_space; +} + +bool dal_ds_translate_regamma_to_external( + const struct ds_regamma_lut *gamma_int, + struct ds_regamma_lut *gamma_ext) +{ + uint32_t i; + + gamma_ext->flags.u32all = 0; + gamma_ext->flags.bits.GAMMA_RAMP_ARRAY = + gamma_int->flags.bits.GAMMA_RAMP_ARRAY; + gamma_ext->flags.bits.COEFF_FROM_EDID = + gamma_int->flags.bits.COEFF_FROM_EDID; + gamma_ext->flags.bits.GAMMA_FROM_EDID_EX = + gamma_int->flags.bits.GAMMA_FROM_EDID_EX; + gamma_ext->flags.bits.GAMMA_FROM_USER = + gamma_int->flags.bits.GAMMA_FROM_USER; + + gamma_ext->flags.bits.COEFF_FROM_USER = + gamma_int->flags.bits.COEFF_FROM_USER; + gamma_ext->flags.bits.COEFF_FROM_EDID = + gamma_int->flags.bits.COEFF_FROM_EDID; + + if (gamma_int->flags.bits.GAMMA_RAMP_ARRAY == 1) { + gamma_ext->flags.bits.APPLY_DEGAMMA = + gamma_int->flags.bits.APPLY_DEGAMMA; + for (i = 0 ; i < REGAMMA_RANGE ; i++) + gamma_ext->gamma.gamma[i] = + gamma_int->gamma.gamma[i]; + } else { + gamma_ext->flags.bits.APPLY_DEGAMMA = 0; + for (i = 0; i < COEFF_RANGE ; i++) { + gamma_ext->coeff.coeff_a0[i] = + gamma_int->coeff.coeff_a0[i]; + gamma_ext->coeff.coeff_a1[i] = + gamma_int->coeff.coeff_a1[i]; + gamma_ext->coeff.coeff_a2[i] = + gamma_int->coeff.coeff_a2[i]; + gamma_ext->coeff.coeff_a3[i] = + gamma_int->coeff.coeff_a3[i]; + gamma_ext->coeff.gamma[i] = + gamma_int->coeff.gamma[i]; + } + } + return true; + +} + +bool dal_ds_translate_regamma_to_internal( + const struct ds_regamma_lut *gamma_ext, + struct ds_regamma_lut *gamma_int) +{ + uint32_t i; + + gamma_int->flags.bits.GAMMA_RAMP_ARRAY = + gamma_ext->flags.bits.GAMMA_RAMP_ARRAY; + if (gamma_ext->flags.bits.GAMMA_FROM_EDID == 1 || + gamma_ext->flags.bits.GAMMA_FROM_EDID_EX == 1 || + gamma_ext->flags.bits.GAMMA_FROM_USER == 1) + return false; + + gamma_int->flags.bits.COEFF_FROM_EDID = + gamma_ext->flags.bits.COEFF_FROM_EDID; + gamma_int->flags.bits.GAMMA_FROM_EDID_EX = + gamma_ext->flags.bits.GAMMA_FROM_EDID_EX; + gamma_int->flags.bits.GAMMA_FROM_USER = + gamma_ext->flags.bits.GAMMA_FROM_USER; + + if (gamma_ext->flags.bits.COEFF_FROM_USER == 1 && + gamma_ext->flags.bits.COEFF_FROM_EDID == 1) + return false; + + gamma_int->flags.bits.COEFF_FROM_USER = + gamma_ext->flags.bits.COEFF_FROM_USER; + gamma_int->flags.bits.COEFF_FROM_EDID = + gamma_ext->flags.bits.COEFF_FROM_EDID; + + if (gamma_ext->flags.bits.GAMMA_RAMP_ARRAY == 1) { + gamma_int->flags.bits.APPLY_DEGAMMA = + gamma_ext->flags.bits.APPLY_DEGAMMA; + for (i = 0 ; i < REGAMMA_RANGE ; i++) + gamma_int->gamma.gamma[i] = + gamma_ext->gamma.gamma[i]; + } else { + gamma_int->flags.bits.APPLY_DEGAMMA = 0; + for (i = 0; i < COEFF_RANGE ; i++) { + gamma_int->coeff.coeff_a0[i] = + gamma_ext->coeff.coeff_a0[i]; + gamma_int->coeff.coeff_a1[i] = + gamma_ext->coeff.coeff_a1[i]; + gamma_int->coeff.coeff_a2[i] = + gamma_ext->coeff.coeff_a2[i]; + gamma_int->coeff.coeff_a3[i] = + gamma_ext->coeff.coeff_a3[i]; + gamma_int->coeff.gamma[i] = + gamma_ext->coeff.gamma[i]; + } + } + return true; +} + +bool dal_ds_translate_regamma_to_hw( + const struct ds_regamma_lut *regumma_lut, + struct hw_regamma_lut *regamma_lut_hw) +{ + bool ret = true; + uint32_t i = 0; + + regamma_lut_hw->flags.bits.gamma_ramp_array = + regumma_lut->flags.bits.GAMMA_RAMP_ARRAY; + regamma_lut_hw->flags.bits.graphics_degamma_srgb = + regumma_lut->flags.bits.GRAPHICS_DEGAMMA_SRGB; + regamma_lut_hw->flags.bits.overlay_degamma_srgb = + regumma_lut->flags.bits.OVERLAY_DEGAMMA_SRGB; + + if (regumma_lut->flags.bits.GAMMA_RAMP_ARRAY == 1) { + regamma_lut_hw->flags.bits.apply_degamma = + regumma_lut->flags.bits.APPLY_DEGAMMA; + + for (i = 0; i < 256 * 3; i++) + regamma_lut_hw->gamma.gamma[i] = regumma_lut->gamma.gamma[i]; + } else { + regamma_lut_hw->flags.bits.apply_degamma = 0; + + for (i = 0; i < 3; i++) { + regamma_lut_hw->coeff.a0[i] = + regumma_lut->coeff.coeff_a0[i]; + regamma_lut_hw->coeff.a1[i] = + regumma_lut->coeff.coeff_a1[i]; + regamma_lut_hw->coeff.a2[i] = + regumma_lut->coeff.coeff_a2[i]; + regamma_lut_hw->coeff.a3[i] = + regumma_lut->coeff.coeff_a3[i]; + regamma_lut_hw->coeff.gamma[i] = + regumma_lut->coeff.gamma[i]; + } + } + return ret; +} + +bool dal_ds_translate_internal_gamut_to_external_parameter( + const struct gamut_data *gamut, + struct ds_gamut_data *data) +{ + if (gamut->option.bits.CUSTOM_GAMUT_SPACE == 0) + data->gamut.predefined = gamut->gamut.predefined.u32all; + else { + data->feature.bits.CUSTOM_GAMUT_SPACE = 0; + data->gamut.custom.red_x = gamut->gamut.custom.red_x; + data->gamut.custom.red_y = gamut->gamut.custom.red_y; + + data->gamut.custom.green_x = gamut->gamut.custom.green_x; + data->gamut.custom.green_y = gamut->gamut.custom.green_y; + + data->gamut.custom.blue_x = gamut->gamut.custom.blue_x; + data->gamut.custom.blue_y = gamut->gamut.custom.blue_y; + } + if (gamut->option.bits.CUSTOM_WHITE_POINT == 0) + data->white_point.predefined = + gamut->white_point.predefined.u32all; + else { + data->feature.bits.CUSTOM_WHITE_POINT = 1; + data->white_point.custom.white_x = + gamut->white_point.custom.white_x; + data->white_point.custom.white_y = + gamut->white_point.custom.white_y; + } + return true; +} + +bool dal_ds_translate_gamut_reference( + const struct ds_gamut_reference_data *ref, + enum adjustment_id *adj_id) +{ + if (ref->gamut_ref == DS_GAMUT_REFERENCE_DESTINATION) + *adj_id = ADJ_ID_GAMUT_DESTINATION; + else { + if (ref->gamut_content == DS_GAMUT_CONTENT_GRAPHICS) + *adj_id = ADJ_ID_GAMUT_SOURCE_GRPH; + else + *adj_id = ADJ_ID_GAMUT_SOURCE_OVL; + } + return true; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/ds_translation.h b/drivers/gpu/drm/amd/dal/display_service/ds_translation.h new file mode 100644 index 000000000000..c314752bfd13 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/ds_translation.h @@ -0,0 +1,99 @@ +/* + * Copyright 2012-15 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 __DAL_DS_TRANSLATION_H__ +#define __DAL_DS_TRANSLATION_H__ + +#include "adjustment_types_internal.h" +#include "include/timing_service_types.h" +#include "include/set_mode_types.h" + +/* Translate adjustment value into supported display content type */ +void dal_ds_translation_translate_content_type(uint32_t val, + enum display_content_type *support); + +/* Get valid 3D format */ +enum timing_3d_format dal_ds_translation_get_active_timing_3d_format( + enum timing_3d_format timing_format, + enum view_3d_format view_format); + +/* Translate timing 3D format to view 3D format*/ +enum view_3d_format dal_ds_translation_3d_format_timing_to_view( + enum timing_3d_format timing_format); + +void dal_ds_translation_patch_hw_view_for_3d( + struct view *view, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format); + +void dal_ds_translation_hw_crtc_timing_from_crtc_timing( + struct hw_crtc_timing *hw_timing, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format, + enum signal_type signal); + +void dal_ds_translation_setup_hw_stereo_mixer_params( + struct hw_mode_info *hw_mode, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format); + +/*enum hw_pixel_format dal_ds_translate_hw_pixel_format_from_pixel_format( + const enum pixel_format pf);*/ + +enum underscan_reason { + UNDERSCAN_REASON_PATCH_TIMING, + UNDERSCAN_REASON_CHECK_STEP, + UNDERSCAN_REASON_SET_ADJUSTMENT, + UNDERSCAN_REASON_GET_INFO, + UNDERSCAN_REASON_PATCH_TIMING_SET_MODE, + UNDERSCAN_REASON_FALL_BACK, +}; + +enum hw_color_space dal_ds_translation_hw_color_space_from_color_space( + enum ds_color_space color_space); + +enum ds_color_space dal_ds_translation_color_space_from_hw_color_space( + enum hw_color_space hw_color_space); + +bool dal_ds_translate_regamma_to_external( + const struct ds_regamma_lut *gamma_int, + struct ds_regamma_lut *gamma_ext); + +bool dal_ds_translate_regamma_to_internal( + const struct ds_regamma_lut *gamma_ext, + struct ds_regamma_lut *gamma_int); + +bool dal_ds_translate_regamma_to_hw( + const struct ds_regamma_lut *regumma_lut, + struct hw_regamma_lut *regamma_lut_hw); + +bool dal_ds_translate_internal_gamut_to_external_parameter( + const struct gamut_data *gamut, + struct ds_gamut_data *data); + +bool dal_ds_translate_gamut_reference( + const struct ds_gamut_reference_data *ref, + enum adjustment_id *adj_id); + +#endif /* __DAL_DS_TRANSLATION_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/gamma_lut.c b/drivers/gpu/drm/amd/dal/display_service/gamma_lut.c new file mode 100644 index 000000000000..90af79cde6f6 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/gamma_lut.c @@ -0,0 +1,391 @@ +/*****************************************************************************\ + * Module Name: GammaLUT.cpp + * Project: DAL 2012 Rearchitecture + * Device: EG and later + * + * Description: Implementation of GammaLUT class + * + * Copyright (c) 2012 Advanced Micro Devices, Inc. (unpublished) + * + * All rights reserved. This notice is intended as a precaution against + * inadvertent publication and does not imply publication or any waiver + * of confidentiality. The year included in the foregoing notice is the + * year of creation of the work. + * + \*****************************************************************************/ + +#include "dal_services.h" +#include "display_service/ds_dispatch.h" +#include "display_service/ds_translation.h" +#include "display_service/grph_colors_group.h" +#include "include/hw_sequencer_interface.h" +#include "include/display_path_interface.h" +#include "include/adjustment_interface.h" +#include "gamma_lut.h" + +static bool grph_gamma_lut_group_construct( + struct grph_gamma_lut_group *grph_gamma_adj, + struct grph_gamma_lut_group_init_data *init_data) { + if (!init_data) + return false; + + grph_gamma_adj->ds = init_data->ds; + grph_gamma_adj->hws = init_data->hws; + grph_gamma_adj->dal_context = init_data->dal_context; + + return true; +} + +struct grph_gamma_lut_group *dal_gamma_adj_group_create( + struct grph_gamma_lut_group_init_data *init_data) { + struct grph_gamma_lut_group *grph_gamma_adj = NULL; + + grph_gamma_adj = dal_alloc(sizeof(*grph_gamma_adj)); + + if (!grph_gamma_adj) + return NULL; + + if (grph_gamma_lut_group_construct(grph_gamma_adj, init_data)) + return grph_gamma_adj; + + dal_free(grph_gamma_adj); + + return NULL; +} + +static bool update_internal_status( + struct ds_dispatch *ds, + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma) +{ + bool ret = false; + struct ds_adjustment_status *status = NULL; + + if (ds == NULL) + return ret; + + switch (adj_id) { + case ADJ_ID_GAMMA_RAMP: + status = &ds->grph_gamma_adj->status_gamma_ramp; + break; + case ADJ_ID_DRIVER_REQUESTED_GAMMA: + default: + break; + } + + if (status != NULL) { + status->bits.SET_TO_HARDWARE = 1; + ret = true; + } + + return ret; +} + +enum ds_return dal_grph_gamma_lut_set_adjustment( + struct ds_dispatch *ds, + const struct display_path *disp_path, + const struct path_mode *disp_path_mode, + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma, + const struct ds_regamma_lut *regumma_lut) { + + enum ds_return ret = DS_ERROR; + struct hw_adjustment_gamma_ramp *hw_gamma_ramp = NULL; + + if (gamma == NULL) + return ret; + + if (ds == NULL) + return ret; + + do { + /* TODO validate to compare if this gamma is already set! */ + if (disp_path == NULL) + break; + + if (!dal_gamma_lut_validate(adj_id, gamma, true)) + break; + + hw_gamma_ramp = dal_alloc( + sizeof(struct hw_adjustment_gamma_ramp)); + + if (hw_gamma_ramp == NULL) + break; + + if (adj_id == ADJ_ID_GAMMA_RAMP) + dal_gamma_lut_set_current_gamma( + ds, + ADJ_ID_DRIVER_REQUESTED_GAMMA, + gamma); + + dal_ds_translate_regamma_to_hw( + regumma_lut, + &hw_gamma_ramp->regamma); + + if (!dal_gamma_lut_translate_to_hw( + ds, disp_path_mode, + disp_path, + gamma, + hw_gamma_ramp)) + break; + + hw_gamma_ramp->flag.uint = 0; + hw_gamma_ramp->flag.bits.config_is_changed = 0; + + if (adj_id == ADJ_ID_GAMMA_RAMP_REGAMMA_UPDATE) + hw_gamma_ramp->flag.bits.regamma_update = 1; + else + hw_gamma_ramp->flag.bits.gamma_update = 1; + + if (dal_hw_sequencer_set_gamma_ramp_adjustment( + ds->hwss, + disp_path, + hw_gamma_ramp) != HWSS_RESULT_OK) + break; + + if (adj_id == ADJ_ID_GAMMA_RAMP) { + dal_gamma_lut_set_current_gamma(ds, adj_id, gamma); + update_internal_status(ds, adj_id, gamma); + } + + ret = DS_SUCCESS; + + } while (0); + + dal_free(hw_gamma_ramp); + return ret; +} + +bool dal_gamma_lut_validate( + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma, + bool validate_all) { + if (adj_id != ADJ_ID_DRIVER_REQUESTED_GAMMA + && adj_id != ADJ_ID_GAMMA_RAMP + && adj_id != ADJ_ID_GAMMA_RAMP_REGAMMA_UPDATE) + return false; + + if (!validate_all) + return true; + + if (gamma == NULL) + return false; + + if (gamma->type != GAMMA_RAMP_TYPE_RGB256 + && gamma->type != GAMMA_RAMP_TYPE_FIXED_POINT) + return false; + + if (gamma->type == GAMMA_RAMP_TYPE_RGB256 + && gamma->size != sizeof(gamma->rgb_256)) + return false; + + return true; +} + +bool dal_gamma_lut_translate_to_hw( + struct ds_dispatch *ds, + const struct path_mode *disp_path_mode, + const struct display_path *disp_path, + const struct raw_gamma_ramp *gamma_in, + struct hw_adjustment_gamma_ramp *gamma_out) { + unsigned int i; + enum pixel_format pix_format = disp_path_mode->pixel_format; + enum ds_color_space color_space = DS_COLOR_SPACE_UNKNOWN; + + uint32_t display_index; + struct adj_container *adj_container = NULL; + + if (!disp_path) + return false; + + display_index = dal_display_path_get_display_index(disp_path); + + adj_container = dal_ds_dispatch_get_adj_container_for_path(ds, + display_index); + + if (gamma_in == NULL) + return false; + + /* translate the PixelFormat */ + gamma_out->surface_pixel_format = pix_format; + + if (gamma_in->type != GAMMA_RAMP_TYPE_RGB256) + return false; + + gamma_out->type = HW_GAMMA_RAMP_RBG_256x3x16; + gamma_out->size = sizeof(gamma_out->gamma_ramp_rgb256x3x16); + + /* copy the rgb */ + for (i = 0; i < NUM_OF_RAW_GAMMA_RAMP_RGB_256; i++) { + gamma_out->gamma_ramp_rgb256x3x16.red[i] = + (unsigned short) (gamma_in->rgb_256[i].red); + gamma_out->gamma_ramp_rgb256x3x16.green[i] = + (unsigned short) (gamma_in->rgb_256[i].green); + gamma_out->gamma_ramp_rgb256x3x16.blue[i] = + (unsigned short) (gamma_in->rgb_256[i].blue); + } + + /* + * logic below builds the color space and it is used for color + * adjustments also + */ + color_space = dal_grph_colors_group_get_color_space( + ds->grph_colors_adj, + &disp_path_mode->mode_timing->crtc_timing, + disp_path, + adj_container); + + gamma_out->color_space = + dal_ds_translation_hw_color_space_from_color_space(color_space); + + return true; +} + +static bool get_parameters( + struct ds_dispatch *ds, + enum adjustment_id adj_id, + struct ds_adjustment_status **adjustment_status, + struct raw_gamma_ramp **gamma) +{ + struct ds_adjustment_status *status = NULL; + struct raw_gamma_ramp *ramp = NULL; + + if (ds == NULL) + return false; + + if (ds->grph_gamma_adj == NULL) + return false; + + switch (adj_id) { + case ADJ_ID_GAMMA_RAMP: + status = &ds->grph_gamma_adj->status_gamma_ramp; + ramp = &ds->grph_gamma_adj->gamma_ramp; + break; + case ADJ_ID_DRIVER_REQUESTED_GAMMA: + status = &ds->grph_gamma_adj->status_original_ramp; + ramp = &ds->grph_gamma_adj->oroginal_ramp; + break; + + default: + break; + } + + if (status != NULL && gamma != NULL) { + if (adjustment_status != NULL) + *adjustment_status = status; + + if (gamma != NULL) + *gamma = ramp; + + return true; + } + + return false; +} + +static bool generated_default_gamma_ramp( + struct ds_dispatch *ds, + enum adjustment_id adj_id) +{ + bool ret = false; + unsigned int i; + + struct raw_gamma_ramp_rgb *rgb256 = NULL; + struct ds_adjustment_status *status = NULL; + struct raw_gamma_ramp *gamma = NULL; + + if (ds == NULL) + return false; + + switch (adj_id) { + case ADJ_ID_GAMMA_RAMP: + gamma = &ds->grph_gamma_adj->gamma_ramp; + status = &ds->grph_gamma_adj->status_gamma_ramp; + rgb256 = ds->grph_gamma_adj->gamma_ramp.rgb_256; + ret = true; + break; + case ADJ_ID_DRIVER_REQUESTED_GAMMA: + gamma = &ds->grph_gamma_adj->oroginal_ramp; + status = &ds->grph_gamma_adj->status_original_ramp; + rgb256 = ds->grph_gamma_adj->oroginal_ramp.rgb_256; + ret = true; + break; + default: + break; + } + + if (ret) { + for (i = 0; i < NUM_OF_RAW_GAMMA_RAMP_RGB_256; ++i) { + rgb256[i].red = i << 8; + rgb256[i].green = i << 8; + rgb256[i].blue = i << 8; + } + status->val = 0; + status->bits.SET_TO_DEFAULT = 1; + gamma->type = GAMMA_RAMP_TYPE_RGB256; + gamma->size = sizeof(struct raw_gamma_ramp_rgb) + * NUM_OF_RAW_GAMMA_RAMP_RGB_256; + } + return ret; +} + +const struct raw_gamma_ramp *dal_gamma_lut_get_current_gamma( + struct ds_dispatch *ds, + enum adjustment_id adj_id) { + struct ds_adjustment_status *adjustment_status = NULL; + struct raw_gamma_ramp *gamma = NULL; + + if (ds == NULL) + return NULL; + + if (!dal_gamma_lut_validate(adj_id, gamma, false)) + return NULL; + + if (get_parameters(ds, adj_id, &adjustment_status, &gamma)) + return gamma; + + if (adjustment_status->bits.SET_FROM_EXTERNAL == 0) + if (generated_default_gamma_ramp(ds, adj_id)) + return gamma; + + return gamma; +} + +bool dal_gamma_lut_set_current_gamma(struct ds_dispatch *ds, + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma) +{ + struct ds_adjustment_status *adjustment_status = NULL; + struct raw_gamma_ramp *ramp = NULL; + + if (!dal_gamma_lut_validate(adj_id, gamma, true)) + return false; + + if (!get_parameters(ds, adj_id, &adjustment_status, &ramp)) + return false; + + dal_memmove(ramp, gamma, sizeof(struct raw_gamma_ramp)); + + /* new external gamma was set , reset to 0 default flag */ + adjustment_status->bits.SET_TO_DEFAULT = 0; + /* new external gamma was set , raise this flag */ + adjustment_status->bits.SET_FROM_EXTERNAL = 1; + /* new external gamma was set , raise this flag */ + adjustment_status->bits.SET_TO_HARDWARE = 0; + + return true; + +} +static void destruct(struct grph_gamma_lut_group *gamma_adj) +{ +} + +void dal_grph_gamma_adj_group_destroy( + struct grph_gamma_lut_group **grph_gamma_adj) { + if (grph_gamma_adj == NULL || *grph_gamma_adj == NULL) + return; + + destruct(*grph_gamma_adj); + dal_free(*grph_gamma_adj); + *grph_gamma_adj = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/gamma_lut.h b/drivers/gpu/drm/amd/dal/display_service/gamma_lut.h new file mode 100644 index 000000000000..2c050c92a46a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/gamma_lut.h @@ -0,0 +1,89 @@ +/* + * Copyright 2014 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 __DAL_GAMMA_LUT_H__ +#define __DAL_GAMMA_LUT_H__ + +#include "adjustment_types_internal.h" +#include "include/display_service_types.h" + +struct ds_dispatch; +struct grph_colors_group; +struct crtc_timing; +struct display_path; +struct adj_container; + +struct grph_gamma_lut_group { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct dal_context *dal_context; + struct ds_adjustment_status status_gamma_ramp; + struct ds_adjustment_status status_original_ramp; + struct raw_gamma_ramp gamma_ramp; + struct raw_gamma_ramp oroginal_ramp; +}; + +struct grph_gamma_lut_group_init_data { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct dal_context *dal_context; +}; + +struct grph_gamma_lut_group *dal_gamma_adj_group_create( + struct grph_gamma_lut_group_init_data *init_data); + +void dal_grph_gamma_adj_group_destroy( + struct grph_gamma_lut_group **grph_gamma_adj); + + +enum ds_return dal_grph_gamma_lut_set_adjustment( + struct ds_dispatch *ds, + const struct display_path *disp_path, + const struct path_mode *disp_path_mode, + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma, + const struct ds_regamma_lut *regumma_lut); + +bool dal_gamma_lut_validate( + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma, + bool validate_all); + +bool dal_gamma_lut_translate_to_hw( + struct ds_dispatch *ds, + const struct path_mode *disp_path_mode, + const struct display_path *disp_path, + const struct raw_gamma_ramp *gamma_in, + struct hw_adjustment_gamma_ramp *gamma_out); + +const struct raw_gamma_ramp *dal_gamma_lut_get_current_gamma( + struct ds_dispatch *ds, + enum adjustment_id adj_id); + +bool dal_gamma_lut_set_current_gamma(struct ds_dispatch *ds, + enum adjustment_id adj_id, + const struct raw_gamma_ramp *gamma); + +#endif /* __DAL_GAMMA_LUT_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/gamut_space.c b/drivers/gpu/drm/amd/dal/display_service/gamut_space.c new file mode 100644 index 000000000000..08207d805eff --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/gamut_space.c @@ -0,0 +1,1140 @@ +/* + * Copyright 2015 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 "dal_services.h" +#include "include/fixed31_32.h" + +#include "gamut_space.h" + +const struct gamut_space_entry gamut_array[] = { + + {1, 6400, 3300, 3000, 6000, 1500, 600, 180000, 4500, 99, 99, 2200 }, + {2, 6400, 3300, 2900, 6000, 1500, 600, 180000, 4500, 99, 99, 2200 }, + {4, 6400, 3300, 2100, 7100, 1500, 600, 180000, 4500, 99, 99, 2200 }, + {8, 6400, 3300, 3000, 6000, 1500, 600, 31308, 12920, 55, 55, 2400 } +}; + +const struct white_point_coodinates_entry white_point_array[] = { + {1, 3473, 3561 }, + {2, 3127, 3290 }, + {4, 3022, 3129 }, + {8, 2866, 2950 } +}; + +void dal_gamut_space_reset_gamut( + struct gamut_data *data, + bool gamut, + bool white_point) +{ + if (gamut == true) { + data->option.bits.CUSTOM_GAMUT_SPACE = 0; + dal_memset(&data->gamut, 0, sizeof(data->gamut)); + } + if (white_point) { + data->option.bits.CUSTOM_WHITE_POINT = 0; + dal_memset(&data->white_point, 0, sizeof(data->white_point)); + } +} +static void set_regamma_support( + struct gamut_parameter *gamut, + struct gamut_data *gamut_dst, + union update_color_flags *flags) +{ + struct color_space_coodinates csc = {0}; + union ds_gamut_spaces + predefined_coordinates = {0}; + union ds_gamut_spaces + predefined_coefficients = {0}; + + if (gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE == 0) { + if (gamut->regamma.flags.bits.GAMMA_RAMP_ARRAY == 1) { + if (!dal_gamut_space_find_predefined_gamut( + gamut->gamut_dst.gamut.predefined, + &csc, + NULL)) + return; + gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE = 1; + gamut->gamut_dst.gamut.custom.red_x = csc.red_x; + gamut->gamut_dst.gamut.custom.red_y = csc.red_y; + gamut->gamut_dst.gamut.custom.green_x = csc.green_x; + gamut->gamut_dst.gamut.custom.green_y = csc.green_y; + gamut->gamut_dst.gamut.custom.blue_x = csc.blue_x; + gamut->gamut_dst.gamut.custom.blue_y = csc.blue_y; + flags->bits.GAMUT_DST = 1; + } else { + bool found_coefficients = + dal_gamut_space_find_regamma_coefficients( + &gamut->regamma.coeff, + &gamut_dst->gamut.predefined); + if (found_coefficients == false || + gamut->gamut_dst.gamut.predefined.u32all != + gamut_dst->gamut.predefined.u32all) { + if (!dal_gamut_space_find_predefined_gamut( + gamut->gamut_dst.gamut.predefined, + &csc, + NULL)) + return; + + gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE + = 1; + gamut->gamut_dst.gamut.custom.red_x = + csc.red_x; + gamut->gamut_dst.gamut.custom.red_y = + csc.red_y; + gamut->gamut_dst.gamut.custom.green_x = + csc.green_x; + gamut->gamut_dst.gamut.custom.green_y = + csc.green_y; + gamut->gamut_dst.gamut.custom.blue_x = + csc.blue_x; + gamut->gamut_dst.gamut.custom.blue_y = + csc.blue_y; + flags->bits.GAMUT_DST = 1; + } + } + } else if (gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE == 1) { + if (gamut->regamma.flags.bits.GAMMA_RAMP_ARRAY == 0 && + gamut->regamma.flags.bits.COEFF_FROM_USER == 1) { + if (!dal_gamut_space_find_color_coordinates( + &csc, + &predefined_coordinates)) + return; + if (!dal_gamut_space_find_regamma_coefficients( + &gamut->regamma.coeff, + &predefined_coefficients)) + return; + if (predefined_coordinates.u32all != + predefined_coefficients.u32all) + return; + + gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE = 0; + gamut->gamut_dst.gamut.predefined.u32all = + predefined_coordinates.u32all; + flags->bits.GAMUT_DST = 1; + + } + } +} + + +bool dal_gamut_space_update_gamut( + struct gamut_parameter *gamut, + bool set_regamma, + union update_color_flags *flags) +{ + struct gamut_data gamut_dst = {{0} }; + + if (gamut->source != GAMUT_SPACE_SOURCE_USER_GAMUT) + return true; + + if (set_regamma == true) + set_regamma_support(gamut, &gamut_dst, flags); + else { + if (gamut->gamut_dst.option.bits.CUSTOM_GAMUT_SPACE + == 1) + return true; + + if (gamut->regamma.flags.bits.GAMMA_RAMP_ARRAY == 1) + return true; + + if (gamut->regamma.flags.bits.COEFF_FROM_USER == 0) + return true; + + if (gamut->regamma.flags.bits.GAMMA_FROM_USER == 0) + return true; + { + struct ds_regamma_coefficients_ex + gamma_coeff = {{0} }; + union update_color_flags aflags = {0}; + + if (!dal_gamut_space_find_predefined_gamut( + gamut->gamut_dst.gamut.predefined, + NULL, + &gamma_coeff)) + return true; + + if (!dal_gamut_space_is_equal_gamma_coefficients( + &gamut->regamma.coeff, + &gamma_coeff, + &aflags)) { + dal_memmove(&gamut->regamma.coeff, + &gamma_coeff, + sizeof(gamut->regamma.coeff)); + flags->bits.REGAMMA = 1; + } + } + } + return true; +} + +bool dal_gamut_space_find_predefined_gamut( + union ds_gamut_spaces predefine, + struct color_space_coodinates *csc, + struct ds_regamma_coefficients_ex *gamma_coeff) +{ + bool ret = false; + const struct gamut_space_entry *p; + uint32_t const_size; + uint32_t i; + + const_size = ARRAY_SIZE(gamut_array); + + for (p = gamut_array ; p < &gamut_array[const_size]; p++) { + if (p->index == predefine.u32all) { + if (csc != NULL) { + csc->red_x = p->red_x; + csc->red_y = p->red_y; + csc->green_x = p->green_x; + csc->green_y = p->green_y; + csc->blue_x = p->blue_x; + csc->blue_y = p->blue_y; + } + if (gamma_coeff != NULL) { + for (i = 0 ; i < COEFF_RANGE ; i++) { + gamma_coeff->coeff_a0[i] = p->a0; + gamma_coeff->coeff_a1[i] = p->a1; + gamma_coeff->coeff_a2[i] = p->a2; + gamma_coeff->coeff_a3[i] = p->a3; + gamma_coeff->gamma[i] = p->gamma; + } + } + ret = true; + break; + } + } + return ret; +} + +bool dal_gamut_space_find_regamma_coefficients( + struct ds_regamma_coefficients_ex *coeff, + union ds_gamut_spaces *predefine) +{ + bool ret = false; + const struct gamut_space_entry *p; + uint32_t const_size; + + if (coeff->coeff_a0[0] != coeff->coeff_a0[1] || + coeff->coeff_a0[0] != coeff->coeff_a0[2]) + return false; + + if (coeff->coeff_a1[0] != coeff->coeff_a1[1] || + coeff->coeff_a1[0] != coeff->coeff_a1[2]) + return false; + + if (coeff->coeff_a2[0] != coeff->coeff_a2[1] || + coeff->coeff_a2[0] != coeff->coeff_a2[2]) + return false; + + if (coeff->coeff_a3[0] != coeff->coeff_a3[1] || + coeff->coeff_a3[0] != coeff->coeff_a3[2]) + return false; + + if (coeff->gamma[0] != coeff->gamma[1] || + coeff->gamma[0] != coeff->gamma[2]) + return false; + + const_size = ARRAY_SIZE(gamut_array); + + for (p = gamut_array; p < &gamut_array[const_size]; p++) { + if (p->a0 == coeff->coeff_a0[0] && + p->a1 == coeff->coeff_a1[0] && + p->a2 == coeff->coeff_a2[0] && + p->a3 == coeff->coeff_a3[0] && + p->gamma == coeff->gamma[0]) { + + predefine->u32all = p->index; + ret = true; + break; + } + } + return ret; +} + +bool dal_gamut_space_find_color_coordinates( + struct color_space_coodinates *csc, + union ds_gamut_spaces *predefine) +{ + bool ret = false; + const struct gamut_space_entry *p; + uint32_t const_size; + + const_size = ARRAY_SIZE(gamut_array); + for (p = gamut_array; p < &gamut_array[const_size]; p++) { + if (p->red_x == csc->red_x && + p->red_y == csc->red_y && + p->green_x == csc->green_x && + p->green_y == csc->green_y && + p->blue_x == csc->blue_x && + p->blue_y == csc->blue_y){ + + predefine->u32all = p->index; + ret = true; + break; + } + } + return ret; +} + +bool dal_gamut_space_is_equal_gamma_coefficients( + struct ds_regamma_coefficients_ex *coeff, + struct ds_regamma_coefficients_ex *coeff_custom, + union update_color_flags *flags) +{ + bool ret = true; + uint32_t i; + + for (i = 0 ; i < COEFF_RANGE ; i++) { + if (coeff->gamma[i] != coeff_custom->gamma[i]) { + flags->bits.REGAMMA = 1; + ret = false; + break; + } + if (coeff->coeff_a0[i] != coeff_custom->coeff_a0[i]) { + flags->bits.REGAMMA = 1; + ret = false; + break; + } + if (coeff->coeff_a1[i] != coeff_custom->coeff_a1[i]) { + flags->bits.REGAMMA = 1; + ret = false; + break; + } + if (coeff->coeff_a2[i] != coeff_custom->coeff_a2[i]) { + flags->bits.REGAMMA = 1; + ret = false; + break; + } + if (coeff->coeff_a3[i] != coeff_custom->coeff_a3[i]) { + flags->bits.REGAMMA = 1; + ret = false; + break; + } + } + return ret; +} + +bool dal_gamut_space_setup_default_gamut( + enum adjustment_id adj_id, + struct gamut_data *data, + bool gamut, + bool white_point) +{ + if (gamut == true) { + data->option.bits.CUSTOM_GAMUT_SPACE = 0; + dal_memset(&data->gamut, 0, sizeof(data->gamut)); + data->gamut.predefined.bits.GAMUT_SPACE_CIERGB = 1; + } + if (white_point == true) { + data->option.bits.CUSTOM_WHITE_POINT = 0; + dal_memset(&data->white_point, 0, sizeof(data->white_point)); + data->white_point.predefined.bits.GAMUT_WHITE_POINT_6500 = 1; + } + return true; +} + +bool dal_gamut_space_build_gamut_space_matrix( + struct gamut_parameter *gamut, + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3], + struct ds_regamma_lut *regamma, + union update_color_flags *flags) +{ + struct gamut_matrixs matrix; + bool ret = false; + + matrix.rgb_coeff_dst = NULL; + + if (gamut->source == GAMUT_SPACE_SOURCE_DEFAULT) { + ret = dal_gamut_space_build_default_unity_matrix( + temp_matrix, regamma); + goto check_result; + + } + if (gamut->source != GAMUT_SPACE_SOURCE_EDID && + gamut->source != GAMUT_SPACE_SOURCE_USER_GAMUT) + goto check_result; + + if (!dal_gamut_space_allocate_matrix(&matrix)) + goto check_result; + + if (gamut->source == GAMUT_SPACE_SOURCE_EDID) { + if (!dal_gamut_space_build_gamut_matrix( + matrix.rgb_coeff_src, + matrix.white_coeff_src, + regamma, + flags, + &gamut->regamma, + &gamut->gamut_src, + true)) + goto check_result; + + if (!dal_gamut_build_edid_matrix( + regamma, + flags, + gamut->color_charact.gamma, + matrix.rgb_coeff_dst, + matrix.white_coeff_dst, + gamut->color_charact.color_charact)) + goto check_result; + + } else if (gamut->source == GAMUT_SPACE_SOURCE_USER_GAMUT) { + if (!dal_gamut_space_build_gamut_matrix( + matrix.rgb_coeff_dst, + matrix.white_coeff_dst, + regamma, + flags, + &gamut->regamma, + &gamut->gamut_dst, + false)) + goto check_result; + + if (!dal_gamut_space_build_gamut_matrix( + matrix.rgb_coeff_src, + matrix.white_coeff_src, + regamma, + flags, + &gamut->regamma, + &gamut->gamut_src, + true)) + goto check_result; + } + if (!dal_gamut_space_gamut_to_color_matrix( + matrix.rgb_coeff_dst, + matrix.white_coeff_dst, + matrix.rgb_coeff_src, + matrix.white_coeff_src, + true, + temp_matrix)) + goto check_result; + + ret = true; + +check_result: + + dal_gamut_space_dellocate_matrix(&matrix); + + if (ret == false) { + ret = dal_gamut_space_build_default_unity_matrix( + temp_matrix, regamma); + flags->bits.REGAMMA = 1; + } + return ret; +} + +bool dal_gamut_space_build_default_unity_matrix( + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3], + struct ds_regamma_lut *regamma) +{ + uint32_t i; + union ds_gamut_spaces def = {0 }; + + if (!temp_matrix || !regamma) + return false; + + for (i = 0 ; i < GAMUT_MATRIX_SIZE_3X3 ; i++) { + if (i == 0 || i == 4 || i == 8) + temp_matrix[i] = GAMUT_DIVIDER; + else + temp_matrix[i] = 0; + } + def.u32all = 0; + def.bits.GAMUT_SPACE_CIERGB = 1; + regamma->flags.u32all = 0; + + if (!dal_gamut_space_find_predefined_gamut( + def, + NULL, + ®amma->coeff)) + return false; + + return true; +} + +bool dal_gamut_space_allocate_matrix( + struct gamut_matrixs *matrix) +{ + uint32_t size_of_allocation; + + size_of_allocation = (sizeof(struct fixed31_32) * 3 + + sizeof(struct fixed31_32) * 9) * 2; + + matrix->rgb_coeff_dst = dal_alloc(size_of_allocation); + + if (!matrix->rgb_coeff_dst) + return false; + matrix->white_coeff_dst = matrix->rgb_coeff_dst + 9; + matrix->rgb_coeff_src = matrix->white_coeff_dst + 3; + matrix->white_coeff_src = matrix->rgb_coeff_src + 9; + + return true; +} + +void dal_gamut_space_dellocate_matrix( + struct gamut_matrixs *matrix) +{ + if (matrix->rgb_coeff_dst != NULL) { + dal_free(matrix->rgb_coeff_dst); + matrix->rgb_coeff_dst = NULL; + } +} + +bool dal_gamut_space_build_gamut_matrix( + struct fixed31_32 *rgb, + struct fixed31_32 *white, + struct ds_regamma_lut *regamma, + union update_color_flags *flags, + struct ds_regamma_lut *custom_regamma, + struct gamut_data *data, + bool is_source) +{ + struct color_space_coodinates csc = {0 }; + struct color_characteristic fcsc; + struct ds_regamma_coefficients_ex coeff; + + dal_memmove(regamma, custom_regamma, sizeof(*regamma)); + if (data->option.bits.CUSTOM_GAMUT_SPACE == 1) { + csc.red_x = data->gamut.custom.red_x; + csc.red_y = data->gamut.custom.red_y; + csc.green_x = data->gamut.custom.green_x; + csc.green_y = data->gamut.custom.green_y; + csc.blue_x = data->gamut.custom.blue_x; + csc.blue_y = data->gamut.custom.blue_y; + } else { + dal_memset(&coeff, 0, sizeof(coeff)); + if (!dal_gamut_space_find_predefined_gamut( + data->gamut.predefined, + &csc, + &coeff)) + return false; + + if (custom_regamma->flags.bits.GAMMA_RAMP_ARRAY + == 0) { + regamma->coeff = coeff; + dal_gamut_space_is_equal_gamma_coefficients( + &coeff, + &custom_regamma->coeff, + flags); + regamma->flags.bits.GAMMA_FROM_USER = 1; + regamma->flags.bits.GAMMA_FROM_EDID = 0; + regamma->flags.bits.GAMMA_FROM_EDID_EX = 0; + } + } + if (data->option.bits.CUSTOM_WHITE_POINT == 1) { + csc.white_x = data->white_point.custom.white_x; + csc.white_y = data->white_point.custom.white_y; + + } else { + if (!dal_gamut_space_find_predefined_white_point( + data->white_point.predefined, + &csc)) + return false; + } + + fcsc.red_x = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.red_x), GAMUT_DIVIDER); + + fcsc.red_y = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.red_y), GAMUT_DIVIDER); + + fcsc.green_x = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.green_x), GAMUT_DIVIDER); + + fcsc.green_y = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.green_y), GAMUT_DIVIDER); + + fcsc.blue_x = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.blue_x), GAMUT_DIVIDER); + + fcsc.blue_y = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.blue_y), GAMUT_DIVIDER); + + fcsc.white_x = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.white_x), GAMUT_DIVIDER); + + fcsc.white_y = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + (int64_t)csc.white_y), GAMUT_DIVIDER); + + if (!dal_gamut_space_build_chromaticity_matrix( + rgb, white, &fcsc)) + return false; + + return true; +} + +bool dal_gamut_build_edid_matrix( + struct ds_regamma_lut *regamma, + union update_color_flags *flags, + uint32_t edid_gamma, + struct fixed31_32 *rgb, + struct fixed31_32 *white, + uint8_t pa[EDID_GAMUT_COORDINATES]) +{ + struct color_characteristic pm = {{0} }; + + dal_gamut_space_convert_edid_format_color_charact( + pa, &pm); + + if (!dal_gamut_space_build_chromaticity_matrix( + rgb, white, &pm)) + return false; + return true; +} + +bool dal_gamut_space_build_chromaticity_matrix( + struct fixed31_32 *rgb, + struct fixed31_32 *white, + struct color_characteristic *pm) +{ + + if (pm->red_y.value == 0 || pm->green_y.value == 0 || + pm->blue_y.value == 0 || pm->white_y.value == 0) + return false; + + rgb[0] = dal_fixed31_32_div( + pm->red_x, pm->red_y); + rgb[1] = dal_fixed31_32_one; + rgb[2] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_sub( + dal_fixed31_32_one, + pm->red_x), + pm->red_y), + pm->red_y); + + rgb[3] = dal_fixed31_32_div( + pm->green_x, pm->green_y); + rgb[4] = dal_fixed31_32_one; + rgb[5] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_sub( + dal_fixed31_32_one, + pm->green_x), + pm->green_y), + pm->green_y); + + rgb[6] = dal_fixed31_32_div( + pm->blue_x, pm->blue_y); + rgb[7] = dal_fixed31_32_one; + rgb[8] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_sub( + dal_fixed31_32_one, + pm->blue_x), + pm->blue_y), + pm->blue_y); + + white[0] = dal_fixed31_32_div( + pm->white_x, pm->white_y); + white[1] = dal_fixed31_32_one; + white[2] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_sub( + dal_fixed31_32_one, + pm->white_x), + pm->white_y), + pm->white_y); + + return true; +} + +bool dal_gamut_space_gamut_to_color_matrix( + struct fixed31_32 *xyz_of_rgb, + struct fixed31_32 *xyz_of_white, + struct fixed31_32 *ref_xyz_of_rgb, + struct fixed31_32 *ref_xyz_of_white, + bool invert, + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3]) +{ + uint32_t i; + bool ret = false; + struct fixed31_32 *xyz_to_rgb_temp; + struct fixed31_32 *xyz_to_rgb_final; + struct gamut_calculation_matrix matrix; + + matrix.transposed = NULL; + + if (!dal_gamut_space_allocate_calculation_matrix( + &matrix)) + return false; + + matrix.xyz_of_white_ref[0] = ref_xyz_of_white[0]; + matrix.xyz_of_white_ref[1] = ref_xyz_of_white[1]; + matrix.xyz_of_white_ref[2] = ref_xyz_of_white[2]; + + matrix.xyz_of_rgb_ref[0] = ref_xyz_of_rgb[0]; + matrix.xyz_of_rgb_ref[1] = ref_xyz_of_rgb[1]; + matrix.xyz_of_rgb_ref[2] = ref_xyz_of_rgb[2]; + matrix.xyz_of_rgb_ref[3] = ref_xyz_of_rgb[3]; + matrix.xyz_of_rgb_ref[4] = ref_xyz_of_rgb[4]; + matrix.xyz_of_rgb_ref[5] = ref_xyz_of_rgb[5]; + matrix.xyz_of_rgb_ref[6] = ref_xyz_of_rgb[6]; + matrix.xyz_of_rgb_ref[7] = ref_xyz_of_rgb[7]; + matrix.xyz_of_rgb_ref[8] = ref_xyz_of_rgb[8]; + + for (i = 0; i < GAMUT_MATRIX_SIZE_3X3 ; i++) { + if (i == 0 || i == 4 || i == 8) + temp_matrix[i] = GAMUT_DIVIDER; + else + temp_matrix[i] = 0; + } + if (invert) { + xyz_to_rgb_temp = + matrix.xyz_to_rgb_custom; + xyz_to_rgb_final = + matrix.xyz_to_rgb_ref; + } else { + xyz_to_rgb_temp = + matrix.xyz_to_rgb_ref; + xyz_to_rgb_final = + matrix.xyz_to_rgb_custom; + } + dal_gamut_space_transpose_matrix( + matrix.xyz_of_rgb_ref, + 3, + 3, + matrix.transposed); + if (!dal_gamut_space_calculate_xyz_to_rgb_M3x3( + matrix.transposed, + matrix.xyz_of_white_ref, + matrix.xyz_to_rgb_ref)) + goto result; + + dal_gamut_space_transpose_matrix( + xyz_of_rgb, + 3, + 3, + matrix.transposed); + if (!dal_gamut_space_calculate_xyz_to_rgb_M3x3( + matrix.transposed, + xyz_of_white, + matrix.xyz_to_rgb_custom)) + goto result; + + if (!dal_gamut_space_compute_inverse_matrix_3x3( + xyz_to_rgb_temp, + matrix.rgb_to_xyz_final)) + goto result; + + dal_gamut_space_multiply_matrices( + matrix.result, + matrix.rgb_to_xyz_final, + xyz_to_rgb_final, + 3, + 3, + 3); + for (i = 0; i < GAMUT_MATRIX_SIZE_3X3 ; i++) + temp_matrix[i] = (uint32_t)dal_fixed31_32_round( + dal_fixed31_32_abs( + dal_fixed31_32_mul_int( + matrix.result[i], GAMUT_DIVIDER))); + ret = true; + +result: + dal_gamut_space_deallocate_calculation_matrix( + &matrix); + return ret; + +} +struct fixed31_32 dal_gamut_space_find_3x3_det( + struct fixed31_32 *m) +{ + struct fixed31_32 det; + struct fixed31_32 a1; + struct fixed31_32 a2; + struct fixed31_32 a3; + + a1 = dal_fixed31_32_mul(m[0], + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[4], m[8]), + dal_fixed31_32_mul(m[5], m[7]))); + + a2 = dal_fixed31_32_mul(m[1], + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[3], m[8]), + dal_fixed31_32_mul(m[5], m[6]))); + + a3 = dal_fixed31_32_mul(m[2], + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[3], m[7]), + dal_fixed31_32_mul(m[4], m[6]))); + + det = dal_fixed31_32_add( + dal_fixed31_32_sub(a1, a2), + a3); + + return det; +} +bool dal_gamut_space_compute_inverse_matrix_3x3( + struct fixed31_32 *m, + struct fixed31_32 *im) +{ + struct fixed31_32 determinant; + struct fixed31_32 neg; + + determinant = dal_gamut_space_find_3x3_det(m); + + if (determinant.value == 0) + return false; + + im[0] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[4], m[8]), + dal_fixed31_32_mul(m[5], m[7])), + determinant); + neg = dal_fixed31_32_neg(dal_fixed31_32_one); + + im[1] = dal_fixed31_32_div( + dal_fixed31_32_mul( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[1], m[8]), + dal_fixed31_32_mul(m[2], m[7])), + neg), + determinant); + + im[2] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[1], m[5]), + dal_fixed31_32_mul(m[2], m[4])), + determinant); + + im[3] = dal_fixed31_32_div( + dal_fixed31_32_mul( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[3], m[8]), + dal_fixed31_32_mul(m[5], m[6])), + neg), + determinant); + + im[4] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[0], m[8]), + dal_fixed31_32_mul(m[2], m[6])), + determinant); + + im[5] = dal_fixed31_32_div( + dal_fixed31_32_mul( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[0], m[5]), + dal_fixed31_32_mul(m[2], m[3])), + neg), + determinant); + + im[6] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[3], m[7]), + dal_fixed31_32_mul(m[4], m[6])), + determinant); + + im[7] = dal_fixed31_32_div( + dal_fixed31_32_mul( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[0], m[7]), + dal_fixed31_32_mul(m[1], m[6])), + neg), + determinant); + + im[8] = dal_fixed31_32_div( + dal_fixed31_32_sub( + dal_fixed31_32_mul(m[0], m[4]), + dal_fixed31_32_mul(m[1], m[3])), + determinant); + + return true; +} + +void dal_gamut_space_multiply_matrices( + struct fixed31_32 *result, + struct fixed31_32 *m1, + struct fixed31_32 *m2, + uint32_t rows, + uint32_t cols1, + uint32_t cols2) +{ + uint32_t i, j, k; + + for (i = 0 ; i < rows ; i++) { + for (j = 0 ; j < cols2 ; j++) { + result[(i * cols2) + j].value = 0; + for (k = 0 ; k < cols1 ; k++) + result[(i * cols2) + j] = + dal_fixed31_32_add( + result[(i * cols2) + j], + dal_fixed31_32_mul( + m1[(i * cols1) + k], + m2[(k * cols2) + j])); + + } + } +} + +bool dal_gamut_space_calculate_xyz_to_rgb_M3x3( + struct fixed31_32 *xyz_of_rgb, + struct fixed31_32 *xyz_of_white, + struct fixed31_32 *xyz_to_rgb) +{ + struct fixed31_32 m_inversed[9]; + struct fixed31_32 s_vector[3]; + + if (!dal_gamut_space_compute_inverse_matrix_3x3( + xyz_of_rgb, m_inversed)) + return false; + + dal_gamut_space_multiply_matrices( + s_vector, + m_inversed, + xyz_of_white, + 3, + 3, + 1); + + xyz_to_rgb[0] = dal_fixed31_32_mul( + xyz_of_rgb[0], s_vector[0]); + xyz_to_rgb[1] = dal_fixed31_32_mul( + xyz_of_rgb[1], s_vector[1]); + xyz_to_rgb[2] = dal_fixed31_32_mul( + xyz_of_rgb[2], s_vector[2]); + xyz_to_rgb[3] = dal_fixed31_32_mul( + xyz_of_rgb[3], s_vector[0]); + xyz_to_rgb[4] = dal_fixed31_32_mul( + xyz_of_rgb[4], s_vector[1]); + xyz_to_rgb[5] = dal_fixed31_32_mul( + xyz_of_rgb[5], s_vector[2]); + xyz_to_rgb[6] = dal_fixed31_32_mul( + xyz_of_rgb[6], s_vector[0]); + xyz_to_rgb[7] = dal_fixed31_32_mul( + xyz_of_rgb[7], s_vector[1]); + xyz_to_rgb[8] = dal_fixed31_32_mul( + xyz_of_rgb[8], s_vector[2]); + + return true; +} + +bool dal_gamut_space_allocate_calculation_matrix( + struct gamut_calculation_matrix *matrix) +{ + uint32_t size_of_allocation; + + size_of_allocation = sizeof(struct fixed31_32) * 9 * 6 + + sizeof(struct fixed31_32) * 3; + + matrix->transposed = dal_alloc(size_of_allocation); + if (!matrix->transposed) + return false; + + matrix->xyz_to_rgb_custom = matrix->transposed + 9; + matrix->xyz_to_rgb_ref = matrix->xyz_to_rgb_custom + 9; + matrix->rgb_to_xyz_final = matrix->xyz_to_rgb_ref + 9; + matrix->result = matrix->rgb_to_xyz_final + 9; + matrix->xyz_of_white_ref = matrix->result + 9; + matrix->xyz_of_rgb_ref = matrix->xyz_of_white_ref + 3; + + return true; +} + +void dal_gamut_space_deallocate_calculation_matrix( + struct gamut_calculation_matrix *matrix) +{ + if (matrix->transposed != NULL) { + dal_free(matrix->transposed); + matrix->transposed = NULL; + } +} + +void dal_gamut_space_transpose_matrix( + struct fixed31_32 *m, + uint32_t rows, + uint32_t cols, + struct fixed31_32 *transposed) +{ + uint32_t i, j; + + for (i = 0 ; i < rows ; i++) { + for (j = 0 ; j < cols ; j++) + transposed[(j * rows) + i] = + m[(i * cols) + j]; + } +} + + +bool dal_gamut_space_setup_white_point( + struct gamut_data *gamut, + struct ds_white_point_coordinates *data) +{ + struct color_space_coodinates csc; + + if (gamut->option.bits.CUSTOM_WHITE_POINT == 1) { + data->white_x = gamut->white_point.custom.white_x; + data->white_y = gamut->white_point.custom.white_y; + return true; + } + + if (dal_gamut_space_find_predefined_white_point( + gamut->white_point.predefined, + &csc)) { + data->white_x = csc.white_x; + data->white_y = csc.white_y; + return true; + } + + return false; +} + +bool dal_gamut_space_find_predefined_white_point( + union ds_gamut_white_point predefined, + struct color_space_coodinates *csc) +{ + bool ret = false; + const struct white_point_coodinates_entry *p; + uint32_t const_size; + + const_size = ARRAY_SIZE(white_point_array); + for (p = white_point_array; + p < &white_point_array[const_size]; p++) { + if (p->index == predefined.u32all) { + csc->white_x = p->white_x; + csc->white_y = p->white_y; + ret = true; + break; + } + } + return ret; +} + + +bool dal_gamut_space_get_supported_gamuts(struct ds_gamut_info *data) +{ + data->gamut_space.bits.GAMUT_SPACE_CCIR709 = 1; + data->gamut_space.bits.GAMUT_SPACE_CCIR601 = 1; + data->gamut_space.bits.GAMUT_SPACE_ADOBERGB = 1; + data->gamut_space.bits.GAMUT_SPACE_CIERGB = 1; + data->gamut_space.bits.GAMUT_SPACE_CUSTOM = 1; + + data->white_point.bits.GAMUT_WHITE_POINT_5000 = 1; + data->white_point.bits.GAMUT_WHITE_POINT_6500 = 1; + data->white_point.bits.GAMUT_WHITE_POINT_7500 = 1; + data->white_point.bits.GAMUT_WHITE_POINT_9300 = 1; + data->white_point.bits.GAMUT_WHITE_POINT_CUSTOM = 1; + return true; +} + +static struct fixed31_32 power_to_fractional(uint8_t two_bytes) +{ + uint32_t i; + struct fixed31_32 data; + struct fixed31_32 pow; + + data.value = 0; + pow = dal_fixed31_32_pow( + dal_fixed31_32_from_int( + (int64_t)EXP_NUMBER_1), + dal_fixed31_32_from_int( + (int64_t)EXP_NUMBER_2)); + + for (i = 0; i < 10 ; i++, two_bytes >>= 1) { + if (two_bytes & 0x0001) + data = dal_fixed31_32_add( + data, pow); + pow = dal_fixed31_32_mul_int( + pow, 2); + } + return data; +} + +bool dal_gamut_space_convert_edid_format_color_charact( + const uint8_t pa[10], + struct color_characteristic *color_charact) +{ + uint8_t temp; + + /* The chromaticity and white point values are expressed as fractional + * numbers accurate to the thousandth place.Each number is represented + * by a binary fraction which is 10 bits in length. In this fraction + * a value of one for the bit immediately right of the decimal point + * (bit 9) represents 2 raised to the -1 power. A value to 1 in the + * right most bit (bit 0) represents a value of 2 raised to the -10 + * power.The high order bits (9 ~ 2) are stored as a single byte. + * The low order bits (1 ~ 0) are paired with other low order bits + to form a byte.*/ + + temp = (pa[2] << SHIFT_2) | ((pa[0] & MASK_C0) >> SHIFT_6); + color_charact->red_x = power_to_fractional(temp); + + temp = (pa[3] << SHIFT_2) | ((pa[0] & MASK_30) >> SHIFT_4); + color_charact->red_y = power_to_fractional(temp); + if (dal_fixed31_32_eq(color_charact->red_y, dal_fixed31_32_zero)) + return false; + + temp = (pa[4] << SHIFT_2) | ((pa[0] & MASK_C) >> SHIFT_2); + color_charact->green_x = power_to_fractional(temp); + + temp = (pa[5] << SHIFT_2) | ((pa[0] & MASK_3)); + color_charact->green_y = power_to_fractional(temp); + if (dal_fixed31_32_eq(color_charact->green_y, dal_fixed31_32_zero)) + return false; + + temp = (pa[6] << SHIFT_2) | ((pa[1] & MASK_C0) >> SHIFT_6); + color_charact->blue_x = power_to_fractional(temp); + + temp = (pa[7] << SHIFT_2) | ((pa[1] & MASK_30) >> SHIFT_4); + color_charact->blue_y = power_to_fractional(temp); + if (dal_fixed31_32_eq(color_charact->blue_y, dal_fixed31_32_zero)) + return false; + + temp = (pa[8] << SHIFT_2) | ((pa[1] & MASK_C) >> SHIFT_2); + color_charact->white_x = power_to_fractional(temp); + + temp = (pa[9] << SHIFT_2) | ((pa[1] & MASK_3)); + color_charact->white_y = power_to_fractional(temp); + if (dal_fixed31_32_eq(color_charact->white_y, dal_fixed31_32_zero)) + return false; + + return true; +} + +bool dal_gamut_space_setup_predefined_regamma_coefficients( + struct gamut_data *data, + struct ds_regamma_lut *regamma) +{ + bool ret = false; + + if (data->option.bits.CUSTOM_GAMUT_SPACE == 0) { + regamma->flags.u32all = 0; + ret = dal_gamut_space_find_predefined_gamut( + data->gamut.predefined, + NULL, + ®amma->coeff); + + regamma->flags.bits.COEFF_FROM_USER = 1; + regamma->flags.bits.GAMMA_FROM_USER = 1; + } + return ret; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/gamut_space.h b/drivers/gpu/drm/amd/dal/display_service/gamut_space.h new file mode 100644 index 000000000000..cd238ec0b882 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/gamut_space.h @@ -0,0 +1,270 @@ +/* + * Copyright 2015 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 __DAL_GAMUT_SPACE_H__ +#define __DAL_GAMUT_SPACE_H__ + +#include "include/adjustment_types.h" +#include "include/fixed32_32.h" + +#define EDID_GAMUT_COORDINATES 10 +#define GAMUT_MATRIX_SIZE_3X3 9 +#define EXP_NUMBER_1 2 +#define EXP_NUMBER_2 -10 +#define MASK_C0 0xC0 +#define MASK_30 0x30 +#define MASK_C 0x0C +#define MASK_3 0x03 +#define SHIFT_2 2 +#define SHIFT_4 4 +#define SHIFT_6 6 + +struct gamut_color_characteristics { + uint32_t gamma; + uint8_t color_charact[EDID_GAMUT_COORDINATES]; +}; + +enum gamut_space_source { + GAMUT_SPACE_SOURCE_DEFAULT, + GAMUT_SPACE_SOURCE_USER_GAMUT, + GAMUT_SPACE_SOURCE_EDID +}; + +struct gamut_parameter { + enum gamut_space_source source; + struct gamut_data gamut_src; + struct ds_regamma_lut regamma; + union { + struct gamut_data gamut_dst; + struct gamut_color_characteristics color_charact; + }; +}; + +struct gamut_space_entry { + uint32_t index; + uint32_t red_x; + uint32_t red_y; + uint32_t green_x; + uint32_t green_y; + uint32_t blue_x; + uint32_t blue_y; + + int32_t a0; + int32_t a1; + int32_t a2; + int32_t a3; + int32_t gamma; +}; + +struct white_point_coodinates_entry { + uint32_t index; + uint32_t white_x; + uint32_t white_y; +}; +union update_color_flags { + uint32_t raw; + struct { + uint32_t REGAMMA:1; + uint32_t GAMUT_DST:1; + uint32_t GAMUT_SRC:1; + uint32_t RESERVED:29; + } bits; +}; + +struct color_characteristic { + struct fixed31_32 red_x; + struct fixed31_32 red_y; + struct fixed31_32 green_x; + struct fixed31_32 green_y; + struct fixed31_32 blue_x; + struct fixed31_32 blue_y; + struct fixed31_32 white_x; + struct fixed31_32 white_y; +}; + +struct color_space_coodinates { + uint32_t red_x; + uint32_t red_y; + uint32_t green_x; + uint32_t green_y; + uint32_t blue_x; + uint32_t blue_y; + uint32_t white_x; + uint32_t white_y; +}; + +struct gamut_matrixs { + struct fixed31_32 *rgb_coeff_dst; + struct fixed31_32 *white_coeff_dst; + struct fixed31_32 *rgb_coeff_src; + struct fixed31_32 *white_coeff_src; +}; + +struct gamut_calculation_matrix { + struct fixed31_32 *transposed; + struct fixed31_32 *xyz_to_rgb_custom; + struct fixed31_32 *xyz_to_rgb_ref; + struct fixed31_32 *rgb_to_xyz_final; + struct fixed31_32 *result; + struct fixed31_32 *xyz_of_white_ref; + struct fixed31_32 *xyz_of_rgb_ref; +}; + +void dal_gamut_space_reset_gamut( + struct gamut_data *data, + bool gamut, + bool white_point); + +bool dal_gamut_space_update_gamut( + struct gamut_parameter *gamut, + bool set_regamma, + union update_color_flags *flags); + +bool dal_gamut_space_setup_default_gamut( + enum adjustment_id adj_id, + struct gamut_data *data, + bool gamut, + bool white_point); + +bool dal_gamut_space_build_gamut_space_matrix( + struct gamut_parameter *gamut, + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3], + struct ds_regamma_lut *regamma, + union update_color_flags *flags); + +bool dal_gamut_space_setup_white_point( + struct gamut_data *gamut, + struct ds_white_point_coordinates *data); + +bool dal_gamut_space_get_supported_gamuts( + struct ds_gamut_info *data); + +bool dal_gamut_space_convert_edid_format_color_charact( + const uint8_t pa[10], + struct color_characteristic *color_charact); + +bool dal_gamut_space_find_predefined_gamut( + union ds_gamut_spaces predefine, + struct color_space_coodinates *csc, + struct ds_regamma_coefficients_ex *gamma_coeff); + +bool dal_gamut_space_find_regamma_coefficients( + struct ds_regamma_coefficients_ex *coeff, + union ds_gamut_spaces *predefine); + +bool dal_gamut_space_find_color_coordinates( + struct color_space_coodinates *csc, + union ds_gamut_spaces *predefine); + +bool dal_gamut_space_is_equal_gamma_coefficients( + struct ds_regamma_coefficients_ex *coeff, + struct ds_regamma_coefficients_ex *coeff_custom, + union update_color_flags *flags); + +bool dal_gamut_space_find_predefined_white_point( + union ds_gamut_white_point predefined, + struct color_space_coodinates *csc); + +bool dal_gamut_space_build_default_unity_matrix( + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3], + struct ds_regamma_lut *regamma); + +bool dal_gamut_space_allocate_matrix( + struct gamut_matrixs *matrix); + +void dal_gamut_space_dellocate_matrix( + struct gamut_matrixs *matrix); + +bool dal_gamut_space_build_gamut_matrix( + struct fixed31_32 *rgb, + struct fixed31_32 *white, + struct ds_regamma_lut *regamma, + union update_color_flags *flags, + struct ds_regamma_lut *custom_regamma, + struct gamut_data *data, + bool is_source); + +bool dal_gamut_build_edid_matrix( + struct ds_regamma_lut *regamma, + union update_color_flags *flags, + uint32_t edid_gamma, + struct fixed31_32 *rgb, + struct fixed31_32 *white, + uint8_t pa[EDID_GAMUT_COORDINATES]); + +bool dal_gamut_space_build_chromaticity_matrix( + struct fixed31_32 *rgb, + struct fixed31_32 *white, + struct color_characteristic *pm); + +bool dal_gamut_space_gamut_to_color_matrix( + struct fixed31_32 *xyz_of_rgb, + struct fixed31_32 *xyz_of_white, + struct fixed31_32 *ref_xyz_of_rgb, + struct fixed31_32 *ref_xyz_of_white, + bool invert, + uint32_t temp_matrix[GAMUT_MATRIX_SIZE_3X3]); + +struct fixed31_32 dal_gamut_space_find_3x3_det( + struct fixed31_32 *m); + +bool dal_gamut_space_compute_inverse_matrix_3x3( + struct fixed31_32 *m, + struct fixed31_32 *im); + +void dal_gamut_space_multiply_matrices( + struct fixed31_32 *result, + struct fixed31_32 *m1, + struct fixed31_32 *m2, + uint32_t rows, + uint32_t cols1, + uint32_t cols2); + +bool dal_gamut_space_calculate_xyz_to_rgb_M3x3( + struct fixed31_32 *xyz_of_rgb, + struct fixed31_32 *xyz_of_white, + struct fixed31_32 *xyz_to_rgb); + +bool dal_gamut_space_allocate_calculation_matrix( + struct gamut_calculation_matrix *matrix); + +void dal_gamut_space_deallocate_calculation_matrix( + struct gamut_calculation_matrix *matrix); + +void dal_gamut_space_transpose_matrix( + struct fixed31_32 *m, + uint32_t rows, + uint32_t cols, + struct fixed31_32 *transposed); + +bool dal_gamut_space_convert_edid_format_color_charact( + const uint8_t pa[10], + struct color_characteristic *color_charact); + +bool dal_gamut_space_setup_predefined_regamma_coefficients( + struct gamut_data *data, + struct ds_regamma_lut *regamma); + +#endif /* __DAL_GAMUT_SPACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.c b/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.c new file mode 100644 index 000000000000..e70624188285 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.c @@ -0,0 +1,1326 @@ +/* + * Copyright 2015 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 "dal_services.h" +#include "include/hw_sequencer_types.h" +#include "include/display_service_types.h" +#include "include/display_path_interface.h" +#include "include/set_mode_interface.h" +#include "include/adjustment_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/hw_adjustment_set.h" +#include "include/logger_interface.h" +#include "include/fixed31_32.h" + +#include "ds_dispatch.h" +#include "adjustment_container.h" +#include "grph_colors_group.h" +#include "gamut_space.h" +#include "color_temperature.h" +#include "ds_translation.h" + +enum ds_color_space dal_grph_colors_group_get_color_space( + struct grph_colors_group *grph_colors_adj, + const struct crtc_timing *timing, + const struct display_path *disp_path, + struct adj_container *adj_container) +{ + const struct adjustment_info *nominal_limited = NULL; + enum ds_color_space color_space = DS_COLOR_SPACE_UNKNOWN; + + if (disp_path == NULL || timing == NULL) + return color_space; + + if (adj_container != NULL) { + nominal_limited = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_NOMINAL_RANGE_RGB_LIMITED); + switch (dal_display_path_get_query_signal( + disp_path, SINK_LINK_INDEX)) { + case SIGNAL_TYPE_MVPU_A: + case SIGNAL_TYPE_MVPU_B: + case SIGNAL_TYPE_MVPU_AB: + color_space = dal_adj_container_get_color_space( + adj_container); + break; + default: + break; + } + } + if (color_space == DS_COLOR_SPACE_UNKNOWN) { + enum ds_color_space hdmi_request_color_space = + DS_COLOR_SPACE_SRGB_LIMITEDRANGE; + if (nominal_limited != NULL && + (nominal_limited->adj_data.ranged.cur == + nominal_limited->adj_data.ranged.min)) + hdmi_request_color_space = + DS_COLOR_SPACE_SRGB_FULLRANGE; + color_space = + dal_grph_colors_group_build_default_color_space( + grph_colors_adj, + timing, + disp_path, + hdmi_request_color_space); + if (nominal_limited != NULL && + nominal_limited->adj_data.ranged.cur != + nominal_limited->adj_data.ranged.min && + color_space == DS_COLOR_SPACE_SRGB_FULLRANGE) + color_space = DS_COLOR_SPACE_SRGB_LIMITEDRANGE; + else if (nominal_limited != NULL && + nominal_limited->adj_data.ranged.cur == + nominal_limited->adj_data.ranged.min && + color_space == DS_COLOR_SPACE_SRGB_LIMITEDRANGE) + color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; + + } + return color_space; +} + +enum ds_return dal_grph_colors_group_set_adjustment( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value) +{ + enum ds_return ret = DS_ERROR; + const struct path_mode_set_with_data *pms_wd; + struct adj_container *adj_container; + const struct path_mode *path_mode; + const struct adjustment_info *adj_info; + struct gamut_parameter *gamut = NULL; + struct ds_regamma_lut *regamma = NULL; + struct hw_adjustment_color_control *color_control = NULL; + uint32_t display_index; + + if (!disp_path) + return DS_ERROR; + + display_index = dal_display_path_get_display_index( + disp_path); + + pms_wd = dal_ds_dispatch_get_active_pms_with_data( + grph_colors_adj->ds); + if (!pms_wd) + return DS_ERROR; + + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, + display_index); + + if (!adj_container) + return DS_ERROR; + + path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + pms_wd, + display_index); + + if (!path_mode) + return DS_ERROR; + + + adj_info = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + adj_id); + + if (!adj_info) + return DS_ERROR; + + if (dal_adj_container_is_adjustment_committed( + adj_container, adj_id) && + (adj_info->adj_data.ranged.cur == value)) + return DS_SUCCESS; + + if (!dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, + adj_id, value)) + return DS_ERROR; + + gamut = dal_alloc(sizeof(*gamut)); + if (!gamut) + goto gamut_fail; + regamma = dal_alloc(sizeof(*regamma)); + if (!regamma) + goto regamma_fail; + color_control = dal_alloc(sizeof(*color_control)); + if (!color_control) + goto color_control_fail; + + if (dal_grph_colors_group_compute_hw_adj_color_control( + grph_colors_adj, + adj_container, + &path_mode->mode_timing->crtc_timing, + disp_path, + adj_id, + gamut, + regamma, + color_control)) { + enum ds_color_space color_space; + + color_control->surface_pixel_format = + path_mode->pixel_format; + + dal_adj_container_set_regamma( + adj_container, + regamma); + + dal_hw_sequencer_set_color_control_adjustment( + grph_colors_adj->hws, + dal_display_path_get_controller(disp_path), + color_control); + + color_space = + dal_ds_translation_color_space_from_hw_color_space( + color_control->color_space); + + dal_adj_container_update_color_space( + adj_container, + color_space); + dal_adj_container_commit_adj(adj_container, adj_id); + ret = DS_SUCCESS; + } + + dal_free(color_control); +color_control_fail: + dal_free(regamma); +regamma_fail: + dal_free(gamut); +gamut_fail: + return ret; +} + +static int32_t get_hw_value_from_sw_value( + const struct hw_adjustment_range *hw_range, + const struct adjustment_info *sw_range) +{ + int32_t sw_val = sw_range->adj_data.ranged.cur; + int32_t sw_min = sw_range->adj_data.ranged.min; + int32_t sw_max = sw_range->adj_data.ranged.max; + int32_t hw_max = hw_range->max; + int32_t hw_min = hw_range->min; + int32_t sw_data = sw_max - sw_min; + int32_t hw_data = hw_max - hw_min; + int32_t hw_val; + + if (sw_data == 0) + return hw_min; + if (sw_data != hw_data) + hw_val = + (sw_val - sw_min) * hw_data / sw_data + hw_min; + else { + hw_val = sw_val; + if (sw_min != hw_min) + hw_val += (hw_min - sw_min); + } + + return hw_val; +} + +static bool is_current_same_as_default( + struct adjustment_info *adj_info) +{ + return adj_info->adj_data.ranged.cur == + adj_info->adj_data.ranged.def; +} + +bool dal_grph_colors_group_compute_hw_adj_color_control( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container, + const struct crtc_timing *timing, + struct display_path *disp_path, + enum adjustment_id reason_id, + struct gamut_parameter *gamut, + struct ds_regamma_lut *regamma, + struct hw_adjustment_color_control *color_control) +{ + struct adjustment_info *brightness = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_BRIGHTNESS); + struct adjustment_info *contrast = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_CONTRAST); + struct adjustment_info *hue = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_HUE); + struct adjustment_info *saturation = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_SATURATION); + struct adjustment_info *temperature = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE); + struct adjustment_info *temperature_src = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE_SOURCE); + const struct display_characteristics *disp_char = + dal_adj_container_get_disp_character( + adj_container); + struct hw_color_control_range hw_color_range; + struct white_point_data white_point; + const struct ds_regamma_lut *const_regamma = NULL; + int32_t requested_temperature; + bool gamut_adj_avails; + enum signal_type signal; + + /* reset updated info */ + grph_colors_adj->regamma_updated = false; + grph_colors_adj->gamut_dst_updated = false; + grph_colors_adj->gamut_src_updated = false; + + if (NULL == grph_colors_adj->hws || + NULL == brightness || + NULL == contrast || + NULL == hue || + NULL == saturation || + NULL == temperature || + NULL == disp_path) + return false; + + dal_memset(&hw_color_range, 0, sizeof(hw_color_range)); + dal_memset(&white_point, 0, sizeof(white_point)); + requested_temperature = temperature->adj_data.ranged.cur; + signal = dal_display_path_get_query_signal( + disp_path, SINK_LINK_INDEX); + color_control->adjust_divider = ADJUST_DIVIDER; + color_control->temperature_divider = GAMUT_DIVIDER; + gamut_adj_avails = + dal_hw_sequencer_is_support_custom_gamut_adj( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE); + + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut->gamut_src)) + return false; + + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &gamut->gamut_dst)) + return false; + + const_regamma = dal_adj_container_get_regamma( + adj_container); + if (!const_regamma) + return false; + dal_memmove(&gamut->regamma, const_regamma, sizeof(gamut->regamma)); + + if (signal == SIGNAL_TYPE_HDMI_TYPE_A) + gamut->source = GAMUT_SPACE_SOURCE_DEFAULT; + else { + gamut->source = + temperature_src->adj_data.ranged.cur == + COLOR_TEMPERATURE_SOURCE_EDID ? + GAMUT_SPACE_SOURCE_EDID : + GAMUT_SPACE_SOURCE_USER_GAMUT; + if (requested_temperature == -1) + gamut->source = GAMUT_SPACE_SOURCE_EDID; + + if (disp_char == NULL && gamut->source == + GAMUT_SPACE_SOURCE_EDID) + gamut->source = GAMUT_SPACE_SOURCE_DEFAULT; + + if (gamut->source == GAMUT_SPACE_SOURCE_EDID) { + uint32_t i; + + dal_gamut_space_reset_gamut( + &gamut->gamut_dst, true, true); + + for (i = 0; + i < sizeof(gamut->color_charact.color_charact); + i++) { + gamut->color_charact.color_charact[i] = + disp_char->color_characteristics[i]; + } + if (disp_char->gamma > 0) + gamut->color_charact.gamma = + (disp_char->gamma + 100)*10; + else + gamut->color_charact.gamma = 0; + } + } + if (gamut->source == GAMUT_SPACE_SOURCE_DEFAULT) { + requested_temperature = temperature->adj_data.ranged.def; + if (!dal_color_temperature_find_white_point( + requested_temperature, + &white_point)) + return false; + dal_gamut_space_reset_gamut( + &gamut->gamut_src, false, true); + gamut->gamut_src.option.bits.CUSTOM_WHITE_POINT = 1; + gamut->gamut_src.white_point.custom.white_x = + white_point.white_x; + gamut->gamut_src.white_point.custom.white_y = + white_point.white_y; + + if (!dal_adj_container_validate_gamut( + adj_container, + &gamut->gamut_src)) + return false; + + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut->gamut_src); + { + struct adjustment_info *non_const_temp = temperature; + + non_const_temp->adj_data.ranged.cur = + requested_temperature; + } + } + { + union update_color_flags flags = {0}; + + if (!dal_gamut_space_update_gamut( + gamut, false, &flags)) + return false; + if (flags.bits.GAMUT_DST == 1) + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &gamut->gamut_dst); + if (reason_id != ADJ_ID_GAMUT_SOURCE_GRPH && + reason_id != ADJ_ID_GAMUT_DESTINATION) { + if (gamut->source == GAMUT_SPACE_SOURCE_EDID) + dal_gamut_space_setup_default_gamut( + reason_id, + &gamut->gamut_src, + false, + true); + } + if (!dal_gamut_space_build_gamut_space_matrix( + gamut, + color_control->temperature_matrix, + regamma, + &flags)) + return false; + + if (flags.bits.REGAMMA > 0) + grph_colors_adj->regamma_updated = true; + if (flags.bits.GAMUT_SRC > 0) + grph_colors_adj->gamut_src_updated = true; + if (flags.bits.GAMUT_DST > 0) + grph_colors_adj->gamut_dst_updated = true; + } + if (dal_hw_sequencer_get_hw_color_adj_range( + grph_colors_adj->hws, + disp_path, + &hw_color_range) != HWSS_RESULT_OK) + return false; + color_control->color_space = + dal_ds_translation_hw_color_space_from_color_space( + dal_grph_colors_group_get_color_space( + grph_colors_adj, + timing, + disp_path, + adj_container)); + if (color_control->color_space == + HW_COLOR_SPACE_UNKNOWN) + return false; + { + struct hw_crtc_timing hw_timing; + enum signal_type asic_signal = + dal_display_path_get_query_signal( + disp_path, ASIC_LINK_INDEX); + dal_ds_translation_hw_crtc_timing_from_crtc_timing( + &hw_timing, + timing, + VIEW_3D_FORMAT_NONE, + asic_signal); + + color_control->color_depth = + hw_timing.flags.COLOR_DEPTH; + color_control->brightness = + get_hw_value_from_sw_value( + &hw_color_range.brightness, + brightness); + color_control->contrast = + get_hw_value_from_sw_value( + &hw_color_range.contrast, + contrast); + color_control->hue = + get_hw_value_from_sw_value( + &hw_color_range.hue, + hue); + color_control->saturation = + get_hw_value_from_sw_value( + &hw_color_range.saturation, + saturation); + if (gamut->source == GAMUT_SPACE_SOURCE_USER_GAMUT && + gamut_adj_avails == false && + is_current_same_as_default(saturation) && + is_current_same_as_default(contrast) && + is_current_same_as_default(brightness) && + is_current_same_as_default(hue) && + is_current_same_as_default(temperature)) + color_control->option = HWS_COLOR_MATRIX_HW_DEFAULT; + else + color_control->option = HWS_COLOR_MATRIX_SW; + + } + + return true; +} + +bool dal_grph_colors_group_build_color_control_adj( + struct grph_colors_group *grph_colors_adj, + const struct path_mode *mode, + struct display_path *disp_path, + struct hw_adjustment_set *set) +{ + struct adj_container *adj_container; + struct gamut_parameter *gamut = NULL; + struct ds_regamma_lut *regamma = NULL; + struct hw_adjustment_color_control *color_control = NULL; + enum ds_color_space color_space; + + if (!disp_path) + return false; + gamut = dal_alloc(sizeof(*gamut)); + if (!gamut) + goto gamut_fail; + regamma = dal_alloc(sizeof(*regamma)); + if (!regamma) + goto regamma_fail; + color_control = dal_alloc(sizeof(*color_control)); + if (!color_control) + goto color_control_fail; + + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, + mode->display_path_index); + if (!adj_container) + goto adj_container_fail; + + color_control->color_space = HW_COLOR_SPACE_UNKNOWN; + + if (!dal_grph_colors_group_compute_hw_adj_color_control( + grph_colors_adj, + adj_container, + &mode->mode_timing->crtc_timing, + disp_path, + ADJ_ID_INVALID, + gamut, + regamma, + color_control)) + goto adj_container_fail; + color_control->surface_pixel_format = mode->pixel_format; + set->color_control = color_control; + color_space = dal_ds_translation_color_space_from_hw_color_space( + color_control->color_space); + dal_adj_container_update_color_space( + adj_container, + color_space); + + dal_free(regamma); + dal_free(gamut); + + return true; + +adj_container_fail: + dal_free(color_control); +color_control_fail: + dal_free(regamma); +regamma_fail: + dal_free(gamut); +gamut_fail: + return false; +} + +enum ds_color_space dal_grph_colors_group_build_default_color_space( + struct grph_colors_group *grph_colors_adj, + const struct crtc_timing *timing, + const struct display_path *disp_path, + enum ds_color_space hdmi_request_color_space) +{ + enum ds_color_space color_space = + DS_COLOR_SPACE_SRGB_FULLRANGE; + + switch (dal_display_path_get_query_signal( + disp_path, SINK_LINK_INDEX)) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 || + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) { + color_space = (timing->pix_clk_khz > PIXEL_CLOCK) ? + DS_COLOR_SPACE_YCBCR709 : + DS_COLOR_SPACE_YCBCR601; + if (timing->flags.YONLY == 1) + color_space = + (timing->pix_clk_khz > PIXEL_CLOCK) ? + DS_COLOR_SPACE_YCBCR709_YONLY : + DS_COLOR_SPACE_YCBCR601_YONLY; + } + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + { + uint32_t pix_clk_khz; + + color_space = hdmi_request_color_space; + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) { + if (timing->timing_standard == + TIMING_STANDARD_CEA770 && + timing->timing_standard == + TIMING_STANDARD_CEA861) + color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; + /* else { + union cea_video_capability_data_block + vcdb = {{0 }}; + bool ret = + dal_edid_base_get_cea_video_capability_data_block( + disp_path->dcs->edid_mgr->edid_list, + &vcdb); + if ((ret == true) && (vcdb.bits.QS == 1)) + color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; + } */ + pix_clk_khz = timing->pix_clk_khz / 10; + if (timing->h_addressable == 640 && + timing->v_addressable == 480 && + (pix_clk_khz == 2520 || pix_clk_khz == 2517)) + color_space = DS_COLOR_SPACE_SRGB_FULLRANGE; + } else { + if (timing->timing_standard == + TIMING_STANDARD_CEA770 || + timing->timing_standard == + TIMING_STANDARD_CEA861) { + /* struct cea_colorimetry_data_block + * data_block = {0}; + if (dal_edid_base_get_cea_colorimetry_data_block( + disp_path->dcs->edid_mgr->edid_list, + &data_block)) { + if (data_block->flag.XV_YCC601 == 1 && + data_block->flag.XV_YCC709 == 1) + color_space = + (timing->pix_clk_khz > PIXEL_CLOCK) ? + DS_COLOR_SPACE_YCBCR709 : + DS_COLOR_SPACE_YCBCR601; + else + color_space = + (data_block->flag.XV_YCC709 == 1) ? + DS_COLOR_SPACE_YCBCR709 : + DS_COLOR_SPACE_YCBCR601; + } else */ + color_space = + (timing->pix_clk_khz > PIXEL_CLOCK) ? + DS_COLOR_SPACE_YCBCR709 : + DS_COLOR_SPACE_YCBCR601; + } + } + break; + } + default: + switch (timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + case PIXEL_ENCODING_YCBCR444: + if (timing->pix_clk_khz > PIXEL_CLOCK) + color_space = DS_COLOR_SPACE_YCBCR709; + else + color_space = DS_COLOR_SPACE_YCBCR601; + break; + default: + break; + } + break; + } + return color_space; +} +enum ds_color_space dal_grph_colors_group_adjust_color_space( + struct grph_colors_group *grph_colors_adj, + enum ds_color_space color_space, + bool rgb_limited) +{ + if (rgb_limited && color_space == DS_COLOR_SPACE_SRGB_FULLRANGE) + color_space = DS_COLOR_SPACE_SRGB_LIMITEDRANGE; + return color_space; +} + +bool dal_grph_colors_group_synch_color_temperature_with_gamut( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container) +{ + struct gamut_data gamut_data; + struct ds_white_point_coordinates data; + struct white_point_data white_data; + int32_t temperature; + bool extra_match; + + dal_memset(&gamut_data, 0, sizeof(gamut_data)); + dal_memset(&data, 0, sizeof(data)); + dal_memset(&white_data, 0, sizeof(white_data)); + + dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_data); + if (!dal_gamut_space_setup_white_point( + &gamut_data, + &data)) + return false; + + white_data.white_x = data.white_x; + white_data.white_y = data.white_y; + + if (!dal_color_temperature_find_color_temperature( + &white_data, + &temperature, + &extra_match)) + return false; + + if (!dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE, + temperature)) + return false; + + return true; +} + +bool dal_grph_colors_group_synch_gamut_with_color_temperature( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container) +{ + struct gamut_data gamut_data; + struct white_point_data white_data; + struct adjustment_info *temperature = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE); + + dal_memset(&gamut_data, 0, sizeof(gamut_data)); + dal_memset(&white_data, 0, sizeof(white_data)); + + if (!dal_color_temperature_find_white_point( + temperature->adj_data.ranged.cur, + &white_data)) + return false; + dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_data); + dal_gamut_space_reset_gamut( + &gamut_data, + false, + true); + + gamut_data.option.bits.CUSTOM_WHITE_POINT = 1; + gamut_data.white_point.custom.white_x = white_data.white_x; + gamut_data.white_point.custom.white_y = white_data.white_y; + + if (!dal_adj_container_validate_gamut( + adj_container, + &gamut_data)) + return false; + + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_data); + return true; + +} + +bool dal_grph_colors_group_get_color_temperature( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container, + int32_t *temp) +{ + struct gamut_data gamut_data; + struct ds_white_point_coordinates data; + struct white_point_data white_data; + bool exact_match; + + dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut_data); + if (dal_gamut_space_setup_white_point( + &gamut_data, + &data)) { + white_data.white_x = data.white_x; + white_data.white_y = data.white_y; + return dal_color_temperature_find_color_temperature( + &white_data, temp, &exact_match); + } + return false; +} + +enum ds_return dal_grph_colors_group_set_color_graphics_gamut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + struct gamut_data *gamut_data, + enum adjustment_id adj_id, + bool apply_to_hw) +{ + uint32_t display_index; + struct adj_container *adj_container = NULL; + const struct path_mode_set_with_data *pms_wd = NULL; + const struct path_mode *path_mode = NULL; + struct hw_adjustment_color_control *color_control = NULL; + struct gamut_parameter *gamut = NULL; + struct ds_regamma_lut *regamma = NULL; + struct ds_regamma_lut *old_regamma = NULL; + + if (!disp_path) + return DS_ERROR; + display_index = dal_display_path_get_display_index( + disp_path); + + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, display_index); + if (!adj_container) + return DS_ERROR; + + pms_wd = dal_ds_dispatch_get_active_pms_with_data( + grph_colors_adj->ds); + if (!pms_wd) + return DS_ERROR; + + path_mode = dal_pms_with_data_get_path_mode_for_display_index( + pms_wd, display_index); + if (!path_mode) + return DS_ERROR; + + if (!dal_hw_sequencer_is_support_custom_gamut_adj( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE)) + return DS_ERROR; + + if (!dal_adj_container_validate_gamut( + adj_container, + gamut_data)) + return DS_ERROR; + + dal_adj_container_update_gamut( + adj_container, + adj_id, gamut_data); + + if (apply_to_hw) { + enum hwss_result result; + enum ds_color_space color_space; + + color_control = dal_alloc(sizeof(*color_control)); + if (!color_control) + return DS_ERROR; + gamut = dal_alloc(sizeof(*gamut)); + if (!gamut) + goto gamut_fail; + regamma = dal_alloc(sizeof(*regamma)); + if (!regamma) + goto regamma_fail; + old_regamma = dal_alloc(sizeof(*old_regamma)); + if (!old_regamma) + goto old_regamma_fail; + + if (!dal_grph_colors_group_compute_hw_adj_color_control( + grph_colors_adj, + adj_container, + &path_mode->mode_timing->crtc_timing, + disp_path, + adj_id, + gamut, + regamma, + color_control)) + goto adj_color_control_fail; + + color_control->surface_pixel_format = + path_mode->pixel_format; + + result = dal_hw_sequencer_set_color_control_adjustment( + grph_colors_adj->hws, + dal_display_path_get_controller(disp_path), + color_control); + + if (result == HWSS_RESULT_OK) { + if (grph_colors_adj->regamma_updated) { + enum ds_return ret; + + if (!dal_adj_container_get_regamma_copy( + adj_container, + old_regamma)) + goto adj_color_control_fail; + + dal_adj_container_set_regamma( + adj_container, + regamma); + ret = dal_ds_dispatch_set_gamma_adjustment( + grph_colors_adj->ds, + display_index, + ADJ_ID_GAMMA_RAMP, + dal_ds_dispatch_get_current_gamma( + grph_colors_adj->ds, + display_index, + ADJ_ID_GAMMA_RAMP)); + if (ret != DS_SUCCESS) + dal_adj_container_set_regamma( + adj_container, + regamma); + } + } + color_space = + dal_ds_translation_color_space_from_hw_color_space( + color_control->color_space); + dal_ds_dispatch_update_adj_container_for_path_with_color_space( + grph_colors_adj->ds, + display_index, + color_space); + } + dal_grph_colors_group_synch_color_temperature_with_gamut( + grph_colors_adj, + adj_container); + return DS_SUCCESS; + +adj_color_control_fail: + dal_free(old_regamma); +old_regamma_fail: + dal_free(regamma); +regamma_fail: + dal_free(gamut); +gamut_fail: + dal_free(color_control); + + return DS_ERROR; +} + +enum ds_return dal_grph_colors_group_get_color_gamut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + const struct ds_gamut_reference_data *ref, + struct ds_get_gamut_data *data) +{ + enum ds_return ret; + uint32_t display_index; + struct adj_container *adj_container = NULL; + struct adjustment_info *temperature_src = NULL; + const struct display_characteristics *disp_char = NULL; + struct color_characteristic color_charact = {{0 } }; + struct gamut_data gamut_data; + enum adjustment_id adj_id; + struct fixed31_32 result; + + if (!disp_path) + return DS_ERROR; + display_index = dal_display_path_get_display_index( + disp_path); + + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, display_index); + if (!adj_container) + return DS_ERROR; + + if (!dal_hw_sequencer_is_support_custom_gamut_adj( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE)) + return DS_ERROR; + + dal_memset(&gamut_data, 0, sizeof(gamut_data)); + if (!dal_ds_translate_gamut_reference( + ref, &adj_id)) + return DS_ERROR; + + if (adj_id == ADJ_ID_GAMUT_DESTINATION) { + temperature_src = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE_SOURCE); + if (temperature_src != NULL && + temperature_src->adj_data.ranged.cur == + COLOR_TEMPERATURE_SOURCE_EDID) { + + disp_char = dal_adj_container_get_disp_character( + adj_container); + if (!disp_char) + return DS_ERROR; + if (!dal_gamut_space_convert_edid_format_color_charact( + disp_char->color_characteristics, + &color_charact)) + return DS_ERROR; + gamut_data.option.bits.CUSTOM_GAMUT_SPACE = 1; + gamut_data.option.bits.CUSTOM_WHITE_POINT = 1; + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.red_x); + gamut_data.gamut.custom.red_x = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.red_y); + gamut_data.gamut.custom.red_y = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.blue_x); + gamut_data.gamut.custom.blue_x = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.blue_y); + gamut_data.gamut.custom.blue_y = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.green_x); + gamut_data.gamut.custom.green_x = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.green_y); + gamut_data.gamut.custom.green_y = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.white_x); + gamut_data.white_point.custom.white_x = + dal_fixed31_32_floor(result); + + result = dal_fixed31_32_mul( + dal_fixed31_32_from_int( + (int64_t)GAMUT_DIVIDER), + color_charact.white_y); + gamut_data.white_point.custom.white_y = + dal_fixed31_32_floor(result); + + ret = DS_SUCCESS; + } + } + if (ret != DS_SUCCESS) + if (!dal_adj_container_get_gamut( + adj_container, + adj_id, + &gamut_data)) + return DS_ERROR; + if (!dal_ds_translate_internal_gamut_to_external_parameter( + &gamut_data, + &data->gamut)) + return DS_ERROR; + return ret; +} + +enum ds_return dal_grph_colors_group_get_color_gamut_info( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + const struct ds_gamut_reference_data *ref, + struct ds_gamut_info *data) +{ + if (!disp_path) + return DS_ERROR; + + if (!dal_hw_sequencer_is_support_custom_gamut_adj( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE)) + return DS_ERROR; + + if (!dal_gamut_space_get_supported_gamuts(data)) + return DS_ERROR; + + return DS_SUCCESS; +} + +enum ds_return dal_grph_colors_group_get_regamma_lut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + struct ds_regamma_lut *data) +{ + uint32_t display_index; + struct adj_container *adj_container = NULL; + const struct ds_regamma_lut *regamma = NULL; + + if (!disp_path) + return DS_ERROR; + display_index = dal_display_path_get_display_index( + disp_path); + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, display_index); + if (!adj_container) + return DS_ERROR; + + if (!dal_hw_sequencer_is_support_custom_gamma_coefficients( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE)) + return DS_ERROR; + + regamma = dal_adj_container_get_regamma(adj_container); + if (!regamma) + return DS_ERROR; + + dal_ds_translate_regamma_to_external(regamma, data); + return DS_SUCCESS; +} + +enum ds_return dal_grph_colors_group_set_regamma_lut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + const struct ds_regamma_lut *data) +{ + uint32_t display_index; + enum ds_return ret; + struct adj_container *adj_container = NULL; + struct ds_regamma_lut *regamma = NULL; + struct ds_regamma_lut *old_regamma = NULL; + const struct raw_gamma_ramp *ramp = NULL; + struct gamut_data original_gamut = {{0 } }; + struct gamut_data updated_gamut = {{0 } }; + bool updated_gamut_dst = false; + + if (!disp_path) + return DS_ERROR; + display_index = dal_display_path_get_display_index( + disp_path); + adj_container = dal_ds_dispatch_get_adj_container_for_path( + grph_colors_adj->ds, display_index); + if (!adj_container) + return DS_ERROR; + + if (data->flags.bits.GAMMA_FROM_EDID_EX == 1 || + data->flags.bits.COEFF_FROM_EDID == 1) + return DS_ERROR; + + if (!dal_hw_sequencer_is_support_custom_gamma_coefficients( + grph_colors_adj->hws, + disp_path, + HW_GRAPHIC_SURFACE)) + return DS_ERROR; + + regamma = dal_alloc(sizeof(*regamma)); + if (!regamma) + goto regamma_fail; + + old_regamma = dal_alloc(sizeof(*old_regamma)); + if (!old_regamma) + goto old_regamma_fail; + + ramp = dal_ds_dispatch_get_current_gamma( + grph_colors_adj->ds, + display_index, + ADJ_ID_GAMMA_RAMP); + if (!dal_adj_container_get_regamma_copy( + adj_container, + old_regamma)) + goto regamma_copy_fail; + + dal_memmove(regamma, old_regamma, sizeof(*regamma)); + + if (!dal_ds_translate_regamma_to_internal( + data, regamma)) + goto regamma_copy_fail; + + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &original_gamut)) + goto regamma_copy_fail; + + if (!dal_adj_container_set_regamma( + adj_container, + regamma)) + goto regamma_copy_fail; + + updated_gamut_dst = dal_grph_colors_group_update_gamut( + grph_colors_adj, + disp_path, + adj_container); + + ret = dal_ds_dispatch_set_gamma_adjustment( + grph_colors_adj->ds, + display_index, + ADJ_ID_GAMMA_RAMP_REGAMMA_UPDATE, + ramp); + + if (ret != DS_SUCCESS) { + if (updated_gamut_dst) + dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &original_gamut); + if (!dal_adj_container_set_regamma( + adj_container, + old_regamma)) + goto regamma_copy_fail; + } else { + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &updated_gamut)) + goto regamma_copy_fail; + + return ret; + } + +regamma_copy_fail: + dal_free(old_regamma); +old_regamma_fail: + dal_free(regamma); +regamma_fail: + return DS_ERROR; +} + +enum ds_return dal_grph_colors_group_update_gamut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + struct adj_container *adj_container) +{ + struct gamut_parameter *gamut = NULL; + enum signal_type signal; + const struct adjustment_info *temperature = NULL; + const struct adjustment_info *temperature_src = NULL; + const struct ds_regamma_lut *const_regamma = NULL; + + if (!disp_path) + return false; + + signal = dal_display_path_get_query_signal( + disp_path, SINK_LINK_INDEX); + if (signal == SIGNAL_TYPE_HDMI_TYPE_A) + return false; + + gamut = dal_alloc(sizeof(*gamut)); + if (!gamut) + return false; + + temperature = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE); + temperature_src = dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, + ADJ_ID_TEMPERATURE_SOURCE); + if (temperature == NULL || + temperature->adj_data.ranged.cur == -1) + goto gamut_fail; + + gamut->source = ((temperature_src != NULL) && + (temperature_src->adj_data.ranged.cur == + COLOR_TEMPERATURE_SOURCE_EDID)) ? + GAMUT_SPACE_SOURCE_EDID : + GAMUT_SPACE_SOURCE_USER_GAMUT; + if (gamut->source != GAMUT_SPACE_SOURCE_USER_GAMUT) + goto gamut_fail; + + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_SOURCE_GRPH, + &gamut->gamut_src)) + goto gamut_fail; + + if (!dal_adj_container_get_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &gamut->gamut_dst)) + goto gamut_fail; + + const_regamma = dal_adj_container_get_regamma( + adj_container); + if (!const_regamma) + goto gamut_fail; + dal_memmove(&gamut->regamma, const_regamma, sizeof(gamut->regamma)); + { + union update_color_flags flags = {0}; + + if (!dal_gamut_space_update_gamut( + gamut, false, &flags)) + goto gamut_fail; + if (flags.bits.GAMUT_DST == 1) + return dal_adj_container_update_gamut( + adj_container, + ADJ_ID_GAMUT_DESTINATION, + &gamut->gamut_dst); + } + +gamut_fail: + dal_free(gamut); + return false; +} + +static bool grph_colors_group_construct( + struct grph_colors_group *grph_colors_adj, + struct grph_colors_group_init_data *init_data) +{ + if (!init_data) + return false; + + grph_colors_adj->ds = init_data->ds; + grph_colors_adj->hws = init_data->hws; + grph_colors_adj->dal_context = init_data->dal_context; + + return true; +} + +struct grph_colors_group *dal_grph_colors_group_create( + struct grph_colors_group_init_data *init_data) +{ + struct grph_colors_group *grph_colors_adj = NULL; + + grph_colors_adj = dal_alloc(sizeof(*grph_colors_adj)); + + if (!grph_colors_adj) + return NULL; + + if (grph_colors_group_construct(grph_colors_adj, init_data)) + return grph_colors_adj; + + dal_free(grph_colors_adj); + + return NULL; +} + +static void destruct( + struct grph_colors_group *grph_colors_adj) +{ + +} + +void dal_grph_colors_adj_group_destroy( + struct grph_colors_group **grph_colors_adj) +{ + if (grph_colors_adj == NULL || *grph_colors_adj == NULL) + return; + destruct(*grph_colors_adj); + dal_free(*grph_colors_adj); + *grph_colors_adj = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.h b/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.h new file mode 100644 index 000000000000..619c7aae4652 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/grph_colors_group.h @@ -0,0 +1,124 @@ +/* + * Copyright 2015 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 __DAL_GRPH_COLORS_GROUP_H__ +#define __DAL_GRPH_COLORS_GROUP_H__ + +#include "include/adjustment_types.h" + +struct grph_colors_group; +struct crtc_timing; +struct display_path; +struct adj_container; +struct gamut_parameter; + +#define PIXEL_CLOCK 27030 + +struct grph_colors_group { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct dal_context *dal_context; + bool regamma_updated; + bool gamut_dst_updated; + bool gamut_src_updated; +}; + +struct grph_colors_group_init_data { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct dal_context *dal_context; +}; + +enum ds_color_space dal_grph_colors_group_get_color_space( + struct grph_colors_group *grph_colors_adj, + const struct crtc_timing *timing, + const struct display_path *disp_path, + struct adj_container *adj_container); + +enum ds_return dal_grph_colors_group_set_adjustment( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value); + +bool dal_grph_colors_group_compute_hw_adj_color_control( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container, + const struct crtc_timing *timing, + struct display_path *disp_path, + enum adjustment_id adj_id, + struct gamut_parameter *gamut, + struct ds_regamma_lut *regamma, + struct hw_adjustment_color_control *color_control); + +bool dal_grph_colors_group_build_color_control_adj( + struct grph_colors_group *grph_colors_adj, + const struct path_mode *mode, + struct display_path *disp_path, + struct hw_adjustment_set *set); + +enum ds_color_space dal_grph_colors_group_build_default_color_space( + struct grph_colors_group *grph_colors_adj, + const struct crtc_timing *timing, + const struct display_path *disp_path, + enum ds_color_space hdmi_request_color_space); + +enum ds_color_space dal_grph_colors_group_adjust_color_space( + struct grph_colors_group *grph_colors_adj, + enum ds_color_space color_space, + bool rgb_limited); + +bool dal_grph_colors_group_synch_color_temperature_with_gamut( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container); + +bool dal_grph_colors_group_synch_gamut_with_color_temperature( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container); + +bool dal_grph_colors_group_get_color_temperature( + struct grph_colors_group *grph_colors_adj, + struct adj_container *adj_container, + int32_t *temp); + +enum ds_return dal_grph_colors_group_set_color_graphics_gamut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + struct gamut_data *gamut_data, + enum adjustment_id adj_id, + bool apply_to_hw); + +enum ds_return dal_grph_colors_group_update_gamut( + struct grph_colors_group *grph_colors_adj, + struct display_path *disp_path, + struct adj_container *adj_container); + +struct grph_colors_group *dal_grph_colors_group_create( + struct grph_colors_group_init_data *init_data); + +void dal_grph_colors_adj_group_destroy( + struct grph_colors_group **grph_colors_adj); + +#endif /* __DAL_GRPH_COLORS_GROUP_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/path_mode_set.c b/drivers/gpu/drm/amd/dal/display_service/path_mode_set.c new file mode 100644 index 000000000000..55e040d63ee2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/path_mode_set.c @@ -0,0 +1,220 @@ +/* + * Copyright 2012-15 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 */ +#include "dal_services.h" +#include "include/set_mode_interface.h" +#include "include/hw_sequencer_types.h" +#include "ds_dispatch.h" + +/* Create path mode set */ +struct path_mode_set *dal_pms_create() +{ + struct path_mode_set *set; + + set = dal_alloc(sizeof(struct path_mode_set)); + + if (set == NULL) + return NULL; + + if (dal_pms_construct(set)) + return set; + + dal_free(set); + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dal_pms_destroy(struct path_mode_set **pms) +{ + if (!pms || !*pms) { + BREAK_TO_DEBUGGER(); + return; + } + + dal_free(*pms); + *pms = NULL; +} + +/* Create a copy of given path mode set */ +struct path_mode_set *dal_pms_copy(const struct path_mode_set *copy) +{ + struct path_mode_set *set = NULL; + + if (copy == NULL) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + set = dal_alloc(sizeof(struct path_mode_set)); + + if (set == NULL) + return NULL; + + if (dal_pms_construct(set)) { + uint32_t i = 0; + + set->count = copy->count; + set->control_flags.all = copy->control_flags.all; + + for (i = 0; i < set->count; i++) + set->path_mode_set[i] = copy->path_mode_set[i]; + + return set; + } + + dal_free(set); + BREAK_TO_DEBUGGER(); + return NULL; + + +} + +/* Constructor for path mode set */ +bool dal_pms_construct(struct path_mode_set *set) +{ + if (set == NULL) + return false; + + set->count = 0; + set->control_flags.all = 0; + + return true; +} + +/* Add a path mode into the set */ +bool dal_pms_add_path_mode( + struct path_mode_set *set, + const struct path_mode *path_mode) +{ + if (set->count >= MAX_COFUNC_PATH) + return false; + + /* Check if display index is already in the set */ + if (dal_pms_get_path_mode_for_display_index( + set, path_mode->display_path_index) != NULL) + return false; + + set->path_mode_set[set->count] = *path_mode; + set->count++; + + return true; +} + +/* Get number of path modes in the set */ +uint32_t dal_pms_get_path_mode_num(const struct path_mode_set *set) +{ + if (set == NULL) { + BREAK_TO_DEBUGGER(); + return 0; + } + return set->count; +} + +/* Return the path mode at the index */ +const struct path_mode *dal_pms_get_path_mode_at_index( + const struct path_mode_set *set, + uint32_t index) +{ + if (index >= set->count) + return NULL; + else + return &set->path_mode_set[index]; +} + +/* Return the path mode for the given display index */ +const struct path_mode *dal_pms_get_path_mode_for_display_index( + const struct path_mode_set *set, + uint32_t index) +{ + uint32_t i; + + for (i = 0; i < set->count; i++) { + if (set->path_mode_set[i].display_path_index == index) + return &set->path_mode_set[i]; + } + return NULL; +} + +/* Add control flag to keep display powered off */ +void dal_pms_keep_display_powered_off( + struct path_mode_set *set, + bool keep) +{ + set->control_flags.bits.KEEP_DISPLAY_POWERED_OFF = keep; +} + +/* Return control flag if display needs to be kept powered off */ +bool dal_pms_is_display_power_off_required(const struct path_mode_set *set) +{ + return set->control_flags.bits.KEEP_DISPLAY_POWERED_OFF; +} + +/* Add control flag to not use default underscan*/ +void dal_pms_fallback_remove_default_underscan( + struct path_mode_set *set, + bool lean) +{ + /* TODO: implementation */ +} + +/* Return control flag if default underscan is not used */ +bool dal_pms_is_fallback_no_default_underscan_enabled( + struct path_mode_set *set) +{ + return false; +} + +/* Remove path mode at index from the set */ +bool dal_pms_remove_path_mode_at_index( + struct path_mode_set *set, + uint32_t index) +{ + if (index < set->count) { + uint32_t i = 0; + + for (i = index; i < set->count; i++) + set->path_mode_set[i] = set->path_mode_set[i + 1]; + set->count--; + } else + return false; + + return true; +} + +/* Remove path mode from the set if given mode is found */ +bool dal_pms_remove_path_mode( + struct path_mode_set *set, + struct path_mode *mode) +{ + uint32_t i; + + for (i = 0; i < set->count; i++) { + if (&set->path_mode_set[i] == mode) + return dal_pms_remove_path_mode_at_index(set, i); + } + + return false; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.c b/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.c new file mode 100644 index 000000000000..43dd1e0a8758 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.c @@ -0,0 +1,308 @@ +/* + * Copyright 2012-15 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 "dal_services.h" + +#include "include/vector.h" +#include "include/plane_types.h" + +#include "path_mode_set_with_data.h" + +/* Set of path modes with corresponding data flags*/ +struct path_mode_set_with_data { + struct path_mode_set base; + struct mode_timing mode_timing[MAX_COFUNC_PATH]; + struct active_path_data path_data[MAX_COFUNC_PATH]; + + struct vector plane_configs[MAX_COFUNC_PATH]; +}; + +#define FROM_PMS(ptr) \ + container_of((ptr), struct path_mode_set_with_data, base) + +/* Constructor for path mode set with data */ +static bool construct(struct path_mode_set_with_data *set) +{ + if (set == NULL) + return false; + + if (!dal_pms_construct(&set->base)) + return false; + + return true; +} + +/* + * Path mode set with data + */ + +/* Create the set for path mode with data */ +struct path_mode_set_with_data *dal_pms_with_data_create() +{ + struct path_mode_set_with_data *set = NULL; + + set = dal_alloc(sizeof(struct path_mode_set_with_data)); + + if (set == NULL) + return NULL; + + if (construct(set)) + return set; + + dal_free(set); + BREAK_TO_DEBUGGER(); + return NULL; +} + +/* Add path mode with data into the set */ +bool dal_pms_with_data_add_path_mode_with_data( + struct path_mode_set_with_data *set, + const struct path_mode *mode, + const struct active_path_data *data) +{ + bool result = dal_pms_add_path_mode(&set->base, mode); + uint32_t index = set->base.count - 1; + const uint32_t preallocate_planes_num = 4; + + if (result) { + set->mode_timing[index] = *mode->mode_timing; + set->base.path_mode_set[index].mode_timing = + &set->mode_timing[index]; + + set->path_data[index].current_underscan.x = 0; + set->path_data[index].current_underscan.y = 0; + set->path_data[index].current_underscan.width = 0; + set->path_data[index].current_underscan.height = 0; + set->path_data[index].flags.all = 0; + /* TODO: set->path_data[index].viewport_adjustment */ + + /* TODO: viewport_adjustment + uint32_t i = 0; + for (i = 0; i < HW_MAX_NUM_VIEW_PORTS; i++) { + + } + */ + + if (data != NULL) { + set->path_data[index].ws_stereo_state = + data->ws_stereo_state; + set->path_data[index].gtc_group = data->gtc_group; + set->path_data[index].display_state.OUTPUT_ENABLED = + data->display_state.OUTPUT_ENABLED; + set->path_data[index].display_state.OUTPUT_BLANKED = + data->display_state.OUTPUT_BLANKED; + } else { + set->path_data[index].ws_stereo_state = + WS_STEREO_STATE_INACTIVE; + set->path_data[index].gtc_group = GTC_GROUP_DISABLED; + set->path_data[index].display_state.OUTPUT_ENABLED = 0; + set->path_data[index].display_state.OUTPUT_BLANKED = 0; + } + + result = dal_vector_construct( + &set->plane_configs[index], + preallocate_planes_num, + sizeof(struct plane_config)); + + } + + return result; +} + +/* Get the data at index */ +struct active_path_data *dal_pms_with_data_get_path_data_at_index( + struct path_mode_set_with_data *set, + uint32_t index) +{ + if (index < set->base.count) + return &set->path_data[index]; + return NULL; +} + +/* Get the data for the given display index in the set*/ +struct active_path_data *dal_pms_with_data_get_path_data_for_display_index( + struct path_mode_set_with_data *set, + uint32_t index) +{ + uint32_t i; + + for (i = 0; i < set->base.count; i++) { + if (set->base.path_mode_set[i].display_path_index == index) + return &set->path_data[i]; + } + + return NULL; +} + +/* Get mode timing at index */ +const struct path_mode *dal_pms_with_data_get_path_mode_at_index( + struct path_mode_set_with_data *set, + uint32_t index) +{ + return dal_pms_get_path_mode_at_index(&set->base, index); +} + +/* Get path mode for the given display index in the set */ +const struct path_mode * +dal_pms_with_data_get_path_mode_for_display_index( + const struct path_mode_set_with_data *set, + uint32_t index) +{ + return dal_pms_get_path_mode_for_display_index(&set->base, index); +} + +bool dal_pms_with_data_remove_path_mode_at_index( + struct path_mode_set_with_data *set_with_data, + uint32_t index) +{ + if (dal_pms_remove_path_mode_at_index(&set_with_data->base, index)) { + uint32_t i; + + for (i = index; i < set_with_data->base.count; i++) { + set_with_data->mode_timing[i] = + set_with_data->mode_timing[i + 1]; + + set_with_data->base.path_mode_set[i].mode_timing = + &set_with_data->mode_timing[i]; + + set_with_data->path_data[i] = + set_with_data->path_data[i + 1]; + dal_vector_destruct(&set_with_data->plane_configs[i]); + set_with_data->plane_configs[i] = + set_with_data->plane_configs[i + 1]; + } + + /* this is the case when we do not enter loop, so destructor + * should be called */ + if (index == set_with_data->base.count) + dal_vector_destruct( + &set_with_data->plane_configs[index]); + } else + return false; + + return true; +} + +bool dal_pms_with_data_remove_path_mode_for_display_index( + struct path_mode_set_with_data *set_with_data, + uint32_t index) +{ + uint32_t i; + + for (i = 0; i < set_with_data->base.count; i++) { + if (set_with_data->base.path_mode_set[i].display_path_index == index) + return dal_pms_with_data_remove_path_mode_at_index( + set_with_data, + i); + } + + return false; +} + +uint32_t dal_pms_with_data_get_path_mode_num( + const struct path_mode_set_with_data *set) +{ + return dal_pms_get_path_mode_num(&set->base); +} + +static bool get_path_mode_index( + struct path_mode_set_with_data *set, + uint32_t display_index, + uint32_t *index) +{ + uint32_t i; + + for (i = 0; i < set->base.count; ++i) { + if (set->base.path_mode_set[i].display_path_index == + display_index) { + *index = i; + return true; + } + } + + return false; +} + +DAL_VECTOR_APPEND(plane_configs, const struct plane_config *) + +void dal_pms_with_data_add_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index, + const struct plane_config *configs, + uint32_t planes_num) +{ + uint32_t i; + uint32_t index; + + if (!get_path_mode_index(set, display_index, &index)) + return; + + for (i = 0; i < planes_num; ++i) + plane_configs_vector_append( + &set->plane_configs[index], + &configs[i]); +} + +struct vector *dal_pms_with_data_get_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index) +{ + uint32_t index; + + if (!get_path_mode_index(set, display_index, &index)) + return NULL; + + return &set->plane_configs[index]; +} + +void dal_pms_with_data_clear_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index) +{ + uint32_t index; + + if (!get_path_mode_index(set, display_index, &index)) + return; + + dal_vector_clear(&set->plane_configs[index]); +} + +static void destruct(struct path_mode_set_with_data *set) +{ + uint32_t i; + + for (i = 0; i < dal_pms_get_path_mode_num(&set->base); ++i) + dal_vector_destruct(&set->plane_configs[i]); +} + +void dal_pms_with_data_destroy(struct path_mode_set_with_data **set) +{ + if (!set || !*set) + return; + + destruct(*set); + dal_free(*set); + *set = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.h b/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.h new file mode 100644 index 000000000000..18d043c0b677 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/path_mode_set_with_data.h @@ -0,0 +1,134 @@ +/* + * Copyright 2012-15 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 __DAL_PATH_MODE_SET_WITH_DATA_H__ +#define __DAL_PATH_MODE_SET_WITH_DATA_H__ + +#include "include/set_mode_types.h" +#include "include/display_service_types.h" +#include "include/path_mode_set_interface.h" +#include "include/timing_service_types.h" + +/* Data flags */ +struct active_path_data { + union active_data_flags { + struct { + uint32_t EXISTING:1; + uint32_t REPROGRAM_HW:1; + uint32_t ENABLE_HW:1; + uint32_t DISABLE_HW:1; + uint32_t KEEP_VCC_ON_DISABLE_HW:1; + uint32_t RESYNC_HW:1; + uint32_t POST_ACTION_DISPLAY_ON:1; + uint32_t TURN_OFF_BACK_END_AND_RX:1; + uint32_t VIEW_RES_CHANGED:1; + uint32_t TIMING_CHANGED:1; + uint32_t PIXEL_ENCODING_CHANGED:1; + uint32_t GAMUT_CHANGED:1; + uint32_t REDUCE_BLANK_ON:1; + uint32_t PENDING_DISPLAY_STEREO:1; + uint32_t SYNC_TIMING_SERVER:1; + uint32_t DISPLAY_PATH_INVALID:1; + uint32_t NO_DEFAULT_DOWN_SCALING:1; + uint32_t AUDIO_BANDWIDTH_CHANGED:1; + uint32_t SKIP_ENABLE:1; + uint32_t SKIP_RESET_HW:1; + } bits; + + uint32_t all; + } flags; + + struct display_state { + uint32_t OUTPUT_ENABLED:1; + uint32_t OUTPUT_BLANKED:1; + } display_state; + + struct ds_underscan_desc current_underscan; + enum ws_stereo_state ws_stereo_state; + enum gtc_group gtc_group; + struct hw_get_viewport_x_adjustments *viewport_adjustment; + struct ranged_timing_preference_flags ranged_timing_pref_flags; +}; + +struct path_mode_set_with_data; + +/* Create the set for path mode with data */ +struct path_mode_set_with_data *dal_pms_with_data_create(void); + +void dal_pms_with_data_destroy(struct path_mode_set_with_data **set); + +/* Add path mode with data into the set */ +bool dal_pms_with_data_add_path_mode_with_data( + struct path_mode_set_with_data *set, + const struct path_mode *mode, + const struct active_path_data *data); + +/* Get the data at index */ +struct active_path_data *dal_pms_with_data_get_path_data_at_index( + struct path_mode_set_with_data *set, + uint32_t index); + +/* Get the data for the given display index in the set*/ +struct active_path_data *dal_pms_with_data_get_path_data_for_display_index( + struct path_mode_set_with_data *set, + uint32_t index); + +/* Get path mode at index */ +const struct path_mode *dal_pms_with_data_get_path_mode_at_index( + struct path_mode_set_with_data *set, + uint32_t index); + +/* Get path mode for the given display index in the set */ +const struct path_mode * +dal_pms_with_data_get_path_mode_for_display_index( + const struct path_mode_set_with_data *set, + uint32_t index); + +uint32_t dal_pms_with_data_get_path_mode_num( + const struct path_mode_set_with_data *set); + +bool dal_pms_with_data_remove_path_mode_at_index( + struct path_mode_set_with_data *set_with_data, + uint32_t index); + +bool dal_pms_with_data_remove_path_mode_for_display_index( + struct path_mode_set_with_data *set_with_data, + uint32_t index); + +void dal_pms_with_data_add_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index, + const struct plane_config *configs, + uint32_t planes_num); + +struct vector *dal_pms_with_data_get_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index); + +void dal_pms_with_data_clear_plane_configs( + struct path_mode_set_with_data *set, + uint32_t display_index); + +#endif diff --git a/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c new file mode 100644 index 000000000000..ba894168dc76 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.c @@ -0,0 +1,944 @@ +/* + * Copyright 2012-15 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 "dal_services.h" + +#include "include/adjustment_interface.h" +#include "include/hw_sequencer_interface.h" +#include "include/hw_path_mode_set_interface.h" +#include "include/hw_adjustment_types.h" +#include "include/adjustment_types.h" +#include "include/display_service_types.h" +#include "include/logger_interface.h" +#include "include/set_mode_types.h" +#include "include/set_mode_interface.h" +#include "include/dcs_types.h" +#include "include/dcs_interface.h" +#include "include/timing_service_types.h" +#include "include/timing_service_interface.h" +#include "include/display_path_interface.h" + +#include "ds_translation.h" +#include "adjustment_container.h" +#include "scaler_adj_group.h" + +/*to chk whether underscan can be applied to current timing*/ +static bool can_scaling_be_applied( + struct adj_container *container, + enum timing_standard timing_std, + enum timing_source timing_src, + enum adjustment_id adj_id, + enum underscan_reason reason) +{ + struct adjustment_info *info = NULL; + enum signal_type type; + + if (!container) + return false; + + if (adj_id != ADJ_ID_MULTIMEDIA_PASS_THROUGH) { + info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + ADJ_ID_MULTIMEDIA_PASS_THROUGH); + if (info) { + /*if indeed multimedia pass thru,we don't need apply + * underscan*/ + if (info->adj_data.ranged.cur > 0) + return false; + } + } + type = dal_adj_container_get_signal_type(container); + if (dal_timing_service_is_ce_timing_standard(timing_std) || + (dal_is_embedded_signal(type) && timing_std == + TIMING_STANDARD_EXPLICIT)) { + if (timing_src != TIMING_SOURCE_CUSTOM && + reason != UNDERSCAN_REASON_FALL_BACK) + return true;/*we will do underscan*/ + } + return false; +} + +static bool is_pass_thru_enabled( + const struct ds_adjustment_scaler *scaler_param, + const struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + enum build_path_set_reason reason) +{ + const struct adjustment_info *info; + enum underscan_reason reason_for_underscan; + + if (scaler_param->adjust_id == ADJ_ID_MULTIMEDIA_PASS_THROUGH && + scaler_param->value > 0) + return true; + + switch (reason) { + case BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN: + reason_for_underscan = UNDERSCAN_REASON_FALL_BACK; + break; + case BUILD_PATH_SET_REASON_SET_MODE: + reason_for_underscan = UNDERSCAN_REASON_PATCH_TIMING; + break; + case BUILD_PATH_SET_REASON_SET_ADJUSTMENT: + default: + reason_for_underscan = UNDERSCAN_REASON_SET_ADJUSTMENT; + break; + } + + if (can_scaling_be_applied( + container, + scaler_param->timing_standard, + scaler_param->timing_source, + scaler_param->adjust_id, + reason_for_underscan)) { + info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + ADJ_ID_MULTIMEDIA_PASS_THROUGH); + if (!info) + return false; + if (info->adj_data.ranged.cur > 0) + return true; + } + + return false; +} + +static bool build_base_avi_info_frame_parameter( + const struct ds_adjustment_scaler *scaler_param, + const struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + const struct hw_path_mode *hw_path_mode, + enum build_path_set_reason reason, + enum hw_scale_options *underscan_avi_rule) +{ + struct cea861_support cea861_support = {0}; + + if (hw_path_mode->mode.ds_info.cea_vic != 0) + if (is_pass_thru_enabled( + scaler_param, underscan_param, container, reason)) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else { + if (dal_adj_container_get_cea861_support(container, + &cea861_support) && + cea861_support.features.UNDER_SCAN == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + *underscan_avi_rule = HW_SCALE_OPTION_UNKNOWN; + } + return true; +} + +static bool build_avi_info_frame_parameter( + const struct ds_adjustment_scaler *scaler_param, + struct ds_underscan_parameter *underscan_param, + struct adj_container *container, + const struct hw_path_mode *hw_path_mode, + enum build_path_set_reason reason, + enum hw_scale_options *underscan_avi_rule) +{ + union cea_video_capability_data_block vcdb = { {0} }; + bool result = false; + + if (dal_adj_container_get_cea_video_cap_data_block(container, &vcdb)) { + if (hw_path_mode->mode.ds_info.DISPLAY_PREFERED_MODE == 1 && + (vcdb.bits.S_PT0 != 0 || vcdb.bits.S_PT1 != 0)) { + if (vcdb.bits.S_PT0 == 1 && vcdb.bits.S_PT1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_PT0 == 0 && vcdb.bits.S_PT1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } else { + if (hw_path_mode->mode.ds_info.cea_vic != 0) { + if (vcdb.bits.S_CE0 == 1 && vcdb.bits.S_CE1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_CE0 == 0 && vcdb.bits.S_CE1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } else { + if (vcdb.bits.S_IT0 == 1 && vcdb.bits.S_IT1 == 0) + *underscan_avi_rule = HW_SCALE_OPTION_OVERSCAN; + else if (vcdb.bits.S_IT0 == 0 && vcdb.bits.S_IT1 == 1) + *underscan_avi_rule = HW_SCALE_OPTION_UNDERSCAN; + else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + } + } + } else + result = build_base_avi_info_frame_parameter( + scaler_param, + underscan_param, + container, + hw_path_mode, + reason, + underscan_avi_rule); + return result; +} + +/*we donot enable underscan on DP since architect decision*/ +static bool is_dp_underscan_disabled( + struct display_path *display_path, + uint32_t idx) +{ + if (display_path) { + enum signal_type signal = + dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + + if (dal_is_dp_signal(signal) || + dal_is_dp_external_signal(signal)) + return true; + } + return false; +} + +static bool setup_parameter( + const struct hw_path_mode *hw_path_mode, + const struct ds_adjustment_scaler *scaler_param, + struct ds_underscan_parameter *underscan_param) +{ + if (scaler_param->flags.bits.IS_UNDERSCAN_DESC != 1 || !scaler_param || + !hw_path_mode) + return false; + + if (scaler_param->underscan_desc.width == 0 || + scaler_param->underscan_desc.height == 0 || + scaler_param->underscan_desc.width < + scaler_param->underscan_desc.x || + scaler_param->underscan_desc.height < + scaler_param->underscan_desc.y) + return false; + + if (scaler_param->underscan_desc.width > + hw_path_mode->mode.timing.h_addressable) + return false; + + if (scaler_param->underscan_desc.height > + hw_path_mode->mode.timing.v_addressable) + return false; + + dal_memset(underscan_param, 0, sizeof(*underscan_param)); + underscan_param->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan_param->data.dimentions.data.width = + scaler_param->underscan_desc.width; + underscan_param->data.dimentions.data.height = + scaler_param->underscan_desc.height; + underscan_param->data.dimentions.data.position_x = + scaler_param->underscan_desc.x; + underscan_param->data.dimentions.data.position_y = + scaler_param->underscan_desc.y; + underscan_param->data.dimentions.modified_boarder_x = + hw_path_mode->mode.timing.h_addressable - + scaler_param->underscan_desc.width; + underscan_param->data.dimentions.modified_boarder_y = + hw_path_mode->mode.timing.v_addressable - + scaler_param->underscan_desc.height; + + return true; + +} + +static void setup_parameters( + const struct hw_path_mode *hw_path_mode, + const struct ds_adjustment_scaler *scaler_param, + struct ds_overscan *overscan, + struct ds_underscan_parameter *underscan_param, + struct timing_info_parameter *timing_info) +{ + overscan->left = hw_path_mode->mode.overscan.left; + overscan->right = hw_path_mode->mode.overscan.right; + overscan->top = hw_path_mode->mode.overscan.top; + overscan->bottom = hw_path_mode->mode.overscan.bottom; + dal_memset(underscan_param, 0, sizeof(*underscan_param)); + underscan_param->type = DS_UNDERSCAN_TYPE_PERCENT; + underscan_param->data.percent.old_dst_x = + hw_path_mode->mode.scaling_info.dst.width; + underscan_param->data.percent.old_dst_y = + hw_path_mode->mode.scaling_info.dst.height; + underscan_param->data.percent.percent_x = + (uint32_t)scaler_param->value; + underscan_param->data.percent.percent_y = + (uint32_t)scaler_param->value; + + dal_memset(timing_info, 0, sizeof(*timing_info)); + timing_info->timing = hw_path_mode->mode.timing; + timing_info->dst_width = + hw_path_mode->mode.scaling_info.dst.width; + timing_info->dst_height = + hw_path_mode->mode.scaling_info.dst.height; + +} + + +static void extract_parameters( + const struct ds_adjustment_scaler *scaler_param, + const struct timing_info_parameter *timing_info, + const struct ds_overscan *overscan, + enum hw_scale_options underscan_avi_rule, + struct hw_path_mode *hw_path_mode) +{ + hw_path_mode->mode.overscan.left = overscan->left; + hw_path_mode->mode.overscan.right = overscan->right; + hw_path_mode->mode.overscan.top = overscan->top; + hw_path_mode->mode.overscan.bottom = overscan->bottom; + hw_path_mode->mode.scaling_info.dst.width = + timing_info->dst_width; + hw_path_mode->mode.scaling_info.dst.height = + timing_info->dst_height; + hw_path_mode->mode.underscan_rule = underscan_avi_rule; + hw_path_mode->mode.ds_info.position_x = overscan->left; + hw_path_mode->mode.ds_info.position_y = overscan->top; + hw_path_mode->mode.ds_info.TIMING_UNDERSCAN_PATCHED = 1; +} + +static void update_underscan_bundle( + const struct ds_adjustment_scaler *scaler_param, + const struct underscan_adjustment_group *group, + const struct timing_info_parameter *timing_info, + struct ds_underscan_parameter *underscan) +{ + switch (group->id_requested) { + case ADJ_ID_UNDERSCAN: + if (scaler_param->flags.bits.IS_TV == 1 && + group->current_underscan_auto == 0) { + underscan->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan->data.dimentions.data.width = + group->current_underscan_desc.width; + underscan->data.dimentions.data.height = + group->current_underscan_desc.height; + underscan->data.dimentions.data.position_x = + group->current_underscan_desc.x; + underscan->data.dimentions.data.position_y = + group->current_underscan_desc.y; + underscan->data.dimentions.modified_boarder_x = + timing_info->timing.h_addressable - + group->current_underscan_desc.width; + underscan->data.dimentions.modified_boarder_y = + timing_info->timing.v_addressable - + group->current_underscan_desc.height; + } else { + underscan->data.percent.percent_x = + (uint32_t)group->requested_value; + underscan->data.percent.percent_y = + (uint32_t)group->requested_value; + } + break; + case ADJ_ID_UNDERSCAN_TYPE: + if (group->requested_value == 0) { + underscan->type = DS_UNDERSCAN_TYPE_DIMENTIONS; + underscan->data.dimentions.data.width = + group->current_underscan_desc.width; + underscan->data.dimentions.data.height = + group->current_underscan_desc.height; + underscan->data.dimentions.data.position_x = + group->current_underscan_desc.x; + underscan->data.dimentions.data.position_y = + group->current_underscan_desc.y; + underscan->data.dimentions.modified_boarder_x = + timing_info->timing.h_addressable - + group->current_underscan_desc.width; + underscan->data.dimentions.modified_boarder_y = + timing_info->timing.v_addressable - + group->current_underscan_desc.height; + } else { + underscan->data.percent.percent_x = + (uint32_t)group->current_percent_x; + underscan->data.percent.percent_y = + (uint32_t)group->current_percent_y; + } + break; + default: + break; + } + +} + +static bool calculate_underscan( + const struct ds_underscan_parameter *underscan_param, + uint32_t *new_dest_x, + uint32_t *new_dest_y, + struct ds_overscan *overscan) +{ + uint32_t underscan_x; + uint32_t underscan_y; + uint32_t underscan_x2; + uint32_t underscan_y2; + + if (!underscan_param || !overscan || !new_dest_x || !new_dest_y) + return false; + + if (underscan_param->type != DS_UNDERSCAN_TYPE_PERCENT && + underscan_param->type != DS_UNDERSCAN_TYPE_DIMENTIONS) + return false; + + if (underscan_param->type == DS_UNDERSCAN_TYPE_PERCENT) { + if (underscan_param->data.percent.old_dst_x == 0 || + underscan_param->data.percent.old_dst_y == 0) + return false; + underscan_x = underscan_param->data.percent.old_dst_x * + underscan_param->data.percent.percent_x / 100; + underscan_y = underscan_param->data.percent.old_dst_y * + underscan_param->data.percent.percent_y / 100; + + if (underscan_param->data.percent.old_dst_x <= underscan_x || + underscan_param->data.percent.old_dst_y <= underscan_y) + return false; + + *new_dest_x = underscan_param->data.percent.old_dst_x - underscan_x; + *new_dest_y = underscan_param->data.percent.old_dst_y - underscan_y; + underscan_x2 = underscan_x>>1; + underscan_x -= underscan_x2; + + underscan_y2 = underscan_y>>1; + underscan_y -= underscan_y2; + + overscan->left += underscan_x; + overscan->right += underscan_x2; + overscan->bottom += underscan_y; + overscan->top += underscan_y2; + + } + /* this underscan is not centered!*/ + else { + if (underscan_param->data.dimentions.data.width == 0 || + underscan_param->data.dimentions.data.height == 0) + return false; + overscan->left += + underscan_param->data.dimentions.data.position_x; + overscan->right += + underscan_param->data.dimentions.modified_boarder_x; + overscan->right = overscan->right >= + underscan_param->data.dimentions.data.position_y ? + overscan->bottom - underscan_param-> + data.dimentions.data.position_y : 0; + *new_dest_x = underscan_param->data.dimentions.data.width; + *new_dest_y = underscan_param->data.dimentions.data.height; + } + if (overscan->left & 0x01) { + overscan->left--; + overscan->right++; + } + /*Top should be even number at interlace mode*/ + if (overscan->top & 0x01) { + overscan->top--; + overscan->bottom++; + } + return true; +} + +bool dal_scaler_adj_group_build_scaler_parameter( + const struct path_mode *path_mode, + struct adj_container *container, + enum build_path_set_reason reason, + enum adjustment_id adjust_id, + int32_t value, + const struct ds_underscan_desc *underscan_desc, + const struct display_path *display_path, + struct ds_adjustment_scaler *param) +{ + struct dcs *dcs = dal_display_path_get_dcs(display_path); + struct dcs_stereo_3d_features feature; + + if (!display_path || !path_mode || !dcs) + return false; + dal_memset(param, 0, sizeof(*param)); + param->timing_source = path_mode->mode_timing-> + mode_info.timing_source; + param->timing_standard = path_mode->mode_timing->crtc_timing. + timing_standard; + param->display_index = path_mode->display_path_index; + if (path_mode->view_3d_format != VIEW_3D_FORMAT_NONE) { + enum timing_3d_format format = + path_mode->mode_timing->crtc_timing.timing_3d_format; + feature = dal_dcs_get_stereo_3d_features(dcs, format); + if (feature.flags.SUPPORTED && !feature.flags.SCALING) + return false; + } + + if (reason == BUILD_PATH_SET_REASON_SET_ADJUSTMENT) { + if (dal_adj_container_get_default_underscan_allow(container)) + return false; + param->flags.bits.IS_FOR_SET_MODE = 0; + param->adjust_id = adjust_id; + param->value = value; + if (underscan_desc) { + param->underscan_desc = *underscan_desc; + param->flags.bits.IS_UNDERSCAN_DESC = 1; + } + } else { + param->flags.bits.IS_FOR_SET_MODE = 1; + param->adjust_id = ADJ_ID_UNDERSCAN; + param->value = 0; + } + return true; +} + +static bool build_underscan_bundle( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + const struct timing_info_parameter *timing_info, + struct underscan_adjustment_group *group) +{ + struct adjustment_info *mm_pass_thur; + struct adjustment_info *underscan; + + dal_memset(group, 0, sizeof(*group)); + group->id_overscan = ADJ_ID_OVERSCAN; + group->id_underscan = ADJ_ID_UNDERSCAN; + group->id_underscan_auto = ADJ_ID_UNDERSCAN_TYPE; + group->id_multi_media_pass_thru = ADJ_ID_MULTIMEDIA_PASS_THROUGH; + + group->id_requested = param->adjust_id; + group->requested_value = param->value; + + if (!param || !container || !timing_info) + return false; + underscan = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + group->id_underscan); + if (!underscan) + return false; + + mm_pass_thur = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + group->id_multi_media_pass_thru); + + + group->current_percent_x = underscan->adj_data.ranged.cur; + group->current_percent_y = underscan->adj_data.ranged.cur; + + if (mm_pass_thur) + group->current_multi_media_pass_thru = + mm_pass_thur->adj_data.ranged.cur; + else + group->current_multi_media_pass_thru = 0; + if (param->flags.bits.IS_FOR_SET_MODE == 1) + group->requested_value = group->current_percent_x; + + return true; +} + +static bool build_underscan_parameters( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + enum build_path_set_reason reason, + struct ds_underscan_parameter *underscan_param, + struct timing_info_parameter *timing_info, + struct ds_overscan *overscan) +{ + struct underscan_adjustment_group group; + + if (!build_underscan_bundle( + param, + container, + timing_info, + &group)) + return false; + /*update parameter if required*/ + update_underscan_bundle( + param, + &group, + timing_info, + underscan_param); + /*calculate underscan and new destination*/ + if (!calculate_underscan( + underscan_param, + &timing_info->dst_width, + &timing_info->dst_height, + overscan)) + return false; + + return true; +} + +static struct hw_path_mode *find_hw_path_mode( + const struct display_path *display_path, + struct hw_path_mode_set *hw_pms) +{ + uint32_t i; + uint32_t num_of_path; + struct hw_path_mode *mode = NULL; + struct hw_path_mode *local_mode; + + num_of_path = dal_hw_path_mode_set_get_paths_number(hw_pms); + for (i = 0; i < num_of_path; i++) { + local_mode = dal_hw_path_mode_set_get_path_by_index(hw_pms, i); + if (local_mode && local_mode->display_path == display_path) { + mode = local_mode; + break; + } + } + return mode; +} + +static bool build_hw_path_set_for_adjustment( + struct ds_dispatch *ds, + const struct ds_adjustment_scaler *param, + const struct display_path *display_path, + uint32_t disp_index, + struct hw_path_mode_set *hw_pms) +{ + struct adjustment_params adj_param; + + if (!param || !display_path || !hw_pms) + return false; + + dal_memset(&adj_param, 0, sizeof(struct adjustment_params)); + adj_param.affected_path = display_path; + adj_param.action = ADJUSTMENT_ACTION_SET_ADJUSTMENT; + adj_param.params.type = ADJUSTMENT_PAR_TYPE_TIMING; + adj_param.params.timings.ajd_id = param->adjust_id; + adj_param.params.timings.adj_id_hw = HW_ADJUSTMENT_ID_OVERSCAN; + if (param->adjust_id == ADJ_ID_UNDERSCAN_TYPE) + adj_param.params.timings.ajd_id = ADJ_ID_UNDERSCAN; + + return dal_ds_dispatch_build_hw_path_set_for_adjustment( + ds, + hw_pms, + &adj_param); +} + +bool dal_scaler_adj_group_apply_scaling( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + enum build_path_set_reason reason, + struct hw_path_mode *hw_path_mode) +{ + struct ds_overscan overscan; + struct ds_underscan_parameter parameter; + struct timing_info_parameter timing_info; + enum hw_scale_options underscan_avi_rule = HW_SCALE_OPTION_UNKNOWN; + enum underscan_reason reason_for_underscan; + + build_avi_info_frame_parameter( + param, + NULL, + container, + hw_path_mode, + reason, + &hw_path_mode->mode.underscan_rule); + + switch (reason) { + case BUILD_PATH_SET_REASON_FALLBACK_UNDERSCAN: + reason_for_underscan = UNDERSCAN_REASON_FALL_BACK; + break; + case BUILD_PATH_SET_REASON_SET_MODE: + reason_for_underscan = UNDERSCAN_REASON_PATCH_TIMING; + break; + case BUILD_PATH_SET_REASON_SET_ADJUSTMENT: + default: + reason_for_underscan = UNDERSCAN_REASON_SET_ADJUSTMENT; + break; + } + + if (!can_scaling_be_applied( + container, + param->timing_standard, + param->timing_source, + param->adjust_id, + reason_for_underscan)) + return false; + + if (param->flags.bits.IS_UNDERSCAN_DESC == 0) { + if (param->flags.bits.IS_FOR_SET_MODE == 1 && + param->adjust_id != ADJ_ID_UNDERSCAN) + return false; + + if (is_dp_underscan_disabled( + hw_path_mode->display_path, + param->display_index)) + return false; + + setup_parameters( + hw_path_mode, + param, + &overscan, + ¶meter, + &timing_info); + if (!build_underscan_parameters( + param, + container, + reason, + ¶meter, + &timing_info, + &overscan)) + return false; + } else { + dal_memset( + ¶meter, + 0, + sizeof(struct ds_underscan_parameter)); + dal_memset( + &timing_info, + 0, + sizeof(struct timing_info_parameter)); + dal_memset(&overscan, 0, sizeof(struct ds_overscan)); + timing_info.timing = hw_path_mode->mode.timing; + if (!setup_parameter( + hw_path_mode, + param, + ¶meter)) + return false; + + if (!calculate_underscan( + ¶meter, + &timing_info.dst_width, + &timing_info.dst_height, + &overscan)) + return false; + } + + build_avi_info_frame_parameter( + param, + ¶meter, + container, + hw_path_mode, + reason, + &underscan_avi_rule); + + extract_parameters( + param, + &timing_info, + &overscan, + underscan_avi_rule, + hw_path_mode); + + return true; +} + +static bool prepare_underscan( + struct ds_dispatch *ds, + const struct path_mode *path_mode, + const struct ds_adjustment_scaler *param, + struct adj_container *container, + const struct display_path *display_path, + struct hw_underscan_adjustment_data **hw_underscan_data, + struct hw_path_mode_set *hw_path_set) +{ + struct hw_underscan_adjustment hw_underscan_adj = { {0} }; + struct hw_path_mode *hw_path_mode; + enum build_path_set_reason reason = + BUILD_PATH_SET_REASON_SET_ADJUSTMENT; + hw_path_set = dal_hw_path_mode_set_create(); + if (!hw_path_set) + return false; + if (!build_hw_path_set_for_adjustment( + ds, + param, + display_path, + path_mode->display_path_index, + hw_path_set)) + return false; + + + hw_path_mode = find_hw_path_mode(display_path, hw_path_set); + if (!hw_path_mode) + return false; + + if (!dal_scaler_adj_group_apply_scaling( + param, + container, + reason, + hw_path_mode)) + return false; + dal_ds_dispatch_setup_info_frame(ds, path_mode, hw_path_mode); + dal_memset(&hw_underscan_adj, + 0, + sizeof(struct hw_underscan_adjustment)); + /* no need call it build_deflicker_adjustment */ + hw_underscan_adj.hw_overscan = hw_path_mode->mode.overscan; + /*just create struct hw_underscan_adjustment_data is enough to use, + * no need a HWAdjustment */ + (*hw_underscan_data)->hw_adj_id = HW_ADJUSTMENT_ID_OVERSCAN; + (*hw_underscan_data)->hw_underscan_adj = hw_underscan_adj; + return true; +} + +static enum ds_return set_underscan_adjustment( + struct ds_dispatch *ds, + struct display_path *display_path, + enum adjustment_id adjust_id, + int32_t value, + const struct path_mode *path_mode, + struct adj_container *container) +{ + enum ds_return result = DS_ERROR; + struct hw_path_mode_set *hw_path_set = NULL; + struct ds_adjustment_scaler scaler; + struct hw_underscan_adjustment_data data = { 0 }; + struct hw_underscan_adjustment_data *hw_underscan_data = &data; + + hw_path_set = dal_hw_path_mode_set_create(); + if (!hw_path_set) + return DS_ERROR; + + if (!dal_scaler_adj_group_build_scaler_parameter( + path_mode, + container, + BUILD_PATH_SET_REASON_SET_ADJUSTMENT, + adjust_id, + value, + NULL, + display_path, + &scaler)) { + result = DS_ERROR; + goto fail; + } + + if (!prepare_underscan( + ds, + path_mode, + &scaler, + container, + display_path, + &hw_underscan_data, + hw_path_set)) { + result = DS_ERROR; + goto fail; + } + + if (dal_hw_sequencer_set_overscan_adj( + ds->hwss, + hw_path_set, + hw_underscan_data) == HWSS_RESULT_OK) + result = DS_SUCCESS; + else if (dal_adj_info_set_update_cur_value( + &container->adj_info_set, + adjust_id, + value)) + result = DS_SUCCESS; + else + result = DS_ERROR; +fail: + dal_hw_path_mode_set_destroy(&hw_path_set); + return result; +} + +enum ds_return dal_scaler_adj_group_set_adjustment( + struct ds_dispatch *ds, + const uint32_t display_index, + struct display_path *display_path, + enum adjustment_id adjust_id, + int32_t value) +{ + struct adj_container *container; + struct path_mode_set_with_data *pms_wd; + const struct path_mode *path_mode; + enum ds_return result = DS_ERROR; + const struct adjustment_info *adj_info; + + pms_wd = dal_ds_dispatch_get_active_pms_with_data(ds); + if (NULL == pms_wd) + return DS_ERROR; + container = dal_ds_dispatch_get_adj_container_for_path( + ds, display_index); + if (NULL == container) + return DS_ERROR; + + path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + pms_wd, + display_index); + + if (NULL == path_mode) + return DS_ERROR; + + adj_info = dal_adj_info_set_get_adj_info( + &container->adj_info_set, + adjust_id); + if (NULL == adj_info) + return DS_ERROR; + + if (adj_info->adj_data.ranged.cur == value) { + if (dal_adj_container_is_adjustment_committed( + container, + adjust_id)) + /* we have set the same adjustment, do nothing, bypass + */ + return DS_SUCCESS; + else if (ADJ_ID_UNDERSCAN == adjust_id) { + if (value == adj_info->adj_data.ranged.def && + value == adj_info->adj_data.ranged.def) { + /* this is the initial call since our setmode's + * default: no underscan. We don't need any 0 + * underscan adjustment after setmode. only set + * committed. + */ + dal_adj_container_commit_adj( + container, + adjust_id); + return DS_SUCCESS; + } + } + } + + if (adj_info->adj_data.ranged.max < value || + adj_info->adj_data.ranged.min > value) + return DS_ERROR; + if (!dal_adj_info_set_update_cur_value( + &container->adj_info_set, + adjust_id, + value)) + return DS_ERROR; + if (adjust_id == ADJ_ID_UNDERSCAN || adjust_id == ADJ_ID_UNDERSCAN_TYPE) + result = set_underscan_adjustment( + ds, + display_path, + adjust_id, + value, + path_mode, + container); + else { + dal_logger_write(ds->dal_context->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "adj_id isn't for scaler_adj_group"); + return DS_ERROR; + } + if (result != DS_ERROR) + dal_adj_container_commit_adj(container, adjust_id); + + return result; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.h b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.h new file mode 100644 index 000000000000..27819a896b4f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/scaler_adj_group.h @@ -0,0 +1,57 @@ +/* + * Copyright 2012-15 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 __DAL_SCALER_ADJ_GROUP_H__ +#define __DAL_SCALER_ADJ_GROUP_H__ + +#include "include/adjustment_types.h" +#include "ds_dispatch.h" + +struct ds_adjustment_scaler; + +enum ds_return dal_scaler_adj_group_set_adjustment( + struct ds_dispatch *ds, + const uint32_t display_index, + struct display_path *display_path, + enum adjustment_id adjust_id, + int32_t value); + +bool dal_scaler_adj_group_apply_scaling( + const struct ds_adjustment_scaler *param, + struct adj_container *container, + enum build_path_set_reason reason, + struct hw_path_mode *hw_path_mode); + +bool dal_scaler_adj_group_build_scaler_parameter( + const struct path_mode *path_mode, + struct adj_container *container, + enum build_path_set_reason reason, + enum adjustment_id adjust_id, + int32_t value, + const struct ds_underscan_desc *underscan_desc, + const struct display_path *display_path, + struct ds_adjustment_scaler *param); + +#endif /* __DAL_SCALER_ADJ_GROUP_H__ */ diff --git a/drivers/gpu/drm/amd/dal/display_service/set_mode_params.c b/drivers/gpu/drm/amd/dal/display_service/set_mode_params.c new file mode 100644 index 000000000000..e49ef22a6c9b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/set_mode_params.c @@ -0,0 +1,822 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/set_mode_interface.h" +#include "include/topology_mgr_interface.h" +#include "include/dcs_interface.h" + +#include "include/hw_path_mode_set_interface.h" +#include "include/display_path_set_interface.h" +#include "include/link_service_interface.h" +#include "include/logger_interface.h" + +#include "ds_translation.h" + +struct hw_path_mode_set_map { + uint32_t display_index; + uint32_t offset_in_hw_path_mode_set; + enum scaling_transformation scl_trans; +}; + +struct set_mode_params { + struct display_path_set *display_path_set; + struct hw_path_mode_set *hw_path_mode_set; + struct hw_path_mode_set *hw_path_mode_set_for_guaranteed; + struct hw_path_mode_set_map map[MAX_COFUNCTIONAL_PATHS]; + struct topology_mgr *tm; + struct hw_sequencer *hws; + struct dal_context *ctx; + uint32_t path_num; + uint32_t guaranteed_validation_count; +}; + +static struct hw_path_mode *get_hw_path_mode_by_display_index( + struct set_mode_params *smp, + uint32_t display_index) +{ + uint32_t i; + + for (i = 0; i < smp->path_num; i++) + if (smp->map[i].display_index == display_index) + return dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set, + smp->map[i].offset_in_hw_path_mode_set); + + return NULL; +} + +bool dal_set_mode_params_update_view_on_path( + struct set_mode_params *smp, + uint32_t display_index, + const struct view *vw) +{ + struct hw_path_mode *path_mode = + get_hw_path_mode_by_display_index(smp, display_index); + + if (path_mode && vw) { + path_mode->mode.view.height = vw->height; + path_mode->mode.view.width = vw->width; + return true; + } + + return false; +} + +/* + * dal_set_mode_params_validate_stereo_3d_format + * + * Validates Stereo Format in logical layer + * + */ +bool dal_set_mode_params_validate_stereo_3d_format( + struct set_mode_params *smp, + struct display_path *display_path, + const struct crtc_timing *timing, + enum view_3d_format view_3d_format) +{ + enum timing_3d_format timing3DFormat = + dal_ds_translation_get_active_timing_3d_format( + timing->timing_3d_format, + view_3d_format); + enum signal_type signal = + dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + + switch (timing3DFormat) { + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_SW_FRAME_PACKING: + /* Frame Packing defined only by DP and HDMI specs */ + if (!dal_is_dp_signal(signal) && !dal_is_hdmi_signal(signal)) + return false; + break; + + case TIMING_3D_FORMAT_SBS_SW_PACKED: + case TIMING_3D_FORMAT_TB_SW_PACKED: + /* Driver supports only HDMI signaling for these formats */ + if (!dal_is_hdmi_signal(signal)) + return false; + + break; + + default: + break; + } + + return true; +} + +bool dal_set_mode_params_update_mode_timing_on_path( + struct set_mode_params *smp, + uint32_t display_index, + const struct mode_timing *mode_timing, + enum view_3d_format format) +{ + struct hw_path_mode *path_mode = + get_hw_path_mode_by_display_index(smp, display_index); + struct display_path *display_path = + dal_display_path_set_index_to_path( + smp->display_path_set, display_index); + enum signal_type asic_signal = + dal_display_path_get_query_signal( + display_path, + ASIC_LINK_INDEX); + + if (path_mode == NULL || mode_timing == NULL) + return false; + + dal_ds_translation_patch_hw_view_for_3d( + &path_mode->mode.view, + &mode_timing->crtc_timing, + format); + dal_ds_translation_hw_crtc_timing_from_crtc_timing( + &path_mode->mode.timing, + &mode_timing->crtc_timing, + format, + asic_signal); + dal_ds_translation_setup_hw_stereo_mixer_params( + &path_mode->mode, + &mode_timing->crtc_timing, + format); + + path_mode->mode.refresh_rate = mode_timing->mode_info.field_rate; + + return dal_set_mode_params_validate_stereo_3d_format( + smp, + display_path, + &mode_timing->crtc_timing, + format); +} + +bool dal_set_mode_params_update_scaling_on_path( + struct set_mode_params *smp, + uint32_t display_index, + enum scaling_transformation st) +{ + uint32_t i; + /* + * we can only compute the scalar src/dst here if we are guaranteed + * there is no update call on View or Timing on this path later. + * Since that's not possible, cache the scalingTrans, and translate just + * before we call HWSS + */ + for (i = 0; i < smp->path_num; ++i) + if (smp->map[i].display_index == display_index) { + smp->map[i].scl_trans = st; + return true; + } + + return false; +} + +bool dal_set_mode_params_update_pixel_format_on_path( + struct set_mode_params *smp, + uint32_t display_index, + enum pixel_format pf) +{ + struct hw_path_mode *path_mode = + get_hw_path_mode_by_display_index(smp, display_index); + + if (path_mode) { + path_mode->mode.pixel_format = pf; + return true; + } else + return false; +} + +bool dal_set_mode_params_update_tiling_mode_on_path( + struct set_mode_params *smp, + uint32_t display_index, + enum tiling_mode tm) +{ + struct hw_path_mode *path_mode = + get_hw_path_mode_by_display_index(smp, display_index); + + if (path_mode) { + path_mode->mode.tiling_mode = tm; + return true; + } else + return false; +} + +static void update_hw_path_mode_scaling_info(struct set_mode_params *smp) +{ + uint32_t i; + + for (i = 0; i < smp->path_num; ++i) { + struct hw_path_mode *path_mode = + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set, + smp->map[i].offset_in_hw_path_mode_set); + + struct view src = path_mode->mode.view; + struct view dst = { path_mode->mode.timing.h_addressable, + path_mode->mode.timing.v_addressable }; + + path_mode->mode.scaling_info.dst = dst; + path_mode->mode.scaling_info.src = src; + path_mode->mode.scaling_info.signal = + dal_display_path_get_config_signal( + path_mode->display_path, SINK_LINK_INDEX); + + switch (smp->map[i].scl_trans) { + case SCALING_TRANSFORMATION_IDENTITY: + case SCALING_TRANSFORMATION_CENTER_TIMING: + path_mode->mode.scaling_info.dst = path_mode->mode.view; + break; + case SCALING_TRANSFORMATION_FULL_SCREEN_SCALE: + path_mode->mode.scaling_info.dst.width = + path_mode->mode.timing.h_addressable; + path_mode->mode.scaling_info.dst.height = + path_mode->mode.timing.v_addressable; + break; + case SCALING_TRANSFORMATION_PRESERVE_ASPECT_RATIO_SCALE: + if (src.width * dst.height < + dst.width * src.height) { + /* dst is wider in aspect ratio, + * shrinking pDest->pixelWidth */ + path_mode->mode.scaling_info.dst.width = + (dst.height * src.width) / + src.height; + } else { + if (src.width * 100 / src.height != + dst.width * 100 / + dst.height) { + /* note: here we will get 1600x900 which + * is using 1776x1000 as based mode, but + * gets 1776x999 as requested + * destination. 1776/1000 = 1.776, + * 1600/900 = 1.777, we + * should treat these two are in same + * ratio. + */ + path_mode->mode.scaling_info.dst. + height = (dst.width * + src.height) / + src.width; + } + } + break; + default: + dal_logger_write(smp->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "%s: something is wrong here, why do we have bogus parameters?", + __func__); + break; + } + } +} + +#define VALID_VIEWS_NUM 2 + +static const struct view valid_views[VALID_VIEWS_NUM] = { + { 640, 480 }, + { 800, 600 } }; + +/* + * packed_pixel_validate_path_mode + * + * Validates Path Mode considering packed pixel format limitations + * (only if this path driven in packed pixel format) + * Limitations are as following: + * 1. No scaling + * 2. Supports ARGB8888 pixel format and ARGB2101010 pixel format only + * 3. Supports only 3 modes: Native, 640x480 and 800x600. + */ +static bool packed_pixel_validate_path_mode( + const struct hw_path_mode *path_mode) +{ + const struct monitor_patch_info *patch_info; + + if (dal_dcs_get_enabled_packed_pixel_format( + dal_display_path_get_dcs(path_mode->display_path)) != + DCS_PACKED_PIXEL_FORMAT_NOT_PACKED) { + uint32_t i = 0; + /* No scaling (centered/identity only) */ + if (path_mode->mode.scaling_info.src.width != + path_mode->mode.scaling_info.dst.width || + path_mode->mode.scaling_info.src.height != + path_mode->mode.scaling_info.dst.height) + return false; + + /* Verify that the pixel format is a supported one * + * Block 8, 16, 64 pixel format, and 32 XRBIAS pixel format, + * because OPENGL/D3d does not support them when packed pixel + * feature enables */ + if (path_mode->mode.pixel_format != PIXEL_FORMAT_ARGB8888 && + path_mode->mode.pixel_format != + PIXEL_FORMAT_ARGB2101010) + return false; + + /* Allow identity */ + if (path_mode->mode.timing.h_addressable == + path_mode->mode.view.width && + path_mode->mode.timing.v_addressable == + path_mode->mode.view.height) + return true; + + patch_info = dal_dcs_get_monitor_patch_info( + dal_display_path_get_dcs( + path_mode->display_path), + MONITOR_PATCH_TYPE_SINGLE_MODE_PACKED_PIXEL); + + if (patch_info) + return false; + + /* Allow predefined views as centered */ + for (i = 0; i < VALID_VIEWS_NUM; ++i) { + if (path_mode->mode.view.width == + valid_views[i].width && + path_mode->mode.view.height == + valid_views[i].height) + return true; + } + + return false; + } + + return true; +} + +/* + * + * + * Validates Path Mode considering wireless display limitations + * (only if this path signal type is wireless) + * Limitations are as following: + * 1. No scaling + * 2. Supports YCbCr444 pixel format only + * + * returns true if this path mode valid, false otherwise + */ +static bool wireless_validate_path_mode(const struct hw_path_mode *path_mode) +{ + enum signal_type signal = + dal_display_path_get_config_signal( + path_mode->display_path, + SINK_LINK_INDEX); + + /* check if this path is Wireless Display path */ + if (signal == SIGNAL_TYPE_WIRELESS) { + /* VCE can only accept YCbCr444 streams for encoding */ + if (path_mode->mode.timing.flags.PIXEL_ENCODING != + HW_PIXEL_ENCODING_YCBCR444) + return false; + } + + return true; +} + +/* + * validate_path_mode + * + * Does the following validations on the given path mode: + * 1. Packed Pixel Format validation + * + * returns true if this path mode valid, false otherwise + */ +static bool validate_path_mode( + struct set_mode_params *smp, + const struct hw_path_mode *path_mode, + bool guaranteed_validation) +{ + /* validate path against packed pixel limitations */ + bool is_valid_path = packed_pixel_validate_path_mode(path_mode); + + /* validate path against wireless limitations */ + if (is_valid_path) + is_valid_path = wireless_validate_path_mode(path_mode); + + /* validate path against link limitations */ + if (is_valid_path) { + struct display_path *display_path = path_mode->display_path; + uint32_t display_index = + dal_display_path_get_display_index(display_path); + uint32_t link_count = + dal_display_path_get_number_of_links(display_path); + uint32_t i; + + struct link_validation_flags flags = { 0 }; + + flags.CANDIDATE_TIMING = guaranteed_validation; + flags.START_OF_VALIDATION = + smp->guaranteed_validation_count == 0; + flags.DYNAMIC_VALIDATION = 1; + + for (i = 0; i < link_count; ++i) { + struct link_service *ls = + dal_display_path_get_link_query_interface( + display_path, i); + + if (!dal_ls_validate_mode_timing( + ls, + display_index, + &path_mode->mode.timing, + flags)) { + is_valid_path = false; + break; + } + } + } + + return is_valid_path; +} + +static bool validate_path_mode_set( + struct set_mode_params *smp, + struct hw_path_mode_set *path_set) +{ + return dal_hw_sequencer_validate_display_hwpms(smp->hws, path_set) == + HWSS_RESULT_OK; +} + +static void package_hw_pms_for_guaranteed_validation( + struct set_mode_params *smp) +{ + uint32_t i; + uint32_t max_cofunctional_targets = + dal_tm_max_num_cofunctional_targets(smp->tm); + struct hw_path_mode *path_mode_src = + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set, 0); + + for (i = 0; i < max_cofunctional_targets; ++i) { + struct hw_path_mode *path_mode_dst = + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set_for_guaranteed, + i); + /* copy the 1 path maxCofunctionalPath times */ + *path_mode_dst = *path_mode_src; + } +} + +bool dal_set_mode_params_is_path_mode_set_supported( + struct set_mode_params *smp) +{ + uint32_t i; + uint32_t paths_number = dal_hw_path_mode_set_get_paths_number( + smp->hw_path_mode_set); + update_hw_path_mode_scaling_info(smp); + + for (i = 0; i < paths_number; ++i) { + if (!validate_path_mode( + smp, + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set, + i), + false)) + return false; + } + + return validate_path_mode_set(smp, smp->hw_path_mode_set); +} + +/* return true if the parameters can be set, and is guaranteed regardless other + * modes being set on other paths + */ +bool dal_set_mode_params_is_path_mode_set_guaranteed( + struct set_mode_params *smp) +{ + uint32_t display_index; + /* guaranteed: + * + * 1. assuming each path is allocated (guaranteed) {[total available + * video memory bandwidth] / [maximum simultaneous enabled display]} to + * work with, does the given configuration passes still passes + * validation? + * + * this basically mean if all path are doing guaranteed mode, upper + * layer can safely assume the mode is cofunctional without calling HW + * to validate if the multiple path modes are cofunctional + * + * 2. only meaningful for 1 path configuration. We will not guarantee + * multiple path mode as this case is not useful + * note: to simplify HW layer code, when we are asked to if a path mode + * is guaranteed (if SetModeParam contain more than 1 path mode this + * method would fail), here we get the max number of cofunctional path + * from TM, and duplicate the 1 path mode [max cofunctional path] times + * and store in HwPathModeSet, and call HWS to validate + */ + + if (!smp->hw_path_mode_set_for_guaranteed) + return false; + + /* When stereo mixer present, we cannot guarantee this solution due to + * exceptional resource arbitration + */ + display_index = + dal_display_path_get_display_index( + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set_for_guaranteed, 0)-> + display_path); + + update_hw_path_mode_scaling_info(smp); + + /* We validate path mode in original set, since guaranteed is not + * prepared yet */ + if (!validate_path_mode( + smp, + dal_hw_path_mode_set_get_path_by_index( + smp->hw_path_mode_set, 0), + true)) + return false; + + smp->guaranteed_validation_count++; + + package_hw_pms_for_guaranteed_validation(smp); + + return validate_path_mode_set( + smp, + smp->hw_path_mode_set_for_guaranteed); +} + +bool dal_set_mode_params_report_single_selected_timing( + struct set_mode_params *smp, uint32_t display_index) +{ + struct display_path *display_path = + dal_tm_display_index_to_display_path(smp->tm, display_index); + if (display_path != NULL && + dal_display_path_get_dcs(display_path) != NULL) + return dal_dcs_report_single_selected_timing( + dal_display_path_get_dcs(display_path)); + + return false; +} + +bool dal_set_mode_params_report_ce_mode_only(struct set_mode_params *smp, + uint32_t display_index) +{ + struct display_path *display_path = + dal_tm_display_index_to_display_path( + smp->tm, + display_index); + struct dcs *dcs = dal_display_path_get_dcs(display_path); + + if (dcs) { + enum signal_type signal = + dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + bool is_hdmi = signal == SIGNAL_TYPE_HDMI_TYPE_A; + bool enabled = false; + + if (dal_dcs_get_fid9204_allow_ce_mode_only_option( + dcs, + is_hdmi, + &enabled)) + return enabled; + } + + return false; +} + +bool dal_set_mode_params_init_with_topology( + struct set_mode_params *smp, + const uint32_t display_idx[], + uint32_t idx_num) +{ + struct hw_path_mode path_mode; + + ASSERT(smp->display_path_set == NULL); + ASSERT(smp->hw_path_mode_set == NULL); + + /* + * acquire DisplayPath with resource allocated from TM + */ + smp->display_path_set = + dal_tm_create_resource_context_for_display_indices( + smp->tm, + display_idx, + idx_num); + + if (smp->display_path_set == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* + * create hw_path_mode_set for validating Guaranteed configuration if + * for signal display path case for multiple display path topology, + * guaranteed is not meaningful, thus can always return false when asked + * is_path_mode_set_guaranteed(). in this case we don't need to create + * the hw_path_mode_set + */ + if (idx_num == 1) { + + smp->hw_path_mode_set_for_guaranteed = + dal_hw_path_mode_set_create(); + + if (smp->hw_path_mode_set_for_guaranteed) { + uint32_t i; + + uint32_t max_cofunctional_targets = + dal_tm_max_num_cofunctional_targets(smp->tm); + + for (i = 0; i < max_cofunctional_targets; i++) { + dal_memset(&path_mode, 0, sizeof(path_mode)); + path_mode.display_path = + dal_display_path_set_index_to_path( + smp->display_path_set, + display_idx[0]); + + dal_hw_path_mode_set_add( + smp->hw_path_mode_set_for_guaranteed, + &path_mode, NULL); + } + } + } + + smp->hw_path_mode_set = dal_hw_path_mode_set_create(); + + if (smp->hw_path_mode_set) { + uint32_t i; + + for (i = 0; i < idx_num; i++) { + dal_memset(&path_mode, 0, sizeof(path_mode)); + + path_mode.display_path = + dal_display_path_set_index_to_path( + smp->display_path_set, + display_idx[i]); + + dal_hw_path_mode_set_add( + smp->hw_path_mode_set, + &path_mode, + &smp->map[i].offset_in_hw_path_mode_set); + + smp->map[i].display_index = display_idx[i]; + } + smp->path_num = idx_num; + + return true; + } else + return false; +} + +struct view_stereo_3d_support dal_set_mode_params_get_stereo_3d_support( + struct set_mode_params *smp, + uint32_t display_index, + enum timing_3d_format timing_3d_format) +{ + struct view_stereo_3d_support view_stereo_3d_support = { + VIEW_3D_FORMAT_NONE }; + struct display_path *display_path = + dal_tm_display_index_to_display_path(smp->tm, display_index); + + if (display_path && dal_display_path_get_dcs(display_path)) { + struct dcs_stereo_3d_features stereo_3d_features = + dal_dcs_get_stereo_3d_features( + dal_display_path_get_dcs(display_path), + timing_3d_format); + if (stereo_3d_features.flags.SUPPORTED) { + view_stereo_3d_support.features.CLONE_MODE = + stereo_3d_features.flags.CLONE_MODE; + view_stereo_3d_support.features.SCALING = + stereo_3d_features.flags.SCALING; + view_stereo_3d_support.features.SINGLE_FRAME_SW_PACKED = + stereo_3d_features.flags.SINGLE_FRAME_SW_PACKED; + view_stereo_3d_support.format = + dal_ds_translation_3d_format_timing_to_view( + timing_3d_format); + } + } + + return view_stereo_3d_support; +} + +/* return true if the parameters can be set, and is guaranteed regardless other + * modes being set on other paths */ +bool dal_set_mode_params_is_multiple_pixel_encoding_supported( + struct set_mode_params *smp, + uint32_t display_index) +{ + struct hw_path_mode *path_mode = + get_hw_path_mode_by_display_index(smp, display_index); + + if (path_mode != NULL && path_mode->display_path != NULL) { + enum signal_type signal = + dal_display_path_get_config_signal( + path_mode->display_path, + SINK_LINK_INDEX); + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_WIRELESS: + return true; + default: + return false; + + } + } + return false; +} + +enum pixel_encoding dal_set_mode_params_get_default_pixel_format_preference( + struct set_mode_params *smp, + uint32_t display_index) +{ + enum pixel_encoding pf = PIXEL_ENCODING_UNDEFINED; + + struct display_path *display_path = + dal_tm_display_index_to_display_path( + smp->tm, + display_index); + struct dcs *dcs = dal_display_path_get_dcs(display_path); + + if (dcs) { + enum signal_type signal = + dal_display_path_get_query_signal( + display_path, + SINK_LINK_INDEX); + bool is_hdmi = signal == SIGNAL_TYPE_HDMI_TYPE_A; + bool enabled = false; + + if (dal_dcs_get_fid9204_allow_ce_mode_only_option( + dcs, is_hdmi, &enabled)) + pf = PIXEL_ENCODING_RGB; + } + + return pf; +} + +static bool construct( + struct set_mode_params *smp, + struct set_mode_params_init_data *init_data) +{ + if (!init_data->hws) + return false; + if (!init_data->tm) + return false; + + smp->ctx = init_data->ctx; + smp->hws = init_data->hws; + smp->tm = init_data->tm; + return true; +} + +struct set_mode_params *dal_set_mode_params_create( + struct set_mode_params_init_data *init_data) +{ + struct set_mode_params *smp = dal_alloc(sizeof(struct set_mode_params)); + + if (!smp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct(smp, init_data)) + return smp; + + BREAK_TO_DEBUGGER(); + dal_free(smp); + + return NULL; +} + +static void destruct(struct set_mode_params *smp) +{ + if (smp->display_path_set) + dal_display_path_set_destroy(&smp->display_path_set); + + if (smp->hw_path_mode_set) + dal_hw_path_mode_set_destroy(&smp->hw_path_mode_set); + + if (smp->hw_path_mode_set_for_guaranteed) + dal_hw_path_mode_set_destroy( + &smp->hw_path_mode_set_for_guaranteed); +} + +void dal_set_mode_params_destroy( + struct set_mode_params **smp) +{ + if (smp == NULL || *smp == NULL) + return; + + destruct(*smp); + + dal_free(*smp); + *smp = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/single_adj_group.c b/drivers/gpu/drm/amd/dal/display_service/single_adj_group.c new file mode 100644 index 000000000000..03c9c7886d71 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/single_adj_group.c @@ -0,0 +1,447 @@ +/* + * Copyright 2012-15 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 "dal_services.h" +#include "include/timing_service_types.h" +#include "include/dcs_interface.h" +#include "include/signal_types.h" +#include "include/topology_mgr_interface.h" +#include "include/adjustment_interface.h" +#include "include/display_service_types.h" +#include "include/hw_adjustment_types.h" +#include "include/hw_sequencer_interface.h" +#include "include/set_mode_interface.h" +#include "include/logger_interface.h" +#include "include/hw_adjustment_set.h" + +#include "ds_dispatch.h" +#include "adjustment_container.h" +#include "single_adj_group.h" + +static void translate_to_hw_dither( + uint32_t value, + enum pixel_encoding pixel_encoding, + union hw_adjustment_bit_depth_reduction *bit_depth) +{ + /* truncation */ + if (DS_BIT_DEPTH_REDUCTION_TRUN6 == value) { + bit_depth->bits.TRUNCATE_ENABLED = 1; + bit_depth->bits.TRUNCATE_DEPTH = 0; + } else if (DS_BIT_DEPTH_REDUCTION_TRUN8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN8_DITH6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN8_FM6 == value) { + bit_depth->bits.TRUNCATE_ENABLED = 1; + bit_depth->bits.TRUNCATE_DEPTH = 1; + } else if (DS_BIT_DEPTH_REDUCTION_TRUN10 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8_FM6 == value) { + bit_depth->bits.TRUNCATE_ENABLED = 1; + bit_depth->bits.TRUNCATE_DEPTH = 2; + } + + if (DS_BIT_DEPTH_REDUCTION_DITH6 == value || + DS_BIT_DEPTH_REDUCTION_DITH6_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_FM6 == value) { + bit_depth->bits.TRUNCATE_ENABLED = 1; + bit_depth->bits.TRUNCATE_DEPTH = 2; + bit_depth->bits.TRUNCATE_MODE = 1; + } + + /* spatial dither */ + if (DS_BIT_DEPTH_REDUCTION_DITH6 == value || + DS_BIT_DEPTH_REDUCTION_DITH6_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN8_DITH6 == value) { + bit_depth->bits.SPATIAL_DITHER_ENABLED = 1; + bit_depth->bits.SPATIAL_DITHER_DEPTH = 0; + bit_depth->bits.HIGHPASS_RANDOM = 1; + bit_depth->bits.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } else if (DS_BIT_DEPTH_REDUCTION_DITH8 == value || + DS_BIT_DEPTH_REDUCTION_DITH8_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_TRUN8_FM6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8_FM6 == value) { + bit_depth->bits.SPATIAL_DITHER_ENABLED = 1; + bit_depth->bits.SPATIAL_DITHER_DEPTH = 1; + bit_depth->bits.HIGHPASS_RANDOM = 1; + bit_depth->bits.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } else if (DS_BIT_DEPTH_REDUCTION_DITH10 == value || + DS_BIT_DEPTH_REDUCTION_DITH10_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM6 == value) { + bit_depth->bits.SPATIAL_DITHER_ENABLED = 1; + bit_depth->bits.SPATIAL_DITHER_DEPTH = 2; + bit_depth->bits.HIGHPASS_RANDOM = 1; + bit_depth->bits.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + } + + if (DS_BIT_DEPTH_REDUCTION_DITH6_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_DITH8_NO_FRAME_RAND == value || + DS_BIT_DEPTH_REDUCTION_DITH10_NO_FRAME_RAND == value) { + bit_depth->bits.FRAME_RANDOM = 0; + } else + bit_depth->bits.FRAME_RANDOM = 1; + + /* temporal dither */ + if (DS_BIT_DEPTH_REDUCTION_FM6 == value || + DS_BIT_DEPTH_REDUCTION_DITH8_FM6 == value || + DS_BIT_DEPTH_REDUCTION_DITH10_FM6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN8_FM6 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8_FM6 == value) { + bit_depth->bits.FRAME_MODULATION_ENABLED = 1; + bit_depth->bits.FRAME_MODULATION_DEPTH = 0; + } else if (DS_BIT_DEPTH_REDUCTION_FM8 == value || + DS_BIT_DEPTH_REDUCTION_DITH10_FM8 == value || + DS_BIT_DEPTH_REDUCTION_TRUN10_FM8 == value) { + bit_depth->bits.FRAME_MODULATION_ENABLED = 1; + bit_depth->bits.FRAME_MODULATION_DEPTH = 1; + } else if (DS_BIT_DEPTH_REDUCTION_FM10 == value) { + bit_depth->bits.FRAME_MODULATION_ENABLED = 1; + bit_depth->bits.FRAME_MODULATION_DEPTH = 2; + } + +} +enum ds_return dal_single_adj_group_set_adjustment( + struct single_adj_group *single_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value) +{ + enum ds_return result = DS_ERROR; + enum hwss_result ret = HWSS_RESULT_ERROR; + union hw_adjustment_bit_depth_reduction adj_bit_depth = {0}; + uint32_t display_index = dal_tm_display_path_to_display_index( + single_adj->tm, + disp_path); + struct adj_container *adj_container = + dal_ds_dispatch_get_adj_container_for_path( + single_adj->ds, + display_index); + const struct adjustment_info *adj_info = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, adj_id); + + if (!adj_container) + return result; + + if (!adj_info) + return result; + + if (adj_info->adj_data_type == ADJ_RANGED) { + if (value < adj_info->adj_data.ranged.min || + value > adj_info->adj_data.ranged.max) + return result; + } + if (adj_id == ADJ_ID_BIT_DEPTH_REDUCTION) { + if (dal_display_path_is_psr_supported(disp_path) || + !dal_single_adj_group_verify_bit_depth_reduction( + single_adj, + disp_path, + value)) { + dal_logger_write(single_adj->dal_context->logger, + LOG_MAJOR_DCP, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Dithering setting %d could not be applied\n", + value); + return result; + } + } + if (!dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, adj_id, value)) + return result; + + if (!dal_single_adj_group_setup_bit_depth_parameters( + single_adj, + disp_path, + value, + &adj_bit_depth)) + return result; + + ret = dal_hw_sequencer_set_bit_depth_reduction_adj( + single_adj->hws, + disp_path, + &adj_bit_depth); + + if (ret == HWSS_RESULT_OK) + result = DS_SUCCESS; + + if (result == DS_SUCCESS) + dal_adj_container_commit_adj(adj_container, adj_id); + + return result; +} + +bool dal_single_adj_group_verify_bit_depth_reduction( + struct single_adj_group *single_adj, + struct display_path *disp_path, + uint32_t value) +{ + enum color_depth_index color_depth; + uint32_t display_index = dal_tm_display_path_to_display_index( + single_adj->tm, + disp_path); + const struct path_mode *path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + single_adj->ds->set, display_index); + + if (value < 0 || value > DS_BIT_DEPTH_REDUCTION_MAX) + return false; + + if (value == DS_BIT_DEPTH_REDUCTION_DISABLE || + value == DS_BIT_DEPTH_REDUCTION_DRIVER_DEFAULT) + return true; + + if (path_mode == NULL || path_mode->mode_timing == NULL) + return false; + + color_depth = + path_mode->mode_timing->crtc_timing.display_color_depth; + if (color_depth == COLOR_DEPTH_INDEX_888) { + if (value == DS_BIT_DEPTH_REDUCTION_DITH8 || + value == DS_BIT_DEPTH_REDUCTION_DITH8_NO_FRAME_RAND || + value == DS_BIT_DEPTH_REDUCTION_FM8 || + value == DS_BIT_DEPTH_REDUCTION_TRUN8 || + value == DS_BIT_DEPTH_REDUCTION_DITH10_FM8 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10_FM8) + return true; + } else if (color_depth == COLOR_DEPTH_INDEX_666) { + if (value == DS_BIT_DEPTH_REDUCTION_DITH6 || + value == DS_BIT_DEPTH_REDUCTION_DITH6_NO_FRAME_RAND || + value == DS_BIT_DEPTH_REDUCTION_FM6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN6 || + value == DS_BIT_DEPTH_REDUCTION_DITH10_FM6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10_DITH6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10_FM6 || + value == DS_BIT_DEPTH_REDUCTION_DITH8_FM6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN8_DITH6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN8_FM6 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10_DITH8_FM6) + return true; + } else if (color_depth == COLOR_DEPTH_INDEX_101010) { + if (value == DS_BIT_DEPTH_REDUCTION_DITH10 || + value == DS_BIT_DEPTH_REDUCTION_DITH10_NO_FRAME_RAND || + value == DS_BIT_DEPTH_REDUCTION_FM10 || + value == DS_BIT_DEPTH_REDUCTION_TRUN10) + return true; + } + return false; +} + +bool dal_single_adj_group_setup_bit_depth_parameters( + struct single_adj_group *single_adj, + struct display_path *disp_path, + uint32_t value, + union hw_adjustment_bit_depth_reduction *bit_depth) +{ + bool ret = true; + enum display_color_depth color_depth; + enum pixel_encoding pixel_encoding; + uint32_t display_index = dal_tm_display_path_to_display_index( + single_adj->tm, + disp_path); + const struct path_mode *path_mode = + dal_pms_with_data_get_path_mode_for_display_index( + single_adj->ds->set, display_index); + + if (path_mode == NULL || path_mode->mode_timing == NULL) + return false; + + color_depth = + path_mode->mode_timing->crtc_timing.display_color_depth; + pixel_encoding = + path_mode->mode_timing->crtc_timing.pixel_encoding; + + /* Disable diethering if FEATURE_PIXEL_PERFECT_OUTPUT + * is set and the display color depth matches the + * surface pixel format (only applies to 8-bit) */ + if (dal_adapter_service_is_feature_supported( + FEATURE_PIXEL_PERFECT_OUTPUT) && + color_depth == DISPLAY_COLOR_DEPTH_888 && + path_mode->pixel_format == PIXEL_FORMAT_ARGB8888) + return true; + + if (value == DS_BIT_DEPTH_REDUCTION_DRIVER_DEFAULT) { + if (color_depth == DISPLAY_COLOR_DEPTH_666) + bit_depth->bits.SPATIAL_DITHER_DEPTH = 0; + else if (color_depth == DISPLAY_COLOR_DEPTH_888) + bit_depth->bits.SPATIAL_DITHER_DEPTH = 1; + else if (color_depth == DISPLAY_COLOR_DEPTH_101010 || + color_depth == DISPLAY_COLOR_DEPTH_121212) + return true; + else + return false; + bit_depth->bits.SPATIAL_DITHER_ENABLED = 1; + bit_depth->bits.FRAME_RANDOM = 1; + bit_depth->bits.RGB_RANDOM = + (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; + return true; + } + translate_to_hw_dither(value, pixel_encoding, bit_depth); + + if (bit_depth->bits.FRAME_MODULATION_ENABLED == 1) { + switch (dal_display_path_get_config_signal( + disp_path, SINK_LINK_INDEX)) { + { + case SIGNAL_TYPE_RGB: + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_SINGLE_LINK1: + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + bit_depth->bits.TEMPORAL_LEVEL = 0; + } + break; + case SIGNAL_TYPE_LVDS: + case SIGNAL_TYPE_EDP: + { + union panel_misc_info panel_info; + struct dcs *dcs = dal_display_path_get_dcs(disp_path); + + bit_depth->bits.TEMPORAL_LEVEL = 0; + if (dal_dcs_get_panel_misc_info(dcs, &panel_info)) { + if (panel_info.bits.GREY_LEVEL) + bit_depth->bits.TEMPORAL_LEVEL = 1; + } + } + break; + default: + ret = false; + break; + } + bit_depth->bits.FRC_25 = 0; + bit_depth->bits.FRC_50 = 0; + bit_depth->bits.FRC_75 = 0; + } + return ret; +} + +bool dal_single_adj_group_include_adjustment( + struct single_adj_group *single_adj, + struct display_path *disp_path, + struct ds_adj_id_value adj, + struct hw_adjustment_set *set) +{ + union hw_adjustment_bit_depth_reduction *bit_depth = NULL; + uint32_t display_index = dal_tm_display_path_to_display_index( + single_adj->tm, disp_path); + struct adj_container *adj_container = + dal_ds_dispatch_get_adj_container_for_path( + single_adj->ds, display_index); + struct adjustment_info *adj_info = + dal_adj_info_set_get_adj_info( + &adj_container->adj_info_set, adj.adj_id); + + bit_depth = dal_alloc(sizeof(*bit_depth)); + if (!bit_depth) + return false; + + if (adj.adj_id == ADJ_ID_BIT_DEPTH_REDUCTION) { + if (dal_display_path_is_psr_supported(disp_path)) + return false; + if (!dal_single_adj_group_verify_bit_depth_reduction( + single_adj, + disp_path, + adj.value)) { + adj.value = adj_info->adj_data.ranged.def; + dal_adj_info_set_update_cur_value( + &adj_container->adj_info_set, + adj.adj_id, adj.value); + dal_adj_container_commit_adj( + adj_container, adj.adj_id); + dal_logger_write(single_adj->dal_context->logger, + LOG_MAJOR_DCP, + LOG_MINOR_COMPONENT_DISPLAY_SERVICE, + "Dithering setting %d no longer matching color depth ,resetting to default\n", + adj.value); + } + dal_single_adj_group_setup_bit_depth_parameters( + single_adj, + disp_path, + adj.value, + bit_depth); + + set->bit_depth = bit_depth; + + } else + return false; + + return true; +} + +static bool single_adj_construct( + struct single_adj_group *single_adj, + struct single_adj_group_init_data *init_data) +{ + if (!init_data) + return false; + + single_adj->ds = init_data->ds; + single_adj->hws = init_data->hws; + single_adj->tm = init_data->tm; + single_adj->dal_context = init_data->dal_context; + return true; +} + +struct single_adj_group *dal_single_adj_group_create( + struct single_adj_group_init_data *init_data) +{ + struct single_adj_group *single_adj = NULL; + + single_adj = dal_alloc(sizeof(*single_adj)); + + if (!single_adj) + return NULL; + + if (single_adj_construct(single_adj, init_data)) + return single_adj; + + dal_free(single_adj); + + return NULL; +} + +static void destruct( + struct single_adj_group *single_adj) +{ +} + +void dal_single_adj_group_destroy( + struct single_adj_group **single_adj) +{ + if (single_adj == NULL || *single_adj == NULL) + return; + destruct(*single_adj); + dal_free(*single_adj); + *single_adj = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/display_service/single_adj_group.h b/drivers/gpu/drm/amd/dal/display_service/single_adj_group.h new file mode 100644 index 000000000000..a3761c962a7d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/display_service/single_adj_group.h @@ -0,0 +1,75 @@ +/* + * Copyright 2012-15 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 __DAL_SINGLE_ADJ_GROUP_H__ +#define __DAL_SINGLE_ADJ_GROUP_H__ + +/* Include */ +#include "include/adjustment_types.h" + +struct single_adj_group { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct topology_mgr *tm; + struct dal_context *dal_context; +}; + +struct single_adj_group_init_data { + struct ds_dispatch *ds; + struct hw_sequencer *hws; + struct topology_mgr *tm; + struct dal_context *dal_context; +}; + +struct single_adj_group *dal_single_adj_group_create( + struct single_adj_group_init_data *init_data); + +void dal_single_adj_group_destroy( + struct single_adj_group **single_adj); + +bool dal_single_adj_group_include_adjustment( + struct single_adj_group *single_adj, + struct display_path *disp_path, + struct ds_adj_id_value adj, + struct hw_adjustment_set *set); + +bool dal_single_adj_group_setup_bit_depth_parameters( + struct single_adj_group *single_adj, + struct display_path *disp_path, + uint32_t value, + union hw_adjustment_bit_depth_reduction *bit_depth); + +bool dal_single_adj_group_verify_bit_depth_reduction( + struct single_adj_group *single_adj, + struct display_path *disp_path, + uint32_t value); + +enum ds_return dal_single_adj_group_set_adjustment( + struct single_adj_group *single_adj, + struct display_path *disp_path, + enum adjustment_id adj_id, + uint32_t value); + +#endif /* __DAL_SINGLE_ADJ_GROUP_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/adjustment_interface.h b/drivers/gpu/drm/amd/dal/include/adjustment_interface.h new file mode 100644 index 000000000000..64a9f9f2dd15 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/adjustment_interface.h @@ -0,0 +1,230 @@ +/* + * Copyright 2012-15 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 __DAL_ADJUSTMENT_INTERFACE_H__ +#define __DAL_ADJUSTMENT_INTERFACE_H__ + +#include "include/display_service_types.h" +#include "include/adjustment_types.h" +#include "include/overlay_types.h" +#include "include/display_path_interface.h" + +struct ds_underscan_desc; +struct adj_container; +struct info_frame; +struct ds_dispatch; +struct hw_adjustment_set; +struct path_mode; +struct hw_path_mode; + +enum build_path_set_reason; + +bool dal_ds_dispatch_is_adjustment_supported( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id); + +enum ds_return dal_ds_dispatch_get_type( + struct ds_dispatch *adj, + enum adjustment_id adjust_id, + enum adjustment_data_type *type); + +enum ds_return dal_ds_dispatch_get_property( + struct ds_dispatch *adj, + uint32_t display_index, + enum adjustment_id adjust_id, + union adjustment_property *property); + +enum ds_return dal_ds_dispatch_set_adjustment( + struct ds_dispatch *ds, + const uint32_t display_index, + enum adjustment_id adjust_id, + int32_t value); + +enum ds_return dal_ds_dispatch_get_adjustment_current_value( + struct ds_dispatch *ds, + struct adj_container *container, + struct adjustment_info *info, + enum adjustment_id id, + bool fall_back_to_default); + +enum ds_return dal_ds_dispatch_get_adjustment_value( + struct ds_dispatch *ds, + struct display_path *disp_path, + enum adjustment_id adj_id, + bool fall_back_to_default, + int32_t *value); + +const struct raw_gamma_ramp *dal_ds_dispatch_get_current_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id); + +const struct raw_gamma_ramp *dal_ds_dispatch_get_default_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id); + +enum ds_return dal_ds_dispatch_set_current_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + const struct raw_gamma_ramp *gamma); + +enum ds_return dal_ds_dispatch_set_gamma( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + const struct raw_gamma_ramp *gamma); + +bool dal_ds_dispatch_get_underscan_info( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_info *info); + +bool dal_ds_dispatch_get_underscan_mode( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_desc *desc); + +bool dal_ds_dispatch_set_underscan_mode( + struct ds_dispatch *ds, + uint32_t display_index, + struct ds_underscan_desc *desc); + +bool dal_ds_dispatch_setup_overlay( + struct ds_dispatch *adj, + uint32_t display_index, + struct overlay_data *data); + +struct adj_container *dal_ds_dispatch_get_adj_container_for_path( + const struct ds_dispatch *ds, + uint32_t display_index); + +void dal_ds_dispatch_set_applicable_adj( + struct ds_dispatch *adj, + uint32_t display_index, + const struct adj_container *applicable); + +enum ds_return dal_ds_dispatch_set_color_gamut( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_set_gamut_data *data); + +enum ds_return dal_ds_dispatch_get_color_gamut( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_gamut_reference_data *ref, + struct ds_get_gamut_data *data); + +enum ds_return dal_ds_dispatch_get_color_gamut_info( + struct ds_dispatch *adj, + uint32_t display_index, + const struct ds_gamut_reference_data *ref, + struct ds_gamut_info *data); + +enum ds_return dal_ds_dispatch_get_regamma_lut( + struct ds_dispatch *adj, + uint32_t display_index, + struct ds_regamma_lut *data); + +enum ds_return dal_ds_dispatch_set_regamma_lut( + struct ds_dispatch *adj, + uint32_t display_index, + struct ds_regamma_lut *data); + +enum ds_return dal_ds_dispatch_set_info_packets( + struct ds_dispatch *adj, + uint32_t display_index, + const struct info_frame *info_frames); + +enum ds_return dal_ds_dispatch_get_info_packets( + struct ds_dispatch *adj, + uint32_t display_index, + struct info_frame *info_frames); + +bool dal_ds_dispatch_initialize_adjustment(struct ds_dispatch *ds); + +void dal_ds_dispatch_cleanup_adjustment(struct ds_dispatch *ds); + +bool dal_ds_dispatch_build_post_set_mode_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *display_path, + struct hw_adjustment_set *set); + +bool dal_ds_dispatch_build_color_control_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *display_path, + struct hw_adjustment_set *set); + +bool dal_ds_dispatch_build_include_adj( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct display_path *display_path, + struct hw_path_mode *hw_mode, + struct hw_adjustment_set *set); + +bool dal_ds_dispatch_apply_scaling( + struct ds_dispatch *ds, + const struct path_mode *mode, + struct adj_container *adj_container, + enum build_path_set_reason reason, + struct hw_path_mode *hw_mode); + +void dal_ds_dispatch_update_adj_container_for_path_with_mode_info( + struct ds_dispatch *ds, + struct display_path *display_path, + const struct path_mode *path_mode); + +enum ds_return dal_ds_dispatch_get_adjustment_info( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id adjust_id, + struct adjustment_info *adj_info); + +bool dal_ds_dispatch_include_adjustment( + struct ds_dispatch *ds, + struct display_path *disp_path, + struct ds_adj_id_value adj, + struct hw_adjustment_set *set); + +enum ds_return dal_ds_dispatch_set_gamma_adjustment( + struct ds_dispatch *ds, + uint32_t display_index, + enum adjustment_id ad_id, + const struct raw_gamma_ramp *gamma); + +void dal_ds_dispatch_update_adj_container_for_path_with_color_space( + struct ds_dispatch *ds, + uint32_t display_index, + enum ds_color_space color_space); + +void dal_ds_dispatch_setup_default_regamma( + struct ds_dispatch *ds, + struct ds_regamma_lut *regamma); + +#endif /* __DAL_ADJUSTMENT_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/display_service_interface.h b/drivers/gpu/drm/amd/dal/include/display_service_interface.h new file mode 100644 index 000000000000..68276babadad --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/display_service_interface.h @@ -0,0 +1,165 @@ +/* + * Copyright 2012-15 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 __DISPLAY_SERVICE_INTERFACE_H__ +#define __DISPLAY_SERVICE_INTERFACE_H__ + +#include "include/display_service_types.h" +#include "include/display_path_types.h" +#include "include/grph_object_ctrl_defs.h" + +struct display_service; +struct ds_overlay; +struct ds_dispatch; +struct ds_synchronization; +struct path_mode_set; + +struct display_service *dal_display_service_create( + struct ds_init_data *data); + +void dal_display_service_destroy( + struct display_service **ds); + +struct ds_dispatch *dal_display_service_get_adjustment_interface( + struct display_service *ds); + +struct ds_overlay *dal_display_service_get_overlay_interface( + struct display_service *ds); + +struct ds_dispatch *dal_display_service_get_set_mode_interface( + struct display_service *ds); + +struct ds_dispatch *dal_display_service_get_reset_mode_interface( + struct display_service *ds); + +struct ds_synchronization *dal_display_service_get_synchronization_interface( + struct display_service *ds); + +enum ds_return dal_display_service_notify_v_sync_int_state( + struct display_service *ds, + uint32_t display_index, + bool maintain_v_sync_phase); + +enum ds_return dal_display_service_target_power_control( + struct display_service *ds, + uint32_t display_index, + bool power_on); + +enum ds_return dal_display_service_power_down_active_hw( + struct display_service *ds, + enum dal_video_power_state state); + +enum ds_return dal_display_service_mem_request_control( + struct display_service *ds, + uint32_t display_index, + bool enable); + +enum ds_return dal_display_service_set_multimedia_pass_through_mode( + struct display_service *ds, + uint32_t display_index, + bool passThrough); + +enum ds_return dal_display_service_set_palette( + struct display_service *ds, + uint32_t display_index, + const struct ds_devclut *palette, + const uint32_t start, + const uint32_t length); + +enum ds_return dal_display_service_apply_pix_clk_range( + struct display_service *ds, + uint32_t display_index, + struct pixel_clock_safe_range *range); + +enum ds_return dal_display_service_get_safe_pix_clk( + struct display_service *ds, + uint32_t display_index, + uint32_t *pix_clk_khz); + +enum ds_return dal_display_service_apply_refreshrate_adjustment( + struct display_service *ds, + uint32_t display_index, + enum ds_refreshrate_adjust_action action, + struct ds_refreshrate *refreshrate); + +enum ds_return dal_display_service_pre_ddc( + struct display_service *ds, + uint32_t display_index); + +enum ds_return dal_display_service_post_ddc( + struct display_service *ds, + uint32_t display_index); + +enum ds_return dal_display_service_backlight_control( + struct display_service *ds, + uint32_t display_index, + bool enable); + +enum ds_return dal_display_service_get_backlight_user_level( + struct display_service *ds, + uint32_t display_index, + uint32_t *level); + +enum ds_return dal_display_service_get_backlight_effective_level( + struct display_service *ds, + uint32_t display_index, + uint32_t *level); + +enum ds_return dal_display_service_enable_hpd( + struct display_service *ds, + uint32_t display_index); + +enum ds_return dal_display_service_disable_hpd( + struct display_service *ds, + uint32_t display_index); + +enum ds_return dal_display_service_get_min_mem_channels( + struct display_service *ds, + const struct path_mode_set *path_mode_set, + uint32_t mem_channels_num, + uint32_t *min_mem_channels_num); + +enum ds_return dal_display_service_enable_advanced_request( + struct display_service *ds, + bool enable); + +/*Audio related*/ +enum ds_return dal_display_service_enable_audio_endpoint( + struct display_service *ds, + uint32_t display_index, + bool enable); + +enum ds_return dal_display_service_mute_audio_endpoint( + struct display_service *ds, + uint32_t display_index, + bool mute); + +bool dal_display_service_calc_view_port_for_wide_display( + struct display_service *ds, + uint32_t display_index, + const struct ds_view_port *set_view_port, + struct ds_get_view_port *get_view_port); + +#endif /* __DISPLAY_SERVICE_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/overlay_interface.h b/drivers/gpu/drm/amd/dal/include/overlay_interface.h new file mode 100644 index 000000000000..c33bd73610e7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/overlay_interface.h @@ -0,0 +1,137 @@ +/* + * Copyright 2012-15 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 __DAL_OVERLAY_INTERFACE_H__ +#define __DAL_OVERLAY_INTERFACE_H__ + +#include "include/overlay_types.h" +#include "include/display_service_types.h" + +struct ds_overlay; +struct path_mode_set; +struct path_mode; +struct view; + +bool dal_ds_overlay_is_active( + struct ds_overlay *ovl, + uint32_t display_index); + +uint32_t dal_ds_overlay_get_controller_handle( + struct ds_overlay *ovl, + uint32_t display_index); + +enum ds_return dal_ds_overlay_alloc( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index, + struct view *view, + struct overlay_data *data); + +enum ds_return dal_ds_overlay_validate( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index, + struct view *view, + struct overlay_data *data); + +enum ds_return dal_ds_overlay_free( + struct ds_overlay *ovl, + struct path_mode_set *path_mode_set, + uint32_t display_index); + +enum ds_return dal_ds_overlay_get_info( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_color_space *color_space, + enum overlay_backend_bpp *backend_bpp, + enum overlay_alloc_option *alloc_option, + enum overlay_format *surface_format); + +enum ds_return dal_ds_overlay_set_otm( + struct ds_overlay *ovl, + uint32_t display_index, + const struct path_mode *current_path_mode); + +enum ds_return dal_ds_overlay_reset_otm( + struct ds_overlay *ovl, + uint32_t display_index, + struct path_mode **saved_path_mode); + +/**is in overlay theater mode*/ +bool dal_ds_overlay_is_in_otm( + struct ds_overlay *ovl, + uint32_t display_index); + +void dal_ds_overlay_set_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + const struct overlay_color_matrix *matrix); + +void dal_ds_overlay_reset_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_csc_matrix_type type); + +const struct overlay_color_matrix *dal_ds_overlay_get_matrix( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_csc_matrix_type type); + +bool dal_ds_overlay_set_color_space( + struct ds_overlay *ovl, + uint32_t display_index, + enum overlay_color_space space); + +bool dal_ds_overlay_get_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index, + enum display_pixel_encoding *pixel_encoding); + +bool dal_ds_overlay_set_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index, + enum display_pixel_encoding pixel_encoding); + +bool dal_ds_overlay_reset_display_pixel_encoding( + struct ds_overlay *ovl, + uint32_t display_index); + +/*After Set Overlay Theatre Mode (OTM) on a display path, + * saving the passed setting of Gpu scaling option for later restore*/ +enum ds_return dal_ds_overlay_save_gpu_scaling_before_otm( + struct ds_overlay *ovl, + uint32_t display_index, + int32_t timing_sel_before_otm); + +/* After reset Overlay Theatre Mode (OTM) on a display path, + * returning the previous Gpu scaling option by SetOverlayTheatreMode*/ +enum ds_return dal_ds_overlay_get_gpu_scaling_before_otm( + struct ds_overlay *ovl, + uint32_t display_index, + int32_t *timing_sel_before_otm); + +uint32_t dal_ds_overlay_get_num_of_allowed(struct ds_overlay *ovl); + +#endif /* __DAL_OVERLAY_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/overlay_types.h b/drivers/gpu/drm/amd/dal/include/overlay_types.h new file mode 100644 index 000000000000..c001edf69f25 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/overlay_types.h @@ -0,0 +1,164 @@ +/* + * Copyright 2012-15 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 __DAL_OVERLAY_TYPES_H__ +#define __DAL_OVERLAY_TYPES_H__ + +enum overlay_color_space { + OVERLAY_COLOR_SPACE_UNINITIALIZED, + OVERLAY_COLOR_SPACE_RGB, /* the first*/ + OVERLAY_COLOR_SPACE_BT601, + OVERLAY_COLOR_SPACE_BT709, /* the last*/ + OVERLAY_COLOR_SPACE_INVALID, + + /* flag the first and last*/ + OVERLAY_COLOR_SPACE_BEGIN = OVERLAY_COLOR_SPACE_RGB, + OVERLAY_COLOR_SPACE_END = OVERLAY_COLOR_SPACE_BT709, +}; + +enum overlay_backend_bpp { + OVERLAY_BACKENDBPP_UNINITIALIZED, + + OVERLAY_BACKEND_BPP_32_FULL_BANDWIDTH,/* the first*/ + OVERLAY_BACKEND_BPP_16_FULL_BANDWIDTH, + OVERLAY_BACKEND_BPP_32_HALF_BANDWIDTH,/* the last*/ + + OVERLAY_BACKEND_BPP_INVALID, + + /* flag the first and last*/ + OVERLAY_BACKEND_BPP_BEGIN = OVERLAY_BACKEND_BPP_32_FULL_BANDWIDTH, + OVERLAY_BACKEND_BPP_END = OVERLAY_BACKEND_BPP_32_HALF_BANDWIDTH, +}; + +enum overlay_alloc_option { + OVERLAY_ALLOC_OPTION_UNINITIALIZED, + + OVERLAY_ALLOC_OPTION_APPLY_OVERLAY_CSC, /* the first*/ + OVERLAY_ALLOC_OPTION_APPLY_DESKTOP_CSC, /* the last*/ + + OVERLAY_ALLOC_OPTION_INVALID, + + /* flag the first and last*/ + OVERLAY_ALLOC_OPTION_BEGIN = OVERLAY_ALLOC_OPTION_APPLY_OVERLAY_CSC, + OVERLAY_ALLOC_OPTION_END = OVERLAY_ALLOC_OPTION_APPLY_DESKTOP_CSC, +}; + +enum overlay_format { + OVERLAY_FORMAT_UNINITIALIZED, + OVERLAY_FORMAT_YUY2, + OVERLAY_FORMAT_UYVY, + OVERLAY_FORMAT_RGB565, + OVERLAY_FORMAT_RGB555, + OVERLAY_FORMAT_RGB32, + OVERLAY_FORMAT_YUV444, + OVERLAY_FORMAT_RGB32_2101010, + + OVERLAY_FORMAT_INVALID, + + /* flag the first and last*/ + OVERLAY_FORMAT_BEGIN = OVERLAY_FORMAT_YUY2, + OVERLAY_FORMAT_END = OVERLAY_FORMAT_RGB32_2101010, +}; + +enum display_pixel_encoding { + DISPLAY_PIXEL_ENCODING_UNDEFINED = 0, + DISPLAY_PIXEL_ENCODING_RGB, + DISPLAY_PIXEL_ENCODING_YCBCR422, + DISPLAY_PIXEL_ENCODING_YCBCR444 +}; + +union overlay_data_status { + uint32_t u32all; + struct { + uint32_t COLOR_SPACE_SET:1; + uint32_t BACKEND_BPP:1; + uint32_t ALLOC_OPTION:1; + uint32_t SURFACE_FORMAT:1; + uint32_t PIXEL_ENCODING:1; + uint32_t reserved:27; + + } bits; +}; + +struct overlay_data { + enum overlay_color_space color_space; + enum overlay_backend_bpp backend_bpp; + enum overlay_alloc_option alloc_option; + enum overlay_format surface_format; +}; + +enum overlay_csc_matrix_type { + OVERLAY_CSC_MATRIX_NOTDEFINED = 0, + OVERLAY_CSC_MATRIX_BT709, + OVERLAY_CSC_MATRIX_BT601, + OVERLAY_CSC_MATRIX_SMPTE240, + OVERLAY_CSC_MATRIX_SRGB, +}; + +#define DEFAULT_APP_MATRIX_DIVIDER 10000 +#define MAX_OVL_MATRIX_COUNTS 2 +#define OVL_BT709 0 +#define OVL_BT601 1 + +#define OVL_MATRIX_ITEM 9 +#define OVL_MATRIX_OFFSET_ITEM 3 + +struct overlay_color_matrix { + enum overlay_csc_matrix_type csc_matrix; +/*3*3 Gamut Matrix (value is the real value * M_GAMUT_PRECISION_MULTIPLIER)*/ + int32_t matrix_settings[OVL_MATRIX_ITEM]; + int32_t offsets[OVL_MATRIX_OFFSET_ITEM]; +}; + +enum setup_adjustment_ovl_value_type { + SETUP_ADJUSTMENT_MIN, + SETUP_ADJUSTMENT_MAX, + SETUP_ADJUSTMENT_DEF, + SETUP_ADJUSTMENT_CURRENT, + SETUP_ADJUSTMENT_BUNDLE_MIN, + SETUP_ADJUSTMENT_BUNDLE_MAX, + SETUP_ADJUSTMENT_BUNDLE_DEF, + SETUP_ADJUSTMENT_BUNDLE_CURRENT +}; + +struct overlay_parameter { + union { + uint32_t u32all; + struct { + uint32_t VALID_OVL_COLOR_SPACE:1; + uint32_t VALID_VALUE_TYPE:1; + uint32_t VALID_OVL_SURFACE_FORMAT:1; + uint32_t CONFIG_IS_CHANGED:1; + uint32_t reserved:28; + + } bits; + }; + /*currently colorSpace here packed, continue this list*/ + enum overlay_color_space color_space; + enum setup_adjustment_ovl_value_type value_type; + enum overlay_format surface_format; +}; + +#endif /* OVERLAY_TYPES_H_ */ -- cgit v1.2.3