From f2be39d08583fcb1ab19e9e4227a67b231a53df1 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 31 Jul 2015 20:59:03 -0400 Subject: amd/dal: Controller Responsible for programming front-end of display path, such as DCP, LB, SCL, CRTC and FMT. SW Layer /===============================================================\ | Timing Asic | | Service Capability | | | | Adapter | | Service | | | |---------------------------------------------------------------| | GPIO IRQ I2cAux BIOS | | Service Manager Parser | | | | Connector GPU Controller | \===============================================================/ HW Layer Signed-off-by: Harry Wentland --- drivers/gpu/drm/amd/dal/Makefile | 4 +- drivers/gpu/drm/amd/dal/controller/Makefile | 32 + drivers/gpu/drm/amd/dal/controller/controller.c | 970 ++++++++++ drivers/gpu/drm/amd/dal/controller/controller.h | 88 + .../drm/amd/dal/controller/crtc_overscan_color.h | 49 + drivers/gpu/drm/amd/dal/controller/csc.c | 46 + drivers/gpu/drm/amd/dal/controller/csc.h | 81 + drivers/gpu/drm/amd/dal/controller/csc_grph.c | 649 +++++++ drivers/gpu/drm/amd/dal/controller/csc_grph.h | 97 + drivers/gpu/drm/amd/dal/controller/csc_video.c | 933 +++++++++ drivers/gpu/drm/amd/dal/controller/csc_video.h | 102 + drivers/gpu/drm/amd/dal/controller/cursor.c | 100 + drivers/gpu/drm/amd/dal/controller/cursor.h | 104 + .../amd/dal/controller/dce110/col_man_csc_dce110.c | 626 +++++++ .../amd/dal/controller/dce110/col_man_csc_dce110.h | 34 + .../dal/controller/dce110/col_man_gamma_dce110.c | 76 + .../dal/controller/dce110/col_man_gamma_dce110.h | 34 + .../amd/dal/controller/dce110/controller_dce110.c | 260 +++ .../amd/dal/controller/dce110/controller_dce110.h | 50 + .../dal/controller/dce110/controller_v_dce110.c | 207 ++ .../dal/controller/dce110/controller_v_dce110.h | 32 + .../gpu/drm/amd/dal/controller/dce110/csc_dce110.c | 322 ++++ .../gpu/drm/amd/dal/controller/dce110/csc_dce110.h | 41 + .../amd/dal/controller/dce110/csc_grph_dce110.c | 778 ++++++++ .../amd/dal/controller/dce110/csc_grph_dce110.h | 34 + .../drm/amd/dal/controller/dce110/cursor_dce110.c | 243 +++ .../drm/amd/dal/controller/dce110/cursor_dce110.h | 35 + .../dce110/dcp_bit_depth_reduction_dce110.c | 611 ++++++ .../dce110/dcp_bit_depth_reduction_dce110.h | 88 + .../gpu/drm/amd/dal/controller/dce110/fbc_dce110.c | 1006 ++++++++++ .../gpu/drm/amd/dal/controller/dce110/fbc_dce110.h | 72 + .../amd/dal/controller/dce110/formatter_dce110.c | 768 ++++++++ .../amd/dal/controller/dce110/formatter_dce110.h | 34 + .../amd/dal/controller/dce110/grph_gamma_dce110.c | 1628 ++++++++++++++++ .../amd/dal/controller/dce110/grph_gamma_dce110.h | 38 + .../amd/dal/controller/dce110/line_buffer_dce110.c | 528 ++++++ .../amd/dal/controller/dce110/line_buffer_dce110.h | 75 + .../dal/controller/dce110/line_buffer_v_dce110.c | 341 ++++ .../dal/controller/dce110/line_buffer_v_dce110.h | 34 + .../dal/controller/dce110/pipe_control_dce110.c | 650 +++++++ .../dal/controller/dce110/pipe_control_dce110.h | 45 + .../dal/controller/dce110/pipe_control_v_dce110.c | 605 ++++++ .../dal/controller/dce110/pipe_control_v_dce110.h | 41 + .../drm/amd/dal/controller/dce110/scaler_dce110.c | 927 +++++++++ .../drm/amd/dal/controller/dce110/scaler_dce110.h | 33 + .../amd/dal/controller/dce110/scaler_v_dce110.c | 728 +++++++ .../amd/dal/controller/dce110/scaler_v_dce110.h | 33 + .../drm/amd/dal/controller/dce110/surface_dce110.c | 574 ++++++ .../drm/amd/dal/controller/dce110/surface_dce110.h | 35 + .../amd/dal/controller/dce110/surface_v_dce110.c | 636 +++++++ .../amd/dal/controller/dce110/surface_v_dce110.h | 34 + .../controller/dce110/timing_generator_dce110.c | 1122 +++++++++++ .../controller/dce110/timing_generator_dce110.h | 45 + .../controller/dce110/timing_generator_v_dce110.c | 507 +++++ .../controller/dce110/timing_generator_v_dce110.h | 39 + .../gpu/drm/amd/dal/controller/dce110/vga_dce110.c | 140 ++ .../gpu/drm/amd/dal/controller/dce110/vga_dce110.h | 36 + drivers/gpu/drm/amd/dal/controller/fbc.c | 159 ++ drivers/gpu/drm/amd/dal/controller/fbc.h | 136 ++ drivers/gpu/drm/amd/dal/controller/fbc_types.h | 104 + drivers/gpu/drm/amd/dal/controller/formatter.c | 42 + drivers/gpu/drm/amd/dal/controller/formatter.h | 77 + .../amd/dal/controller/graphics_and_video_gamma.c | 841 +++++++++ .../amd/dal/controller/graphics_and_video_gamma.h | 231 +++ drivers/gpu/drm/amd/dal/controller/grph_gamma.c | 1841 ++++++++++++++++++ drivers/gpu/drm/amd/dal/controller/grph_gamma.h | 208 ++ .../gpu/drm/amd/dal/controller/grph_gamma_types.h | 130 ++ .../amd/dal/controller/internal_types_wide_gamut.h | 90 + drivers/gpu/drm/amd/dal/controller/line_buffer.c | 222 +++ drivers/gpu/drm/amd/dal/controller/line_buffer.h | 102 + .../drm/amd/dal/controller/lut_and_gamma_types.h | 33 + drivers/gpu/drm/amd/dal/controller/pipe_control.h | 67 + drivers/gpu/drm/amd/dal/controller/scaler.c | 292 +++ drivers/gpu/drm/amd/dal/controller/scaler.h | 105 ++ drivers/gpu/drm/amd/dal/controller/scaler_filter.c | 1978 ++++++++++++++++++++ drivers/gpu/drm/amd/dal/controller/scaler_filter.h | 70 + drivers/gpu/drm/amd/dal/controller/surface.c | 77 + drivers/gpu/drm/amd/dal/controller/surface.h | 87 + .../gpu/drm/amd/dal/controller/timing_generator.c | 300 +++ .../gpu/drm/amd/dal/controller/timing_generator.h | 227 +++ drivers/gpu/drm/amd/dal/controller/vga.h | 45 + drivers/gpu/drm/amd/dal/controller/video_gamma.c | 1007 ++++++++++ drivers/gpu/drm/amd/dal/controller/video_gamma.h | 114 ++ 83 files changed, 26063 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/amd/dal/controller/Makefile create mode 100644 drivers/gpu/drm/amd/dal/controller/controller.c create mode 100644 drivers/gpu/drm/amd/dal/controller/controller.h create mode 100644 drivers/gpu/drm/amd/dal/controller/crtc_overscan_color.h create mode 100644 drivers/gpu/drm/amd/dal/controller/csc.c create mode 100644 drivers/gpu/drm/amd/dal/controller/csc.h create mode 100644 drivers/gpu/drm/amd/dal/controller/csc_grph.c create mode 100644 drivers/gpu/drm/amd/dal/controller/csc_grph.h create mode 100644 drivers/gpu/drm/amd/dal/controller/csc_video.c create mode 100644 drivers/gpu/drm/amd/dal/controller/csc_video.h create mode 100644 drivers/gpu/drm/amd/dal/controller/cursor.c create mode 100644 drivers/gpu/drm/amd/dal/controller/cursor.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/controller/fbc.c create mode 100644 drivers/gpu/drm/amd/dal/controller/fbc.h create mode 100644 drivers/gpu/drm/amd/dal/controller/fbc_types.h create mode 100644 drivers/gpu/drm/amd/dal/controller/formatter.c create mode 100644 drivers/gpu/drm/amd/dal/controller/formatter.h create mode 100644 drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.c create mode 100644 drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.h create mode 100644 drivers/gpu/drm/amd/dal/controller/grph_gamma.c create mode 100644 drivers/gpu/drm/amd/dal/controller/grph_gamma.h create mode 100644 drivers/gpu/drm/amd/dal/controller/grph_gamma_types.h create mode 100644 drivers/gpu/drm/amd/dal/controller/internal_types_wide_gamut.h create mode 100644 drivers/gpu/drm/amd/dal/controller/line_buffer.c create mode 100644 drivers/gpu/drm/amd/dal/controller/line_buffer.h create mode 100644 drivers/gpu/drm/amd/dal/controller/lut_and_gamma_types.h create mode 100644 drivers/gpu/drm/amd/dal/controller/pipe_control.h create mode 100644 drivers/gpu/drm/amd/dal/controller/scaler.c create mode 100644 drivers/gpu/drm/amd/dal/controller/scaler.h create mode 100644 drivers/gpu/drm/amd/dal/controller/scaler_filter.c create mode 100644 drivers/gpu/drm/amd/dal/controller/scaler_filter.h create mode 100644 drivers/gpu/drm/amd/dal/controller/surface.c create mode 100644 drivers/gpu/drm/amd/dal/controller/surface.h create mode 100644 drivers/gpu/drm/amd/dal/controller/timing_generator.c create mode 100644 drivers/gpu/drm/amd/dal/controller/timing_generator.h create mode 100644 drivers/gpu/drm/amd/dal/controller/vga.h create mode 100644 drivers/gpu/drm/amd/dal/controller/video_gamma.c create mode 100644 drivers/gpu/drm/amd/dal/controller/video_gamma.h diff --git a/drivers/gpu/drm/amd/dal/Makefile b/drivers/gpu/drm/amd/dal/Makefile index 13dd3f14c4f6..68e30446979a 100644 --- a/drivers/gpu/drm/amd/dal/Makefile +++ b/drivers/gpu/drm/amd/dal/Makefile @@ -7,8 +7,8 @@ AMDDALPATH = $(RELATIVE_AMD_DAL_PATH) subdir-ccflags-y += -I$(AMDDALPATH)/ -I$(AMDDALPATH)/include -DDAL_CZ_BRINGUP -DAL_LIBS = adapter amdgpu_dm asic_capability basics bios connector gpio gpu \ - i2caux irq timing_service +DAL_LIBS = adapter amdgpu_dm asic_capability basics bios connector controller \ + gpio gpu i2caux irq timing_service AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/,$(DAL_LIBS))) diff --git a/drivers/gpu/drm/amd/dal/controller/Makefile b/drivers/gpu/drm/amd/dal/controller/Makefile new file mode 100644 index 000000000000..599526652c48 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +CONTROLLER = controller.o timing_generator.o csc_video.o \ + csc_grph.o scaler.o grph_gamma.o video_gamma.o \ + scaler_filter.o graphics_and_video_gamma.o fbc.o formatter.o \ + csc.o surface.o line_buffer.o cursor.o + +AMD_DAL_CONTROLLER = $(addprefix $(AMDDALPATH)/controller/,$(CONTROLLER)) + +AMD_DAL_FILES += $(AMD_DAL_CONTROLLER) + +############################################################################### +# DCE 11x +############################################################################### +ifdef CONFIG_DRM_AMD_DAL_DCE11_0 +CONTROLLER_DCE110 = controller_dce110.o fbc_dce110.o vga_dce110.o \ + timing_generator_dce110.o dcp_bit_depth_reduction_dce110.o \ + csc_dce110.o csc_grph_dce110.o formatter_dce110.o \ + grph_gamma_dce110.o scaler_dce110.o pipe_control_dce110.o \ + surface_dce110.o controller_v_dce110.o scaler_v_dce110.o \ + surface_v_dce110.o timing_generator_v_dce110.o \ + pipe_control_v_dce110.o line_buffer_dce110.o \ + line_buffer_v_dce110.o col_man_csc_dce110.o \ + col_man_gamma_dce110.o cursor_dce110.o + +AMD_DAL_CONTROLLER_DCE110 = $(addprefix \ + $(AMDDALPATH)/controller/dce110/,$(CONTROLLER_DCE110)) + +AMD_DAL_FILES += $(AMD_DAL_CONTROLLER_DCE110) +endif diff --git a/drivers/gpu/drm/amd/dal/controller/controller.c b/drivers/gpu/drm/amd/dal/controller/controller.c new file mode 100644 index 000000000000..e587b7058958 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/controller.c @@ -0,0 +1,970 @@ +/* + * 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/adapter_service_interface.h" +#include "include/signal_types.h" +#include "include/controller_interface.h" +#include "include/line_buffer_interface.h" +#include "include/display_clock_interface.h" +#include "include/dc_clock_generator_interface.h" +#include "include/fixed31_32.h" + +#include "controller.h" +#include "timing_generator.h" +#include "csc.h" +#include "scaler.h" +#include "surface.h" +#include "formatter.h" +#include "pipe_control.h" +#include "vga.h" +#include "line_buffer.h" +#include "cursor.h" + +#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) +#include "dce110/controller_dce110.h" +#include "dce110/controller_v_dce110.h" +#endif + +/* + * ************************************************************************** + * ************************* Basic functionality on Controllers *************** + * ************************************************************************** + */ + +bool dal_controller_base_construct( + struct controller *crtc, + struct controller_init_data *init_data) +{ + if (!init_data) + return false; + + if (!init_data->as) + return false; + + crtc->dal_context = init_data->dal_context; + crtc->id = init_data->controller; + crtc->paired_id = init_data->paired_controller; + + return true; +} + +/* destruct actions common for ALL versions of DCE. */ +void dal_controller_base_destruct(struct controller *crtc) +{ + if (crtc->cursor) + crtc->cursor->funcs->destroy(&crtc->cursor); + + if (crtc->surface) + crtc->surface->funcs->destroy(&crtc->surface); + + if (crtc->pc) + crtc->pc->funcs->destroy(&crtc->pc); + + if (crtc->vga) + crtc->vga->funcs->destroy(&crtc->vga); + + if (crtc->fmt) + crtc->fmt->funcs->destroy(&crtc->fmt); + + if (crtc->csc) + crtc->csc->funcs->destroy(&crtc->csc); + + if (crtc->video_gamma) + crtc->video_gamma->funcs->destroy(&crtc->video_gamma); + + if (crtc->grph_gamma) + crtc->grph_gamma->funcs->destroy(&crtc->grph_gamma); + + if (crtc->scl) + crtc->scl->funcs->destroy(&crtc->scl); + + if (crtc->tg) + crtc->tg->funcs->destroy(&crtc->tg); + + if (crtc->lb) + dal_line_buffer_destroy(&crtc->lb); +} + +struct controller *dal_controller_create(struct controller_init_data *init_data) +{ + struct controller *crtc = NULL; + enum dce_version dce_version; + + if (!init_data) + return NULL; + if (!init_data->as) + return NULL; + + dce_version = dal_adapter_service_get_dce_version(init_data->as); + + switch (dce_version) { +#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + case DCE_VERSION_11_0: + if (IS_UNDERLAY_CONTROLLER(init_data->controller)) + crtc = dal_controller_v_dce110_create(init_data); + else + crtc = dal_controller_dce110_create(init_data); + break; +#endif + default: + BREAK_TO_DEBUGGER(); + break; + } + + if (NULL == crtc) + return NULL; + + crtc->go_id = dal_graphics_object_id_init(init_data->controller, + ENUM_ID_1, OBJECT_TYPE_CONTROLLER); + + return crtc; +} + +void dal_controller_destroy(struct controller **crtc) +{ + if (!crtc || !*crtc) { + BREAK_TO_DEBUGGER(); + return; + } + + (*crtc)->funcs.destroy(crtc); + *crtc = NULL; +} + +const struct graphics_object_id dal_controller_get_graphics_object_id( + const struct controller *crtc) +{ + return crtc->go_id; +} + +void dal_controller_power_up(struct controller *crtc) +{ + dal_line_buffer_power_up(crtc->lb); +} + +void dal_controller_power_down(struct controller *crtc) +{ + crtc->tg->funcs->disable_crtc(crtc->tg); +} + +bool dal_controller_power_gating_enable( + struct controller *crtc, + enum pipe_gating_control cntl) +{ + if (crtc->pc) + return crtc->pc->funcs-> + enable_disp_power_gating(crtc->pc, cntl); + else + return PIPE_GATING_CONTROL_DISABLE == cntl; +} + +enum controller_id dal_controller_get_id(struct controller *crtc) +{ + return crtc->id; +} + +enum controller_id dal_controller_get_paired_controller_id( + struct controller *crtc) +{ + return crtc->paired_id; +} +enum sync_source dal_controller_get_sync_source(struct controller *crtc) +{ + switch (crtc->id) { + case CONTROLLER_ID_D0: + return SYNC_SOURCE_CONTROLLER0; + case CONTROLLER_ID_D1: + return SYNC_SOURCE_CONTROLLER1; + case CONTROLLER_ID_D2: + return SYNC_SOURCE_CONTROLLER2; + case CONTROLLER_ID_D3: + return SYNC_SOURCE_CONTROLLER3; + case CONTROLLER_ID_D4: + return SYNC_SOURCE_CONTROLLER4; + case CONTROLLER_ID_D5: + return SYNC_SOURCE_CONTROLLER5; + default: + return SYNC_SOURCE_NONE; + } +} + +/* + * ************************************************************************** + * ******************************* Shared Objects Pointers ****************** + * ************************************************************************** + */ + +/* Get */ +struct line_buffer *dal_controller_get_line_buffer(struct controller *crtc) +{ + return crtc->lb; +} + +struct display_clock *dal_controller_get_display_clock( + struct controller *crtc) +{ + return crtc->dc; +} + +struct bandwidth_manager *dal_controller_get_bandwidth_manager( + struct controller *crtc) +{ + return crtc->bm; +} + +struct dc_clock_generator *dal_controller_get_dc_clock_generator( + struct controller *crtc) +{ + return crtc->dc_clk_gen; +} + +/* Set */ +void dal_controller_set_display_clock( + struct controller *crtc, + struct display_clock *dc) +{ + crtc->dc = dc; +} + +void dal_controller_set_bandwidth_manager( + struct controller *crtc, + struct bandwidth_manager *bm) +{ + crtc->bm = bm; +} + +void dal_controller_set_dc_clock_generator( + struct controller *crtc, + struct dc_clock_generator *dc_clk_gen) +{ + crtc->dc_clk_gen = dc_clk_gen; +} + +/* + * ************************************************************************** + * ********************* Timing Generator Interface ************************* + * ************************************************************************** + */ + +/* Get */ +void dal_controller_get_crtc_timing( + struct controller *crtc, + struct hw_crtc_timing *hw_crtc_timing) +{ + crtc->tg->funcs->get_crtc_timing(crtc->tg, hw_crtc_timing); +} + +bool dal_controller_is_counter_moving(struct controller *crtc) +{ + return crtc->tg->funcs->is_counter_moving(crtc->tg); +} + +void dal_controller_get_crtc_position( + struct controller *crtc, + struct crtc_position *crtc_position) +{ + crtc->tg->funcs->get_crtc_position(crtc->tg, crtc_position); +} + +void dal_controller_wait_for_vblank(struct controller *crtc) +{ + crtc->tg->funcs->wait_for_vblank(crtc->tg); +} + +void dal_controller_wait_for_vactive(struct controller *crtc) +{ + crtc->tg->funcs->wait_for_vactive(crtc->tg); +} + +void dal_controller_wait_frames(struct controller *crtc, uint32_t num_of_frames) +{ + uint32_t i; + + for (i = 0; i < num_of_frames; i++) { + crtc->tg->funcs->wait_for_vactive(crtc->tg); + crtc->tg->funcs->wait_for_vblank(crtc->tg); + } +} + +bool dal_controller_validate_timing( + struct controller *crtc, + const struct hw_crtc_timing *timing, + enum signal_type signal) +{ + return crtc->tg->funcs->validate_timing(crtc->tg, timing, signal); +} + +bool dal_controller_get_active_pll_id( + struct controller *crtc, + enum signal_type signal, + bool *dto_mode, + enum clock_source_id *clk_src_id) +{ + return crtc->funcs.get_active_pll_id( + crtc, signal, dto_mode, clk_src_id); +} + +uint32_t dal_controller_get_crtc_scanoutpos( + struct controller *crtc, + int32_t *vpos, + int32_t *hpos) +{ + return crtc->tg->funcs->get_crtc_scanoutpos( + crtc->tg, + vpos, + hpos); +} + +/* Set */ +bool dal_controller_enable_timing_generator(struct controller *crtc) +{ + return crtc->tg->funcs->enable_crtc(crtc->tg); +} + +bool dal_controller_disable_timing_generator(struct controller *crtc) +{ + return crtc->tg->funcs->disable_crtc(crtc->tg); +} + +bool dal_controller_program_timing_generator( + struct controller *crtc, + struct hw_crtc_timing *timing) +{ + return crtc->tg->funcs->program_timing_generator(crtc->tg, timing); +} + +void dal_controller_program_drr( + struct controller *crtc, + const struct hw_ranged_timing *timing) +{ + crtc->tg->funcs->program_drr(crtc->tg, timing); +} + +bool dal_controller_blank_crtc(struct controller *crtc, enum color_space cs) +{ + return crtc->tg->funcs->blank_crtc(crtc->tg, cs); +} + +bool dal_controller_unblank_crtc(struct controller *crtc, enum color_space cs) +{ + return crtc->tg->funcs->unblank_crtc(crtc->tg, cs); +} + +void dal_controller_reprogram_timing( + struct controller *crtc, + const struct hw_crtc_timing *ref_timing, + const struct hw_crtc_timing *new_timing) +{ + crtc->tg->funcs->reprogram_timing(crtc->tg, ref_timing, new_timing); +} + +void dal_controller_program_vbi_end_signal( + struct controller *crtc, + const struct vbi_end_signal_setup *setup) +{ + crtc->tg->funcs->program_vbi_end_signal(crtc->tg, setup); +} + +void dal_controller_program_blanking( + struct controller *crtc, + const struct hw_crtc_timing *timing) +{ + crtc->tg->funcs->program_blanking(crtc->tg, timing); +} + +/* + * ***************************************************************************** + * ********************* Generic Control Interface **************************** + * ***************************************************************************** + */ + +/* (Get) Controller IO sequence */ +bool dal_controller_get_io_sequence( + struct controller *crtc, + enum io_register_sequence sequence, + struct io_reg_sequence *reg_sequence) +{ + return crtc->tg->funcs-> + get_io_sequence(crtc->tg, sequence, reg_sequence); +} + +/* (Set) Pipe control */ +void dal_controller_set_fe_clock(struct controller *crtc, bool enable) +{ + if (crtc->pc) + crtc->pc->funcs->enable_fe_clock(crtc->pc, enable); +} + +void dal_controller_enable_display_pipe_clock_gating( + struct controller *crtc, + bool enable) +{ + if (crtc->pc) + crtc->pc->funcs-> + enable_display_pipe_clock_gating(crtc->pc, enable); +} + +bool dal_controller_pipe_control_lock( + struct controller *crtc, + uint32_t control_mask, + bool lock) +{ + if (crtc->pc) + return crtc->pc->funcs->pipe_control_lock( + crtc->pc, + control_mask, + lock); + else + return false; +} + +/* (Set) Enable/disable triggered CRTC reset */ +bool dal_controller_enable_reset_trigger( + struct controller *crtc, + const struct trigger_params *params) +{ + return crtc->tg->funcs->enable_reset_trigger(crtc->tg, params); +} + +void dal_controller_disable_reset_trigger(struct controller *crtc) +{ + crtc->tg->funcs->disable_reset_trigger(crtc->tg); +} + +bool dal_controller_force_triggered_reset_now( + struct controller *crtc, + const struct trigger_params *params) +{ + return crtc->tg->funcs->force_triggered_reset_now(crtc->tg, params); +} + +bool dal_controller_program_flow_control( + struct controller *crtc, + enum sync_source source) +{ + return crtc->tg->funcs->program_flow_control(crtc->tg, source); +} + +void dal_controller_set_early_control( + struct controller *crtc, + uint32_t early_cntl) +{ + crtc->tg->funcs->set_early_control(crtc->tg, early_cntl); +} + +void dal_controller_set_advanced_request( + struct controller *crtc, + bool enable, + const struct hw_crtc_timing *timing) +{ + crtc->tg->funcs->enable_advanced_request(crtc->tg, enable, timing); +} + +/* (Set) Double buffering */ +void dal_controller_set_lock_timing_registers( + struct controller *crtc, + bool lock) +{ + crtc->tg->funcs->set_lock_timing_registers(crtc->tg, lock); +} + +void dal_controller_set_lock_graph_surface_registers( + struct controller *crtc, + bool lock) +{ + crtc->tg->funcs->set_lock_graph_surface_registers(crtc->tg, lock); +} + +void dal_controller_set_lock_master(struct controller *crtc, bool lock) +{ + crtc->tg->funcs->set_lock_master(crtc->tg, lock); +} + +/* (Set/Get) Global Swap Lock */ +void dal_controller_setup_global_swaplock( + struct controller *crtc, + const struct dcp_gsl_params *params) +{ + crtc->tg->funcs->setup_global_swap_lock(crtc->tg, params); +} + +void dal_controller_get_global_swaplock_setup( + struct controller *crtc, + struct dcp_gsl_params *params) +{ + crtc->tg->funcs->get_global_swap_lock_setup(crtc->tg, params); +} + +/* + * ************************************************************************** + * ***************************** VGA *************************************** + * ************************************************************************** + */ + +void dal_controller_disable_vga(struct controller *crtc) +{ + if (crtc->vga) + crtc->vga->funcs->disable_vga(crtc->vga); +} + +/* + * ************************************************************************** + * *************************** Display Scaler ******************************* + * ************************************************************************** + */ + +/* Get */ +enum scaler_validation_code dal_controller_get_optimal_taps_number( + struct controller *crtc, + struct scaler_validation_params *params, + struct scaling_tap_info *taps) +{ + return crtc->scl->funcs->get_optimal_taps_number(params, taps); +} + +enum scaler_validation_code dal_controller_get_next_lower_taps_number( + struct controller *crtc, + struct scaler_validation_params *params, + struct scaling_tap_info *taps) +{ + return crtc->scl->funcs->get_next_lower_taps_number(params, taps); +} + +/* Set */ +/* General purpose scaler programming interface */ +bool dal_controller_set_scaler_wrapper( + struct controller *crtc, + const struct scaler_data *data) +{ + return crtc->scl->funcs->set_scaler_wrapper(crtc->scl, data); +} + +/* not use scaling */ +void dal_controller_set_scaler_bypass(struct controller *crtc) +{ + crtc->scl->funcs->set_scaler_bypass(crtc->scl); +} + +bool dal_controller_is_scaling_enabled(struct controller *crtc) +{ + return crtc->scl->funcs->is_scaling_enabled(crtc->scl); +} + +bool dal_controller_update_viewport( + struct controller *crtc, + const struct rect *view_port, + bool is_fbc_attached) +{ + return + dal_scaler_update_viewport( + crtc->scl, + view_port, + is_fbc_attached); +} + +/* + * ************************************************************************** + * *************************** LUT and Gamma ****************************** + * ************************************************************************** + */ + +/* Set */ +bool dal_controller_set_gamma_ramp( + struct controller *crtc, + const struct gamma_ramp *ramp, + const struct gamma_parameters *params) +{ + return crtc->grph_gamma->funcs->set_gamma_ramp( + crtc->grph_gamma, ramp, params); +} + +bool dal_controller_set_palette( + struct controller *crtc, + const struct dev_c_lut *palette, + uint32_t start, + uint32_t length, + enum pixel_format surface_format) +{ + return dal_grph_gamma_set_palette( + crtc->grph_gamma, palette, start, length, surface_format); +} + +/* set driver default gamma without any adjustment */ +bool dal_controller_set_default_gamma( + struct controller *crtc, + enum pixel_format surface_format) +{ + return dal_grph_gamma_set_default_gamma( + crtc->grph_gamma, surface_format); +} + +/* + ******************************************************************************* + ************************* Display color space and color adjustment *********** + ******************************************************************************* + */ + +/******************************************************************************* +* GetGrphAdjustmentRange +* @param [in] alphatype: one of the graphic matrix adjustment type +* @param [out] pAdjustRange: adjustment HW range. +* @return +* @note +* HW graphic color matrix adjustment range, DS provides API range, HWSS +* converts HW adjustment unit to do adjustment. +* @see +*******************************************************************************/ +void dal_controller_get_grph_adjustment_range( + struct controller *crtc, + enum grph_csc_adjust_item adjust_item, + struct hw_adjustment_range *adjust_range) +{ + dal_csc_grph_get_graphic_color_adjustment_range( + adjust_item, adjust_range); +} + +bool dal_controller_is_supported_custom_gamut_adjustment( + struct controller *crtc, + enum surface_type surface_type) +{ + return crtc->csc->funcs-> + is_supported_custom_gamut_adjustment(crtc->csc); +} + +bool dal_controller_is_supported_custom_gamma_coefficients( + struct controller *crtc, + enum surface_type surface_type) +{ + return surface_type == GRAPHIC_SURFACE; +} + +bool dal_controller_is_supported_overlay_alpha_adjustment( + struct controller *crtc) +{ + return crtc->csc->funcs-> + is_supported_overlay_alpha_adjustment(crtc->csc); +} + +bool dal_controller_set_input_csc( + struct controller *crtc, + const enum color_space color_space) +{ + return crtc->csc->funcs-> + set_input_csc(crtc->csc, color_space); +} + +void dal_controller_get_overlay_adjustment_range( + struct controller *crtc, + enum ovl_csc_adjust_item overlay_adjust_item, + struct hw_adjustment_range *adjust_range) +{ + dal_csc_video_get_ovl_adjustment_range( + crtc->csc->csc_video, + overlay_adjust_item, + adjust_range); +} + +void dal_controller_set_grph_csc_default( + struct controller *crtc, + const struct default_adjustment *default_adjust) +{ + if (default_adjust) + crtc->csc->funcs-> + set_grph_csc_default(crtc->csc, default_adjust); +} + +void dal_controller_set_grph_csc_adjustment( + struct controller *crtc, + const struct grph_csc_adjustment *adjust) +{ + crtc->csc->funcs->set_grph_csc_adjustment(crtc->csc, adjust); +} + +void dal_controller_set_overscan_color_black( + struct controller *crtc, + enum color_space color_space) +{ + crtc->csc->funcs->set_overscan_color_black(crtc->csc, color_space); +} + +void dal_controller_set_ovl_csc_adjustment( + struct controller *crtc, + const struct ovl_csc_adjustment *adjust, + enum color_space color_space) +{ + crtc->csc->funcs-> + set_ovl_csc_adjustment(crtc->csc, adjust, color_space); + + ASSERT(0 != adjust->overlay_gamma.adjust_divider); + if (adjust->overlay_gamma.adjust_divider) { + struct overlay_gamma_parameters *data = NULL; + + data = dal_alloc(sizeof(*data)); + + if (!data) { + BREAK_TO_DEBUGGER(); + /* memory allocation failure */ + return; + } + + data->ovl_gamma_cont = adjust->overlay_gamma.adjust + / adjust->overlay_gamma.adjust_divider; + data->adjust_type = adjust->adjust_gamma_type; + data->desktop_surface = adjust->desktop_surface_pixel_format; + data->flag.u_all = adjust->flag.u_all; + + dal_memmove(&data->regamma, &adjust->regamma, + sizeof(data->regamma)); + + crtc->video_gamma->funcs-> + set_overlay_pwl_adjustment(crtc->video_gamma, data); + + dal_free(data); + } +} + +void dal_controller_set_vertical_sync_adjustment( + struct controller *crtc, + uint32_t v_sync_polarity) +{ + crtc->tg->funcs->set_vertical_sync_polarity(crtc->tg, v_sync_polarity); +} + +void dal_controller_set_horizontal_sync_adjustment( + struct controller *crtc, + uint32_t h_sync_polarity) +{ + crtc->tg->funcs->set_horizontal_sync_polarity( + crtc->tg, + h_sync_polarity); +} + +void dal_controller_set_horizontal_sync_composite( + struct controller *crtc, + uint32_t h_sync_composite) +{ + crtc->tg->funcs-> + set_horizontal_sync_composite(crtc->tg, h_sync_composite); +} + +/* + ******************************************************************************* + **************************** FMT (Dithering) ********************************** + ******************************************************************************* + */ + +/* Formatter Block */ +void dal_controller_program_formatter_bit_depth_reduction( + struct controller *crtc, + const struct bit_depth_reduction_params *info) +{ + crtc->fmt->funcs->program_bit_depth_reduction(crtc->fmt, info); +} + +void dal_controller_program_formatter_clamping_and_pixel_encoding( + struct controller *crtc, + const struct clamping_and_pixel_encoding_params + *info) +{ + crtc->fmt->funcs->program_clamping_and_pixel_encoding(crtc->fmt, info); +} + +/* Deep color in FMAT */ +void dal_controller_formatter_set_dyn_expansion( + struct controller *crtc, + enum color_space color_space, + enum color_depth color_depth, + enum signal_type signal) +{ + crtc->fmt->funcs-> + set_dyn_expansion(crtc->fmt, color_space, color_depth, signal); +} + +/* + * ***************************************************************************** + * ************************** Stereo ******************************************* + * ***************************************************************************** + */ + +bool dal_controller_get_stereo_status( + struct controller *crtc, + struct crtc_stereo_status *status) +{ + return crtc->tg->funcs->get_stereo_status(crtc->tg, status); +} + +void dal_controller_enable_stereo( + struct controller *crtc, + const struct crtc_stereo_parameters *params) +{ + if (!params->FRAME_PACKED) { + if (params->PROGRAM_STEREO) + crtc->fmt->funcs->setup_stereo_polarity(crtc->fmt, + FMT_STEREO_ACTION_ENABLE, + params->RIGHT_EYE_POLARITY); + else if (params->PROGRAM_POLARITY) + crtc->fmt->funcs->setup_stereo_polarity(crtc->fmt, + FMT_STEREO_ACTION_UPDATE_POLARITY, + params->RIGHT_EYE_POLARITY); + } + + crtc->tg->funcs->enable_stereo(crtc->tg, params); +} + +void dal_controller_disable_stereo(struct controller *crtc) +{ + crtc->fmt->funcs->setup_stereo_polarity(crtc->fmt, + FMT_STEREO_ACTION_DISABLE, + false); + crtc->tg->funcs->disable_stereo(crtc->tg); +} + +void dal_controller_force_stereo_next_eye( + struct controller *crtc, + bool right_eye) +{ + crtc->tg->funcs->force_stereo_next_eye(crtc->tg, right_eye); +} + +void dal_controller_reset_stereo_3d_phase(struct controller *crtc) +{ + crtc->tg->funcs->reset_stereo_3d_phase(crtc->tg); +} + +void dal_controller_enable_stereo_mixer( + struct controller *crtc, + const struct crtc_mixer_params *params) +{ + if (crtc->pc) + crtc->pc->funcs->enable_stereo_mixer(crtc->pc, params); +} + +void dal_controller_disable_stereo_mixer(struct controller *crtc) +{ + if (crtc->pc) + crtc->pc->funcs->disable_stereo_mixer(crtc->pc); +} + +void dal_controller_set_test_pattern( + struct controller *crtc, + enum dp_test_pattern test_pattern, + enum hw_color_depth color_depth) +{ + crtc->tg->funcs->set_test_pattern(crtc->tg, test_pattern, color_depth); +} + +void dal_controller_set_scaler_filter( + struct controller *crtc, + struct scaler_filter *filter) +{ + crtc->scl->filter = filter; +} + +/* + * dal_controller_get_vblank_counter + * + * @brief + * Get counter of vblanks + * + * @param + * struct controller *crtc - [in] desired controller + * + * @return + * Counter of frames + */ +uint32_t dal_controller_get_vblank_counter(struct controller *crtc) +{ + return crtc->tg->funcs->get_vblank_counter(crtc->tg); +} + +void dal_controller_set_blender_mode( + struct controller *crtc, + enum blender_mode mode) +{ + crtc->pc->funcs->set_blender_mode(crtc->pc, mode); +} + +bool dal_controller_program_alpha_blending( + struct controller *crtc, + const struct alpha_mode_cfg *cfg) +{ + return crtc->pc->funcs->program_alpha_blending(crtc->pc, cfg); +} + +bool dal_controller_is_surface_supported( + struct controller *crtc, + const struct plane_config *pl_cfg) +{ + return crtc->funcs.is_surface_supported(crtc, pl_cfg); +} + +/* + * ************************************************************************** + * ********************* Surface Interface ************************* + * ************************************************************************** + */ + +bool dal_controller_program_surface_config( + struct controller *crtc, + const struct plane_surface_config *configs) +{ + return dal_surface_program_config( + crtc->surface, + configs); +} + +bool dal_controller_program_surface_flip_and_addr( + struct controller *crtc, + const struct plane_addr_flip_info *flip_info) +{ + return dal_surface_program_flip_and_addr( + crtc->surface, + flip_info); +} + +/* + * ************************************************************************** + * ********************* Cursor Interface ************************* + * ************************************************************************** + */ +bool dal_controller_set_cursor_position( + struct controller *crtc, + const struct cursor_position *position) +{ + return dal_cursor_set_position( + crtc->cursor, + position); +} + +bool dal_controller_set_cursor_attributes( + struct controller *crtc, + const struct cursor_attributes *attributes) +{ + return dal_cursor_set_attributes( + crtc->cursor, + attributes); +} diff --git a/drivers/gpu/drm/amd/dal/controller/controller.h b/drivers/gpu/drm/amd/dal/controller/controller.h new file mode 100644 index 000000000000..d2c1152fb09d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/controller.h @@ -0,0 +1,88 @@ +/* + * 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_CONTROLLER_H__ +#define __DAL_CONTROLLER_H__ + +#include "timing_generator.h" +#include "scaler.h" +#include "surface.h" +#include "formatter.h" +#include "grph_gamma.h" +#include "video_gamma.h" + +#include "lut_and_gamma_types.h" + +#include "csc.h" + +struct controller; +struct controller_funcs { + bool (*get_active_pll_id)( + struct controller *controller, + enum signal_type signal, + bool *dto_mode, + enum clock_source_id *clock_source_id); + void (*destroy)(struct controller **controller); + bool (*is_surface_supported)( + struct controller *crtc, + const struct plane_config *pl_cfg); +}; + +struct controller { + struct dal_context *dal_context; + struct controller_funcs funcs; + + enum controller_id id; + enum controller_id paired_id; + + struct csc *csc; + struct grph_gamma *grph_gamma; + struct video_gamma *video_gamma; + + struct formatter *fmt; + struct scaler *scl; + struct timing_generator *tg; + struct pipe_control *pc; + struct vga *vga; + struct line_buffer *lb; + struct display_clock *dc; + struct bandwidth_manager *bm; + struct dc_clock_generator *dc_clk_gen; + + struct graphics_object_id go_id; + + struct surface *surface; /* One surface for each controller */ + + struct cursor *cursor; +}; + +struct controller_init_data; +bool dal_controller_base_construct( + struct controller *crtc, + struct controller_init_data *init_data); + +void dal_controller_base_destruct(struct controller *crtc); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/crtc_overscan_color.h b/drivers/gpu/drm/amd/dal/controller/crtc_overscan_color.h new file mode 100644 index 000000000000..2903d7af4fb4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/crtc_overscan_color.h @@ -0,0 +1,49 @@ +/* + * 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_CRTC_OVERSCAN_COLOR_H__ +#define __DAL_CRTC_OVERSCAN_COLOR_H__ + +/* overscan in blank for YUV color space. For RGB, it is zero for black. */ +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV 0x1f4 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4CV 0x40 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV 0x1f4 + +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV 0x200 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV 0x40 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV 0x200 + +/* overscan in blank for YUV color space when in SuperAA crossfire mode */ +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA 0x1a2 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA 0x20 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA 0x1a2 + +/* OVERSCAN COLOR FOR RGB LIMITED RANGE + * (16~253) 16*4 (Multiple over 256 code leve) =64 (0x40) */ +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE 0x40 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE 0x40 +#define CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE 0X40 + +#endif /* __DAL_CRTC_OVERSCAN_COLOR_H__ */ diff --git a/drivers/gpu/drm/amd/dal/controller/csc.c b/drivers/gpu/drm/amd/dal/controller/csc.c new file mode 100644 index 000000000000..b1763c4781c3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc.c @@ -0,0 +1,46 @@ +/* + * 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 "csc.h" + +bool dal_csc_construct( + struct csc *csc, + const struct csc_init_data *init_data) +{ + if (!init_data) + return false; + + csc->ctx = init_data->ctx; + return true; +} + +bool dal_csc_set_input_csc( + struct csc *csc, + const enum color_space color_space) +{ + return false; +} diff --git a/drivers/gpu/drm/amd/dal/controller/csc.h b/drivers/gpu/drm/amd/dal/controller/csc.h new file mode 100644 index 000000000000..302108b1ba8b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc.h @@ -0,0 +1,81 @@ +/* + * 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_CSC_H__ +#define __DAL_CSC_H__ + +#include "include/fixed31_32.h" +#include "include/grph_csc_types.h" +#include "include/video_csc_types.h" + +#include "csc_grph.h" +#include "csc_video.h" + +struct csc_init_data { + enum controller_id id; + struct dal_context *ctx; + struct adapter_service *as; +}; + +struct csc; + +struct csc_funcs { + void (*set_grph_csc_default)( + struct csc *csc, + const struct default_adjustment *adjust); + void (*set_grph_csc_adjustment)( + struct csc *csc, + const struct grph_csc_adjustment *adjust); + void (*set_overscan_color_black)( + struct csc *csc, + enum color_space black_color); + void (*set_ovl_csc_adjustment)( + struct csc *csc, + const struct ovl_csc_adjustment *adjust, + enum color_space color_space); + bool (*is_supported_custom_gamut_adjustment)(struct csc *csc); + bool (*is_supported_overlay_alpha_adjustment)(struct csc *csc); + bool (*set_input_csc)( + struct csc *csc, + const enum color_space color_space); + void (*destroy)(struct csc **csc); +}; + +struct csc { + const struct csc_funcs *funcs; + struct csc_grph *csc_grph; + struct csc_video *csc_video; + struct dal_context *ctx; +}; + +bool dal_csc_construct( + struct csc *csc, + const struct csc_init_data *init_data); + +bool dal_csc_set_input_csc( + struct csc *csc, + const enum color_space color_space); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/csc_grph.c b/drivers/gpu/drm/amd/dal/controller/csc_grph.c new file mode 100644 index 000000000000..bbe5e31b274e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc_grph.c @@ -0,0 +1,649 @@ +/* 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/fixed31_32.h" + +#include "csc_grph.h" + +static void setup_adjustments( + const struct grph_csc_adjustment *adjust, + struct csc_adjustments *adjustments); +static void initialize_color_float_adj_reference_values( + const struct grph_csc_adjustment *adjust, + struct fixed31_32 *grph_cont, + struct fixed31_32 *grph_sat, + struct fixed31_32 *grph_bright, + struct fixed31_32 *sin_grph_hue, + struct fixed31_32 *cos_grph_hue); + +/** + ***************************************************************************** + * Function: dal_csc_grph_wide_gamut_set_gamut_remap + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +void dal_csc_grph_wide_gamut_set_gamut_remap( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust) +{ + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW || + adjust->temperature_divider == 0) + cg->funcs->program_gamut_remap(cg, NULL); + else { + struct fixed31_32 arr_matrix[MATRIX_CONST]; + uint16_t arr_reg_val[MATRIX_CONST]; + + arr_matrix[0] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[0], + adjust->temperature_divider); + arr_matrix[1] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[1], + adjust->temperature_divider); + arr_matrix[2] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[2], + adjust->temperature_divider); + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[3], + adjust->temperature_divider); + arr_matrix[5] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[4], + adjust->temperature_divider); + arr_matrix[6] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[5], + adjust->temperature_divider); + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[6], + adjust->temperature_divider); + arr_matrix[9] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[7], + adjust->temperature_divider); + arr_matrix[10] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[8], + adjust->temperature_divider); + arr_matrix[11] = dal_fixed31_32_zero; + + dal_controller_convert_float_matrix( + arr_reg_val, arr_matrix, MATRIX_CONST); + + cg->funcs->program_gamut_remap(cg, arr_reg_val); + } +} + +/** + ***************************************************************************** + * Function: dal_csc_grph_wide_gamut_set_rgb_limited_range_adjustment + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB limited color space + * + * @see + * + ***************************************************************************** + */ +void dal_csc_grph_wide_gamut_set_rgb_limited_range_adjustment( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust) +{ + struct dcp_color_matrix reg_matrix; + struct fixed31_32 change_matrix[MATRIX_CONST]; + struct fixed31_32 matrix[MATRIX_CONST]; + struct csc_adjustments adjustments; + struct fixed31_32 ideals[MATRIX_CONST]; + + dal_controller_prepare_tv_rgb_ideal(ideals); + + setup_adjustments(adjust, &adjustments); + + dal_controller_calculate_adjustments(ideals, &adjustments, matrix); + + dal_memmove(change_matrix, matrix, sizeof(matrix)); + + /* from 1 -> 3 */ + matrix[8] = change_matrix[0]; + matrix[9] = change_matrix[1]; + matrix[10] = change_matrix[2]; + matrix[11] = change_matrix[3]; + + /* from 2 -> 1 */ + matrix[0] = change_matrix[4]; + matrix[1] = change_matrix[5]; + matrix[2] = change_matrix[6]; + matrix[3] = change_matrix[7]; + + /* from 3 -> 2 */ + matrix[4] = change_matrix[8]; + matrix[5] = change_matrix[9]; + matrix[6] = change_matrix[10]; + matrix[7] = change_matrix[11]; + + dal_memset(®_matrix, 0, sizeof(struct dcp_color_matrix)); + + dal_controller_setup_reg_format(matrix, reg_matrix.regval); + + cg->funcs->program_color_matrix(cg, ®_matrix, GRPH_COLOR_MATRIX_SW); +} + +/** + ***************************************************************************** + * Function: dal_csc_grph_wide_gamut_set_yuv_adjustment + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for YUV color spaces + * + * @see + * + ***************************************************************************** + */ +void dal_csc_grph_wide_gamut_set_yuv_adjustment( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust) +{ + bool b601 = (adjust->c_space == COLOR_SPACE_YPBPR601) || + (adjust->c_space == COLOR_SPACE_YCBCR601) || + (adjust->c_space == COLOR_SPACE_YCBCR601_YONLY); + struct dcp_color_matrix reg_matrix; + struct fixed31_32 matrix[MATRIX_CONST]; + struct csc_adjustments adjustments; + struct fixed31_32 ideals[MATRIX_CONST]; + + dal_controller_prepare_yuv_ideal(b601, ideals); + + setup_adjustments(adjust, &adjustments); + + if ((adjust->c_space == COLOR_SPACE_YCBCR601_YONLY) || + (adjust->c_space == COLOR_SPACE_YCBCR709_YONLY)) + dal_controller_calculate_adjustments_y_only( + ideals, &adjustments, matrix); + else + dal_controller_calculate_adjustments( + ideals, &adjustments, matrix); + + dal_memset(®_matrix, 0, sizeof(struct dcp_color_matrix)); + + dal_controller_setup_reg_format(matrix, reg_matrix.regval); + + cg->funcs->program_color_matrix(cg, ®_matrix, GRPH_COLOR_MATRIX_SW); +} + +/** + ***************************************************************************** + * Function: dal_csc_grph_wide_gamut_set_rgb_adjustment_legacy + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB color space + * + * @see + * + ***************************************************************************** + */ +void dal_csc_grph_wide_gamut_set_rgb_adjustment_legacy( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust) +{ + const struct fixed31_32 k1 = + dal_fixed31_32_from_fraction(701000, 1000000); + const struct fixed31_32 k2 = + dal_fixed31_32_from_fraction(236568, 1000000); + const struct fixed31_32 k3 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k4 = + dal_fixed31_32_from_fraction(464432, 1000000); + const struct fixed31_32 k5 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k6 = + dal_fixed31_32_from_fraction(-701000, 1000000); + const struct fixed31_32 k7 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k8 = + dal_fixed31_32_from_fraction(-292569, 1000000); + const struct fixed31_32 k9 = + dal_fixed31_32_from_fraction(413000, 1000000); + const struct fixed31_32 k10 = + dal_fixed31_32_from_fraction(-92482, 1000000); + const struct fixed31_32 k11 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k12 = + dal_fixed31_32_from_fraction(385051, 1000000); + const struct fixed31_32 k13 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k14 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k15 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k16 = + dal_fixed31_32_from_fraction(-741914, 1000000); + const struct fixed31_32 k17 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k18 = + dal_fixed31_32_from_fraction(-144086, 1000000); + + const struct fixed31_32 luma_r = + dal_fixed31_32_from_fraction(299, 1000); + const struct fixed31_32 luma_g = + dal_fixed31_32_from_fraction(587, 1000); + const struct fixed31_32 luma_b = + dal_fixed31_32_from_fraction(114, 1000); + + struct dcp_color_matrix tbl_entry; + struct fixed31_32 matrix[MATRIX_CONST]; + + struct fixed31_32 grph_cont; + struct fixed31_32 grph_sat; + struct fixed31_32 grph_bright; + struct fixed31_32 sin_grph_hue; + struct fixed31_32 cos_grph_hue; + + initialize_color_float_adj_reference_values( + adjust, &grph_cont, &grph_sat, + &grph_bright, &sin_grph_hue, &cos_grph_hue); + + /* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + + * Sin(GrphHue) * K2)) */ + /* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2) */ + matrix[0] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k1), + dal_fixed31_32_mul(sin_grph_hue, k2)); + /* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */ + matrix[0] = dal_fixed31_32_mul(grph_sat, matrix[0]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)) */ + matrix[0] = dal_fixed31_32_add(luma_r, matrix[0]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * + * K2)) */ + matrix[0] = dal_fixed31_32_mul(grph_cont, matrix[0]); + + /* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + + * Sin(GrphHue) * K4)) */ + /* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k3), + dal_fixed31_32_mul(sin_grph_hue, k4)); + /* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = dal_fixed31_32_mul(grph_sat, matrix[1]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)) */ + matrix[1] = dal_fixed31_32_add(luma_g, matrix[1]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * + * K4)) */ + matrix[1] = dal_fixed31_32_mul(grph_cont, matrix[1]); + + /* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + + * Sin(GrphHue) * K6)) */ + /* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k5), + dal_fixed31_32_mul(sin_grph_hue, k6)); + /* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_mul(grph_sat, matrix[2]); + /* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_add(luma_b, matrix[2]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * + * K6)) */ + matrix[2] = dal_fixed31_32_mul(grph_cont, matrix[2]); + + /* COEF_1_4 = GrphBright */ + matrix[3] = grph_bright; + + /* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + + * Sin(GrphHue) * K8)) */ + /* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k7), + dal_fixed31_32_mul(sin_grph_hue, k8)); + /* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = dal_fixed31_32_mul(grph_sat, matrix[4]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)) */ + matrix[4] = dal_fixed31_32_add(luma_r, matrix[4]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * + * K8)) */ + matrix[4] = dal_fixed31_32_mul(grph_cont, matrix[4]); + + /* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + + * Sin(GrphHue) * K10)) */ + /* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k9), + dal_fixed31_32_mul(sin_grph_hue, k10)); + /* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_sat, matrix[5]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_add(luma_g, matrix[5]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * + * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_cont, matrix[5]); + + /* COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + + * Sin(GrphHue) * K12)) */ + /* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k11), + dal_fixed31_32_mul(sin_grph_hue, k12)); + /* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_sat, matrix[6]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_add(luma_b, matrix[6]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * + * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_cont, matrix[6]); + + /* COEF_2_4 = GrphBright */ + matrix[7] = grph_bright; + + /* COEF_3_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + + * Sin(GrphHue) * K14)) */ + /* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k13), + dal_fixed31_32_mul(sin_grph_hue, k14)); + /* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_sat, matrix[8]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_add(luma_r, matrix[8]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * + * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_cont, matrix[8]); + + /* COEF_3_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + + * Sin(GrphHue) * K16)) */ + /* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */ + matrix[9] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k15), + dal_fixed31_32_mul(sin_grph_hue, k16)); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_sat, matrix[9]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_add(luma_g, matrix[9]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * + * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_cont, matrix[9]); + + /* COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + + * Sin(GrphHue) * K18)) */ + /* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k17), + dal_fixed31_32_mul(sin_grph_hue, k18)); + /* GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_sat, matrix[10]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_add(luma_b, matrix[10]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * + * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_cont, matrix[10]); + + /* COEF_3_4 = GrphBright */ + matrix[11] = grph_bright; + + tbl_entry.color_space = adjust->c_space; + + dal_controller_convert_float_matrix( + tbl_entry.regval, matrix, MATRIX_CONST); + + cg->funcs->program_color_matrix( + cg, &tbl_entry, adjust->color_adjust_option); +} + +#define DISP_BRIGHTNESS_DEFAULT_HW 0 +#define DISP_BRIGHTNESS_MIN_HW -25 +#define DISP_BRIGHTNESS_MAX_HW 25 +#define DISP_BRIGHTNESS_STEP_HW 1 +#define DISP_BRIGHTNESS_HW_DIVIDER 100 + +#define DISP_HUE_DEFAULT_HW 0 +#define DISP_HUE_MIN_HW -30 +#define DISP_HUE_MAX_HW 30 +#define DISP_HUE_STEP_HW 1 +#define DISP_HUE_HW_DIVIDER 1 + +#define DISP_CONTRAST_DEFAULT_HW 100 +#define DISP_CONTRAST_MIN_HW 50 +#define DISP_CONTRAST_MAX_HW 150 +#define DISP_CONTRAST_STEP_HW 1 +#define DISP_CONTRAST_HW_DIVIDER 100 + +#define DISP_SATURATION_DEFAULT_HW 100 +#define DISP_SATURATION_MIN_HW 0 +#define DISP_SATURATION_MAX_HW 200 +#define DISP_SATURATION_STEP_HW 1 +#define DISP_SATURATION_HW_DIVIDER 100 + +#define DISP_KELVIN_DEGRES_DEFAULT 6500 +#define DISP_KELVIN_DEGRES_MIN 4000 +#define DISP_KELVIN_DEGRES_MAX 10000 +#define DISP_KELVIN_DEGRES_STEP 100 +#define DISP_KELVIN_HW_DIVIDER 10000 + +/******************************************************************************* +* dal_csc_grph_get_graphic_color_adjustment_range +* @param [in] grph_adjust_item: one of the graphic color matrix adjustment type +* @param [out] adjust_range: adjustment within HW range. +* @return None +* @note +* HW graphic (display) matrix adjustment range, DS provides API range, HWSS +* converts HW adjustment unit to do adjustment. +* @see HW register spec. COLOR_MATRIX_* +*******************************************************************************/ +void dal_csc_grph_get_graphic_color_adjustment_range( + enum grph_csc_adjust_item grph_adjust_item, + struct hw_adjustment_range *adjust_range) +{ + if (!adjust_range) { + BREAK_TO_DEBUGGER(); + /* NULL point input! */ + return; + } + + dal_memset(adjust_range, 0, sizeof(struct hw_adjustment_range)); + + switch (grph_adjust_item) { + case GRPH_ADJUSTMENT_CONTRAST: + /* default is disable. */ + adjust_range->hw_default = DISP_CONTRAST_DEFAULT_HW; + adjust_range->min = DISP_CONTRAST_MIN_HW; + adjust_range->max = DISP_CONTRAST_MAX_HW; + adjust_range->step = DISP_CONTRAST_STEP_HW; + /* 100,(actually HW range is min/divider; divider !=0) */ + adjust_range->divider = DISP_CONTRAST_HW_DIVIDER; + break; + + case GRPH_ADJUSTMENT_SATURATION: + /* default is disable. */ + adjust_range->hw_default = DISP_SATURATION_DEFAULT_HW; + adjust_range->min = DISP_SATURATION_MIN_HW; + adjust_range->max = DISP_SATURATION_MAX_HW; + adjust_range->step = DISP_SATURATION_STEP_HW; + /* 100,(actually HW range is min/divider; divider !=0) */ + adjust_range->divider = DISP_SATURATION_HW_DIVIDER; + break; + + case GRPH_ADJUSTMENT_HUE: + /* default is disable. */ + adjust_range->hw_default = DISP_HUE_DEFAULT_HW; + adjust_range->min = DISP_HUE_MIN_HW; + adjust_range->max = DISP_HUE_MAX_HW; + adjust_range->step = DISP_HUE_STEP_HW; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = DISP_HUE_HW_DIVIDER; + break; + + case GRPH_ADJUSTMENT_BRIGHTNESS: + /* default is disable. */ + adjust_range->hw_default = DISP_BRIGHTNESS_DEFAULT_HW; + adjust_range->min = DISP_BRIGHTNESS_MIN_HW; + adjust_range->max = DISP_BRIGHTNESS_MAX_HW; + adjust_range->step = DISP_BRIGHTNESS_STEP_HW; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = DISP_BRIGHTNESS_HW_DIVIDER; + break; + + case GRPH_ADJUSTMENT_COLOR_TEMPERATURE: + /* default is disable. */ + adjust_range->hw_default = DISP_KELVIN_DEGRES_DEFAULT; + adjust_range->min = DISP_KELVIN_DEGRES_MIN; + adjust_range->max = DISP_KELVIN_DEGRES_MAX; + adjust_range->step = DISP_KELVIN_DEGRES_STEP; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = DISP_KELVIN_HW_DIVIDER; + break; + + default: + break; + } +} + +/* + * initialize_color_float_adj_reference_values + * This initialize display color adjust input from API to HW range for later + * calculation use. This is shared by all the display color adjustment. + * @param : + * @return None + */ +static void initialize_color_float_adj_reference_values( + const struct grph_csc_adjustment *adjust, + struct fixed31_32 *grph_cont, + struct fixed31_32 *grph_sat, + struct fixed31_32 *grph_bright, + struct fixed31_32 *sin_grph_hue, + struct fixed31_32 *cos_grph_hue) +{ + /* Hue adjustment could be negative. -45 ~ +45 */ + struct fixed31_32 hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); + + *sin_grph_hue = dal_fixed31_32_sin(hue); + *cos_grph_hue = dal_fixed31_32_cos(hue); + + if (adjust->adjust_divider) { + *grph_cont = + dal_fixed31_32_from_fraction( + adjust->grph_cont, + adjust->adjust_divider); + *grph_sat = + dal_fixed31_32_from_fraction( + adjust->grph_sat, + adjust->adjust_divider); + *grph_bright = + dal_fixed31_32_from_fraction( + adjust->grph_bright, + adjust->adjust_divider); + } else { + *grph_cont = dal_fixed31_32_from_int(adjust->grph_cont); + *grph_sat = dal_fixed31_32_from_int(adjust->grph_sat); + *grph_bright = dal_fixed31_32_from_int(adjust->grph_bright); + } +} + +/** + ***************************************************************************** + * Function: setup_adjustments + * @note prepare to setup the values + * + * @see + * + ***************************************************************************** + */ +static void setup_adjustments(const struct grph_csc_adjustment *adjust, + struct csc_adjustments *adjustments) +{ + if (adjust->adjust_divider != 0) { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, + adjust->adjust_divider); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, + adjust->adjust_divider); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, + adjust->adjust_divider); + } else { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, 1); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, 1); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, 1); + } + + /* convert degrees into radians */ + adjustments->hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); +} + +bool dal_csc_grph_construct( + struct csc_grph *cg, + struct csc_grph_init_data *init_data) +{ + if (!init_data) + return false; + + cg->ctx = init_data->ctx; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/csc_grph.h b/drivers/gpu/drm/amd/dal/controller/csc_grph.h new file mode 100644 index 000000000000..9d66c0321518 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc_grph.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_CSC_GRPH_H__ +#define __DAL_CSC_GRPH_H__ + +#include "include/hw_sequencer_types.h" +#include "csc.h" +#include "graphics_and_video_gamma.h" + +struct csc_grph; + +void dal_csc_grph_wide_gamut_set_gamut_remap( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust); +void dal_csc_grph_wide_gamut_set_rgb_limited_range_adjustment( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust); +void dal_csc_grph_wide_gamut_set_yuv_adjustment( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust); +void dal_csc_grph_wide_gamut_set_rgb_adjustment_legacy( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust); +void dal_csc_grph_get_graphic_color_adjustment_range( + enum grph_csc_adjust_item grph_adjust_item, + struct hw_adjustment_range *adjust_range); + +struct csc_grph_init_data { + struct dal_context *ctx; + enum controller_id id; +}; + +bool dal_csc_grph_construct( + struct csc_grph *cg, + struct csc_grph_init_data *init_data); + +#define MATRIX_CONST 12 + +struct dcp_color_matrix { + enum color_space color_space; + uint16_t regval[MATRIX_CONST]; +}; + +struct csc_grph_funcs { + void (*set_overscan_color_black)( + struct csc_grph *csc, + enum color_space color_space); + void (*set_grph_csc_default)( + struct csc_grph *csc, + const struct default_adjustment *adjustment); + void (*set_grph_csc_adjustment)( + struct csc_grph *csc, + const struct grph_csc_adjustment *adjustment); + void (*program_color_matrix)( + struct csc_grph *cg, + const struct dcp_color_matrix *tbl_entry, + enum grph_color_adjust_option options); + void (*program_gamut_remap)( + struct csc_grph *cg, + const uint16_t *reg_val); + bool (*configure_graphics_mode)( + struct csc_grph *cg, + enum wide_gamut_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum color_space color_space); + void (*destroy)(struct csc_grph *csc); +}; + +struct csc_grph { + const struct csc_grph_funcs *funcs; + const uint32_t *regs; + struct dal_context *ctx; +}; + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/csc_video.c b/drivers/gpu/drm/amd/dal/controller/csc_video.c new file mode 100644 index 000000000000..0774fa8598e7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc_video.c @@ -0,0 +1,933 @@ +/* + * 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/fixed31_32.h" + +#include "csc_video.h" + +static void set_gamut_remap( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); +static void program_csc_output( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust, + enum color_space cs); + +/******************************************************************************* + * dal_csc_video_set_ovl_csc_adjustment + * @param [in] adjust: one of the overlay adjustment set + * @return + * @note + * HW overlay adjustment input is HW adjustment unit. HWSS needs convert API + * adjustment to HW adjustment. + * @see --R500 Display Color Spaces.xls from HW + * this actually programs Overlay_Matrix_Coefxx registers. +*******************************************************************************/ +void dal_csc_video_set_ovl_csc_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust, + enum color_space cs) +{ + if (!adjust) { + BREAK_TO_DEBUGGER(); + /* adjust is NULL! */ + return; + } + + set_gamut_remap(cv, adjust); + + cv->funcs->program_ovl_prescale(cv, adjust); + + cv->funcs->program_csc_input(cv, adjust); + + program_csc_output(cv, adjust, cs); + + cv->funcs->configure_overlay_mode(cv, + WIDE_GAMUT_COLOR_MODE_OVERLAY_MATRIX_B, + adjust->adjust_csc_type, cs); +} + +void dal_csc_video_build_input_matrix( + const struct dcp_video_matrix *tbl_entry, + struct fixed31_32 *matrix) +{ + uint32_t i; + + for (i = 0; i < MAXTRIX_COEFFICIENTS_NUMBER; ++i) + matrix[i] = + dal_fixed31_32_from_fraction( + tbl_entry->value[i], + 1000000); + + for (; i < MAXTRIX_COEFFICIENTS_WRAP_NUMBER; ++i) + matrix[i] = dal_fixed31_32_zero; +} + +/** + ***************************************************************************** + * Function: dal_csc_video_apply_oem_matrix + * + * @param [in ] const struct ovl_csc_adjustment *adjust + * @param [in out] struct fixed31_32 *matrix + * + * @return + * void + * + * @note apply oem matrix on top of matrix and keep result in matrix + * + ***************************************************************************** + */ + +void dal_csc_video_apply_oem_matrix( + const struct ovl_csc_adjustment *adjust, + struct fixed31_32 *matrix) +{ + struct fixed31_32 oem_matrix[MAXTRIX_COEFFICIENTS_WRAP_NUMBER]; + struct fixed31_32 result[16]; + uint32_t i; + uint32_t index = 0; + + for (i = 0; i < MAXTRIX_COEFFICIENTS_NUMBER; ++i) { + oem_matrix[i] = + dal_fixed31_32_from_fraction( + adjust->matrix[i], + adjust->matrix_divider); + } + + /* We ignore OEM offsets coefficients because NI has work around for + * offset and it is implemented in prescale BIAS. + * DAL1 also ignores the offsets. We assume that OEM uses matrix 3x3 and + * not 3x4. In case if OEM requires 3x4 then new offsets should be + * recalculated, normalized and programmed to prescale BIAS. */ + oem_matrix[3] = dal_fixed31_32_zero; + oem_matrix[7] = dal_fixed31_32_zero; + oem_matrix[11] = dal_fixed31_32_zero; + + for (; i < MAXTRIX_COEFFICIENTS_WRAP_NUMBER; ++i) + oem_matrix[i] = dal_fixed31_32_zero; + + for (i = 0; i < 4; ++i) { + uint32_t j; + + for (j = 0; j < 4; ++j) { + struct fixed31_32 value = dal_fixed31_32_zero; + uint32_t k; + + for (k = 0; k < 4; ++k) + value = + dal_fixed31_32_add( + value, + dal_fixed31_32_mul( + matrix[(i << 2) + k], + oem_matrix[(k << 2) + j])); + + result[(i << 2) + j] = value; + } + } + + for (i = 0; i < 3; ++i) { + uint32_t j; + + for (j = 0; j < 4; ++j) + matrix[index++] = result[(i << 2) + j]; + } +} + +/** + ***************************************************************************** + * Function: set_gamut_remap + * + * @param [in] const struct ovl_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +static void set_gamut_remap( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust) +{ + if ((adjust->adjust_gamut_type == OVERLAY_GAMUT_ADJUST_TYPE_BYPASS) || + (adjust->temperature_divider == 0)) + cv->funcs->program_gamut_remap(cv, NULL); + else { + struct fixed31_32 arr_matrix[MAX_OVL_MATRIX_COUNT]; + uint16_t arr_reg_val[MAX_OVL_MATRIX_COUNT]; + + arr_matrix[0] = dal_fixed31_32_from_fraction( + adjust->f_temperature[0], + adjust->temperature_divider); + arr_matrix[1] = dal_fixed31_32_from_fraction( + adjust->f_temperature[1], + adjust->temperature_divider); + arr_matrix[2] = dal_fixed31_32_from_fraction( + adjust->f_temperature[2], + adjust->temperature_divider); + arr_matrix[3] = dal_fixed31_32_zero; + arr_matrix[4] = dal_fixed31_32_from_fraction( + adjust->f_temperature[3], + adjust->temperature_divider); + arr_matrix[5] = dal_fixed31_32_from_fraction( + adjust->f_temperature[4], + adjust->temperature_divider); + arr_matrix[6] = dal_fixed31_32_from_fraction( + adjust->f_temperature[5], + adjust->temperature_divider); + arr_matrix[7] = dal_fixed31_32_zero; + arr_matrix[8] = dal_fixed31_32_from_fraction( + adjust->f_temperature[6], + adjust->temperature_divider); + arr_matrix[9] = dal_fixed31_32_from_fraction( + adjust->f_temperature[7], + adjust->temperature_divider); + arr_matrix[10] = dal_fixed31_32_from_fraction( + adjust->f_temperature[8], + adjust->temperature_divider); + arr_matrix[11] = dal_fixed31_32_zero; + + dal_controller_convert_float_matrix( + arr_reg_val, + arr_matrix, + MAX_OVL_MATRIX_COUNT); + + cv->funcs->program_gamut_remap(cv, arr_reg_val); + } +} + +static void set_ovl_csc_rgb_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); +static bool set_ovl_csc_yuv_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); +static void set_ovl_csc_rgb_limited_range_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); + +static void program_csc_output( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust, + enum color_space cs) +{ + switch (cs) { + case COLOR_SPACE_SRGB_FULL_RANGE: + set_ovl_csc_rgb_adjustment(cv, adjust); + break; + + case COLOR_SPACE_SRGB_LIMITED_RANGE: + set_ovl_csc_rgb_limited_range_adjustment(cv, adjust); + break; + + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YPBPR709: + set_ovl_csc_yuv_adjustment(cv, adjust); + break; + + default: + set_ovl_csc_rgb_adjustment(cv, adjust); + } +} + +static void swap_columns(struct fixed31_32 *matrix); +static void setup_adjustments( + const struct ovl_csc_adjustment *adjust, + struct csc_adjustments *adjusts); + +/* + ******************************************************************************* + * Function: set_ovl_csc_rgb_adjustment + * + * @param [in] const struct ovl_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB color space + * + * @see + * + ******************************************************************************* + */ +static void set_ovl_csc_rgb_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust) +{ + const struct fixed31_32 k1 = + dal_fixed31_32_from_fraction(701000, 1000000); + const struct fixed31_32 k2 = + dal_fixed31_32_from_fraction(236568, 1000000); + const struct fixed31_32 k3 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k4 = + dal_fixed31_32_from_fraction(464432, 1000000); + const struct fixed31_32 k5 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k6 = + dal_fixed31_32_from_fraction(-701000, 1000000); + const struct fixed31_32 k7 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k8 = + dal_fixed31_32_from_fraction(-292569, 1000000); + const struct fixed31_32 k9 = + dal_fixed31_32_from_fraction(413000, 1000000); + const struct fixed31_32 k10 = + dal_fixed31_32_from_fraction(-92482, 1000000); + const struct fixed31_32 k11 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k12 = + dal_fixed31_32_from_fraction(385051, 1000000); + const struct fixed31_32 k13 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k14 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k15 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k16 = + dal_fixed31_32_from_fraction(-741914, 1000000); + const struct fixed31_32 k17 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k18 = + dal_fixed31_32_from_fraction(-144086, 1000000); + + const struct fixed31_32 luma_r = + dal_fixed31_32_from_fraction(299, 1000); + const struct fixed31_32 luma_g = + dal_fixed31_32_from_fraction(587, 1000); + const struct fixed31_32 luma_b = + dal_fixed31_32_from_fraction(114, 1000); + + struct fixed31_32 matrix[12]; + struct fixed31_32 sin_hue; + struct fixed31_32 cos_hue; + + struct csc_adjustments adjustments; + + setup_adjustments(adjust, &adjustments); + + sin_hue = dal_fixed31_32_sin(adjustments.hue); + cos_hue = dal_fixed31_32_cos(adjustments.hue); + + /* COEF_1_1 = ovlCont * (LumaR + GrphSat * (Cos(ovlHue) * K1 + + * Sin(ovlHue) * K2)) */ + /* (Cos(GrphHue) * K1 + Sin(ovlHue) * K2) */ + matrix[0] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_hue, k1), + dal_fixed31_32_mul(sin_hue, k2)); + /* ovlSat * (Cos(ovlHue) * K1 + Sin(ovlHue) * K2 */ + matrix[0] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[0]); + /* (LumaR + ovlSat * (Cos(ovlHue) * K1 + Sin(ovlHue) * K2)) */ + matrix[0] = + dal_fixed31_32_add( + luma_r, + matrix[0]); + /* GrphCont * (LumaR + ovlSat * (Cos(ovlHue) * K1 + Sin(ovlHue) * + * K2)) */ + matrix[0] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[0]); + + /* COEF_1_2 = ovlCont * (LumaG + GrphSat * (Cos(ovlHue) * K3 + + * Sin(ovlHue) * K4)) */ + /* (Cos(ovlHue) * K3 + Sin(ovlHue) * K4) */ + matrix[1] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k3), + dal_fixed31_32_mul( + sin_hue, + k4)); + /* ovlSat * (Cos(ovlHue) * K3 + Sin(ovlHue) * K4) */ + matrix[1] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[1]); + /* (LumaG + ovlSat * (Cos(ovlHue) * K3 + Sin(ovlHue) * K4)) */ + matrix[1] = + dal_fixed31_32_add( + luma_g, + matrix[1]); + /* ovlCont * (LumaG + ovlSat * (Cos(ovlHue) * K3 + Sin(ovlHue) * K4)) */ + matrix[1] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[1]); + + /* COEF_1_3 = ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K5 + + * Sin(ovlHue) * K6)) */ + /* (Cos(ovlHue) * K5 + Sin(ovlHue) * K6) */ + matrix[2] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k5), + dal_fixed31_32_mul( + sin_hue, + k6)); + /* ovlSat * (Cos(ovlHue) * K5 + Sin(ovlHue) * K6) */ + matrix[2] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[2]); + /* LumaB + ovlSat * (Cos(ovlHue) * K5 + Sin(ovlHue) * K6) */ + matrix[2] = + dal_fixed31_32_add( + luma_b, + matrix[2]); + /* ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K5 + Sin(ovlHue) * K6)) */ + matrix[2] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[2]); + + /* COEF_1_4 = ovlBright */ + matrix[3] = adjustments.brightness; + + /* COEF_2_1 = ovlCont * (LumaR + ovlSat * (Cos(ovlHue) * K7 + + * Sin(ovlHue) * K8)) */ + /* (Cos(ovlHue) * K7 + Sin(ovlHue) * K8) */ + matrix[4] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k7), + dal_fixed31_32_mul( + sin_hue, + k8)); + /* ovlSat * (Cos(ovlHue) * K7 + Sin(ovlHue) * K8) */ + matrix[4] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[4]); + /* (LumaR + ovlSat * (Cos(ovlHue) * K7 + Sin(ovlHue) * K8)) */ + matrix[4] = + dal_fixed31_32_add( + luma_r, + matrix[4]); + /* ovlCont * (LumaR + ovlSat * (Cos(ovlHue) * K7 + Sin(ovlHue) * K8)) */ + matrix[4] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[4]); + + /* COEF_2_2 = ovlCont * (LumaG + ovlSat * (Cos(ovlHue) * K9 + + * Sin(ovlHue) * K10)) */ + /* (Cos(ovlHue) * K9 + Sin(ovlHue) * K10)) */ + matrix[5] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k9), + dal_fixed31_32_mul( + sin_hue, + k10)); + /* ovlSat * (Cos(ovlHue) * K9 + Sin(ovlHue) * K10)) */ + matrix[5] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[5]); + /* (LumaG + ovlSat * (Cos(ovlHue) * K9 + Sin(ovlHue) * K10)) */ + matrix[5] = + dal_fixed31_32_add( + luma_g, + matrix[5]); + /* ovlCont * (LumaG + ovlSat * (Cos(ovlHue) * K9 + Sin(ovlHue) * K10)) + */ + matrix[5] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[5]); + + /* COEF_2_3 = ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K11 + + * Sin(ovlHue) * K12)) */ + /* (Cos(ovlHue) * K11 + Sin(ovlHue) * K12)) */ + matrix[6] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k11), + dal_fixed31_32_mul( + sin_hue, + k12)); + /* ovlSat * (Cos(ovlHue) * K11 + Sin(ovlHue) * K12)) */ + matrix[6] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[6]); + /* (LumaB + ovlSat * (Cos(ovlHue) * K11 + Sin(ovlHue) * K12)) */ + matrix[6] = + dal_fixed31_32_add( + luma_b, + matrix[6]); + /* ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K11 + Sin(ovlHue) * K12)) + */ + matrix[6] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[6]); + + /* COEF_2_4 = ovlBright */ + matrix[7] = adjustments.brightness; + + /* COEF_3_1 = ovlCont * (LumaR + ovlSat * (Cos(ovlHue) * K13 + + * Sin(ovlHue) * K14)) */ + /* (Cos(ovlHue) * K13 + Sin(ovlHue) * K14)) */ + matrix[8] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k13), + dal_fixed31_32_mul( + sin_hue, + k14)); + /* ovlSat * (Cos(ovlHue) * K13 + Sin(ovlHue) * K14)) */ + matrix[8] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[8]); + /* (LumaR + ovlSat * (Cos(ovlHue) * K13 + Sin(ovlHue) * K14)) */ + matrix[8] = + dal_fixed31_32_add( + luma_r, + matrix[8]); + /* ovlCont * (LumaR + ovlSat * (Cos(ovlHue) * K13 + Sin(ovlHue) * K14)) + */ + matrix[8] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[8]); + + /* COEF_3_2 = ovlCont * (LumaG + ovlSat * (Cos(ovlHue) * K15 + + * Sin(ovlHue) * K16)) */ + /* ovlSat * (Cos(ovlHue) * K15 + Sin(ovlHue) * K16) */ + matrix[9] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k15), + dal_fixed31_32_mul( + sin_hue, + k16)); + /* (LumaG + ovlSat * (Cos(ovlHue) * K15 + Sin(ovlHue) * K16)) */ + matrix[9] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[9]); + /* (LumaG + ovlSat * (Cos(ovlHue) * K15 + Sin(ovlHue) * K16)) */ + matrix[9] = + dal_fixed31_32_add(luma_g, matrix[9]); + /* ovlCont * (LumaG + ovlSat * (Cos(ovlHue) * K15 + Sin(ovlHue) * + * K16)) */ + matrix[9] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[9]); + + /* COEF_3_3 = ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K17 + + * Sin(ovlHue) * K18)) */ + /* (Cos(ovlHue) * K17 + Sin(ovlHue) * K18)) */ + matrix[10] = + dal_fixed31_32_add( + dal_fixed31_32_mul( + cos_hue, + k17), + dal_fixed31_32_mul( + sin_hue, + k18)); + /* ovlSat * (Cos(ovlHue) * K17 + Sin(ovlHue) * K18)) */ + matrix[10] = + dal_fixed31_32_mul( + adjustments.saturation, + matrix[10]); + /* (LumaB + ovlSat * (Cos(ovlHue) * K17 + Sin(ovlHue) * K18)) */ + matrix[10] = + dal_fixed31_32_add( + luma_b, + matrix[10]); + /* ovlCont * (LumaB + ovlSat * (Cos(ovlHue) * K17 + Sin(ovlHue) * + * K18)) */ + matrix[10] = + dal_fixed31_32_mul( + adjustments.contrast, + matrix[10]); + + /* COEF_3_4 = ovlBright */ + matrix[11] = adjustments.brightness; + + swap_columns(matrix); + + { + uint16_t tbl_entry[12]; + + dal_controller_setup_reg_format(matrix, tbl_entry); + + cv->funcs->program_ovl_matrix(cv, tbl_entry); + } +} + +static bool set_ovl_csc_yuv_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust) +{ + struct fixed31_32 ideals[12]; + struct csc_adjustments adjustments; + struct fixed31_32 matrix[12]; + + dal_controller_prepare_yuv_ideal( + adjust->ovl_cs == OVL_COLOR_SPACE_YUV601, ideals); + + setup_adjustments(adjust, &adjustments); + + dal_controller_calculate_adjustments(ideals, &adjustments, matrix); + + { + uint16_t tbl_entry[12]; + + dal_controller_setup_reg_format(matrix, tbl_entry); + + cv->funcs->program_ovl_matrix(cv, tbl_entry); + } + + return true; +} + +static void set_ovl_csc_rgb_limited_range_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust) +{ + struct fixed31_32 ideals[12]; + struct csc_adjustments adjustments; + struct fixed31_32 matrix[12]; + + dal_controller_prepare_tv_rgb_ideal(ideals); + + setup_adjustments(adjust, &adjustments); + + dal_controller_calculate_adjustments(ideals, &adjustments, matrix); + + swap_columns(matrix); + + { + uint16_t tbl_entry[12]; + + dal_controller_setup_reg_format(matrix, tbl_entry); + + cv->funcs->program_ovl_matrix(cv, tbl_entry); + } +} + +/* + ******************************************************************************* + * Function: swap_columns + * + * + * @param [in/out ] struct fixed31_32 *matrix for column swap + * + * @return + * + * @see The column reordering is caused because functions , like, + * PrepareYuvIdeal for easy control show the ideal values into same order as + * they are listed in color excel spread sheet. which provides the algorithm + * for calculation. We listed our values also in same order, but usage requires + * swap except for YCbCr's + * + ******************************************************************************* + */ +static void swap_columns(struct fixed31_32 *matrix) +{ + struct fixed31_32 tmp_matrix[4]; + + /* original 3rd column */ + tmp_matrix[0] = matrix[8]; + tmp_matrix[1] = matrix[9]; + tmp_matrix[2] = matrix[10]; + tmp_matrix[3] = matrix[11]; + + /* from column 1 -> 3 */ + matrix[8] = matrix[0]; + matrix[9] = matrix[1]; + matrix[10] = matrix[2]; + matrix[11] = matrix[3]; + + /* from column 2 -> 1 */ + matrix[0] = matrix[4]; + matrix[1] = matrix[5]; + matrix[2] = matrix[6]; + matrix[3] = matrix[7]; + + /* from original column 3 -> 2 */ + matrix[4] = tmp_matrix[0]; + matrix[5] = tmp_matrix[1]; + matrix[6] = tmp_matrix[2]; + matrix[7] = tmp_matrix[3]; +} + +static void setup_adjustments( + const struct ovl_csc_adjustment *adjust, + struct csc_adjustments *adjusts) +{ + if (adjust->overlay_brightness.adjust_divider != 0) + adjusts->brightness = + dal_fixed31_32_from_fraction( + adjust->overlay_brightness.adjust, + adjust->overlay_brightness.adjust_divider); + else + adjusts->brightness = + dal_fixed31_32_from_fraction( + adjust->overlay_brightness.adjust, + 1); + + if (adjust->overlay_contrast.adjust_divider != 0) + adjusts->contrast = + dal_fixed31_32_from_fraction( + adjust->overlay_contrast.adjust, + adjust->overlay_contrast.adjust_divider); + else + adjusts->contrast = + dal_fixed31_32_from_fraction( + adjust->overlay_contrast.adjust, + 1); + + if (adjust->overlay_saturation.adjust_divider != 0) + adjusts->saturation = + dal_fixed31_32_from_fraction( + adjust->overlay_saturation.adjust, + adjust->overlay_saturation.adjust_divider); + else + adjusts->saturation = + dal_fixed31_32_from_fraction( + adjust->overlay_saturation.adjust, + 1); + + if (adjust->overlay_hue.adjust_divider != 0) + adjusts->hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction( + adjust->overlay_hue.adjust, + adjust->overlay_hue.adjust_divider), + dal_fixed31_32_div( + dal_fixed31_32_pi, + dal_fixed31_32_from_fraction(180, 1))); + else + adjusts->hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction( + adjust->overlay_hue.adjust, + 180), + dal_fixed31_32_pi); +} + +enum { + OVL_ALPHA_MIN = 0, + /* indicate that we will set the hw default which is 255.plus alpha + * blend off. */ + OVL_ALPHA_MAX = 256, + OVL_ALPHA_STEP = 1, + OVL_ALPHA_DEFAULT = OVL_ALPHA_MAX, + OVL_REQ_HW_DEFAULT = 255, /* the real default. */ + OVL_ALPHA_DIVIDER = 1, + + OVERLAY_ALPHAPERPIX_ADJUSTMENT_DISABLE = 0x00000000, + OVERLAY_ALPHAPERPIX_ADJUSTMENT_INV = 0x00000001, + OVERLAY_ALPHAPERPIX_ADJUSTMENT_PREMULT = 0x00000002, + OVERLAY_ALPHAPERPIX_ADJUSTMENT_ON = 0x00000004, + + OVL_ALPHAPERPIX_DEFAULT = 0, + OVL_ALPHAPERPIX_MIN = 0, + OVL_ALPHAPERPIX_STEP = 1, + OVL_ALPHAPERPIX_DIVIDER = 1, + OVL_ALPHAPERPIX_MAX = (OVERLAY_ALPHAPERPIX_ADJUSTMENT_ON + | OVERLAY_ALPHAPERPIX_ADJUSTMENT_PREMULT + | OVERLAY_ALPHAPERPIX_ADJUSTMENT_INV), + /* Saturation: 0 - 2.0, default 1.0 */ + OVL_SATURATION_DEFAULT = 100, /* 1.00 */ + OVL_SATURATION_MIN = 0, + OVL_SATURATION_MAX = 200, /* 2.00 */ + OVL_SATURATION_STEP = 1, /* 0.01 */ + /* actual max overlay saturation value = OVL_SATURATION_MAX / + * OVL_SATURATION_DIVIDER */ + OVL_SATURATION_DIVIDER = 100, + + /* constrast:0 ~1.0 */ + OVL_CONTRAST_DEFAULT = 100, + OVL_CONTRAST_MAX = 200, + OVL_CONTRAST_MIN = 0, + OVL_CONTRAST_STEP = 1, + OVL_CONTRAST_DIVIDER = 100, + + /* Hue */ + OVL_HUE_DEFAULT = 0, + OVL_HUE_MIN = -300, + OVL_HUE_MAX = 300, + OVL_HUE_STEP = 5, + OVL_HUE_DIVIDER = 10, /* HW range: -30 ~ +30 */ + + /* Gamma factor is 1 / 7 */ + OVL_GAMMA_FACTOR = 1, + OVL_GAMMA_FACTOR_DIVIDER = 7, + OVL_KELVIN_TEMPERATURE_DEFAULT = 6500, + OVL_KELVIN_TEMPERATURE_MIN = 4000, + OVL_KELVIN_TEMPERATURE_MAX = 10000, + OVL_KELVIN_TEMPERATURE_STEP = 100, + OVL_KELVIN_TEMPERATURE_DIVIDER = 10000, + /* expose to user min = -1, max = 6, step = 1 */ + OVL_GAMMA_DEFAULT = 1, + OVL_GAMMA_MIN = -1, + OVL_GAMMA_MAX = 6, + OVL_GAMMA_STEP = 1, + OVL_GAMMA_DIVIDER = 1, + /* / Brightness: -.25 ~ .25, default 0.0 */ + OVL_BRIGHTNESS_DEFAULT = 0, + OVL_BRIGHTNESS_MIN = -25, /* 0.25 */ + OVL_BRIGHTNESS_MAX = 25, /* 0.25 */ + OVL_BRIGHTNESS_STEP = 1, /* .01 */ + OVL_BRIGHTNESS_DIVIDER = 100, + /* / Brightness factor 0.025 */ + OVL_BRIGHTNESS_FACTOR = 25, + OVL_BRIGHTNESS_FACTOR_DIVIDER = 1000, + /* Hardware min = 1, max = 2.0, default = 1 */ + OVL_PWL_GAMMA_DEFAULT = 25, + OVL_PWL_GAMMA_MIN = 20, + OVL_PWL_GAMMA_MAX = 28, + OVL_PWL_GAMMA_STEP = 1, + OVL_PWL_GAMMA_DIVIDER = 10, +}; + +void dal_csc_video_get_ovl_adjustment_range( + struct csc_video *cv, + enum ovl_csc_adjust_item overlay_adjust_item, + struct hw_adjustment_range *adjust_range) +{ + if (!adjust_range) { + BREAK_TO_DEBUGGER(); + /* NULL point input! */ + return; + } + + dal_memset(adjust_range, 0, sizeof(struct hw_adjustment_range)); + + switch (overlay_adjust_item) { + case OVERLAY_ALPHA: + /* default is disable. */ + adjust_range->hw_default = OVL_ALPHA_MAX; + adjust_range->min = OVL_ALPHA_MIN; + /* HW 0xFF is actual Max, 0x100 means disable. */ + adjust_range->max = OVL_ALPHA_MAX; + adjust_range->step = OVL_ALPHA_STEP; + /* 1, (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_ALPHA_DIVIDER; + break; + case OVERLAY_ALPHA_PER_PIX: + /* default is disable. */ + adjust_range->hw_default = OVL_ALPHAPERPIX_DEFAULT; + adjust_range->min = OVL_ALPHAPERPIX_MIN; + adjust_range->max = OVL_ALPHAPERPIX_MAX; + adjust_range->step = OVL_ALPHAPERPIX_STEP; + /* 1, (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_ALPHAPERPIX_DIVIDER; + break; + case OVERLAY_CONTRAST: + /* default is disable. */ + adjust_range->hw_default = OVL_CONTRAST_DEFAULT; + adjust_range->min = OVL_CONTRAST_MIN; + adjust_range->max = OVL_CONTRAST_MAX; + adjust_range->step = OVL_CONTRAST_STEP; + /* 100,(actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_CONTRAST_DIVIDER; + break; + case OVERLAY_SATURATION: + /* default is disable. */ + adjust_range->hw_default = OVL_SATURATION_DEFAULT; + adjust_range->min = OVL_SATURATION_MIN; + adjust_range->max = OVL_SATURATION_MAX; + adjust_range->step = OVL_SATURATION_STEP; + /* 100,(actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_SATURATION_DIVIDER; + break; + case OVERLAY_HUE: + /* default is disable. */ + adjust_range->hw_default = OVL_HUE_DEFAULT; + adjust_range->min = OVL_HUE_MIN; + adjust_range->max = OVL_HUE_MAX; + adjust_range->step = OVL_HUE_STEP; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_HUE_DIVIDER; + break; + case OVERLAY_GAMMA: + /* default is disable. */ + adjust_range->hw_default = OVL_GAMMA_DEFAULT; + adjust_range->min = OVL_GAMMA_MIN; + adjust_range->max = OVL_GAMMA_MAX; + adjust_range->step = OVL_GAMMA_STEP; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_GAMMA_DIVIDER; + break; + case OVERLAY_BRIGHTNESS: + /* default is disable. */ + adjust_range->hw_default = OVL_BRIGHTNESS_DEFAULT; + adjust_range->min = OVL_BRIGHTNESS_MIN; + adjust_range->max = OVL_BRIGHTNESS_MAX; + adjust_range->step = OVL_BRIGHTNESS_STEP; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_BRIGHTNESS_DIVIDER; + break; + case OVERLAY_COLOR_TEMPERATURE: + /* default is disable. */ + adjust_range->hw_default = OVL_KELVIN_TEMPERATURE_DEFAULT; + adjust_range->min = OVL_KELVIN_TEMPERATURE_MIN; + adjust_range->max = OVL_KELVIN_TEMPERATURE_MAX; + adjust_range->step = OVL_KELVIN_TEMPERATURE_STEP; + /* (actually HW range is min/divider; divider !=0) */ + adjust_range->divider = OVL_KELVIN_TEMPERATURE_DIVIDER; + break; + default: + break; + } +} + +bool dal_csc_video_construct( + struct csc_video *cv, + struct csc_video_init_data *init_data) +{ + if (!init_data) + return false; + + cv->ctx = init_data->ctx; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/csc_video.h b/drivers/gpu/drm/amd/dal/controller/csc_video.h new file mode 100644 index 000000000000..3afff496ffd8 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/csc_video.h @@ -0,0 +1,102 @@ +/* + * 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_CSC_VIDEO_H__ +#define __DAL_CSC_VIDEO_H__ + +#include "include/grph_csc_types.h" +#include "include/video_csc_types.h" +#include "include/hw_sequencer_types.h" +#include "internal_types_wide_gamut.h" +#include "graphics_and_video_gamma.h" + +#define MAXTRIX_COEFFICIENTS_NUMBER 12 +#define MAXTRIX_COEFFICIENTS_WRAP_NUMBER (MAXTRIX_COEFFICIENTS_NUMBER + 4) + +#define MAX_OVL_MATRIX_COUNT 12 + +struct dcp_video_matrix { + enum ovl_color_space color_space; + int32_t value[MAXTRIX_COEFFICIENTS_NUMBER]; +}; + +struct csc_video; + +struct csc_video_funcs { + void (*program_input_matrix)( + struct csc_video *cv, + const uint16_t regval[]); + void (*program_ovl_prescale)( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); + void (*program_csc_input)( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust); + bool (*configure_overlay_mode)( + struct csc_video *cv, + enum wide_gamut_color_mode config, + enum overlay_csc_adjust_type adjust_csc_type, + enum color_space color_space); + void (*program_gamut_remap)( + struct csc_video *cv, + const uint16_t *matrix); + void (*program_ovl_matrix)( + struct csc_video *cv, + const uint16_t *matrix); + void (*destroy)(struct csc_video **csc_video); +}; + +struct csc_video { + const struct csc_video_funcs *funcs; + const uint32_t *regs; + struct dal_context *ctx; +}; + + +struct csc_video_init_data { + struct dal_context *ctx; + enum controller_id id; +}; + +void dal_csc_video_set_ovl_csc_adjustment( + struct csc_video *cv, + const struct ovl_csc_adjustment *adjust, + enum color_space cs); +void dal_csc_video_build_input_matrix( + const struct dcp_video_matrix *tbl_entry, + struct fixed31_32 *matrix); +void dal_csc_video_apply_oem_matrix( + const struct ovl_csc_adjustment *adjust, + struct fixed31_32 *matrix); +void dal_csc_video_get_ovl_adjustment_range( + struct csc_video *cv, + enum ovl_csc_adjust_item overlay_adjust_item, + struct hw_adjustment_range *adjust_range); + +bool dal_csc_video_construct( + struct csc_video *cv, + struct csc_video_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/cursor.c b/drivers/gpu/drm/amd/dal/controller/cursor.c new file mode 100644 index 000000000000..c208e959e036 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/cursor.c @@ -0,0 +1,100 @@ +/* + * 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 "cursor.h" + +bool dal_cursor_construct( + struct cursor *cur, + struct cursor_init_data *init_data) +{ + if (!init_data) + return false; + + cur->id = init_data->id; + cur->ctx = init_data->dal_ctx; + + cur->is_enabled = false; + + return true; +} + +bool dal_cursor_set_position( + struct cursor *cur, + const struct cursor_position *position) +{ + /* lock cursor registers */ + cur->funcs->lock(cur, true); + + /* Flag passed in structure differentiates cursor enable/disable. */ + /* Update if it differs from cached state. */ + cur->funcs->enable(cur, position->enable); + + cur->funcs->program_position(cur, position->x, position->y); + + if (position->hot_spot_enable) + cur->funcs->program_hotspot( + cur, + position->x_origin, + position->y_origin); + + /* unlock cursor registers */ + cur->funcs->lock(cur, false); + + return true; +} + +bool dal_cursor_set_attributes( + struct cursor *cur, + const struct cursor_attributes *attributes) +{ + /* Lock cursor registers */ + cur->funcs->lock(cur, true); + + /* Program cursor control */ + cur->funcs->program_control( + cur, + attributes->color_format, + attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, + attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); + + /* Program hot spot coordinates */ + cur->funcs->program_hotspot(cur, attributes->x_hot, attributes->y_hot); + + /* + * Program cursor size -- NOTE: HW spec specifies that HW register + * stores size as (height - 1, width - 1) + */ + cur->funcs->program_size(cur, attributes->width, attributes->height); + + /* Program cursor surface address */ + cur->funcs->program_address(cur, attributes->address); + + /* Unlock Cursor registers. */ + cur->funcs->lock(cur, false); + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/cursor.h b/drivers/gpu/drm/amd/dal/controller/cursor.h new file mode 100644 index 000000000000..42e6af5bc3cb --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/cursor.h @@ -0,0 +1,104 @@ +/* + * 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_CURSOR_H__ +#define __DAL_CURSOR_H__ + +#include "include/plane_types.h" +#include "include/grph_object_id.h" + +struct cursor; + +struct cursor_funcs { + void (*enable)( + struct cursor *cur, + bool enable); + + void (*lock)( + struct cursor *cur, + bool lock); + + void (*program_position)( + struct cursor *cur, + uint32_t x, + uint32_t y); + + bool (*program_control)( + struct cursor *cur, + enum cursor_color_format color_format, + bool enable_magnifcation, + bool inverse_transparent_clamping); + + void (*program_hotspot)( + struct cursor *cur, + uint32_t x, + uint32_t y); + + void (*program_size)( + struct cursor *cur, + uint32_t width, + uint32_t height); + + void (*program_address)( + struct cursor *cur, + PHYSICAL_ADDRESS_LOC address); + + void (*destroy)(struct cursor **cur); + +}; + +struct cursor_init_data { + struct dal_context *dal_ctx; + enum controller_id id; +}; + +enum cursor_tri_state { + CURSOR_STATE_TRUE, + CURSOR_STATE_FALSE, + CURSOR_STATE_UNKNOWN +}; + +struct cursor { + const struct cursor_funcs *funcs; + enum controller_id id; + const uint32_t *regs; + struct dal_context *ctx; + bool is_enabled; +}; + +bool dal_cursor_construct( + struct cursor *cur, + struct cursor_init_data *init_data); + +bool dal_cursor_set_position( + struct cursor *cur, + const struct cursor_position *position); + +bool dal_cursor_set_attributes( + struct cursor *cur, + const struct cursor_attributes *attributes); + + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.c new file mode 100644 index 000000000000..5d11efb32685 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.c @@ -0,0 +1,626 @@ +/* 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "../csc.h" +#include "../crtc_overscan_color.h" + +#include "col_man_csc_dce110.h" + +static void destroy(struct csc **csc) +{ + dal_free(*csc); + *csc = NULL; +} + +const struct input_csc_matrix input_csc_matrix_reg[] = { + /* 1_1 1_2 1_3 1_4 2_1 2_2 2_3 2_4 3_1 3_2 3_3 3_4 */ + { COLOR_SPACE_SRGB_FULL_RANGE , { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0 } }, + { COLOR_SPACE_SRGB_LIMITED_RANGE , { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0 } }, + { COLOR_SPACE_YPBPR601 , { 0x2cdd, 0x2000, 0, 0xe991, 0xe926, 0x2000, 0xf4fd, 0x10ef, 0x0, 0x2000, 0x38b4, 0xe3a6 } }, + { COLOR_SPACE_YCBCR601 , { 0x3353, 0x2568, 0, 0xe400, 0xe5dc, 0x2568, 0xf367, 0x1108, 0, 0x2568, 0x40de, 0xdd3a } }, + { COLOR_SPACE_YPBPR709 , { 0x3265, 0x2000, 0, 0xe6ce, 0xf105, 0x2000, 0xfa01, 0xa7d, 0, 0x2000, 0x3b61, 0xe24f } }, + { COLOR_SPACE_YCBCR709 , { 0x39a6, 0x2568, 0, 0xe0d6, 0xeedd, 0x2568, 0xf925, 0x9a8, 0, 0x2568, 0x43ee, 0xdbb2 } }, + + /* TODO: add others */ +}; + +/******************************************************************************* + * Method: set_denormalization + * + * The method converts the output data from internal floating point format + * to fixed point determined by the required output color depth. + * + * @param [in] enum csc_color_depth display_color_depth + * + * @return + * void + * + * @note +DENORM_MODE 2:0 + POSSIBLE VALUES: + 00 - DENORM_CLAMP_MODE_UNITY: unity (default) + 01 - DENORM_CLAMP_MODE_8: 8 bit (255/256) + 02 - DENORM_CLAMP_MODE_10: 10 bit (1023/1024) + 03 - DENORM_CLAMP_MODE_12: 12 bit (4095/4096) + + DENORM_10BIT_OUT 8 + POSSIBLE VALUES: + 00 - clamp and round to 12 bits + 01 - clamp and round to 10 bits + * + ****************************************************************************/ +static void set_denormalization( + struct csc *csc, + enum csc_color_depth display_color_depth) +{ + uint32_t addr = mmDENORM_CLAMP_CONTROL; + uint32_t value = dal_read_reg(csc->ctx, addr); + + switch (display_color_depth) { + case CSC_COLOR_DEPTH_888: + /* 255/256 for 8 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 2, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + default: + /* not valid used case! */ + break; + } + + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_10BIT_OUT); + + dal_write_reg(csc->ctx, addr, value); +} + +bool configure_graphics_mode( + struct csc *csc, + enum wide_gamut_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum color_space color_space) +{ + uint32_t addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + uint32_t value = dal_read_reg(csc->ctx, addr); + + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == WIDE_GAMUT_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + /* already programmed during program_color_matrix() + * the reason to do it there is that we alternate + * between 2 sets of coefficients and OUTPUT_CSC_MODE + * needs to be in sync with what was programmed there. + * Existing framework doesn't allow that info to be + * passed (both this function and the + * program_color_matrix are called from a common base + * method) so we handle it in one place. + */ + return true; + } + + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* bypass */ + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* by pass */ + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + } + + dal_write_reg(csc->ctx, addr, value); + + return true; +} + +static void set_grph_csc_default( + struct csc *csc, + const struct default_adjustment *adjust) +{ + enum wide_gamut_color_mode config = + WIDE_GAMUT_COLOR_MODE_GRAPHICS_PREDEFINED; + + /* currently parameter not in use as we always get force_hw_default == + * false */ + + /* TODO: add implementation + if (!adjust->force_hw_default) { + + } + */ + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardrware default from ROM_A and we do not need to program + * matrix + */ + + configure_graphics_mode( + csc, + config, + adjust->csc_adjust_type, + adjust->color_space); + + set_denormalization(csc, adjust->color_depth); +} + +static void set_overscan_color_black( + struct csc *csc, + enum color_space black_color) +{ + uint32_t value = 0; + + /* Overscan Color for YUV display modes: + * to achieve a black color for both the explicit and implicit overscan, + * the overscan color registers should be programmed to: */ + + switch (black_color) { + case COLOR_SPACE_YPBPR601: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_YONLY: + case COLOR_SPACE_YCBCR709_YONLY: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + + default: + /* default is sRGB black 0. */ + break; + } + dal_write_reg(csc->ctx, mmCRTCV_OVERSCAN_COLOR, value); + dal_write_reg(csc->ctx, mmCRTCV_BLACK_COLOR, value); + + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + dal_write_reg(csc->ctx, mmCRTCV_BLANK_DATA_COLOR, value); +} + +static void program_input_csc( + struct csc *csc, + const unsigned short *input_csc_matrix) +{ + uint32_t value = 0; + uint32_t field = 0; + bool use_mode_A; + + value = dal_read_reg(csc->ctx, mmCOL_MAN_INPUT_CSC_CONTROL); + + field = get_reg_field_value( + value, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + + /* If we are not using MODE A, then use MODE A; otherwise use MODE B */ + if (field != 1) + use_mode_A = true; + else + use_mode_A = false; + + if (use_mode_A) { + value = 0; + set_reg_field_value( + value, + input_csc_matrix[0], + INPUT_CSC_C11_C12_A, + INPUT_CSC_C11_A); + set_reg_field_value( + value, + input_csc_matrix[1], + INPUT_CSC_C11_C12_A, + INPUT_CSC_C12_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C11_C12_A, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[2], + INPUT_CSC_C13_C14_A, + INPUT_CSC_C13_A); + set_reg_field_value( + value, + input_csc_matrix[3], + INPUT_CSC_C13_C14_A, + INPUT_CSC_C14_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C13_C14_A, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[4], + INPUT_CSC_C21_C22_A, + INPUT_CSC_C21_A); + set_reg_field_value( + value, + input_csc_matrix[5], + INPUT_CSC_C21_C22_A, + INPUT_CSC_C22_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C21_C22_A, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[6], + INPUT_CSC_C23_C24_A, + INPUT_CSC_C23_A); + set_reg_field_value( + value, + input_csc_matrix[7], + INPUT_CSC_C23_C24_A, + INPUT_CSC_C24_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C23_C24_A, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[8], + INPUT_CSC_C31_C32_A, + INPUT_CSC_C31_A); + set_reg_field_value( + value, + input_csc_matrix[9], + INPUT_CSC_C31_C32_A, + INPUT_CSC_C32_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C31_C32_A, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[10], + INPUT_CSC_C33_C34_A, + INPUT_CSC_C33_A); + set_reg_field_value( + value, + input_csc_matrix[11], + INPUT_CSC_C33_C34_A, + INPUT_CSC_C34_A); + dal_write_reg(csc->ctx, mmINPUT_CSC_C33_C34_A, value); + } else { + value = 0; + set_reg_field_value( + value, + input_csc_matrix[0], + INPUT_CSC_C11_C12_B, + INPUT_CSC_C11_B); + set_reg_field_value( + value, + input_csc_matrix[1], + INPUT_CSC_C11_C12_B, + INPUT_CSC_C12_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C11_C12_B, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[2], + INPUT_CSC_C13_C14_B, + INPUT_CSC_C13_B); + set_reg_field_value( + value, + input_csc_matrix[3], + INPUT_CSC_C13_C14_B, + INPUT_CSC_C14_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C13_C14_B, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[4], + INPUT_CSC_C21_C22_B, + INPUT_CSC_C21_B); + set_reg_field_value( + value, + input_csc_matrix[5], + INPUT_CSC_C21_C22_B, + INPUT_CSC_C22_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C21_C22_B, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[6], + INPUT_CSC_C23_C24_B, + INPUT_CSC_C23_B); + set_reg_field_value( + value, + input_csc_matrix[7], + INPUT_CSC_C23_C24_B, + INPUT_CSC_C24_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C23_C24_B, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[8], + INPUT_CSC_C31_C32_B, + INPUT_CSC_C31_B); + set_reg_field_value( + value, + input_csc_matrix[9], + INPUT_CSC_C31_C32_B, + INPUT_CSC_C32_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C31_C32_B, value); + + value = 0; + set_reg_field_value( + value, + input_csc_matrix[10], + INPUT_CSC_C33_C34_B, + INPUT_CSC_C33_B); + set_reg_field_value( + value, + input_csc_matrix[11], + INPUT_CSC_C33_C34_B, + INPUT_CSC_C34_B); + dal_write_reg(csc->ctx, mmINPUT_CSC_C33_C34_B, value); + } + + value = 0; + set_reg_field_value( + value, + 0x0, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_CONVERSION_MODE); + set_reg_field_value( + value, + 0x2, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_INPUT_TYPE); + + if (use_mode_A) + set_reg_field_value( + value, + 0x1, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + else + set_reg_field_value( + value, + 0x2, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + + dal_write_reg(csc->ctx, mmCOL_MAN_INPUT_CSC_CONTROL, value); +} + +bool set_input_csc( + struct csc *csc, + const enum color_space color_space) +{ + + /* TODO: Check for other color spaces and limited ranges too */ + + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + program_input_csc(csc, input_csc_matrix_reg[0].regval); + return true; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + program_input_csc(csc, input_csc_matrix_reg[1].regval); + return true; + case COLOR_SPACE_YPBPR601: + program_input_csc(csc, input_csc_matrix_reg[2].regval); + return true; + case COLOR_SPACE_YCBCR601: + program_input_csc(csc, input_csc_matrix_reg[3].regval); + return true; + case COLOR_SPACE_YPBPR709: + program_input_csc(csc, input_csc_matrix_reg[4].regval); + return true; + case COLOR_SPACE_YCBCR709: + program_input_csc(csc, input_csc_matrix_reg[5].regval); + return true; + default: + /* not valid color space! */ + break; + } + + return false; +} + +void set_grph_csc_adjustment( + struct csc *csc, + const struct grph_csc_adjustment *adjust) +{ + enum wide_gamut_color_mode config = + WIDE_GAMUT_COLOR_MODE_GRAPHICS_PREDEFINED; + + /* TODO: add implementation */ + + configure_graphics_mode( + csc, + config, + adjust->csc_adjust_type, + adjust->c_space); + + set_denormalization(csc, adjust->color_depth); +} + +static const struct csc_funcs col_man_csc_dce110_funcs = { + .set_grph_csc_default = set_grph_csc_default, + .set_grph_csc_adjustment = set_grph_csc_adjustment, + .set_overscan_color_black = set_overscan_color_black, + .set_ovl_csc_adjustment = NULL, + .is_supported_custom_gamut_adjustment = + NULL, + .is_supported_overlay_alpha_adjustment = + NULL, + .set_input_csc = set_input_csc, + .destroy = destroy, +}; + +static bool col_man_csc_dce110_construct( + struct csc *csc, + const struct csc_init_data *init_data) +{ + if (!dal_csc_construct(csc, init_data)) + return false; + + csc->funcs = &col_man_csc_dce110_funcs; + return true; +} + +struct csc *dal_col_man_csc_dce110_create( + const struct csc_init_data *init_data) +{ + struct csc *csc = dal_alloc(sizeof(*csc)); + + if (!csc) + return NULL; + + if (col_man_csc_dce110_construct(csc, init_data)) + return csc; + + dal_free(csc); + ASSERT_CRITICAL(false); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.h new file mode 100644 index 000000000000..9d4672dee8f3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_csc_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_COL_MAN_CSC_DCE110_H__ +#define __DAL_COL_MAN_CSC_DCE110_H__ + +#include "../csc.h" + +struct csc *dal_col_man_csc_dce110_create( + const struct csc_init_data *metricies); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.c new file mode 100644 index 000000000000..ddc7dbac535a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.c @@ -0,0 +1,76 @@ +/* + * 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 "col_man_gamma_dce110.h" + +static void program_lut_gamma( + struct grph_gamma *gg, + const struct dev_c_lut16 *gamma, + const struct gamma_parameters *params) +{ + /* TODO: add implementation */ +} + +static void destroy(struct grph_gamma **gg) +{ + dal_grph_gamma_destruct(*gg); + + dal_free(*gg); + *gg = NULL; +} + +static const struct grph_gamma_funcs funcs = { + .program_lut_gamma = program_lut_gamma, + .destroy = destroy +}; + +static bool construct( + struct grph_gamma *gg, + struct grph_gamma_init_data *data) +{ + if (!dal_grph_gamma_construct(gg, data)) + return false; + + gg->funcs = &funcs; + + return true; +} + +struct grph_gamma *dal_col_man_grph_dce110_create( + struct grph_gamma_init_data *data) +{ + struct grph_gamma *gg = dal_alloc(sizeof(*gg)); + + if (!gg) + return NULL; + + if (construct(gg, data)) + return gg; + + dal_free(gg); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.h new file mode 100644 index 000000000000..055a27656824 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/col_man_gamma_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_COL_MAN_GAMMA_DCE110_H__ +#define __DAL_COL_MAN_GAMMA_DCE110_H__ + +#include "../grph_gamma.h" + +struct grph_gamma *dal_col_man_grph_dce110_create( + struct grph_gamma_init_data *data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.c new file mode 100644 index 000000000000..b6b50e210074 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.c @@ -0,0 +1,260 @@ +/* + * 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/logger_interface.h" +#include "include/adapter_service_interface.h" + +#include "controller_dce110.h" +#include "csc_dce110.h" +#include "timing_generator_dce110.h" +#include "formatter_dce110.h" +#include "vga_dce110.h" +#include "grph_gamma_dce110.h" +#include "scaler_dce110.h" +#include "pipe_control_dce110.h" +#include "line_buffer_dce110.h" +#include "surface_dce110.h" +#include "cursor_dce110.h" + + +/***************************************************************************** + * functions + *****************************************************************************/ +static bool is_surface_supported( + struct controller *crtc, + const struct plane_config *pl_cfg) +{ + bool rc; + + /* TODO: check programming guide or DAL2 for types of supported + * surfaces. */ + /* Primary Pipe doesn't support 4:2:0 video surfaces (maybe more + * see the TODO). */ + switch (pl_cfg->config.format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + rc = false; + break; + default: + rc = true; + break; + } + + return rc; +} + +void dal_controller_dce110_destruct(struct controller_dce110 *controller110) +{ + dal_controller_base_destruct(&controller110->base); +} + +static void destroy(struct controller **controller) +{ + struct controller_dce110 *controller110 = + CONTROLLER_DCE110_FROM_BASE(*controller); + + dal_controller_dce110_destruct(controller110); + + dal_free(controller110); + + *controller = NULL; +} + +bool dal_controller_dce110_construct( + struct controller_dce110 *controller110, + struct controller_init_data *init_data) +{ + struct controller *base = &controller110->base; + + if (false == dal_controller_base_construct(base, init_data)) + return false; + + switch (base->id) { + case CONTROLLER_ID_D0: + case CONTROLLER_ID_D1: + case CONTROLLER_ID_D2: + break; + default: + dal_controller_base_destruct(base); + return false; + } + + base->funcs.destroy = destroy; + base->funcs.is_surface_supported = is_surface_supported; + + { + struct grph_gamma_init_data gg_init_data = {0}; + + gg_init_data.as = init_data->as; + gg_init_data.ctx = base->dal_context; + gg_init_data.id = base->id; + base->grph_gamma = dal_grph_gamma_dce110_create(&gg_init_data); + } + + if (!base->grph_gamma) + goto grph_gamma_fail; + + { + struct csc_init_data csc_init_data = {0}; + + csc_init_data.id = base->id; + csc_init_data.ctx = base->dal_context; + csc_init_data.as = init_data->as; + + base->csc = + dal_csc_dce110_create(&csc_init_data); + } + + if (!base->csc) + goto csc_fail; + + base->vga = dal_vga_dce110_create(init_data->as, + base->dal_context, + base->id); + + if (!base->vga) + goto vga_fail; + + base->pc = + dal_pipe_control_dce110_create(init_data->as, + base->dal_context, + base->id); + + if (!base->pc) + goto pipe_control_fail; + + base->tg = dal_timing_generator_dce110_create(init_data->as, + base->dal_context, + base->id); + if (!base->tg) + goto tg_fail; + + { + struct formatter_init_data fmt_init_data = {0}; + + fmt_init_data.ctx = base->dal_context; + fmt_init_data.id = base->id; + base->fmt = dal_formatter_dce110_create(&fmt_init_data); + } + + if (base->fmt == NULL) + goto fmt_fail; + + { + struct scaler_init_data scl_init_data = {0}; + + scl_init_data.bp = + dal_adapter_service_get_bios_parser(init_data->as); + scl_init_data.dal_ctx = base->dal_context; + scl_init_data.id = base->id; + base->scl = dal_scaler_dce110_create(&scl_init_data); + } + + if (base->scl == NULL) + goto scl_fail; + + { + + struct line_buffer_init_data lb_init_data = {0}; + + lb_init_data.dal_context = base->dal_context; + lb_init_data.as = init_data->as; + lb_init_data.id = init_data->controller; + base->lb = dal_line_buffer_dce110_create(&lb_init_data); + } + + if (!base->lb) + goto line_buffer_fail; + + { + struct surface_init_data surf_init_data = {0}; + + surf_init_data.dal_ctx = base->dal_context; + surf_init_data.id = base->id; + base->surface = dal_surface_dce110_create(&surf_init_data); + + } + + if (base->surface == NULL) + goto surface_fail; + + { + struct cursor_init_data cur_init_data = {0}; + + cur_init_data.dal_ctx = base->dal_context; + cur_init_data.id = base->id; + base->cursor = dal_cursor_dce110_create(&cur_init_data); + + } + + if (base->cursor == NULL) + goto cursor_fail; + + + + return true; + +cursor_fail: + base->surface->funcs->destroy(&base->surface); +surface_fail: + base->lb->funcs->destroy(&base->lb); +line_buffer_fail: + base->scl->funcs->destroy(&base->scl); +scl_fail: + base->fmt->funcs->destroy(&base->fmt); +fmt_fail: + base->tg->funcs->destroy(&base->tg); +tg_fail: + base->pc->funcs->destroy(&base->pc); +pipe_control_fail: + base->vga->funcs->destroy(&base->vga); +vga_fail: + base->csc->funcs->destroy(&base->csc); +csc_fail: + base->grph_gamma->funcs->destroy(&base->grph_gamma); + +grph_gamma_fail: + return false; +} + +struct controller *dal_controller_dce110_create( + struct controller_init_data *init_data) +{ + struct controller_dce110 *controller110; + + controller110 = dal_alloc(sizeof(struct controller_dce110)); + + if (!controller110) + return NULL; + + if (dal_controller_dce110_construct(controller110, init_data)) + return &controller110->base; + + dal_free(controller110); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.h new file mode 100644 index 000000000000..c52269982c5d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/controller_dce110.h @@ -0,0 +1,50 @@ +/* + * 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_CONTROLLER_DCE110_H__ +#define __DAL_CONTROLLER_DCE110_H__ + +#include "include/controller_interface.h" + +#include "../controller.h" + +struct controller_dce110 { + struct controller base; +}; + +#define CONTROLLER_DCE110_FROM_BASE(controller_base) \ + container_of(controller_base, struct controller_dce110, base) + +struct controller *dal_controller_dce110_create( + struct controller_init_data *init_data); + +bool dal_controller_dce110_construct( + struct controller_dce110 *controller_dce110, + struct controller_init_data *init_data); + +void dal_controller_dce110_destruct( + struct controller_dce110 *controller_dce110); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.c new file mode 100644 index 000000000000..b67f2669c935 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.c @@ -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 + * + */ + +#include "dal_services.h" + +#include "include/grph_object_id.h" +#include "include/controller_interface.h" +#include "include/adapter_service_interface.h" + +#include "controller_v_dce110.h" +#include "pipe_control_v_dce110.h" +#include "timing_generator_v_dce110.h" +#include "scaler_v_dce110.h" +#include "line_buffer_v_dce110.h" +#include "col_man_csc_dce110.h" +#include "col_man_gamma_dce110.h" +#include "surface_v_dce110.h" +#include "include/logger_interface.h" + +#include "../controller.h" + +static bool is_surface_supported( + struct controller *crtc, + const struct plane_config *pl_cfg) +{ + bool rc; + + /* TODO: check programming guide or DAL2 for types of supported + * surfaces. */ + + /* Underlay Pipe supports all pixel formats. */ + + /* Underlay has upper limit of 1080. */ + if (pl_cfg->config.format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + /* this is a video surface */ + if (pl_cfg->attributes.src_rect.height > 1080) { + dal_logger_write(crtc->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: surface height is more than 1080\n", + __func__); + rc = false; + } else { + rc = true; + } + + } else { + /* This is a graphics surface, no special limitations + * (only regular Bandwidth limitiations apply) */ + rc = true; + } + + return rc; +} + +static void destroy(struct controller **crtc) +{ + dal_controller_base_destruct(*crtc); + + dal_free(*crtc); + + *crtc = NULL; +} + +static bool construct( + struct controller *crtc, + struct controller_init_data *init_data) +{ + struct scaler_init_data scl_init_data = {0}; + struct surface_init_data surf_init_data = {0}; + + if (!dal_controller_base_construct(crtc, init_data)) + return false; + + scl_init_data.bp = dal_adapter_service_get_bios_parser( + init_data->as); + scl_init_data.dal_ctx = init_data->dal_context; + scl_init_data.id = CONTROLLER_ID_UNDERLAY0; + + crtc->scl = dal_scaler_v_dce110_create(&scl_init_data); + + if (!crtc->scl) + goto scl_fail; + + + surf_init_data.dal_ctx = init_data->dal_context; + surf_init_data.id = CONTROLLER_ID_UNDERLAY0; + crtc->surface = dal_surface_v_dce110_create(&surf_init_data); + + if (!crtc->surface) + goto surface_fail; + + crtc->pc = dal_pipe_control_v_dce110_create( + init_data->as, + init_data->dal_context, + init_data->controller); + + if (!crtc->pc) + goto pc_fail; + + crtc->tg = dal_timing_generator_v_dce110_create( + init_data->as, + init_data->dal_context, + init_data->controller); + + if (!crtc->tg) + goto tg_fail; + + { + struct line_buffer_init_data lb_init_data = {0}; + + lb_init_data.dal_context = crtc->dal_context; + lb_init_data.as = init_data->as; + lb_init_data.id = init_data->controller; + crtc->lb = + dal_line_buffer_v_dce110_create(&lb_init_data); + } + + if (!crtc->lb) + goto lb_fail; + + { + struct csc_init_data csc_init_data = {0}; + + csc_init_data.id = init_data->controller; + csc_init_data.ctx = crtc->dal_context; + csc_init_data.as = init_data->as; + crtc->csc = + dal_col_man_csc_dce110_create(&csc_init_data); + } + + if (!crtc->csc) + goto csc_fail; + + { + struct grph_gamma_init_data gg_init_data = {0}; + + gg_init_data.as = init_data->as; + gg_init_data.ctx = crtc->dal_context; + gg_init_data.id = init_data->controller; + crtc->grph_gamma = + dal_col_man_grph_dce110_create(&gg_init_data); + } + + if (!crtc->grph_gamma) + goto gamma_fail; + + /* all OK */ + crtc->funcs.destroy = destroy; + crtc->funcs.is_surface_supported = is_surface_supported; + return true; + +gamma_fail: + crtc->csc->funcs->destroy(&crtc->csc); +csc_fail: + crtc->lb->funcs->destroy(&crtc->lb); +lb_fail: + crtc->tg->funcs->destroy(&crtc->tg); +tg_fail: + crtc->pc->funcs->destroy(&crtc->pc); +pc_fail: + crtc->surface->funcs->destroy(&crtc->surface); +surface_fail: + crtc->scl->funcs->destroy(&crtc->scl); +scl_fail: + return false; +} + +struct controller *dal_controller_v_dce110_create( + struct controller_init_data *init_data) +{ + struct controller *crtc; + + crtc = dal_alloc(sizeof(*crtc)); + + if (!crtc) + return NULL; + + if (construct(crtc, init_data)) + return crtc; + + dal_free(crtc); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.h new file mode 100644 index 000000000000..fdecd1a52747 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/controller_v_dce110.h @@ -0,0 +1,32 @@ +/* + * 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_CONTROLLER_V_DCE110_H__ +#define __DAL_CONTROLLER_V_DCE110_H__ + +struct controller *dal_controller_v_dce110_create( + struct controller_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.c new file mode 100644 index 000000000000..0dd15e31d377 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.c @@ -0,0 +1,322 @@ +/* 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/fixed31_32.h" + +#include "../csc.h" + +#include "csc_dce110.h" +#include "csc_grph_dce110.h" + +static bool csc_dce110_construct( + struct csc_dce110 *csc, + const struct csc_init_data *init_data); + +struct csc *dal_csc_dce110_create( + const struct csc_init_data *init_data) +{ + struct csc_dce110 *csc = dal_alloc(sizeof(struct csc_dce110)); + + if (!csc) + return NULL; + + if (csc_dce110_construct(csc, init_data)) + return &csc->base; + + dal_free(csc); + ASSERT_CRITICAL(false); + return NULL; +} + +#define FROM_CSC(csc)\ + container_of(csc, struct csc_dce110, base) + +static void csc_dce110_destruct(struct csc_dce110 *csc); + +static void destroy(struct csc **csc) +{ + csc_dce110_destruct(FROM_CSC(*csc)); + dal_free(FROM_CSC(*csc)); + *csc = NULL; +} + +static void csc_dce110_destruct(struct csc_dce110 *csc) +{ + csc->base.csc_grph->funcs->destroy(csc->base.csc_grph); + dal_dcp_bit_depth_reduction_dce110_destroy( + &csc->dcp_bit_depth_reduction); +} + +static void set_denormalization( + struct csc *csc, + enum csc_color_depth display_color_depth, + uint32_t lb_color_depth); + +static void set_grph_csc_default( + struct csc *csc, + const struct default_adjustment *adjust) +{ + csc->csc_grph->funcs->set_grph_csc_default(csc->csc_grph, adjust); + + set_denormalization(csc, adjust->color_depth, + adjust->lb_color_depth); + + /* program dcp bit depth reduction */ + dal_dcp_bit_depth_reduction_dce110_program( + FROM_CSC(csc)->dcp_bit_depth_reduction, + adjust->color_depth); +} + +static void set_grph_csc_adjustment( + struct csc *csc, + const struct grph_csc_adjustment *adjust) +{ + /* color adjustment */ + csc->csc_grph->funcs->set_grph_csc_adjustment(csc->csc_grph, adjust); + + set_denormalization(csc, adjust->color_depth, adjust->lb_color_depth); + + /* program dcp bit depth reduction */ + dal_dcp_bit_depth_reduction_dce110_program( + FROM_CSC(csc)->dcp_bit_depth_reduction, + adjust->color_depth); +} + +static void set_overscan_color_black( + struct csc *csc, + enum color_space black_color) +{ + csc->csc_grph->funcs-> + set_overscan_color_black(csc->csc_grph, black_color); +} + +static void set_ovl_csc_adjustment( + struct csc *csc, + const struct ovl_csc_adjustment *adjust, + enum color_space color_space) +{ +} + +/******************************************************************************* + * Method: set_denormalization + * + * The method converts the output data from internal floating point format + * to fixed point determined by the required output color depth. + * + * @param [in] enum csc_color_depth display_color_depth + * + * @return + * void + * + * @note +DENORM_MODE 2:0 0x3 De-normalization mode + POSSIBLE VALUES: + 00 - unity + 01 - 63/64 for 6bit + 02 - 255/256 for 8bit + 03 - 1023/1024 for 10bit + 04 - 2047/2048 for 11bit + 05 - 4095/4096 for 12 bit + 06 - reserved + 07 - reserved +DENORM_14BIT_OUT 4 0x0 POSSIBLE VALUES: + 00 - De-norm output to 12-bit + 01 - De-norm output to 14-bit * + @see + 1. If the call goes from set mode context then lbColorDepth has a + meaningful value 6, 8, 10 or 12. + We are allowed to change lb format in set mode context + 2. If the call goes from set adjustment context the lbColorDepth is 0 + because we do not change lb format on the fly + * + ****************************************************************************/ +static void set_denormalization( + struct csc *csc, + enum csc_color_depth display_color_depth, + uint32_t lb_color_depth) +{ + uint32_t value = dal_read_reg(csc->ctx, + FROM_CSC(csc)->dcp_denorm_control_offset); + + switch (display_color_depth) { + case CSC_COLOR_DEPTH_666: + /* 63/64 for 6 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_888: + /* Unity for 8 bit output color depth + * because prescale is disabled by default */ + set_reg_field_value( + value, + 0, + DENORM_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_111111: + /* 1023/1024 for 11 bit output color depth */ + set_reg_field_value( + value, + 4, + DENORM_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 5, + DENORM_CONTROL, + DENORM_MODE); + break; + case CSC_COLOR_DEPTH_141414: + case CSC_COLOR_DEPTH_161616: + default: + /* not valid used case! */ + break; + } + + if (display_color_depth == CSC_COLOR_DEPTH_121212 && + lb_color_depth == 10) { + set_reg_field_value( + value, + 1, + DENORM_CONTROL, + DENORM_14BIT_OUT); + } else { + set_reg_field_value( + value, + 0, + DENORM_CONTROL, + DENORM_14BIT_OUT); + } + + dal_write_reg(csc->ctx, + FROM_CSC(csc)->dcp_denorm_control_offset, value); + +} + +/******************************************************************************* + * is_supported_custom_gamut_adjustment + * + * @return supported always for NI family + * + ******************************************************************************/ + +static bool is_supported_custom_gamut_adjustment(struct csc *csc) +{ + return true; +} +/******************************************************************************* + * is_supported_overlay_alfa_adjustment + * + * Check if hardware supports ovl alfa blending + * + * @return true always for Evergreen , but for NI false; + * + ******************************************************************************/ +static bool is_supported_overlay_alpha_adjustment(struct csc *csc) +{ + return false; +} + +static const struct csc_funcs csc_dce110_funcs = { + .set_grph_csc_default = set_grph_csc_default, + .set_grph_csc_adjustment = set_grph_csc_adjustment, + .set_overscan_color_black = set_overscan_color_black, + .set_ovl_csc_adjustment = set_ovl_csc_adjustment, + .is_supported_custom_gamut_adjustment = + is_supported_custom_gamut_adjustment, + .is_supported_overlay_alpha_adjustment = + is_supported_overlay_alpha_adjustment, + .set_input_csc = dal_csc_set_input_csc, + .destroy = destroy, +}; + +static bool csc_dce110_construct( + struct csc_dce110 *csc, + const struct csc_init_data *init_data) +{ + if (!dal_csc_construct(&csc->base, init_data)) + return false; + + switch (init_data->id) { + case CONTROLLER_ID_D0: + csc->dcp_denorm_control_offset = mmDCP0_DENORM_CONTROL; + break; + case CONTROLLER_ID_D1: + csc->dcp_denorm_control_offset = mmDCP1_DENORM_CONTROL; + break; + case CONTROLLER_ID_D2: + csc->dcp_denorm_control_offset = mmDCP2_DENORM_CONTROL; + break; + default: + ASSERT_CRITICAL(false); /* invalid matrix id ! */ + return false; + } + + csc->dcp_bit_depth_reduction = + dal_dcp_bit_depth_reduction_dce110_create( + init_data->id, csc->base.ctx, init_data->as); + + if (!csc->dcp_bit_depth_reduction) + return false; + + { + struct csc_grph_init_data cg_init_data; + + cg_init_data.ctx = csc->base.ctx; + cg_init_data.id = init_data->id; + csc->base.csc_grph = + dal_csc_grph_dce110_create(&cg_init_data); + } + + if (!csc->base.csc_grph) + goto fail_csc_grph; + + csc->base.funcs = &csc_dce110_funcs; + return true; + +fail_csc_grph: + dal_dcp_bit_depth_reduction_dce110_destroy( + &csc->dcp_bit_depth_reduction); + return false; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.h new file mode 100644 index 000000000000..8204d20ac450 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/csc_dce110.h @@ -0,0 +1,41 @@ +/* + * 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_CSC_DCE110_H__ +#define __DAL_CSC_DCE110_H__ + +#include "../csc.h" +#include "dcp_bit_depth_reduction_dce110.h" + +struct csc_dce110 { + struct csc base; + uint32_t dcp_denorm_control_offset; + struct dcp_bit_depth_reduction_dce110 *dcp_bit_depth_reduction; +}; + +struct csc *dal_csc_dce110_create( + const struct csc_init_data *metricies); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.c new file mode 100644 index 000000000000..f82890305f25 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.c @@ -0,0 +1,778 @@ +/* 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_id.h" +#include "include/fixed31_32.h" + +#include "../csc.h" +#include "../crtc_overscan_color.h" + +#include "csc_grph_dce110.h" + +enum csc_grph_regs_idx { + IDX_CRTC_OVERSCAN_COLOR, + IDX_CRTC_BLACK_COLOR, + IDX_CRTC_BLANK_DATA_COLOR, + IDX_PRESCALE_VALUES_GRPH_G, + IDX_PRESCALE_VALUES_GRPH_B, + IDX_PRESCALE_VALUES_GRPH_R, + IDX_PRESCALE_GRPH_CONTROL, + IDX_DENORM_CONTROL, + IDX_GAMUT_REMAP_11_12, + IDX_GAMUT_REMAP_13_14, + IDX_GAMUT_REMAP_21_22, + IDX_GAMUT_REMAP_23_24, + IDX_GAMUT_REMAP_31_32, + IDX_GAMUT_REMAP_33_34, + IDX_GAMUT_REMAP_CONTROL, + IDX_OUTPUT_CSC_C11_12, + IDX_OUTPUT_CSC_C13_14, + IDX_OUTPUT_CSC_C21_22, + IDX_OUTPUT_CSC_C23_24, + IDX_OUTPUT_CSC_C31_32, + IDX_OUTPUT_CSC_C33_34, + IDX_OUTPUT_CSC_CONTROL, + CG_REGS_IDX_SIZE +}; + +#define regs_for_csc_grph(id)\ +[CONTROLLER_ID_D ## id - 1] = {\ + [IDX_CRTC_OVERSCAN_COLOR] = mmCRTC ## id ## _CRTC_OVERSCAN_COLOR,\ + [IDX_CRTC_BLACK_COLOR] = mmCRTC ## id ## _CRTC_BLACK_COLOR,\ + [IDX_CRTC_BLANK_DATA_COLOR] = mmCRTC ## id ## _CRTC_BLANK_DATA_COLOR,\ + [IDX_PRESCALE_VALUES_GRPH_G] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_G,\ + [IDX_PRESCALE_VALUES_GRPH_B] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_B,\ + [IDX_PRESCALE_VALUES_GRPH_R] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_R,\ + [IDX_PRESCALE_GRPH_CONTROL] = mmDCP ## id ## _PRESCALE_GRPH_CONTROL,\ + [IDX_DENORM_CONTROL] = mmDCP ## id ## _DENORM_CONTROL,\ + [IDX_GAMUT_REMAP_11_12] = mmDCP ## id ## _GAMUT_REMAP_C11_C12,\ + [IDX_GAMUT_REMAP_13_14] = mmDCP ## id ## _GAMUT_REMAP_C13_C14,\ + [IDX_GAMUT_REMAP_21_22] = mmDCP ## id ## _GAMUT_REMAP_C21_C22,\ + [IDX_GAMUT_REMAP_23_24] = mmDCP ## id ## _GAMUT_REMAP_C23_C24,\ + [IDX_GAMUT_REMAP_31_32] = mmDCP ## id ## _GAMUT_REMAP_C31_C32,\ + [IDX_GAMUT_REMAP_33_34] = mmDCP ## id ## _GAMUT_REMAP_C33_C34,\ + [IDX_GAMUT_REMAP_CONTROL] = mmDCP ## id ## _GAMUT_REMAP_CONTROL,\ + [IDX_OUTPUT_CSC_C11_12] = mmDCP ## id ## _OUTPUT_CSC_C11_C12,\ + [IDX_OUTPUT_CSC_C13_14] = mmDCP ## id ## _OUTPUT_CSC_C13_C14,\ + [IDX_OUTPUT_CSC_C21_22] = mmDCP ## id ## _OUTPUT_CSC_C21_C22,\ + [IDX_OUTPUT_CSC_C23_24] = mmDCP ## id ## _OUTPUT_CSC_C23_C24,\ + [IDX_OUTPUT_CSC_C31_32] = mmDCP ## id ## _OUTPUT_CSC_C31_C32,\ + [IDX_OUTPUT_CSC_C33_34] = mmDCP ## id ## _OUTPUT_CSC_C33_C34,\ + [IDX_OUTPUT_CSC_CONTROL] = mmDCP ## id ## _OUTPUT_CSC_CONTROL} + +static const uint32_t csc_grph_regs[][CG_REGS_IDX_SIZE] = { + regs_for_csc_grph(0), + regs_for_csc_grph(1), + regs_for_csc_grph(2), + regs_for_csc_grph(3), + regs_for_csc_grph(4), + regs_for_csc_grph(5) +}; + +static const struct dcp_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB_FULL_RANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED_RANGE, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* YOnly same as YCbCr709 but Y in Full range -To do. */ +{ COLOR_SPACE_YCBCR601_YONLY, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_YONLY, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +/** +* set_overscan_color_black +* +* @param :black_color is one of the color space +* :this routine will set overscan black color according to the color space. +* @return none +*/ + +static void set_overscan_color_black( + struct csc_grph *cg, + enum color_space black_color) +{ + uint32_t value = 0; + + /* Overscan Color for YUV display modes: + * to achieve a black color for both the explicit and implicit overscan, + * the overscan color registers should be programmed to: */ + + switch (black_color) { + case COLOR_SPACE_YPBPR601: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_YONLY: + case COLOR_SPACE_YCBCR709_YONLY: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + + case COLOR_SPACE_N_MVPU_SUPER_AA: + /* In crossfire SuperAA mode, the slave overscan data is forced + * to 0 in the pixel mixer on the master. As a result, we need + * to adjust the blank color so that after blending the + * master+slave, it will appear black */ + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + + case COLOR_SPACE_SRGB_LIMITED_RANGE: + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + break; + + default: + /* default is sRGB black 0. */ + break; + } + dal_write_reg(cg->ctx, + cg->regs[IDX_CRTC_OVERSCAN_COLOR], value); + dal_write_reg(cg->ctx, + cg->regs[IDX_CRTC_BLACK_COLOR], value); + + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + dal_write_reg(cg->ctx, + cg->regs[IDX_CRTC_BLANK_DATA_COLOR], value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ + +} + +static void set_grph_csc_default( + struct csc_grph *cg, + const struct default_adjustment *default_adjust) +{ + enum wide_gamut_color_mode config = + WIDE_GAMUT_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct dcp_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->color_space) + continue; + /* program the matrix with default values from this + * file */ + cg->funcs->program_color_matrix(cg, elm, option); + config = WIDE_GAMUT_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix */ + + cg->funcs->configure_graphics_mode(cg, config, + default_adjust->csc_adjust_type, + default_adjust->color_space); + +} + +static void set_grph_csc_adjustment( + struct csc_grph *cg, + const struct grph_csc_adjustment *adjust) +{ + enum wide_gamut_color_mode config = + WIDE_GAMUT_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + /* 1. Apply color temperature adjustment and chromaticity adjustment */ + dal_csc_grph_wide_gamut_set_gamut_remap(cg, adjust); + + /* 2. Apply color adjustments: brightness, saturation, hue, contrast and + * CSC. No need for different color space routine, color space defines + * the ideal values only, but keep original design to allow quick switch + * to the old legacy routines */ + switch (adjust->c_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + dal_csc_grph_wide_gamut_set_rgb_adjustment_legacy(cg, adjust); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + dal_csc_grph_wide_gamut_set_rgb_limited_range_adjustment( + cg, adjust); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YCBCR601_YONLY: + case COLOR_SPACE_YCBCR709_YONLY: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YPBPR709: + dal_csc_grph_wide_gamut_set_yuv_adjustment(cg, adjust); + break; + default: + dal_csc_grph_wide_gamut_set_rgb_adjustment_legacy(cg, adjust); + break; + } + + /* 3 We did everything ,now program DxOUTPUT_CSC_CONTROL */ + cg->funcs->configure_graphics_mode(cg, config, adjust->csc_adjust_type, + adjust->c_space); +} + +static void program_color_matrix( + struct csc_grph *cg, + const struct dcp_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C11); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C12); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C11_12], value); + } + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C13); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C14); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C13_14], value); + } + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C21); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C22); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C21_22], value); + } + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C23); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C24); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C23_24], value); + } + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C31); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C32); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C31_32], value); + } + { + uint32_t value = 0; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C33); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C34); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_C33_34], value); + } +} + +static void program_gamut_remap( + struct csc_grph *cg, + const uint16_t *reg_val) +{ + uint32_t value = 0; + + /* the register controls ovl also */ + value = dal_read_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_CONTROL]); + + if (reg_val) { + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[0], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C11); + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[1], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C12); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_11_12], + reg_data); + } + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[2], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C13); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[3], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C14); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_13_14], + reg_data); + } + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[4], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C21); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[5], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C22); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_21_22], + reg_data); + } + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[6], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C23); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[7], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C24); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_23_24], + reg_data); + } + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[8], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C31); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[9], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C32); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_31_32], + reg_data); + } + { + uint32_t reg_data = 0; + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[10], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C33); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[11], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C34); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_33_34], + reg_data); + } + + set_reg_field_value( + value, + 1, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + } else + set_reg_field_value( + value, + 0, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + dal_write_reg(cg->ctx, + cg->regs[IDX_GAMUT_REMAP_CONTROL], value); +} + +static bool configure_graphics_mode( + struct csc_grph *cg, + enum wide_gamut_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum color_space color_space) +{ + uint32_t value = dal_read_reg( + cg->ctx, + cg->regs[IDX_OUTPUT_CSC_CONTROL]); + + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == WIDE_GAMUT_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + set_reg_field_value( + value, + 4, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + } else if (config == WIDE_GAMUT_COLOR_MODE_GRAPHICS_MATRIX_B) { + set_reg_field_value( + value, + 5, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + } else { + + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB_FULL_RANGE: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED_RANGE: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_YONLY: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_YONLY: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + dal_write_reg(cg->ctx, + cg->regs[IDX_OUTPUT_CSC_CONTROL], value); + + return true; +} + + +static void destruct(struct csc_grph *cg) +{ + +} + +static void destroy(struct csc_grph *cg) +{ + destruct(cg); + dal_free(cg); +} + +static const struct csc_grph_funcs csc_grph_dce110_funcs = { + .set_overscan_color_black = set_overscan_color_black, + .set_grph_csc_default = set_grph_csc_default, + .set_grph_csc_adjustment = set_grph_csc_adjustment, + .program_color_matrix = program_color_matrix, + .program_gamut_remap = program_gamut_remap, + .configure_graphics_mode = configure_graphics_mode, + .destroy = destroy +}; + +static bool construct( + struct csc_grph *cg, + struct csc_grph_init_data *init_data) +{ + if (!dal_csc_grph_construct(cg, init_data)) + return false; + + cg->regs = csc_grph_regs[init_data->id - 1]; + cg->funcs = &csc_grph_dce110_funcs; + return true; +} +struct csc_grph *dal_csc_grph_dce110_create( + struct csc_grph_init_data *init_data) +{ + struct csc_grph *cg = dal_alloc(sizeof(*cg)); + + if (!cg) { + ASSERT_CRITICAL(false); + return NULL; + } + + if (construct(cg, init_data)) + return cg; + + ASSERT_CRITICAL(false); + dal_free(cg); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.h new file mode 100644 index 000000000000..ca8f645367b2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/csc_grph_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_CSC_GRPH_DCE110_H__ +#define __DAL_CSC_GRPH_DCE110_H__ + +#include "../csc_grph.h" + +struct csc_grph *dal_csc_grph_dce110_create( + struct csc_grph_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.c new file mode 100644 index 000000000000..baffd60bb89e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.c @@ -0,0 +1,243 @@ +/* + * 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/logger_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "cursor_dce110.h" + +#define CURSOR_COLOR_BLACK 0x00000000 +#define CURSOR_COLOR_WHITE 0xFFFFFFFF + +enum cur_regs_idx { + IDX_CUR_CONTROL, + IDX_CUR_UPDATE, + IDX_CUR_POSITION, + IDX_CUR_HOT_SPOT, + IDX_CUR_SIZE, + + IDX_CUR_COLOR1, + IDX_CUR_COLOR2, + + IDX_CUR_SURFACE_ADDRESS, + IDX_CUR_SURFACE_ADDRESS_HIGH, + + CUR_REGS_IDX_SIZE +}; + +#define regs_for_cursor(id)\ +[CONTROLLER_ID_D ## id - 1] = {\ + [IDX_CUR_CONTROL] = mmDCP ## id ## _CUR_CONTROL,\ + [IDX_CUR_UPDATE] = mmDCP ## id ## _CUR_UPDATE,\ + [IDX_CUR_POSITION] = mmDCP ## id ## _CUR_POSITION,\ + [IDX_CUR_HOT_SPOT] = mmDCP ## id ## _CUR_HOT_SPOT,\ + [IDX_CUR_SIZE] = mmDCP ## id ## _CUR_SIZE,\ + [IDX_CUR_COLOR1] = mmDCP ## id ## _CUR_COLOR1,\ + [IDX_CUR_COLOR2] = mmDCP ## id ## _CUR_COLOR2,\ + [IDX_CUR_SURFACE_ADDRESS] = mmDCP ## id ## _CUR_SURFACE_ADDRESS,\ + [IDX_CUR_SURFACE_ADDRESS_HIGH] =\ + mmDCP ## id ## _CUR_SURFACE_ADDRESS_HIGH,\ +} + + + +static const uint32_t cur_regs[][CUR_REGS_IDX_SIZE] = { + regs_for_cursor(0), + regs_for_cursor(1), + regs_for_cursor(2), +}; + +static void enable( + struct cursor *cur, bool enable) +{ + uint32_t value = 0; + + value = dal_read_reg(cur->ctx, cur->regs[IDX_CUR_CONTROL]); + set_reg_field_value(value, enable, CUR_CONTROL, CURSOR_EN); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_CONTROL], value); + cur->is_enabled = enable; +} + +static void lock( + struct cursor *cur, bool lock) +{ + uint32_t value = 0; + + value = dal_read_reg(cur->ctx, cur->regs[IDX_CUR_UPDATE]); + set_reg_field_value(value, lock, CUR_UPDATE, CURSOR_UPDATE_LOCK); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_UPDATE], value); +} + +static void program_position( + struct cursor *cur, + uint32_t x, + uint32_t y) +{ + uint32_t value = 0; + + value = dal_read_reg(cur->ctx, cur->regs[IDX_CUR_POSITION]); + set_reg_field_value(value, x, CUR_POSITION, CURSOR_X_POSITION); + set_reg_field_value(value, y, CUR_POSITION, CURSOR_Y_POSITION); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_POSITION], value); +} + +static bool program_control( + struct cursor *cur, + enum cursor_color_format color_format, + bool enable_magnifcation, + bool inverse_transparent_clamping) +{ + uint32_t value = 0; + uint32_t mode = 0; + + switch (color_format) { + case CURSOR_MODE_MONO: + mode = 0; + break; + case CURSOR_MODE_COLOR_1BIT_AND: + mode = 1; + break; + case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: + mode = 2; + break; + case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: + mode = 3; + break; + default: + return false; + } + + set_reg_field_value(value, mode, CUR_CONTROL, CURSOR_MODE); + set_reg_field_value(value, enable_magnifcation, + CUR_CONTROL, CURSOR_2X_MAGNIFY); + set_reg_field_value(value, inverse_transparent_clamping, + CUR_CONTROL, CUR_INV_TRANS_CLAMP); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_CONTROL], value); + + if (color_format == CURSOR_MODE_MONO) { + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_COLOR1], + CURSOR_COLOR_BLACK); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_COLOR2], + CURSOR_COLOR_WHITE); + } + return true; +} + +static void program_hotspot( + struct cursor *cur, + uint32_t x, + uint32_t y) +{ + uint32_t value = 0; + + value = dal_read_reg(cur->ctx, cur->regs[IDX_CUR_HOT_SPOT]); + set_reg_field_value(value, x, CUR_HOT_SPOT, CURSOR_HOT_SPOT_X); + set_reg_field_value(value, y, CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_HOT_SPOT], value); +} + +static void program_size( + struct cursor *cur, + uint32_t width, + uint32_t height) +{ + uint32_t value = 0; + + value = dal_read_reg(cur->ctx, cur->regs[IDX_CUR_SIZE]); + set_reg_field_value(value, width, CUR_SIZE, CURSOR_WIDTH); + set_reg_field_value(value, height, CUR_SIZE, CURSOR_HEIGHT); + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_SIZE], value); +} + +static void program_address( + struct cursor *cur, + PHYSICAL_ADDRESS_LOC address) +{ + /* SURFACE_ADDRESS_HIGH: Higher order bits (39:32) of hardware cursor + * surface base address in byte. It is 4K byte aligned. + * The correct way to program cursor surface address is to first write + * to CUR_SURFACE_ADDRESS_HIGH, and then write to CUR_SURFACE_ADDRESS */ + + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_SURFACE_ADDRESS_HIGH], + address.high_part); + + dal_write_reg(cur->ctx, cur->regs[IDX_CUR_SURFACE_ADDRESS], + address.low_part); +} + + +/*****************************************/ +/* Constructor, Destructor, fcn pointers */ +/*****************************************/ + +static void destroy(struct cursor **cur) +{ + dal_free(*cur); + *cur = NULL; +} + +static const struct cursor_funcs cur_funcs = { + .enable = enable, + .lock = lock, + .program_position = program_position, + .program_control = program_control, + .program_hotspot = program_hotspot, + .program_size = program_size, + .program_address = program_address, + .destroy = destroy +}; + +static bool cursor_dce110_construct( + struct cursor *cur, + struct cursor_init_data *init_data) +{ + if (!dal_cursor_construct(cur, init_data)) + return false; + + cur->regs = cur_regs[init_data->id - 1]; + + cur->funcs = &cur_funcs; + return true; +} + +struct cursor *dal_cursor_dce110_create( + struct cursor_init_data *init_data) +{ + struct cursor *cur = dal_alloc(sizeof(struct cursor)); + + if (!cur) + return NULL; + + if (!cursor_dce110_construct(cur, init_data)) + goto fail; + + return cur; +fail: + dal_free(cur); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.h new file mode 100644 index 000000000000..af82507494be --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/cursor_dce110.h @@ -0,0 +1,35 @@ +/* + * 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_CURSOR_DCE110_H__ +#define __DAL_CURSOR_DCE110_H__ + +#include "../cursor.h" + +struct cursor *dal_cursor_dce110_create( + struct cursor_init_data *init_data); + + +#endif /*__DAL_CURSOR_DCE110_H__*/ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.c new file mode 100644 index 000000000000..63eef8008ff6 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.c @@ -0,0 +1,611 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_id.h" +#include "include/adapter_service_interface.h" + +#include "dcp_bit_depth_reduction_dce110.h" + +enum dcp_bit_depth_reduction_regs_idx { + IDX_OUT_CLAMP_CONTROL_B_CB, + IDX_OUT_CLAMP_CONTROL_G_Y, + IDX_OUT_CLAMP_CONTROL_R_CR, + IDX_OUT_ROUND_CONTROL, + IDX_DCP_SPATIAL_DITHER_CNTL, + DCP_BIT_DEPTH_REDUCTION_REGS_IDX_SIZE +}; + +#define regs_for_dcp_bit_depth_reduction(id)\ + [CONTROLLER_ID_D ## id - 1] = {\ + [IDX_DCP_SPATIAL_DITHER_CNTL] =\ + mmDCP ## id ## _DCP_SPATIAL_DITHER_CNTL,\ + [IDX_OUT_CLAMP_CONTROL_B_CB] =\ + mmDCP ## id ## _OUT_CLAMP_CONTROL_B_CB,\ + [IDX_OUT_CLAMP_CONTROL_G_Y] =\ + mmDCP ## id ## _OUT_CLAMP_CONTROL_G_Y,\ + [IDX_OUT_CLAMP_CONTROL_R_CR] =\ + mmDCP ## id ## _OUT_CLAMP_CONTROL_R_CR,\ + [IDX_OUT_ROUND_CONTROL] = mmDCP ## id ## _OUT_ROUND_CONTROL,\ + } + +static const uint32_t +dcp_bit_depth_reduction_regs[][DCP_BIT_DEPTH_REDUCTION_REGS_IDX_SIZE] = { + regs_for_dcp_bit_depth_reduction(0), + regs_for_dcp_bit_depth_reduction(1), + regs_for_dcp_bit_depth_reduction(2) +}; + +static bool dcp_bit_depth_reduction_dce110_construct( + struct dcp_bit_depth_reduction_dce110 *bdr, + struct dal_context *ctx, + enum controller_id id, + struct adapter_service *as); + +struct dcp_bit_depth_reduction_dce110 +*dal_dcp_bit_depth_reduction_dce110_create( + enum controller_id id, + struct dal_context *ctx, + struct adapter_service *as) +{ + struct dcp_bit_depth_reduction_dce110 *bdr = + dal_alloc(sizeof(struct dcp_bit_depth_reduction_dce110)); + + if (!bdr) + return NULL; + + if (dcp_bit_depth_reduction_dce110_construct(bdr, ctx, id, as)) + return bdr; + + dal_free(bdr); + return NULL; +} + +void dal_dcp_bit_depth_reduction_dce110_destroy( + struct dcp_bit_depth_reduction_dce110 **bdr) +{ + if (!bdr) + return; + if (!*bdr) + return; + + dal_free(*bdr); + *bdr = NULL; +} + +static bool dcp_bit_depth_reduction_dce110_construct( + struct dcp_bit_depth_reduction_dce110 *bdr, + struct dal_context *ctx, + enum controller_id id, + struct adapter_service *as) +{ + if (!as) + return false; + bdr->ctx = ctx; + bdr->regs = dcp_bit_depth_reduction_regs[id - 1]; + bdr->as = as; + return true; +} + +static bool set_clamp( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum csc_color_depth depth); +static bool set_round( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth); +static bool set_dither( + struct dcp_bit_depth_reduction_dce110 *bdr, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable); + +/** + ******************************************************************************* + * dal_dcp_bit_depth_reduction_dce110_program + * + * @brief + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, + * Dither) + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @return + * true if succeeds. + ******************************************************************************* + */ +bool dal_dcp_bit_depth_reduction_dce110_program( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum csc_color_depth depth) +{ + enum dcp_bit_depth_reduction_mode depth_reduction_mode; + enum dcp_spatial_dither_mode spatial_dither_mode; + bool frame_random_enable; + bool rgb_random_enable; + bool highpass_random_enable; + + if (depth > CSC_COLOR_DEPTH_121212) { + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_INVALID; + if (!dal_adapter_service_get_feature_value( + FEATURE_DCP_BIT_DEPTH_REDUCTION_MODE, + &depth_reduction_mode, + sizeof(depth_reduction_mode))) { + /* Failed to get value for + * FEATURE_DCP_BIT_DEPTH_REDUCTION_MODE */ + ASSERT_CRITICAL(false); + return false; + } + + spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_INVALID; + if (!dal_adapter_service_get_feature_value( + FEATURE_DCP_DITHER_MODE, &spatial_dither_mode, + sizeof(spatial_dither_mode))) { + /* Failed to get value for + * FEATURE_DCP_DITHER_MODE */ + ASSERT_CRITICAL(false); + return false; + } + + frame_random_enable = false; + if (!dal_adapter_service_get_feature_value( + FEATURE_DCP_DITHER_FRAME_RANDOM_ENABLE, + &frame_random_enable, + sizeof(frame_random_enable))) { + /* Failed to get value for + * FEATURE_DCP_DITHER_FRAME_RANDOM_ENABLE */ + ASSERT_CRITICAL(false); + return false; + } + + rgb_random_enable = false; + if (!dal_adapter_service_get_feature_value( + FEATURE_DCP_DITHER_RGB_RANDOM_ENABLE, + &rgb_random_enable, sizeof(rgb_random_enable))) { + /* Failed to get value for + * FEATURE_DCP_DITHER_RGB_RANDOM_ENABLE */ + ASSERT_CRITICAL(false); + return false; + } + + highpass_random_enable = false; + if (!dal_adapter_service_get_feature_value( + FEATURE_DCP_DITHER_HIGH_PASS_RANDOM_ENABLE, + &highpass_random_enable, + sizeof(highpass_random_enable))) { + /* Failed to get value for + * FEATURE_DCP_DITHER_HIGH_PASS_RANDOM_ENABLE */ + ASSERT_CRITICAL(false); + return false; + } + + if (!set_clamp(bdr, depth)) { + /* Failure in set_clamp() */ + ASSERT_CRITICAL(false); + return false; + } + switch (depth_reduction_mode) { + case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: + /* Spatial Dither: Set round/truncate to bypass (12bit), + * enable Dither (30bpp) */ + set_round(bdr, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(bdr, true, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: + /* Round: Enable round (10bit), disable Dither */ + set_round(bdr, + DCP_OUT_TRUNC_ROUND_MODE_ROUND, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(bdr, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ + /* Truncate: Enable truncate (10bit), disable Dither */ + set_round(bdr, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(bdr, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + + case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ + /* Truncate: Set round/truncate to bypass (12bit), + * disable Dither */ + set_round(bdr, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(bdr, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + default: + /* Invalid DCP Depth reduction mode */ + ASSERT_CRITICAL(false); + break; + } + + return true; +} + +/** + ******************************************************************************* + * set_clamp + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @brief + * Programs clamp according to panel bit depth. + * + * @return + * true if succeeds + * + ******************************************************************************* + */ +static bool set_clamp( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum csc_color_depth depth) +{ + uint32_t clamp_max = 0; + + /* At the clamp block the data will be MSB aligned, so we set the max + * clamp accordingly. + * For example, the max value for 6 bits MSB aligned (14 bit bus) would + * be "11 1111 0000 0000" in binary, so 0x3F00. + */ + switch (depth) { + case CSC_COLOR_DEPTH_666: + /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ + clamp_max = 0x3F00; + break; + case CSC_COLOR_DEPTH_888: + /* 8bit MSB aligned on 14 bit bus '11 1111 1100 0000' */ + clamp_max = 0x3FC0; + break; + case CSC_COLOR_DEPTH_101010: + /* 10bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ + clamp_max = 0x3FFC; + break; + case CSC_COLOR_DEPTH_111111: + /* 11bit MSB aligned on 14 bit bus '11 1111 1111 1110' */ + clamp_max = 0x3FFE; + break; + case CSC_COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ + clamp_max = 0x3FFF; + break; + default: + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MIN_B_CB); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MAX_B_CB); + + dal_write_reg(bdr->ctx, + bdr->regs[IDX_OUT_CLAMP_CONTROL_B_CB], + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MIN_G_Y); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MAX_G_Y); + + dal_write_reg(bdr->ctx, + bdr->regs[IDX_OUT_CLAMP_CONTROL_G_Y], + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MIN_R_CR); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + + dal_write_reg(bdr->ctx, + bdr->regs[IDX_OUT_CLAMP_CONTROL_R_CR], + value); + } + + return true; +} + +/** + ******************************************************************************* + * set_round + * + * @brief + * Programs Round/Truncate + * + * @param [in] mode :round or truncate + * @param [in] depth :bit depth to round/truncate to + OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode + POSSIBLE VALUES: + 00 - truncate to u0.12 + 01 - truncate to u0.11 + 02 - truncate to u0.10 + 03 - truncate to u0.9 + 04 - truncate to u0.8 + 05 - reserved + 06 - truncate to u0.14 + 07 - truncate to u0.13 set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + 08 - round to u0.12 + 09 - round to u0.11 + 10 - round to u0.10 + 11 - round to u0.9 + 12 - round to u0.8 + 13 - reserved + 14 - round to u0.14 + 15 - round to u0.13 + + * @return + * true if succeeds. + ******************************************************************************* + */ +static bool set_round( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth) +{ + uint32_t depth_bits = 0; + uint32_t mode_bit = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up bit depth */ + switch (depth) { + case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: + depth_bits = 6; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: + depth_bits = 7; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: + depth_bits = 0; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: + depth_bits = 1; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: + depth_bits = 2; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: + depth_bits = 3; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: + depth_bits = 4; + break; + default: + /* Invalid dcp_out_trunc_round_depth */ + ASSERT_CRITICAL(false); + return false; + + } + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* set up round or truncate */ + switch (mode) { + case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: + mode_bit = 0; + break; + case DCP_OUT_TRUNC_ROUND_MODE_ROUND: + mode_bit = 1; + break; + default: + /* Invalid dcp_out_trunc_round_mode */ + ASSERT_CRITICAL(false); + return false; + + } + + depth_bits |= mode_bit << 3; + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* write the register */ + dal_write_reg(bdr->ctx, + bdr->regs[IDX_OUT_ROUND_CONTROL], value); + + return true; +} + +/** + ******************************************************************************* + * set_dither + * + * @brief + * Programs Dither + * + * @param [in] dither_enable : enable dither + * @param [in] dither_mode : dither mode to set + * @param [in] dither_depth : bit depth to dither to + * @param [in] frame_random_enable : enable frame random + * @param [in] rgb_random_enable : enable rgb random + * @param [in] highpass_random_enable : enable highpass random + * + * @return + * true if succeeds. + ******************************************************************************* + */ + +static bool set_dither( + struct dcp_bit_depth_reduction_dce110 *bdr, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable) +{ + uint32_t dither_depth_bits = 0; + uint32_t dither_mode_bits = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up the fields */ + if (dither_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_EN); + + switch (dither_mode) { + case DCP_SPATIAL_DITHER_MODE_AAAA: + dither_mode_bits = 0; + break; + case DCP_SPATIAL_DITHER_MODE_A_AA_A: + dither_mode_bits = 1; + break; + case DCP_SPATIAL_DITHER_MODE_AABBAABB: + dither_mode_bits = 2; + break; + case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: + dither_mode_bits = 3; + break; + default: + /* Invalid dcp_spatial_dither_mode */ + ASSERT_CRITICAL(false); + return false; + + } + set_reg_field_value( + value, + dither_mode_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_MODE); + + switch (dither_depth) { + case DCP_SPATIAL_DITHER_DEPTH_30BPP: + dither_depth_bits = 0; + break; + case DCP_SPATIAL_DITHER_DEPTH_24BPP: + dither_depth_bits = 1; + break; + default: + /* Invalid dcp_spatial_dither_depth */ + ASSERT_CRITICAL(false); + return false; + + } + + set_reg_field_value( + value, + dither_depth_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_DEPTH); + + if (frame_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_FRAME_RANDOM_ENABLE); + + if (rgb_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_RGB_RANDOM_ENABLE); + + if (highpass_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_HIGHPASS_RANDOM_ENABLE); + + /* write the register */ + dal_write_reg(bdr->ctx, + bdr->regs[IDX_DCP_SPATIAL_DITHER_CNTL], value); + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.h new file mode 100644 index 000000000000..6b795b06a978 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/dcp_bit_depth_reduction_dce110.h @@ -0,0 +1,88 @@ +/* + * 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 __DCE110_BIT_DEPTH_REDUCTION_H__ +#define __DCE110_BIT_DEPTH_REDUCTION_H__ + +#include "include/grph_object_id.h" +#include "include/csc_common_types.h" + +enum dcp_out_trunc_round_mode { + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_MODE_ROUND +}; + +enum dcp_out_trunc_round_depth { + DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_8BIT +}; + +/* defines the various methods of bit reduction available for use */ +enum dcp_bit_depth_reduction_mode { + DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, + DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, + DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, + DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, + DCP_BIT_DEPTH_REDUCTION_MODE_INVALID +}; + +enum dcp_spatial_dither_mode { + DCP_SPATIAL_DITHER_MODE_AAAA, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_MODE_AABBAABB, + DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, + DCP_SPATIAL_DITHER_MODE_INVALID +}; + +enum dcp_spatial_dither_depth { + DCP_SPATIAL_DITHER_DEPTH_30BPP, + DCP_SPATIAL_DITHER_DEPTH_24BPP +}; + +struct dcp_bit_depth_reduction_dce110 { + const uint32_t *regs; + struct adapter_service *as; + struct dal_context *ctx; +}; + +struct dcp_bit_depth_reduction_dce110 +*dal_dcp_bit_depth_reduction_dce110_create( + enum controller_id id, + struct dal_context *ctx, + struct adapter_service *as); + +void dal_dcp_bit_depth_reduction_dce110_destroy( + struct dcp_bit_depth_reduction_dce110 **bdr); + +bool dal_dcp_bit_depth_reduction_dce110_program( + struct dcp_bit_depth_reduction_dce110 *bdr, + enum csc_color_depth depth); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.c new file mode 100644 index 000000000000..cd605bad8178 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.c @@ -0,0 +1,1006 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "gmc/gmc_8_2_sh_mask.h" +#include "gmc/gmc_8_2_d.h" + +#include "include/logger_interface.h" +#include "include/adapter_service_interface.h" + +#include "fbc_dce110.h" + +static const uint32_t compressed_surface_address_high_reg[] = { + mmDCP0_GRPH_COMPRESS_SURFACE_ADDRESS_HIGH, + mmDCP1_GRPH_COMPRESS_SURFACE_ADDRESS_HIGH, + mmDCP2_GRPH_COMPRESS_SURFACE_ADDRESS_HIGH, + mmDCP3_GRPH_COMPRESS_SURFACE_ADDRESS_HIGH +}; + +static const uint32_t compressed_surface_address_reg[] = { + mmDCP0_GRPH_COMPRESS_SURFACE_ADDRESS, + mmDCP1_GRPH_COMPRESS_SURFACE_ADDRESS, + mmDCP2_GRPH_COMPRESS_SURFACE_ADDRESS, + mmDCP3_GRPH_COMPRESS_SURFACE_ADDRESS +}; + +static const uint32_t compressed_surface_pitch[] = { + mmDCP0_GRPH_COMPRESS_PITCH, + mmDCP1_GRPH_COMPRESS_PITCH, + mmDCP2_GRPH_COMPRESS_PITCH, + mmDCP3_GRPH_COMPRESS_PITCH +}; + +static const uint32_t stutter_control_non_lpt_ch_reg[] = { + mmDMIF_PG0_DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + mmDMIF_PG1_DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + mmDMIF_PG2_DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + mmDMIF_PG3_DPG_PIPE_STUTTER_CONTROL_NONLPTCH +}; + +static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; + +/* + * DCE 11 Frame Buffer Compression Implementation + */ + +void dal_fbc_dce110_wait_for_fbc_state_changed( + struct fbc *fbc, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dal_read_reg(fbc->context, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + dal_delay_in_microseconds(10); + counter++; + } + + if (counter == 10) { + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +static uint32_t lpt_required_size(struct fbc *fbc, uint32_t fbc_size) +{ + uint32_t chan_divider = 1; + /* chan_divider = 2 pow (LOW_POWER_TILING_MODE) */ + if (fbc->lpt_channels_num == 1) + chan_divider = 1; + else + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Unexpected DCE11 number of LPT DRAM channels", + __func__); + + /* LPT_SURFACE_SIZE (in bytes) = FBC_COMPRESSED_SURFACE_SIZE (in bytes) + * * DRAM_CHANNELS / 2 pow (LOW_POWER_TILING_MODE). + * fbc->m_numOfLPTChannels = 1 == > chanDivider = 2 pow + * (LOW_POWER_TILING_MODE) = 2 pow 0 = 1 + */ + return fbc_size * fbc->memory_bus_width / 64 / chan_divider; +} + +static uint32_t lpt_size_alignment(struct fbc *fbc) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return fbc->raw_size * fbc->banks_num * fbc->dram_channels_num; +} + +void dal_fbc_dce110_power_up_fbc(struct fbc *fbc) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (fbc->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurment comparision. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dal_write_reg(fbc->context, addr, value); + + addr = mmFBC_COMP_MODE; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dal_write_reg(fbc->context, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dal_write_reg(fbc->context, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dal_write_reg(fbc->context, addr, value); + fbc->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dal_write_reg(fbc->context, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dal_write_reg(fbc->context, mmFBC_IND_LUT1, value); +} + +static void destroy(struct fbc **fbc) +{ + dal_free(*fbc); + *fbc = NULL; +} + +bool dal_fbc_dce110_get_required_compressed_surface_size( + struct fbc *fbc, + struct fbc_input_info *input_info, + struct fbc_requested_compressed_size *size) +{ + bool result = false; + + if (fbc->options.bits.LPT_MC_CONFIG == 0) { + if (input_info->lpt_config.banks_num == 0 || + input_info->lpt_config.row_size == 0) { + dal_logger_write(fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: incorrect input data\n", + __func__); + } + + fbc->banks_num = input_info->lpt_config.banks_num; + fbc->raw_size = input_info->lpt_config.row_size; + fbc->channel_interleave_size = + input_info->lpt_config.chan_interleave_size; + fbc->dram_channels_num = + input_info->lpt_config.mem_channels_num; + fbc->options.bits.LPT_MC_CONFIG = 1; + } + + if (input_info->dynamic_fbc_buffer_alloc == 0) { + /* This is default option at boot up time */ + if (fbc->embedded_panel_h_size != 0 + && fbc->embedded_panel_v_size != 0) { + /* Align horizontal to the required number of chunks + * (256 pixels (by 2 lines)) (min compression ratio = + * 1:1 for DCE 11) */ + size->prefered_size = + dal_fbc_align_to_chunks_number_per_line( + fbc, + fbc->embedded_panel_h_size) + /* For FBC when LPT not supported */ + * fbc->embedded_panel_v_size * 4; + size->min_size = size->prefered_size; + /* For FBC when LPT not supported */ + size->prefered_size_alignment = 0x100; + size->min_size_alignment = 0x100; + if (fbc->options.bits.LPT_SUPPORT == true) { + size->prefered_size = lpt_required_size( + fbc, + size->min_size); + size->prefered_size_alignment = + lpt_size_alignment(fbc); + } + size->flags.PREFERED_MUST_BE_FRAME_BUFFER_POOL = 1; + size->flags.MIN_MUST_BE_FRAME_BUFFER_POOL = 1; + fbc->preferred_requested_size = size->prefered_size; + result = true; + } else { + /* For DCE11 here use Max HW supported size */ + /* Use: 18000 Chunks * 256 * 2 pixels * 4 bytes. + * (For FBC when LPT not supported). */ + size->min_size = 18000 * 256 * 2 * 4; + size->prefered_size = size->min_size; + /* For FBC when LPT not supported */ + size->prefered_size_alignment = 0x100; + size->min_size_alignment = 0x100; + if (fbc->options.bits.LPT_SUPPORT == true) { + size->prefered_size = lpt_required_size( + fbc, + size->min_size); + size->prefered_size_alignment = + lpt_size_alignment(fbc); + } + size->flags.PREFERED_MUST_BE_FRAME_BUFFER_POOL = 1; + size->flags.MIN_MUST_BE_FRAME_BUFFER_POOL = 1; + fbc->preferred_requested_size = size->prefered_size; + result = true; + } + } else { + /* Dynamic allocation at mode set time + * For DCE11 here use actual mode that is going to be set - LPT + * not supported in this case, FBC Compressed surface can be in + * System (Scaterred) memory without LPT support. If Embedded + * Panel present FBC is supported only up to Embedded Panel size + */ + if (dal_fbc_is_source_bigger_than_epanel_size( + fbc, + input_info->source_view_width, + input_info->source_view_height)) { + /* Align horizontal to the required number of chunks + * (256 pixels (by 2 lines)) (min compression ratio = + * 1:1 for DCE 11) */ + /*For FBC when LPT not supported */ + size->min_size = + dal_fbc_align_to_chunks_number_per_line( + fbc, + fbc->embedded_panel_h_size) + * fbc->embedded_panel_v_size * 4; + size->prefered_size = size->min_size; + /* For FBC when LPT not supported */ + size->prefered_size_alignment = 0x100; + size->min_size_alignment = 0x100; + } else { + /* (For FBC when LPT not supported). */ + size->min_size = + dal_fbc_align_to_chunks_number_per_line( + fbc, + input_info->source_view_width) + * input_info->source_view_height * 4; + size->prefered_size = size->min_size; + + if (input_info->source_view_width * + input_info->source_view_height + > FBC_MAX_X * FBC_MAX_Y) { + /* can not exceed HW limit. */ + size->prefered_size = + /* (For FBC when LPT not supported).*/ + dal_fbc_align_to_chunks_number_per_line(fbc, + FBC_MAX_X) * FBC_MAX_Y * 4; + /* Note: + * this is the same as 18000 * 256 * 2 * 4; + * Use: 18000 Chunks * 256 * 2 pixels * 4 bytes. + * (For FBC when LPT not supported). + * FBC mixed mode is disabled by default -- + * which disable FBC for resolutions bigger + * than supported by FBC HW, + * partial compression not supported. */ + } + + /* For FBC when LPT not supported */ + size->prefered_size_alignment = 0x100; + size->min_size_alignment = 0x100; + } + size->flags.PREFERED_MUST_BE_FRAME_BUFFER_POOL = 0; + size->flags.MIN_MUST_BE_FRAME_BUFFER_POOL = 0; + fbc->preferred_requested_size = size->prefered_size; + result = true; + + /* In case more than 2 displays will be active on this mode set + * do not even allocate compressed surface as FBC will not be + * enabled. This code may be enabled after Initial bringup and + * testing on Carrizo */ + /* + if (pFBCInpuInfo->numOfActiveTargets > 2) + { + pSize->preferedSize = pSize->minSize = 0; + pSize->preferedSizeAlignment = pSize->minSizeAlignment = 0; + pSize->bits.preferedMustBeFrameBufferPool = 0; + pSize->bits.minMustBeFrameBufferPool = 0; + fbc->m_preferredRequestedSize = 0; + fbc->m_comprSurfaceAddress.QuadPart = 0; + bReturn = false; + } */ + } + + return result; +} + +static void disable_fbc(struct fbc *fbc) +{ + if (fbc->options.bits.FBC_SUPPORT && + fbc->funcs->is_fbc_enabled_in_hw(fbc, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dal_read_reg(fbc->context, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dal_write_reg(fbc->context, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + fbc->attached_controller_id = CONTROLLER_ID_UNDEFINED; + + /* Whenever disabling FBC make sure LPT is disabled if LPT + * supported */ + if (fbc->options.bits.LPT_SUPPORT) + fbc->funcs->disable_lpt(fbc); + + dal_fbc_dce110_wait_for_fbc_state_changed(fbc, false); + } +} + +void dal_fbc_dce110_program_compressed_surface_address_and_pitch( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params) +{ + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t inx = fbc->funcs->controller_idx( + fbc, + params->controller_id); + uint32_t compressed_surf_address_low_part = + fbc->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dal_write_reg( + fbc->context, + compressed_surface_address_high_reg[inx], + 0); + dal_write_reg(fbc->context, compressed_surface_address_reg[inx], 0); + + if (fbc->options.bits.LPT_SUPPORT) { + uint32_t lpt_alignment = lpt_size_alignment(fbc); + + if (lpt_alignment != 0) { + compressed_surf_address_low_part = + ((compressed_surf_address_low_part + + (lpt_alignment - 1)) / lpt_alignment) + * lpt_alignment; + } + } + + /* Write address, HIGH has to be first. */ + dal_write_reg(fbc->context, compressed_surface_address_high_reg[inx], + fbc->compr_surface_address.addr.high_part); + dal_write_reg(fbc->context, compressed_surface_address_reg[inx], + compressed_surf_address_low_part); + + fbc_pitch = dal_fbc_align_to_chunks_number_per_line( + fbc, + params->source_view_width); + + if (fbc->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dal_write_reg(fbc->context, compressed_surface_pitch[inx], 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dal_write_reg(fbc->context, compressed_surface_pitch[inx], value); + +} + +/* + FBCIdleForce_DisplayRegisterUpdate = 0x00000001, bit 0 + FBCIdleForce_MemoryWriteOtherThanMCIF = 0x10000000, bit 28 + FBCIdleForce_CGStaticScreenIsInactive = 0x20000000, bit 29 + */ +static void set_fbc_invalidation_triggers(struct fbc *fbc, uint32_t fbc_trigger) +{ + uint32_t value; + uint32_t addr; + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G */ + addr = mmFBC_CLIENT_REGION_MASK; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dal_write_reg(fbc->context, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid). + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - display register updated + * - memory write from any client except from MCIF + * - CG static screen signal is inactive */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + fbc_trigger, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dal_write_reg(fbc->context, addr, value); + + /* DAL2 CL#1051812: enable this cause display flashing on register + * read when SG enabled, according the HW, this FBC_IDLE_MASK is + * obsolete, SW could remove programming it */ +#if 0 + addr = mmFBC_IDLE_MASK; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value(value, fbc_trigger, FBC_IDLE_MASK, FBC_IDLE_MASK); + dal_write_reg(fbc->context, addr, value); +#endif +} + +void dal_fbc_dce110_enable_fbc( + struct fbc *fbc, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + if (fbc->options.bits.FBC_SUPPORT && + (fbc->options.bits.DUMMY_BACKEND == 0) && + (!fbc->funcs->is_fbc_enabled_in_hw(fbc, NULL)) && + (!dal_fbc_is_source_bigger_than_epanel_size( + fbc, + params->source_view_width, + params->source_view_height))) { + uint32_t value; + uint32_t addr; + + /* Before enabling FBC first need to enable LPT if applicable + * LPT state should always be changed (enable/disable) while FBC + * is disabled */ + if (fbc->options.bits.LPT_SUPPORT && (paths_num < 2) && + (params->source_view_width * + params->source_view_height <= + dce11_one_lpt_channel_max_resolution)) { + fbc->funcs->enable_lpt( + fbc, paths_num, params->controller_id); + } + + addr = mmFBC_CNTL; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + fbc->funcs->controller_idx(fbc, params->controller_id), + FBC_CNTL, FBC_SRC_SEL); + dal_write_reg(fbc->context, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + fbc->attached_controller_id = params->controller_id; + + /*Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dal_write_reg(fbc->context, addr, value); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dal_write_reg(fbc->context, addr, value); + + dal_fbc_dce110_wait_for_fbc_state_changed(fbc, true); + } +} + +bool dal_fbc_dce110_is_fbc_enabled_in_hw( + struct fbc *fbc, + enum controller_id *fbc_mapped_crtc_id) +{ + /* Check the hardware register */ + uint32_t value; + + value = dal_read_reg(fbc->context, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (fbc_mapped_crtc_id != NULL) + *fbc_mapped_crtc_id = fbc->attached_controller_id; + return true; + } + + value = dal_read_reg(fbc->context, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dal_read_reg(fbc->context, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (fbc_mapped_crtc_id != NULL) + *fbc_mapped_crtc_id = + fbc->attached_controller_id; + return true; + } + } + if (fbc_mapped_crtc_id != NULL) + *fbc_mapped_crtc_id = CONTROLLER_ID_UNDEFINED; + return false; +} + +bool dal_fbc_dce110_is_lpt_enabled_in_hw(struct fbc *fbc) +{ + /* Check the hardware register */ + uint32_t value = dal_read_reg(fbc->context, mmLOW_POWER_TILING_CONTROL); + + return get_reg_field_value( + value, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); +} + +void dal_fbc_dce110_disable_lpt(struct fbc *fbc) +{ + uint32_t value; + uint32_t addr; + uint32_t inx; + + /* Disable all pipes LPT Stutter */ + for (inx = 0; inx < 3; inx++) { + value = + dal_read_reg( + fbc->context, + stutter_control_non_lpt_ch_reg[inx]); + set_reg_field_value( + value, + 0, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dal_write_reg( + fbc->context, + stutter_control_non_lpt_ch_reg[inx], + value); + } + /* Disable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 0, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dal_write_reg(fbc->context, addr, value); + + /* Disable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dal_write_reg(fbc->context, addr, value); + + /* Clear selection of Channel(s) containing Compressed Surface */ + addr = mmGMCON_LPT_TARGET; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 0xFFFFFFFF, + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dal_write_reg(fbc->context, mmGMCON_LPT_TARGET, addr); +} + +void dal_fbc_dce110_enable_lpt( + struct fbc *fbc, + uint32_t paths_num, + enum controller_id cntl_id) +{ + uint32_t inx = fbc->funcs->controller_idx(fbc, cntl_id); + uint32_t value; + uint32_t addr; + uint32_t value_control; + uint32_t channels; + + /* Enable LPT Stutter from Display pipe */ + value = dal_read_reg(fbc->context, stutter_control_non_lpt_ch_reg[inx]); + set_reg_field_value( + value, + 1, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dal_write_reg(fbc->context, stutter_control_non_lpt_ch_reg[inx], value); + + /* Enable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dal_write_reg(fbc->context, addr, value); + + /* Selection of Channel(s) containing Compressed Surface: 0xfffffff + * will disable LPT. + * STCTRL_LPT_TARGETn corresponds to channel n. */ + addr = mmLOW_POWER_TILING_CONTROL; + value_control = dal_read_reg(fbc->context, addr); + channels = get_reg_field_value(value_control, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + + addr = mmGMCON_LPT_TARGET; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + channels + 1, /* not mentioned in programming guide, + but follow DCE8.1 */ + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dal_write_reg(fbc->context, addr, value); + + /* Enable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dal_read_reg(fbc->context, addr); + set_reg_field_value( + value, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dal_write_reg(fbc->context, addr, value); +} + +static uint32_t lpt_memory_control_config(struct fbc *fbc, uint32_t lpt_control) +{ + /*LPT MC Config */ + if (fbc->options.bits.LPT_MC_CONFIG == 1) { + /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): + * 00 - 1 CHANNEL + * 01 - 2 CHANNELS + * 02 - 4 OR 6 CHANNELS + * (Only for discrete GPU, N/A for CZ) + * 03 - 8 OR 12 CHANNELS + * (Only for discrete GPU, N/A for CZ) */ + switch (fbc->dram_channels_num) { + case 2: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + case 1: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid LPT NUM_PIPES!!!", + __func__); + break; + } + + /* The mapping for LPT NUM_BANKS is in + * GRPH_CONTROL.GRPH_NUM_BANKS register field + * Specifies the number of memory banks for tiling + * purposes. Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: + * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK + * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK + * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK + * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ + switch (fbc->banks_num) { + case 16: + set_reg_field_value( + lpt_control, + 3, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 8: + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 4: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 2: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid LPT NUM_BANKS!!!", + __func__); + break; + } + + /* The mapping is in DMIF_ADDR_CALC. + * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for + * Carrizo specifies the memory interleave per pipe. + * It effectively specifies the location of pipe bits in + * the memory address. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte + * interleave + * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte + * interleave + */ + switch (fbc->channel_interleave_size) { + case 256: /*256B */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + case 512: /*512B */ + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid LPT INTERLEAVE_SIZE!!!", + __func__); + break; + } + + /* The mapping for LOW_POWER_TILING_ROW_SIZE is in + * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field + * for Carrizo. Specifies the size of dram row in bytes. + * This should match up with NOOFCOLS field in + * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). + * This register DMIF_ADDR_CALC is not used by the + * hardware as it is only used for addrlib assertions. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row + * boundary + * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row + * boundary + * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row + * boundary */ + switch (fbc->raw_size) { + case 4096: /*4 KB */ + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 2048: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 1024: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: LPT MC Configuration is not provided", + __func__); + } + + return lpt_control; +} + +void dal_fbc_dce110_program_lpt_control( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params) +{ + uint32_t rows_per_channel; + uint32_t lpt_alignment; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t lpt_control = 0; + + if (!fbc->options.bits.LPT_SUPPORT) + return; + + lpt_control = dal_read_reg(fbc->context, mmLOW_POWER_TILING_CONTROL); + + /* POSSIBLE VALUES for Low Power Tiling Mode: + * 00 - Use channel 0 + * 01 - Use Channel 0 and 1 + * 02 - Use Channel 0,1,2,3 + * 03 - reserved */ + switch (fbc->lpt_channels_num) { + /* case 2: + * Use Channel 0 & 1 / Not used for DCE 11 */ + case 1: + /*Use Channel 0 for LPT for DCE 11 */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + break; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Invalid selected DRAM channels for LPT!!!", + __func__); + break; + } + + lpt_control = lpt_memory_control_config(fbc, lpt_control); + + /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on + * FBC compressed surface pitch. + * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * + * Surface Pitch) / (Row Size * Number of Channels * + * Number of Banks)). */ + rows_per_channel = 0; + lpt_alignment = lpt_size_alignment(fbc); + source_view_width = + dal_fbc_align_to_chunks_number_per_line( + fbc, + params->source_view_width); + source_view_height = (params->source_view_height + 1) & (~0x1); + + if (lpt_alignment != 0) { + rows_per_channel = source_view_width * source_view_height * 4; + rows_per_channel = + (rows_per_channel % lpt_alignment) ? + (rows_per_channel / lpt_alignment + 1) : + rows_per_channel / lpt_alignment; + } + + set_reg_field_value( + lpt_control, + rows_per_channel, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROWS_PER_CHAN); + + dal_write_reg(fbc->context, mmLOW_POWER_TILING_CONTROL, lpt_control); +} + +static uint32_t controller_idx(struct fbc *fbc, enum controller_id id) +{ + switch (id) { + case CONTROLLER_ID_D0: + return 0; + case CONTROLLER_ID_D1: + return 1; + case CONTROLLER_ID_D2: + return 2; + default: + dal_logger_write( + fbc->context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s::%s: unexpected controller id: %d\n", + __FILE__, + __func__, + id); + return 0; + } +} + +static const struct fbc_funcs funcs = { + .power_up_fbc = dal_fbc_dce110_power_up_fbc, + .disable_fbc = disable_fbc, + .enable_fbc = dal_fbc_dce110_enable_fbc, + .is_fbc_enabled_in_hw = dal_fbc_dce110_is_fbc_enabled_in_hw, + .is_lpt_enabled_in_hw = dal_fbc_dce110_is_lpt_enabled_in_hw, + .set_fbc_invalidation_triggers = set_fbc_invalidation_triggers, + .get_required_compressed_surface_size = + dal_fbc_dce110_get_required_compressed_surface_size, + .program_compressed_surface_address_and_pitch = + dal_fbc_dce110_program_compressed_surface_address_and_pitch, + .disable_lpt = dal_fbc_dce110_disable_lpt, + .enable_lpt = dal_fbc_dce110_enable_lpt, + .program_lpt_control = dal_fbc_dce110_program_lpt_control, + .controller_idx = controller_idx, + .destroy = destroy +}; + +bool dal_fbc_dce110_construct(struct fbc *fbc, struct fbc_init_data *data) +{ + if (!dal_fbc_construct(fbc, data)) + return false; + + fbc->funcs = &funcs; + fbc->options.bits.FBC_SUPPORT = true; + if (!(dal_adapter_service_is_feature_supported( + FEATURE_DISABLE_LPT_SUPPORT))) + fbc->options.bits.LPT_SUPPORT = true; + /* For DCE 11 always use one DRAM channel for LPT */ + fbc->lpt_channels_num = 1; + + if (dal_adapter_service_is_feature_supported(FEATURE_DUMMY_FBC_BACKEND)) + fbc->options.bits.DUMMY_BACKEND = true; + + /* Check if this system has more than 1 DRAM channel; if only 1 then LPT + * should not be supported */ + if (fbc->memory_bus_width == 64) + fbc->options.bits.LPT_SUPPORT = false; + + if (dal_adapter_service_is_feature_supported( + FEATURE_DISABLE_FBC_COMP_CLK_GATE)) + fbc->options.bits.CLK_GATING_DISABLED = true; + + return true; +} + +struct fbc *dal_fbc_dce110_create(struct fbc_init_data *data) +{ + struct fbc *fbc = dal_alloc(sizeof(*fbc)); + + if (!fbc) + return NULL; + + if (dal_fbc_dce110_construct(fbc, data)) + return fbc; + + dal_free(fbc); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.h new file mode 100644 index 000000000000..0ecbe171fb18 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/fbc_dce110.h @@ -0,0 +1,72 @@ +/* + * 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_FBC_DCE110_H__ +#define __DAL_FBC_DCE110_H__ + +#include "../fbc.h" + +struct fbc *dal_fbc_dce110_create(struct fbc_init_data *data); + +bool dal_fbc_dce110_construct(struct fbc *fbc, struct fbc_init_data *data); + +void dal_fbc_dce110_power_up_fbc(struct fbc *fbc); + +void dal_fbc_dce110_enable_fbc( + struct fbc *fbc, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +bool dal_fbc_dce110_is_fbc_enabled_in_hw( + struct fbc *fbc, + enum controller_id *fbc_mapped_crtc_id); + +bool dal_fbc_dce110_is_lpt_enabled_in_hw(struct fbc *fbc); + +bool dal_fbc_dce110_get_required_compressed_surface_size( + struct fbc *fbc, + struct fbc_input_info *input_info, + struct fbc_requested_compressed_size *size); + +void dal_fbc_dce110_program_compressed_surface_address_and_pitch( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params); + +void dal_fbc_dce110_disable_lpt(struct fbc *fbc); + +void dal_fbc_dce110_enable_lpt( + struct fbc *fbc, + uint32_t paths_num, + enum controller_id cntl_id); + +void dal_fbc_dce110_program_lpt_control( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params); + +void dal_fbc_dce110_wait_for_fbc_state_changed( + struct fbc *fbc, + bool enabled); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.c new file mode 100644 index 000000000000..04f8ccce29f8 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.c @@ -0,0 +1,768 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "formatter_dce110.h" + +#define regs_for_formatter(id) \ +[CONTROLLER_ID_D ## id - 1] = {\ +[IDX_FMT_BIT_DEPTH_CONTROL] = mmFMT ## id ## _FMT_BIT_DEPTH_CONTROL,\ +[IDX_FMT_DITHER_RAND_R_SEED] = mmFMT ## id ## _FMT_DITHER_RAND_R_SEED,\ +[IDX_FMT_DITHER_RAND_G_SEED] = mmFMT ## id ## _FMT_DITHER_RAND_G_SEED,\ +[IDX_FMT_DITHER_RAND_B_SEED] = mmFMT ## id ## _FMT_DITHER_RAND_B_SEED,\ +[IDX_FMT_TEMPORAL_DITHER_PATTERN_CONTROL] =\ + mmFMT ## id ## _FMT_TEMPORAL_DITHER_PATTERN_CONTROL,\ +[IDX_FMT_TEMPORAL_DITHER_PATTERN_S_MATRIX] =\ + mmFMT ## id ## _FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX,\ +[IDX_FMT_TEMPORAL_DITHER_PATTERN_T_MATRIX] =\ + mmFMT ## id ## _FMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX,\ +[IDX_FMT_CLAMP_CNTL] = mmFMT ## id ## _FMT_CLAMP_CNTL,\ +[IDX_FMT_CONTROL] = mmFMT ## id ## _FMT_CONTROL,\ +[IDX_FMT_DYNAMIC_EXP_CNTL] = mmFMT ## id ## _FMT_DYNAMIC_EXP_CNTL,\ +[IDX_FMT_CLAMP_COMPONENT_R] = mmFMT ## id ## _FMT_CLAMP_COMPONENT_R,\ +[IDX_FMT_CLAMP_COMPONENT_G] = mmFMT ## id ## _FMT_CLAMP_COMPONENT_G,\ +[IDX_FMT_CLAMP_COMPONENT_B] = mmFMT ## id ## _FMT_CLAMP_COMPONENT_B,\ +} + +enum fmt_regs_idx { + IDX_FMT_BIT_DEPTH_CONTROL, + IDX_FMT_DITHER_RAND_R_SEED, + IDX_FMT_DITHER_RAND_G_SEED, + IDX_FMT_DITHER_RAND_B_SEED, + IDX_FMT_TEMPORAL_DITHER_PATTERN_CONTROL, + IDX_FMT_TEMPORAL_DITHER_PATTERN_S_MATRIX, + IDX_FMT_TEMPORAL_DITHER_PATTERN_T_MATRIX, + IDX_FMT_CLAMP_CNTL, + IDX_FMT_CONTROL, + IDX_FMT_DYNAMIC_EXP_CNTL, + IDX_FMT_CLAMP_COMPONENT_R, + IDX_FMT_CLAMP_COMPONENT_G, + IDX_FMT_CLAMP_COMPONENT_B, + FMT_REGS_IDX_SIZE +}; + +static const uint32_t fmt_regs[][FMT_REGS_IDX_SIZE] = { + regs_for_formatter(0), + regs_for_formatter(1), + regs_for_formatter(2) +}; + +static bool formatter_dce110_construct( + struct formatter *fmt, + struct formatter_init_data *init_data); + +struct formatter *dal_formatter_dce110_create( + struct formatter_init_data *init_data) +{ + struct formatter *fmt = dal_alloc(sizeof(struct formatter)); + + if (!fmt) + return NULL; + + if (formatter_dce110_construct(fmt, init_data)) + return fmt; + + dal_free(fmt); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void destroy(struct formatter **fmt) +{ + dal_free(*fmt); + *fmt = NULL; +} + +static void set_dyn_expansion( + struct formatter *fmt, + enum color_space color_sp, + enum color_depth color_dpth, + enum signal_type signal) +{ + uint32_t value; + bool enable_dyn_exp = false; + + value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_DYNAMIC_EXP_CNTL]); + + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + + + + /* From HW programming guide: + FMT_DYNAMIC_EXP_EN = 0 for limited RGB or YCbCr output + FMT_DYNAMIC_EXP_EN = 1 for RGB full range only*/ + if (color_sp == COLOR_SPACE_SRGB_FULL_RANGE) + enable_dyn_exp = true; + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (color_dpth) { + case COLOR_DEPTH_24: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 1, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_30: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_36: + break; + default: + break; + } + } + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_DYNAMIC_EXP_CNTL], + value); +} + +/** + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void set_truncation( + struct formatter *fmt, + const struct bit_depth_reduction_params *params) +{ + uint32_t value = 0; + + /*Disable truncation*/ + value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL]); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], value); + + /* no 10bpc trunc on DCE11*/ + if (params->flags.TRUNCATE_ENABLED == 0 || + params->flags.TRUNCATE_DEPTH == 2) + return; + + /*Set truncation depth and Enable truncation*/ + set_reg_field_value(value, 1, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, params->flags.TRUNCATE_MODE, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + set_reg_field_value(value, params->flags.TRUNCATE_DEPTH, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], value); + +} + +/** + * set_spatial_dither + * 1) set spatial dithering mode: pattern of seed + * 2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp + * 3) set random seed + * 4) set random mode + * lfsr is reset every frame or not reset + * RGB dithering method + * 0: RGB data are all dithered with x^28+x^3+1 + * 1: R data is dithered with x^28+x^3+1 + * G data is dithered with x^28+X^9+1 + * B data is dithered with x^28+x^13+1 + * enable high pass filter or not + * 5) enable spatical dithering + */ +static void set_spatial_dither( + struct formatter *fmt, + const struct bit_depth_reduction_params *params) +{ + uint32_t depth_cntl_value = 0; + uint32_t fmt_cntl_value = 0; + uint32_t dither_r_value = 0; + uint32_t dither_g_value = 0; + uint32_t dither_b_value = 0; + + /*Disable spatial (random) dithering*/ + depth_cntl_value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL]); + + fmt_cntl_value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_CONTROL]); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], depth_cntl_value); + + /* no 10bpc on DCE11*/ + if (params->flags.SPATIAL_DITHER_ENABLED == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 2) + return; + + /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ + if (params->flags.FRAME_RANDOM == 1) { + if (params->flags.SPATIAL_DITHER_DEPTH == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 1) { + set_reg_field_value(fmt_cntl_value, 15, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 2, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { + set_reg_field_value(fmt_cntl_value, 3, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 1, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } else + return; + } else { + set_reg_field_value(fmt_cntl_value, 0, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 0, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } + + dal_write_reg(fmt->ctx, fmt->regs[IDX_FMT_CONTROL], + fmt_cntl_value); + + /*Set seed for random values for + * spatial dithering for R,G,B channels*/ + set_reg_field_value(dither_r_value, params->r_seed_value, + FMT_DITHER_RAND_R_SEED, + FMT_RAND_R_SEED); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_DITHER_RAND_R_SEED], + dither_r_value); + + set_reg_field_value(dither_g_value, + params->g_seed_value, + FMT_DITHER_RAND_G_SEED, + FMT_RAND_G_SEED); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_DITHER_RAND_G_SEED], + dither_g_value); + + set_reg_field_value(dither_b_value, params->b_seed_value, + FMT_DITHER_RAND_B_SEED, + FMT_RAND_B_SEED); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_DITHER_RAND_B_SEED], + dither_b_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + /*Set spatial dithering bit depth*/ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_DEPTH); + + /* Set spatial dithering mode + * (default is Seed patterrn AAAA...) + */ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_MODE, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_MODE); + + /*Reset only at startup*/ + set_reg_field_value(depth_cntl_value, + params->flags.FRAME_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_RGB_RANDOM_ENABLE); + + /*Set RGB data dithered with x^28+x^3+1*/ + set_reg_field_value(depth_cntl_value, + params->flags.RGB_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_RGB_RANDOM_ENABLE); + + /*Disable High pass filter*/ + set_reg_field_value(depth_cntl_value, + params->flags.HIGHPASS_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE); + + /*Enable spatial dithering*/ + set_reg_field_value(depth_cntl_value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], + depth_cntl_value); + +} + +/** + * SetTemporalDither (Frame Modulation) + * 1) set temporal dither depth + * 2) select pattern: from hard-coded pattern or programmable pattern + * 3) select optimized strips for BGR or RGB LCD sub-pixel + * 4) set s matrix + * 5) set t matrix + * 6) set grey level for 0.25, 0.5, 0.75 + * 7) enable temporal dithering + */ +static void set_temporal_dither( + struct formatter *fmt, + const struct bit_depth_reduction_params *params) +{ + uint32_t value; + + /*Disable temporal (frame modulation) dithering first*/ + value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL]); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], value); + + /* no 10bpc dither on DCE11*/ + if (params->flags.FRAME_MODULATION_ENABLED == 0 || + params->flags.FRAME_MODULATION_DEPTH == 2) + return; + + /* Set temporal dithering depth*/ + set_reg_field_value(value, + params->flags.FRAME_MODULATION_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + + /*Select legacy pattern based on FRC and Temporal level*/ + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_TEMPORAL_DITHER_PATTERN_CONTROL], 0); + /*Set s matrix*/ + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_TEMPORAL_DITHER_PATTERN_S_MATRIX], 0); + /*Set t matrix*/ + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_TEMPORAL_DITHER_PATTERN_T_MATRIX], 0); + + /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ + set_reg_field_value(value, + params->flags.TEMPORAL_LEVEL, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + + set_reg_field_value(value, + params->flags.FRC25, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC50, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC75, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + /*Enable bit reduction by temporal (frame modulation) dithering*/ + set_reg_field_value(value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_BIT_DEPTH_CONTROL], + value); + +} + +/** + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +static void set_clamping( + struct formatter *fmt, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t clamp_cntl_value = 0; + uint32_t red_clamp_value = 0; + uint32_t green_clamp_value = 0; + uint32_t blue_clamp_value = 0; + + clamp_cntl_value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_CLAMP_CNTL]); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + + case CLAMPING_LIMITED_RANGE_8BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + + case CLAMPING_LIMITED_RANGE_10BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 2, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_12BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 3, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 7, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + /*set the defaults*/ + set_reg_field_value(red_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_LOWER_R); + + set_reg_field_value(red_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_UPPER_R); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_CLAMP_COMPONENT_R], + red_clamp_value); + + set_reg_field_value(green_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_LOWER_G); + + set_reg_field_value(green_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_UPPER_G); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_CLAMP_COMPONENT_G], + green_clamp_value); + + set_reg_field_value(blue_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_LOWER_B); + + set_reg_field_value(blue_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_UPPER_B); + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_CLAMP_COMPONENT_B], + blue_clamp_value); + + break; + + default: + break; + } + + /*Set clamp control*/ + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_CLAMP_CNTL], + clamp_cntl_value); + +} + +/** + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ + +static void set_pixel_encoding( + struct formatter *fmt, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t fmt_cntl_value; + + /*RGB 4:4:4 or YCbCr 4:4:4 - 0; YCbCr 4:2:2 -1.*/ + fmt_cntl_value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_CONTROL]); + + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + if (params->pixel_encoding == CNTL_PIXEL_ENCODING_YCBCR422) { + set_reg_field_value(fmt_cntl_value, + 1, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + /*00 - Pixels drop mode ,01 - Pixels average mode*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_MODE); + + /*00 - Cb before Cr ,01 - Cr before Cb*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_ORDER); + } + dal_write_reg(fmt->ctx, fmt->regs[IDX_FMT_CONTROL], fmt_cntl_value); + +} + +static void program_bit_depth_reduction( + struct formatter *fmt, + const struct bit_depth_reduction_params *params) +{ + set_truncation(fmt, params); + set_spatial_dither(fmt, params); + set_temporal_dither(fmt, params); +} + +static void program_clamping_and_pixel_encoding( + struct formatter *fmt, + const struct clamping_and_pixel_encoding_params *params) +{ + set_clamping(fmt, params); + set_pixel_encoding(fmt, params); +} + +/** + * setup_stereo_polarity: Programs fields relating + * to the FMT Block + * This function is only called by + * DisplayController::EnableStereo + */ +static void setup_stereo_polarity( + struct formatter *fmt, + enum fmt_stereo_action action, + bool right_eye_polarity) +{ + uint32_t fmt_cntl_value; + + fmt_cntl_value = dal_read_reg(fmt->ctx, + fmt->regs[IDX_FMT_CONTROL]); + + switch (action) { + case FMT_STEREO_ACTION_ENABLE: + set_reg_field_value(fmt_cntl_value, + 1, + FMT_CONTROL, + FMT_STEREOSYNC_OVERRIDE); + + set_reg_field_value(fmt_cntl_value, + right_eye_polarity ? 1:0, + FMT_CONTROL, + FMT_STEREOSYNC_OVR_POL); + break; + + case FMT_STEREO_ACTION_DISABLE: + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_STEREOSYNC_OVERRIDE); + break; + + case FMT_STEREO_ACTION_UPDATE_POLARITY: + set_reg_field_value(fmt_cntl_value, + right_eye_polarity ? 1:0, + FMT_CONTROL, + FMT_STEREOSYNC_OVR_POL); + break; + + default: + break; + } + + dal_write_reg(fmt->ctx, + fmt->regs[IDX_FMT_CONTROL], + fmt_cntl_value); + +} + +static const struct formatter_funcs formatter_dce110_funcs = { + .set_dyn_expansion = + set_dyn_expansion, + .program_bit_depth_reduction = + program_bit_depth_reduction, + .program_clamping_and_pixel_encoding = + program_clamping_and_pixel_encoding, + .setup_stereo_polarity = setup_stereo_polarity, + .destroy = destroy, +}; + +static bool formatter_dce110_construct( + struct formatter *fmt, + struct formatter_init_data *init_data) +{ + if (!dal_formatter_construct(fmt, init_data)) + return false; + + fmt->regs = fmt_regs[init_data->id - 1]; + fmt->funcs = &formatter_dce110_funcs; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.h new file mode 100644 index 000000000000..50e9d44ea244 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/formatter_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_FORMATTER_DCE110_H__ +#define __DAL_FORMATTER_DCE110_H__ + +#include "../formatter.h" + +struct formatter *dal_formatter_dce110_create( + struct formatter_init_data *init_data); + +#endif /*__DAL_FORMATTER_DCE110_H__*/ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.c new file mode 100644 index 000000000000..4e32adb24915 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.c @@ -0,0 +1,1628 @@ +/* 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/fixed31_32.h" +#include "include/fixed32_32.h" +#include "include/adapter_service_interface.h" +#include "include/logger_interface.h" + +#include "grph_gamma_dce110.h" + +#include "../lut_and_gamma_types.h" +#include "../grph_gamma.h" + +enum gg_regs_idx { + IDX_INPUT_GAMMA_CONTROL, + IDX_DEGAMMA_CONTROL, + IDX_REGAMMA_CONTROL, + + IDX_REGAMMA_CNTLA_START, + IDX_REGAMMA_CNTLA_SLOPE, + IDX_REGAMMA_CNTLA_END1, + IDX_REGAMMA_CNTLA_END2, + + IDX_REGAMMA_CNTLA_REGION_0_1, + IDX_REGAMMA_CNTLA_REGION_2_3, + IDX_REGAMMA_CNTLA_REGION_4_5, + IDX_REGAMMA_CNTLA_REGION_6_7, + + IDX_REGAMMA_CNTLA_REGION_8_9, + IDX_REGAMMA_CNTLA_REGION_10_11, + IDX_REGAMMA_CNTLA_REGION_12_13, + IDX_REGAMMA_CNTLA_REGION_14_15, + + IDX_REGAMMA_LUT_DATA, + IDX_REGAMMA_LUT_INDEX, + IDX_REGAMMA_LUT_WRITE_EN_MASK, + + IDX_GRPH_CONTROL, + + IDX_DCFE_MEM_PWR_CTRL, + IDX_DCFE_MEM_PWR_STATUS, + + GG_REGS_IDX_SIZE +}; + +#define regs_for_graphics_gamma(id)\ + [CONTROLLER_ID_D ## id - 1] = {\ +[IDX_DEGAMMA_CONTROL] = mmDCP ## id ## _DEGAMMA_CONTROL,\ +[IDX_REGAMMA_CONTROL] = mmDCP ## id ## _REGAMMA_CONTROL,\ +\ +[IDX_REGAMMA_CNTLA_START] = mmDCP ## id ## _REGAMMA_CNTLA_START_CNTL,\ +[IDX_REGAMMA_CNTLA_SLOPE] = mmDCP ## id ## _REGAMMA_CNTLA_SLOPE_CNTL,\ +[IDX_REGAMMA_CNTLA_END1] = mmDCP ## id ## _REGAMMA_CNTLA_END_CNTL1,\ +[IDX_REGAMMA_CNTLA_END2] = mmDCP ## id ## _REGAMMA_CNTLA_END_CNTL2,\ +\ +[IDX_REGAMMA_CNTLA_REGION_0_1] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_0_1,\ +[IDX_REGAMMA_CNTLA_REGION_2_3] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_2_3,\ +[IDX_REGAMMA_CNTLA_REGION_4_5] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_4_5,\ +[IDX_REGAMMA_CNTLA_REGION_6_7] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_6_7,\ +\ +[IDX_REGAMMA_CNTLA_REGION_8_9] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_8_9,\ +[IDX_REGAMMA_CNTLA_REGION_10_11] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_10_11,\ +[IDX_REGAMMA_CNTLA_REGION_12_13] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_12_13,\ +[IDX_REGAMMA_CNTLA_REGION_14_15] = mmDCP ## id ## _REGAMMA_CNTLA_REGION_14_15,\ +\ +[IDX_REGAMMA_LUT_DATA] = mmDCP ## id ## _REGAMMA_LUT_DATA,\ +[IDX_REGAMMA_LUT_INDEX] = mmDCP ## id ## _REGAMMA_LUT_INDEX,\ +[IDX_REGAMMA_LUT_WRITE_EN_MASK] = mmDCP ## id ## _REGAMMA_LUT_WRITE_EN_MASK,\ +\ +[IDX_GRPH_CONTROL] = mmDCP ## id ## _GRPH_CONTROL,\ +[IDX_DCFE_MEM_PWR_CTRL] = mmDCFE ## id ## _DCFE_MEM_PWR_CTRL,\ +[IDX_DCFE_MEM_PWR_STATUS] = mmDCFE ## id ## _DCFE_MEM_PWR_STATUS\ +} + +static const uint32_t gg_regs[][GG_REGS_IDX_SIZE] = { + regs_for_graphics_gamma(0), + regs_for_graphics_gamma(1), + regs_for_graphics_gamma(2) +}; + +enum dce110_gg_legacy_regs_idx { + IDX_LUT_30_COLOR, + IDX_LUT_SEQ_COLOR, + IDX_LUT_WRITE_EN_MASK, + IDX_LUT_RW_MODE, + IDX_LUT_RW_INDEX, + IDX_LUT_PWL_DATA, + + IDX_GRPH_LUT_10BIT_BYPASS, + + IDX_PRESCALE_VALUES_GRPH_R, + IDX_PRESCALE_VALUES_GRPH_G, + IDX_PRESCALE_VALUES_GRPH_B, + IDX_PRESCALE_GRPH_CONTROL, + + IDX_LUT_CONTROL, + + IDX_LUT_WHITE_OFFSET_RED, + IDX_LUT_WHITE_OFFSET_GREEN, + IDX_LUT_WHITE_OFFSET_BLUE, + + IDX_LUT_BLACK_OFFSET_RED, + IDX_LUT_BLACK_OFFSET_GREEN, + IDX_LUT_BLACK_OFFSET_BLUE, + + GG_dce110_LEGACY_REGS_IDX_SIZE +}; + +#define dce110_legacy_regs_for_graphics_gamma(id)\ + [CONTROLLER_ID_D ## id - 1] = {\ +[IDX_LUT_30_COLOR] = mmDCP ## id ## _DC_LUT_30_COLOR,\ +[IDX_LUT_SEQ_COLOR] = mmDCP ## id ## _DC_LUT_SEQ_COLOR,\ +[IDX_LUT_WRITE_EN_MASK] = mmDCP ## id ## _DC_LUT_WRITE_EN_MASK,\ +[IDX_LUT_RW_MODE] = mmDCP ## id ## _DC_LUT_RW_MODE,\ +[IDX_LUT_RW_INDEX] = mmDCP ## id ## _DC_LUT_RW_INDEX,\ +[IDX_LUT_PWL_DATA] = mmDCP ## id ## _DC_LUT_PWL_DATA,\ +\ +[IDX_GRPH_LUT_10BIT_BYPASS] = mmDCP ## id ## _GRPH_LUT_10BIT_BYPASS,\ +\ +[IDX_PRESCALE_VALUES_GRPH_R] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_R,\ +[IDX_PRESCALE_VALUES_GRPH_G] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_G,\ +[IDX_PRESCALE_VALUES_GRPH_B] = mmDCP ## id ## _PRESCALE_VALUES_GRPH_B,\ +[IDX_PRESCALE_GRPH_CONTROL] = mmDCP ## id ## _PRESCALE_GRPH_CONTROL,\ +\ +[IDX_LUT_CONTROL] = mmDCP ## id ## _DC_LUT_CONTROL,\ +\ +[IDX_LUT_WHITE_OFFSET_RED] = mmDCP ## id ## _DC_LUT_WHITE_OFFSET_RED,\ +[IDX_LUT_WHITE_OFFSET_GREEN] = mmDCP ## id ## _DC_LUT_WHITE_OFFSET_GREEN,\ +[IDX_LUT_WHITE_OFFSET_BLUE] = mmDCP ## id ## _DC_LUT_WHITE_OFFSET_BLUE,\ +\ +[IDX_LUT_BLACK_OFFSET_RED] = mmDCP ## id ## _DC_LUT_BLACK_OFFSET_RED,\ +[IDX_LUT_BLACK_OFFSET_GREEN] = mmDCP ## id ## _DC_LUT_BLACK_OFFSET_GREEN,\ +[IDX_LUT_BLACK_OFFSET_BLUE] = mmDCP ## id ## _DC_LUT_BLACK_OFFSET_BLUE,\ +} + +static const uint32_t +dce110_gg_legacy_regs[][GG_dce110_LEGACY_REGS_IDX_SIZE] = { + dce110_legacy_regs_for_graphics_gamma(0), + dce110_legacy_regs_for_graphics_gamma(1), + dce110_legacy_regs_for_graphics_gamma(2) +}; + +#define FROM_GRAPH_GAMMA(ptr)\ + (container_of((ptr), struct grph_gamma_dce110, base)) + +/* + * set_legacy_mode + * uses HW in DCE40 manner + */ +static void set_legacy_mode(struct grph_gamma *gg, bool is_legacy) +{ + const uint32_t addr = gg->regs[IDX_INPUT_GAMMA_CONTROL]; + uint32_t value = dal_read_reg(gg->ctx, addr); + + set_reg_field_value( + value, + is_legacy, + INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE); + + dal_write_reg(gg->ctx, addr, value); +} + +static bool setup_distribution_points( + struct grph_gamma *gg) +{ + uint32_t hw_points_num = MAX_PWL_ENTRY * 2; + + struct curve_config cfg; + + cfg.offset = 0; + + cfg.segments[0] = 3; + cfg.segments[1] = 4; + cfg.segments[2] = 4; + cfg.segments[3] = 4; + cfg.segments[4] = 4; + cfg.segments[5] = 4; + cfg.segments[6] = 4; + cfg.segments[7] = 4; + cfg.segments[8] = 5; + cfg.segments[9] = 5; + cfg.segments[10] = 0; + cfg.segments[11] = -1; + cfg.segments[12] = -1; + cfg.segments[13] = -1; + cfg.segments[14] = -1; + cfg.segments[15] = -1; + + cfg.begin = -10; + + if (!dal_grph_gamma_build_hw_curve_configuration( + &cfg, gg->arr_curve_points, gg->arr_points, + gg->coordinates_x, &hw_points_num)) { + ASSERT_CRITICAL(false); + return false; + } + + gg->hw_points_num = hw_points_num; + + return true; +} + +static void program_black_offsets( + struct grph_gamma *gg, + struct dev_c_lut16 *offset) +{ + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_BLACK_OFFSET_RED], offset->red); + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_BLACK_OFFSET_GREEN], offset->green); + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_BLACK_OFFSET_BLUE], offset->blue); +} + +static void program_white_offsets( + struct grph_gamma *gg, + struct dev_c_lut16 *offset) +{ + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_WHITE_OFFSET_RED], offset->red); + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_WHITE_OFFSET_GREEN], offset->green); + dal_write_reg(gg->ctx, + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_WHITE_OFFSET_BLUE], offset->blue); +} + +/* + ***************************************************************************** + * Function: configure_degamma_mode + * + *program regamma block, using ROM_X, predefined coeff. + *if FP16 - by pass + * + *@param [in ] parameters interface parameters + *@return void + * + *@note + *@see + * + ***************************************************************************** + */ +static void configure_degamma_mode( + struct grph_gamma *gg, + const struct gamma_parameters *parameters, + bool force_bypass) +{ + uint32_t value; + const uint32_t addr = gg->regs[IDX_DEGAMMA_CONTROL]; + + /* 1 -RGB 2.4 + * 2 -YCbCr 2.22 */ + + uint32_t degamma_type = + parameters->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB == 1 ? + 1 : 2; + + value = dal_read_reg(gg->ctx, addr); + + /* if by pass - no degamma + * when legacy and regamma LUT's we do degamma */ + if (parameters->degamma_adjust_type == GRAPHICS_DEGAMMA_ADJUST_BYPASS || + (parameters->surface_pixel_format == PIXEL_FORMAT_FP16 && + parameters->selected_gamma_lut == + GRAPHICS_GAMMA_LUT_REGAMMA)) + degamma_type = 0; + + if (force_bypass) + degamma_type = 0; + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + GRPH_DEGAMMA_MODE); + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + CURSOR_DEGAMMA_MODE); + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + CURSOR2_DEGAMMA_MODE); + + dal_write_reg(gg->ctx, addr, value); +} + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct grph_gamma *gg) +{ + struct gamma_curve *curve; + uint32_t value = 0; + + { + set_reg_field_value( + value, + gg->arr_points[0].custom_float_x, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_START], value); + } + { + value = 0; + set_reg_field_value( + value, + gg->arr_points[0].custom_float_slope, + REGAMMA_CNTLA_SLOPE_CNTL, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_SLOPE], value); + } + { + value = 0; + set_reg_field_value( + value, + gg->arr_points[1].custom_float_x, + REGAMMA_CNTLA_END_CNTL1, + REGAMMA_CNTLA_EXP_REGION_END); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_END1], value); + } + { + value = 0; + set_reg_field_value( + value, + gg->arr_points[2].custom_float_slope, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + gg->arr_points[1].custom_float_y, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_END2], value); + } + + curve = gg->arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dal_write_reg( + gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_0_1], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_2_3], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_4_5], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_6_7], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_8_9], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_10_11], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_12_13], + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_CNTLA_REGION_14_15], + value); + } +} + +static void configure_regamma_mode( + struct grph_gamma *gg, + const struct gamma_parameters *params, + bool force_bypass) +{ + const uint32_t addr = gg->regs[IDX_REGAMMA_CONTROL]; + + enum wide_gamut_regamma_mode mode = + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A; + + uint32_t value = dal_read_reg(gg->ctx, addr); + + if (force_bypass) { + + set_reg_field_value( + value, + 0, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + + dal_write_reg(gg->ctx, addr, value); + + return; + } + + if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_BYPASS) + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; + else if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_HW) { + if (params->surface_pixel_format == PIXEL_FORMAT_FP16) + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; + else + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24; + } + + switch (mode) { + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS: + set_reg_field_value( + value, + 0, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24: + set_reg_field_value( + value, + 1, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22: + set_reg_field_value( + value, + 2, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A: + set_reg_field_value( + value, + 3, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B: + set_reg_field_value( + value, + 4, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + default: + break; + } + + dal_write_reg(gg->ctx, addr, value); +} + +static void program_pwl( + struct grph_gamma *gg, + const struct gamma_parameters *params) +{ + uint32_t value; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + value = dal_read_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL]); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL], value); + + while (counter < max_tries) { + value = + dal_read_reg( + gg->ctx, + gg->regs[IDX_DCFE_MEM_PWR_STATUS]); + + if (get_reg_field_value( + value, + DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE) == 0) + break; + + ++counter; + } + + if (counter == max_tries) { + dal_logger_write(gg->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: regamma lut was not powered on in a timely manner, programming still proceeds\n", + __func__); + } + } + + value = 0; + + set_reg_field_value( + value, + 7, + REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK); + + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_LUT_WRITE_EN_MASK], value); + dal_write_reg(gg->ctx, + gg->regs[IDX_REGAMMA_LUT_INDEX], 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = gg->regs[IDX_REGAMMA_LUT_DATA]; + + uint32_t i = 0; + + struct pwl_result_data *rgb = gg->rgb_resulted; + + while (i != gg->hw_points_num) { + dal_write_reg(gg->ctx, addr, rgb->red_reg); + dal_write_reg(gg->ctx, addr, rgb->green_reg); + dal_write_reg(gg->ctx, addr, rgb->blue_reg); + + dal_write_reg(gg->ctx, addr, rgb->delta_red_reg); + dal_write_reg(gg->ctx, addr, rgb->delta_green_reg); + dal_write_reg(gg->ctx, addr, rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + value = dal_read_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL]); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL], value); +} + + +/* + ***************************************************************************** + * Function: set_gamma_ramp + * + * main interface method which operates the helper functions + * calculates and programs hardware blocks : degamma and regamma. + * + * @param [in ] gamma_ramp given gamma + * @param [in ] params given describe the gamma + * + * @return true if success + * + * @note + * + * @see + * + ***************************************************************************** + */ +static bool set_gamma_ramp( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + bool use_palette; + + { + /*Power on LUT memory*/ + uint32_t value = + dal_read_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL]); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_LUT_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL], value); + } + + /* we can go to new DCP or legacy Old DCP */ + if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8 || + params->selected_gamma_lut == GRAPHICS_GAMMA_LUT_LEGACY) { + /* do legacy DCP for 256 colors if we are requested to do so */ + gg->funcs->set_gamma_ramp_legacy( + gg, gamma_ramp, params); + + /* set bypass */ + gg->funcs->program_prescale_legacy( + gg, PIXEL_FORMAT_UNINITIALIZED); + + gg->funcs->set_legacy_mode(gg, true); + + configure_degamma_mode(gg, params, true); + + configure_regamma_mode(gg, params, true); + + return true; + } else if (params->selected_gamma_lut == GRAPHICS_GAMMA_LUT_REGAMMA) { + gg->funcs->program_prescale_legacy( + gg, params->surface_pixel_format); + + gg->funcs->set_legacy_mode(gg, false); + } + + use_palette = params->surface_pixel_format == PIXEL_FORMAT_INDEX8; + + /* 1. Scale gamma to 0 - 1 to m_pRgbUser */ + if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) + dal_grph_gamma_scale_rgb256x3x16(gg, use_palette, + &gamma_ramp->gamma_ramp_rgb256x3x16); + else + dal_grph_gamma_scale_dx(gg, params->surface_pixel_format, + gamma_ramp->gamma_ramp_dxgi1.gamma_curve); + + /* + * 2. Do degamma step : remove the given gamma value from FB. + * For FP16 or no degamma do by pass + */ + configure_degamma_mode(gg, params, false); + + /* 3. Configure regamma curve without analysis (future task) */ + /* and program the PWL regions and segments */ + if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_SW || + params->surface_pixel_format == PIXEL_FORMAT_FP16) { + + /* 4. Setup x exponentially distributed points */ + if (!gg->funcs->setup_distribution_points(gg)) { + ASSERT_CRITICAL(false); + /* invalid option */ + return false; + } + + /* 5. Build ideal regamma curve */ + if (!dal_grph_gamma_build_regamma_curve(gg, params)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 6. Map user gamma (evenly distributed x points) to new curve + * when x is y from ideal regamma , step 5 */ + if (!dal_grph_gamma_map_regamma_hw_to_x_user( + gg, gamma_ramp, params)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + /* 7.Build and verify resulted curve */ + dal_grph_gamma_build_new_custom_resulted_curve(gg, params); + + /* 8. Build and translate x to hw format */ + if (!dal_grph_gamma_rebuild_curve_configuration_magic(gg)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 9 convert all parameters to the custom float format */ + if (!dal_grph_gamma_convert_to_custom_float(gg)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 10 program regamma curve configuration */ + regamma_config_regions_and_segments(gg); + + /* 11. Programm PWL */ + program_pwl(gg, params); + } + + /* + * 12. program regamma config + */ + configure_regamma_mode(gg, params, false); + + { + /*re-enable low power mode for LUT memory*/ + uint32_t value = dal_read_reg(gg->ctx, mmDCFE_MEM_PWR_CTRL); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_LUT_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, mmDCFE_MEM_PWR_CTRL, value); + } + + return true; +} + +static void program_black_white_offset( + struct grph_gamma_dce110 *gg, + enum pixel_format surface_pixel_format) +{ + struct dev_c_lut16 black_offset; + struct dev_c_lut16 white_offset; + + /* get black offset */ + + switch (surface_pixel_format) { + case PIXEL_FORMAT_FP16: + /* sRGB gamut, [0.0...1.0] */ + black_offset.red = 0; + black_offset.green = 0; + black_offset.blue = 0; + break; + + case PIXEL_FORMAT_ARGB2101010_XRBIAS: + /* [-1.0...3.0] */ + black_offset.red = 0x100; + black_offset.green = 0x100; + black_offset.blue = 0x100; + break; + + default: + black_offset.red = 0; + black_offset.green = 0; + black_offset.blue = 0; + } + + /* get white offset */ + + switch (surface_pixel_format) { + case PIXEL_FORMAT_FP16: + white_offset.red = 0x3BFF; + white_offset.green = 0x3BFF; + white_offset.blue = 0x3BFF; + break; + + case PIXEL_FORMAT_ARGB2101010_XRBIAS: + white_offset.red = 0x37E; + white_offset.green = 0x37E; + white_offset.blue = 0x37E; + break; + + case PIXEL_FORMAT_ARGB8888: + white_offset.red = 0xFF; + white_offset.green = 0xFF; + white_offset.blue = 0xFF; + break; + + default: + white_offset.red = 0x3FF; + white_offset.green = 0x3FF; + white_offset.blue = 0x3FF; + } + + gg->base.funcs->program_black_offsets(&gg->base, &black_offset); + gg->base.funcs->program_white_offsets(&gg->base, &white_offset); +} + +static void set_lut_inc( + struct grph_gamma *gg, + uint8_t inc, + bool is_float, + bool is_signed) +{ + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)->legacy_regs[IDX_LUT_CONTROL]; + + uint32_t value = dal_read_reg(gg->ctx, addr); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_R); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_G); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_B); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_R_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_G_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_B_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_R_SIGNED_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_G_SIGNED_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_B_SIGNED_EN); + + dal_write_reg(gg->ctx, addr, value); +} + +static void select_lut( + struct grph_gamma *gg) +{ + uint32_t value = 0; + + set_lut_inc(gg, 0, false, false); + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_WRITE_EN_MASK]; + + value = dal_read_reg(gg->ctx, addr); + + /* enable all */ + set_reg_field_value( + value, + 0x7, + DC_LUT_WRITE_EN_MASK, + DC_LUT_WRITE_EN_MASK); + + dal_write_reg(gg->ctx, addr, value); + } + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_RW_MODE]; + + value = dal_read_reg(gg->ctx, addr); + + set_reg_field_value( + value, + 0, + DC_LUT_RW_MODE, + DC_LUT_RW_MODE); + + dal_write_reg(gg->ctx, addr, value); + } + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_CONTROL]; + + value = dal_read_reg(gg->ctx, addr); + + /* 00 - new u0.12 */ + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_R_FORMAT); + + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_G_FORMAT); + + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_B_FORMAT); + + dal_write_reg(gg->ctx, addr, value); + } + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_RW_INDEX]; + + value = dal_read_reg(gg->ctx, addr); + + set_reg_field_value( + value, + 0, + DC_LUT_RW_INDEX, + DC_LUT_RW_INDEX); + + dal_write_reg(gg->ctx, addr, value); + } +} + +static void program_lut_gamma( + struct grph_gamma *gg, + const struct dev_c_lut16 *gamma, + const struct gamma_parameters *params) +{ + uint32_t i = 0; + uint32_t value = 0; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + value = dal_read_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL]); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL], value); + + while (counter < max_tries) { + value = + dal_read_reg( + gg->ctx, + gg->regs[IDX_DCFE_MEM_PWR_STATUS]); + + if (get_reg_field_value( + value, + DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE) == 0) + break; + + ++counter; + } + + if (counter == max_tries) { + dal_logger_write(gg->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: regamma lut was not powered on in a timely manner, programming still proceeds\n", + __func__); + } + } + + program_black_white_offset( + FROM_GRAPH_GAMMA(gg), params->surface_pixel_format); + + select_lut(gg); + + if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8) { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_SEQ_COLOR]; + + do { + struct dev_c_lut *index = gg->saved_palette + i; + + set_reg_field_value( + value, + gamma[index->red].red, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + + set_reg_field_value( + value, + gamma[index->green].green, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + + set_reg_field_value( + value, + gamma[index->blue].blue, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + ++i; + } while (i != RGB_256X3X16); + } else { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_LUT_SEQ_COLOR]; + + do { + set_reg_field_value( + value, + gamma[i].red, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + + set_reg_field_value( + value, + gamma[i].green, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + + set_reg_field_value( + value, + gamma[i].blue, + DC_LUT_SEQ_COLOR, + DC_LUT_SEQ_COLOR); + dal_write_reg(gg->ctx, addr, value); + + ++i; + } while (i != RGB_256X3X16); + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + value = dal_read_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL]); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dal_write_reg(gg->ctx, gg->regs[IDX_DCFE_MEM_PWR_CTRL], value); +} + +static bool set_gamma_ramp_legacy_rgb256x3x16( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + struct dev_c_lut16 *gamma16 = + dal_alloc(sizeof(struct dev_c_lut16) * MAX_LUT_ENTRY); + + if (!gamma16) + return false; + + dal_grph_gamma_convert_256_lut_entries_to_gxo_format( + &gamma_ramp->gamma_ramp_rgb256x3x16, gamma16); + + if ((params->surface_pixel_format != PIXEL_FORMAT_ARGB2101010) && + (params->surface_pixel_format != + PIXEL_FORMAT_ARGB2101010_XRBIAS) && + (params->surface_pixel_format != PIXEL_FORMAT_FP16)) { + gg->funcs->program_lut_gamma(gg, gamma16, params); + dal_free(gamma16); + return true; + } + + /* TODO process DirectX-specific formats*/ + dal_free(gamma16); + return false; +} + +static bool set_gamma_ramp_legacy_dxgi1( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + struct dev_c_lut16 *gamma16 = + dal_alloc(sizeof(struct dev_c_lut16) * MAX_LUT_ENTRY); + + if (!gamma16) + return false; + + if ((params->surface_pixel_format != PIXEL_FORMAT_ARGB2101010) && + (params->surface_pixel_format != + PIXEL_FORMAT_ARGB2101010_XRBIAS) && + (params->surface_pixel_format != PIXEL_FORMAT_FP16)) { + dal_grph_gamma_convert_udx_gamma_entries_to_gxo_format( + &gamma_ramp->gamma_ramp_dxgi1, gamma16); + gg->funcs->program_lut_gamma(gg, gamma16, params); + dal_free(gamma16); + return true; + } + + /* TODO process DirectX-specific formats*/ + dal_free(gamma16); + return false; +} + +static bool set_gamma_ramp_legacy( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + switch (gamma_ramp->type) { + case GAMMA_RAMP_RBG256X3X16: + return set_gamma_ramp_legacy_rgb256x3x16( + gg, gamma_ramp, params); + case GAMMA_RAMP_DXGI_1: + return set_gamma_ramp_legacy_dxgi1( + gg, gamma_ramp, params); + default: + ASSERT_CRITICAL(false); + return false; + } +} + +static void program_prescale_legacy( + struct grph_gamma *gg, + enum pixel_format pixel_format) +{ + uint32_t prescale_control; + uint32_t prescale_values_grph_r = 0; + uint32_t prescale_values_grph_g = 0; + uint32_t prescale_values_grph_b = 0; + + uint32_t prescale_num; + uint32_t prescale_denom = 1; + uint16_t prescale_hw; + uint32_t bias_num = 0; + uint32_t bias_denom = 1; + uint16_t bias_hw; + + const uint32_t addr_control = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_PRESCALE_GRPH_CONTROL]; + + prescale_control = dal_read_reg(gg->ctx, addr_control); + + set_reg_field_value( + prescale_control, + 0, + PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS); + + switch (pixel_format) { + case PIXEL_FORMAT_RGB565: + prescale_num = 64; + prescale_denom = 63; + break; + + case PIXEL_FORMAT_ARGB8888: + /* This function should only be called when using regamma + * and bypassing legacy INPUT GAMMA LUT (function name is + * misleading) + */ + prescale_num = 256; + prescale_denom = 255; + break; + + case PIXEL_FORMAT_ARGB2101010: + prescale_num = 1024; + prescale_denom = 1023; + break; + + case PIXEL_FORMAT_ARGB2101010_XRBIAS: + prescale_num = 1024; + prescale_denom = 510; + bias_num = 384; + bias_denom = 1024; + break; + + case PIXEL_FORMAT_FP16: + prescale_num = 1; + break; + + default: + prescale_num = 1; + + set_reg_field_value( + prescale_control, + 1, + PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS); + } + + prescale_hw = dal_controller_float_to_hw_setting( + dal_fixed31_32_from_fraction(prescale_num, prescale_denom), + 2, 13); + + bias_hw = dal_controller_float_to_hw_setting( + dal_fixed31_32_from_fraction(bias_num, bias_denom), + 2, 13); + + + set_reg_field_value( + prescale_values_grph_r, + prescale_hw, + PRESCALE_VALUES_GRPH_R, + GRPH_PRESCALE_SCALE_R); + + set_reg_field_value( + prescale_values_grph_r, + bias_hw, + PRESCALE_VALUES_GRPH_R, + GRPH_PRESCALE_BIAS_R); + + + set_reg_field_value( + prescale_values_grph_g, + prescale_hw, + PRESCALE_VALUES_GRPH_G, + GRPH_PRESCALE_SCALE_G); + + set_reg_field_value( + prescale_values_grph_g, + bias_hw, + PRESCALE_VALUES_GRPH_G, + GRPH_PRESCALE_BIAS_G); + + + set_reg_field_value( + prescale_values_grph_b, + prescale_hw, + PRESCALE_VALUES_GRPH_B, + GRPH_PRESCALE_SCALE_B); + + set_reg_field_value( + prescale_values_grph_b, + bias_hw, + PRESCALE_VALUES_GRPH_B, + GRPH_PRESCALE_BIAS_B); + + dal_write_reg(gg->ctx, + addr_control, prescale_control); + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_PRESCALE_VALUES_GRPH_R]; + dal_write_reg(gg->ctx, + addr, prescale_values_grph_r); + } + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_PRESCALE_VALUES_GRPH_G]; + dal_write_reg(gg->ctx, + addr, prescale_values_grph_g); + } + + { + const uint32_t addr = + FROM_GRAPH_GAMMA(gg)-> + legacy_regs[IDX_PRESCALE_VALUES_GRPH_B]; + dal_write_reg(gg->ctx, + addr, prescale_values_grph_b); + } +} + +static void dal_grph_gamma_dce110_destruct( + struct grph_gamma_dce110 *gamma) +{ + dal_grph_gamma_destruct(&gamma->base); +} + +static void destroy( + struct grph_gamma **ptr) +{ + struct grph_gamma_dce110 *gamma; + + gamma = FROM_GRAPH_GAMMA(*ptr); + + if (!gamma) { + ASSERT_CRITICAL(false); + return; + } + + dal_grph_gamma_dce110_destruct(gamma); + + dal_free(gamma); + + *ptr = NULL; +} +static const struct grph_gamma_funcs grph_gamma_funcs = { + .set_gamma_ramp = set_gamma_ramp, + .set_gamma_ramp_legacy = set_gamma_ramp_legacy, + .set_legacy_mode = set_legacy_mode, + .program_prescale_legacy = program_prescale_legacy, + .setup_distribution_points = setup_distribution_points, + .program_black_offsets = program_black_offsets, + .program_white_offsets = program_white_offsets, + .program_lut_gamma = program_lut_gamma, + .set_lut_inc = set_lut_inc, + .select_lut = select_lut, + .destroy = destroy, +}; +static bool construct( + struct grph_gamma_dce110 *gamma, + struct grph_gamma_init_data *init_data) +{ + if (!dal_grph_gamma_construct(&gamma->base, init_data)) { + ASSERT_CRITICAL(false); + return false; + } + + gamma->base.funcs = &grph_gamma_funcs; + gamma->base.regs = gg_regs[init_data->id - 1]; + gamma->legacy_regs = dce110_gg_legacy_regs[init_data->id - 1]; + + return true; +} + +struct grph_gamma *dal_grph_gamma_dce110_create( + struct grph_gamma_init_data *init_data) +{ + struct grph_gamma_dce110 *gamma = + dal_alloc(sizeof(*gamma)); + + if (!gamma) { + ASSERT_CRITICAL(false); + return NULL; + } + + if (construct(gamma, init_data)) + return &gamma->base; + + ASSERT_CRITICAL(false); + + dal_free(gamma); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.h new file mode 100644 index 000000000000..b0035e142563 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/grph_gamma_dce110.h @@ -0,0 +1,38 @@ +/* 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_GRPH_GAMMA_DCE110_H__ +#define __DAL_GRPH_GAMMA_DCE110_H__ + +#include "../grph_gamma.h" + +struct grph_gamma_dce110 { + struct grph_gamma base; + const uint32_t *legacy_regs; +}; + +struct grph_gamma *dal_grph_gamma_dce110_create( + struct grph_gamma_init_data *data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.c new file mode 100644 index 000000000000..992dbb6d2672 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.c @@ -0,0 +1,528 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/logger_interface.h" +#include "include/adapter_service_interface.h" +#include "include/asic_capability_types.h" +#include "include/fixed32_32.h" + +#include "line_buffer_dce110.h" + +/****************************************************************************** + * static functions + *****************************************************************************/ + +static void destruct(struct line_buffer_dce110 *lb) +{ + dal_line_buffer_destruct_base(&lb->base); +} + +static void destroy(struct line_buffer **base) +{ + struct line_buffer_dce110 *lb; + + lb = LB110_FROM_BASE(*base); + + destruct(lb); + + dal_free(lb); + + *base = NULL; +} + +/* LB_MEMORY_CONFIG + * 00 - Use all three pieces of memory + * 01 - Use only one piece of memory of total 720x144 bits + * 10 - Use two pieces of memory of total 960x144 bits + * 11 - reserved + * + * LB_MEMORY_SIZE + * Total entries of LB memory. + * This number should be larger than 960. The default value is 1712(0x6B0) */ +static void power_up(struct line_buffer *base) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value; + + value = dal_read_reg(base->dal_context, lb->lbx_memory_ctrl); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LB_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, LB_ENTRIES_TOTAL_NUMBER, LB_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dal_write_reg(base->dal_context, lb->lbx_memory_ctrl, value); +} + +/* + * reset_lb_on_vblank + * + * @brief + * Resets LB on first VBlank + */ +static void reset_lb_on_vblank( + struct line_buffer *lb, + enum controller_id idx) +{ + uint32_t addr = 0; + uint32_t value = 0; + struct line_buffer_dce110 *lb_dce110 = LB110_FROM_BASE(lb); + + addr = lb_dce110->crtcx_crtc_status_position; + value = dal_read_reg(lb->dal_context, addr); + + /* Wait for one frame if CRTC is moving */ + if (value != dal_read_reg(lb->dal_context, addr)) { + uint32_t retry_count = 0; + + addr = lb_dce110->lbx_lb_sync_reset_sel; + value = dal_read_reg(lb->dal_context, addr); + set_reg_field_value( + value, 3, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL); + set_reg_field_value( + value, 1, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2); + dal_write_reg(lb->dal_context, addr, value); + + /* mmCRTCx_CRTC_STATUS_FRAME_COUNT */ + addr = lb_dce110->crtcx_crtc_status_frame_count; + value = dal_read_reg(lb->dal_context, addr); + for (retry_count = 100; retry_count > 0; retry_count--) { + if (value != + dal_read_reg(lb->dal_context, addr)) + break; + dal_sleep_in_milliseconds(1); + } + } + + addr = lb_dce110->lbx_lb_sync_reset_sel; + value = dal_read_reg(lb->dal_context, addr); + set_reg_field_value(value, 2, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL); + set_reg_field_value(value, 0, LB_SYNC_RESET_SEL, LB_SYNC_RESET_SEL2); + dal_write_reg(lb->dal_context, addr, value); +} + +static void set_vblank_irq( + struct line_buffer *lb, + bool enable) +{ + struct line_buffer_dce110 *lb_dce110 = LB110_FROM_BASE(lb); + uint32_t addr = lb_dce110->lbx_interrupt_mask; + uint32_t value = dal_read_reg(lb->dal_context, addr); + + set_reg_field_value(value, enable?1:0, LB_INTERRUPT_MASK, + VBLANK_INTERRUPT_MASK); + dal_write_reg(lb->dal_context, addr, value); +} + +bool dal_line_buffer_dce110_get_pixel_storage_depth( + struct line_buffer *base, + uint32_t display_bpp, + enum lb_pixel_depth *depth) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + enum lb_pixel_depth display_depth; + + *depth = lb->default_pixel_depth; + + /* default value */ + display_depth = dal_line_buffer_display_bpp_to_lb_depth(display_bpp); + + if (lb->caps & display_depth) { + /*we got the match lb and display bpp*/ + *depth = display_depth; + } else { + /*we have to go the higher lb if it is possible*/ + uint32_t i; + uint32_t max_depth = LB_PIXEL_DEPTH_36BPP; + + for (i = display_depth; i <= max_depth; i <<= 1) { + if (i & lb->caps) { + *depth = i; + break; + } + } + } + + return true; +} + +static uint32_t calculate_pitch(uint32_t depth, uint32_t width) +{ + uint32_t pitch = 0; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pitch = (width + 7) >> 3; + break; + + case LB_PIXEL_DEPTH_24BPP: + pitch = ((width + 7) / 8) * 683; + pitch = (pitch + 511) >> 9; + break; + + case LB_PIXEL_DEPTH_30BPP: + pitch = ((width + 7) / 8) * 854; + pitch = (pitch + 511) >> 9; + break; + + case LB_PIXEL_DEPTH_36BPP: + pitch = (width + 3) >> 2; + break; + } + return pitch; +} + +bool dal_line_buffer_dce110_get_max_num_of_supported_lines( + struct line_buffer *lb, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines) +{ + uint32_t pitch; + + if (pixel_width == 0) + return false; + + pitch = calculate_pitch(depth, pixel_width); + if (pitch == 0 || pitch > LB_ENTRIES_TOTAL_NUMBER) + return false; + + *lines = LB_ENTRIES_TOTAL_NUMBER / pitch; + return true; +} + +static bool get_current_pixel_storage_depth( + struct line_buffer *base, + enum lb_pixel_depth *depth) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value = 0; + + if (depth == NULL) + return false; + + value = dal_read_reg( + base->dal_context, + lb->lbx_data_format); + + switch (get_reg_field_value(value, LB_DATA_FORMAT, PIXEL_DEPTH)) { + case 0: + *depth = LB_PIXEL_DEPTH_30BPP; + break; + case 1: + *depth = LB_PIXEL_DEPTH_24BPP; + break; + case 2: + *depth = LB_PIXEL_DEPTH_18BPP; + break; + case 3: + *depth = LB_PIXEL_DEPTH_36BPP; + break; + default: + dal_logger_write(base->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + *depth = LB_PIXEL_DEPTH_30BPP; + break; + } + return true; + +} + +static bool set_pixel_storage_depth( + struct line_buffer *base, + enum lb_pixel_depth depth) +{ + bool ret = true; + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value; + + value = dal_read_reg( + base->dal_context, + lb->lbx_data_format); + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + set_reg_field_value(value, 2, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_24BPP: + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_30BPP: + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_36BPP: + set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + default: + ret = false; + break; + } + + if (ret == true) { + + set_reg_field_value(value, 0, LB_DATA_FORMAT, ALPHA_EN); + dal_write_reg( + base->dal_context, lb->lbx_data_format, value); + if (!(lb->caps & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dal_logger_write(base->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Capability not supported", + __func__); + } + + } + + return ret; +} + +static bool enable_power_gating( + struct line_buffer *base, + enum controller_id idx, + struct lb_config_data *lb_config) +{ + /* do not power gate */ + return true; +} + +static void enable_alpha( + struct line_buffer *base, + bool enable) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + struct dal_context *dal_ctx = base->dal_context; + uint32_t value; + uint32_t addr = lb->lbx_data_format; + + value = dal_read_reg(dal_ctx, addr); + + if (enable == 1) + set_reg_field_value( + value, + 1, + LB_DATA_FORMAT, + ALPHA_EN); + else + set_reg_field_value( + value, + 0, + LB_DATA_FORMAT, + ALPHA_EN); + + dal_write_reg(dal_ctx, addr, value); +} + +static enum lb_pixel_depth translate_display_bpp_to_lb_depth( + uint32_t display_bpp) +{ + switch (display_bpp) { + case 18: + return LB_PIXEL_DEPTH_18BPP; + case 24: + return LB_PIXEL_DEPTH_24BPP; + case 36: + case 42: + case 48: + return LB_PIXEL_DEPTH_36BPP; + case 30: + default: + return LB_PIXEL_DEPTH_30BPP; + } +} + +static bool get_next_lower_pixel_storage_depth( + struct line_buffer *base, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + enum lb_pixel_depth depth_req_by_display = + translate_display_bpp_to_lb_depth(display_bpp); + uint32_t current_required_depth = depth_req_by_display; + uint32_t current_depth = depth; + + /* if required display depth < current we could go down, for example + * from LB_PIXEL_DEPTH_30BPP to LB_PIXEL_DEPTH_24BPP + */ + if (current_required_depth < current_depth) { + current_depth = current_depth >> 1; + if (lb->caps & current_depth) { + *lower_depth = current_depth; + return true; + } + } + return false; +} + +static bool is_prefetch_supported( + struct line_buffer *base, + struct lb_config_data *lb_config) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value = dal_read_reg(base->dal_context, lb->lbx_data_format); + + if (get_reg_field_value(value, LB_DATA_FORMAT, PREFETCH) == 1) + return true; + + return false; +} + +static const struct line_buffer_funcs funcs = { + .destroy = destroy, + .power_up = power_up, + .enable_power_gating = enable_power_gating, + .set_pixel_storage_depth = set_pixel_storage_depth, + .get_current_pixel_storage_depth = get_current_pixel_storage_depth, + .get_pixel_storage_depth = + dal_line_buffer_dce110_get_pixel_storage_depth, + .get_next_lower_pixel_storage_depth = + get_next_lower_pixel_storage_depth, + .get_max_num_of_supported_lines = + dal_line_buffer_dce110_get_max_num_of_supported_lines, + .reset_lb_on_vblank = reset_lb_on_vblank, + .set_vblank_irq = set_vblank_irq, + .enable_alpha = enable_alpha, + .is_prefetch_supported = is_prefetch_supported +}; + +bool dal_line_buffer_dce110_construct( + struct line_buffer_dce110 *lb, + struct line_buffer_init_data *init_data) +{ + bool ret = true; + struct line_buffer *base; + + base = &lb->base; + /*init_data->lb_split = true;*/ + + if (!dal_line_buffer_construct_base(&lb->base, init_data)) + return false; + + /*funcs init*/ + base->funcs = &funcs; + + /* data members init */ + lb->caps = LB_PIXEL_DEPTH_30BPP; + lb->default_pixel_depth = LB_PIXEL_DEPTH_30BPP; + lb->controller_id = init_data->id; + + if (init_data->as != NULL) { + struct asic_feature_flags flags; + + flags = dal_adapter_service_get_feature_flags(init_data->as); + + /*we may change lb capability here*/ + if (flags.bits.WORKSTATION) { + lb->caps |= LB_PIXEL_DEPTH_18BPP; + lb->caps |= LB_PIXEL_DEPTH_24BPP; + /* 04/13: HW removed 12bpc LB. */ + + } else if (dal_adapter_service_is_feature_supported( + FEATURE_LINE_BUFFER_ENHANCED_PIXEL_DEPTH)) { + lb->caps |= LB_PIXEL_DEPTH_18BPP; + lb->caps |= LB_PIXEL_DEPTH_24BPP; + } + + /* TODO: read FEATURE_POWER_GATING_LINE_BUFFER_PORTION when + * LB PG feature is implemented for DCE11. */ + } + + switch (lb->controller_id) { + case CONTROLLER_ID_D0: + lb->lbx_memory_ctrl = mmLB0_LB_MEMORY_CTRL; + lb->lbx_data_format = mmLB0_LB_DATA_FORMAT; + lb->lbx_interrupt_mask = mmLB0_LB_INTERRUPT_MASK; + lb->lbx_lb_sync_reset_sel = mmLB0_LB_SYNC_RESET_SEL; + lb->crtcx_crtc_status_position = mmCRTC0_CRTC_STATUS_POSITION; + lb->crtcx_crtc_status_frame_count = + mmCRTC0_CRTC_STATUS_FRAME_COUNT; + break; + case CONTROLLER_ID_D1: + lb->lbx_memory_ctrl = mmLB1_LB_MEMORY_CTRL; + lb->lbx_data_format = mmLB1_LB_DATA_FORMAT; + lb->lbx_interrupt_mask = mmLB1_LB_INTERRUPT_MASK; + lb->lbx_lb_sync_reset_sel = mmLB1_LB_SYNC_RESET_SEL; + lb->crtcx_crtc_status_position = mmCRTC1_CRTC_STATUS_POSITION; + lb->crtcx_crtc_status_frame_count = + mmCRTC1_CRTC_STATUS_FRAME_COUNT; + break; + case CONTROLLER_ID_D2: + lb->lbx_memory_ctrl = mmLB2_LB_MEMORY_CTRL; + lb->lbx_data_format = mmLB2_LB_DATA_FORMAT; + lb->lbx_interrupt_mask = mmLB2_LB_INTERRUPT_MASK; + lb->lbx_lb_sync_reset_sel = mmLB2_LB_SYNC_RESET_SEL; + lb->crtcx_crtc_status_position = mmCRTC2_CRTC_STATUS_POSITION; + lb->crtcx_crtc_status_frame_count = + mmCRTC2_CRTC_STATUS_FRAME_COUNT; + break; + case CONTROLLER_ID_UNDERLAY0: + break; + default: + ret = false; + break; + } + + if (false == ret) + dal_line_buffer_destruct_base(base); + + return ret; +} + + +/****************************************************************************** + * non-static functions + *****************************************************************************/ + +struct line_buffer *dal_line_buffer_dce110_create( + struct line_buffer_init_data *init_data) +{ + struct line_buffer_dce110 *lb = NULL; + + lb = dal_alloc(sizeof(struct line_buffer_dce110)); + if (lb == NULL) + return NULL; + + if (dal_line_buffer_dce110_construct(lb, init_data)) + return &lb->base; + + /* fail to construct */ + dal_free(lb); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.h new file mode 100644 index 000000000000..e4bf5256ad33 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_dce110.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_LINE_BUFFER_DCE110_H__ +#define __DAL_LINE_BUFFER_DCE110_H__ + +#include "../line_buffer.h" + +#define LB_ENTRIES_NUMBER_PART_1 720 +#define LB_ENTRIES_NUMBER_PART_2 960 +#define LB_ENTRIES_TOTAL_NUMBER 1712 + +struct line_buffer_dce110 { + struct line_buffer base; + enum controller_id controller_id; + + enum lb_pixel_depth default_pixel_depth; + + uint32_t caps; + uint32_t index; + uint32_t power_gating; + + uint32_t lbx_memory_ctrl; + uint32_t lbx_data_format; + uint32_t lbx_interrupt_mask; + + uint32_t lbx_lb_sync_reset_sel; + uint32_t crtcx_crtc_status_position; + uint32_t crtcx_crtc_status_frame_count; +}; + +#define LB110_FROM_BASE(lb_base) \ + container_of(lb_base, struct line_buffer_dce110, base) + +struct line_buffer *dal_line_buffer_dce110_create( + struct line_buffer_init_data *init_data); + +bool dal_line_buffer_dce110_construct( + struct line_buffer_dce110 *lb, + struct line_buffer_init_data *init_data); + +bool dal_line_buffer_dce110_get_max_num_of_supported_lines( + struct line_buffer *lb, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines); + +bool dal_line_buffer_dce110_get_pixel_storage_depth( + struct line_buffer *base, + uint32_t display_bpp, + enum lb_pixel_depth *depth); + +#endif /* __DAL_LINE_BUFFER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.c new file mode 100644 index 000000000000..fc2fd88a0aa9 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.c @@ -0,0 +1,341 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/logger_interface.h" +#include "include/adapter_service_interface.h" +#include "include/asic_capability_types.h" +#include "include/fixed32_32.h" + +#include "line_buffer_v_dce110.h" + +/****************************************************************************** + * static functions + *****************************************************************************/ + +static void destruct(struct line_buffer_dce110 *lb) +{ + dal_line_buffer_destruct_base(&lb->base); +} + +static void destroy(struct line_buffer **base) +{ + struct line_buffer_dce110 *lb; + + lb = LB110_FROM_BASE(*base); + + destruct(lb); + + dal_free(lb); + + *base = NULL; +} + +/* LB_MEMORY_CONFIG + * 00 - Use all three pieces of memory + * 01 - Use only one piece of memory of total 720x144 bits + * 10 - Use two pieces of memory of total 960x144 bits + * 11 - reserved + * + * LB_MEMORY_SIZE + * Total entries of LB memory. + * This number should be larger than 960. The default value is 1712(0x6B0) */ +static void power_up(struct line_buffer *base) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value; + + value = dal_read_reg(base->dal_context, lb->lbx_memory_ctrl); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LBV_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value( + value, + LB_ENTRIES_TOTAL_NUMBER, + LBV_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dal_write_reg(base->dal_context, lb->lbx_memory_ctrl, value); +} + +static bool get_current_pixel_storage_depth( + struct line_buffer *base, + enum lb_pixel_depth *depth) +{ + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value = 0; + + if (depth == NULL) + return false; + + value = dal_read_reg(base->dal_context, lb->lbx_data_format); + + switch (get_reg_field_value(value, LBV_DATA_FORMAT, PIXEL_DEPTH)) { + case 0: + *depth = LB_PIXEL_DEPTH_30BPP; + break; + case 1: + *depth = LB_PIXEL_DEPTH_24BPP; + break; + case 2: + *depth = LB_PIXEL_DEPTH_18BPP; + break; + case 3: + *depth = LB_PIXEL_DEPTH_36BPP; + break; + default: + dal_logger_write(base->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + *depth = LB_PIXEL_DEPTH_30BPP; + break; + } + return true; + +} + +static bool set_pixel_storage_depth( + struct line_buffer *base, + enum lb_pixel_depth depth) +{ + bool ret = true; + struct line_buffer_dce110 *lb = LB110_FROM_BASE(base); + uint32_t value; + + value = dal_read_reg(base->dal_context, lb->lbx_data_format); + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + set_reg_field_value(value, 2, LBV_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_REDUCE_MODE); + set_reg_field_value(value, 1, LBV_DATA_FORMAT, DITHER_EN); + break; + case LB_PIXEL_DEPTH_24BPP: + set_reg_field_value(value, 1, LBV_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_REDUCE_MODE); + set_reg_field_value(value, 0, LBV_DATA_FORMAT, DITHER_EN); + break; + case LB_PIXEL_DEPTH_30BPP: + set_reg_field_value(value, 0, LBV_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + PIXEL_REDUCE_MODE); + set_reg_field_value(value, 0, LBV_DATA_FORMAT, DITHER_EN); + break; + case LB_PIXEL_DEPTH_36BPP: + set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value( + value, + 0, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + set_reg_field_value( + value, + 0, + LBV_DATA_FORMAT, + PIXEL_REDUCE_MODE); + set_reg_field_value(value, 0, LBV_DATA_FORMAT, DITHER_EN); + break; + default: + ret = false; + break; + } + + if (ret == true) { + set_reg_field_value( + value, + 1, + LBV_DATA_FORMAT, + DOWNSCALE_PREFETCH_EN); + + set_reg_field_value(value, 0, LBV_DATA_FORMAT, ALPHA_EN); + dal_write_reg(base->dal_context, lb->lbx_data_format, value); + if (!(lb->caps & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dal_logger_write(base->dal_context->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Capability not supported", + __func__); + } + + } + + return ret; +} + +static bool enable_power_gating( + struct line_buffer *base, + enum controller_id idx, + struct lb_config_data *lb_config) +{ + return true; +} + +static void enable_alpha( + struct line_buffer *base, + bool enable) +{ +} + +static bool is_prefetch_supported( + struct line_buffer *base, + struct lb_config_data *lb_config_data) +{ + uint32_t pitch_l = + dal_line_buffer_base_calculate_pitch( + lb_config_data->depth, + lb_config_data->src_pixel_width); + uint32_t pitch_c = + dal_line_buffer_base_calculate_pitch( + lb_config_data->depth, + lb_config_data->src_pixel_width_c); + + /* Required number of lines from taps, minimum is 3 for prefetch */ + uint32_t num_lines_required_l = lb_config_data->taps.v_taps + 2; + uint32_t num_lines_required_c = lb_config_data->taps.v_taps_c + 2; + uint32_t min_req_lb_entries_l; + uint32_t min_req_lb_entries_c; + + if (num_lines_required_l < 3) + num_lines_required_l = 3; + + if (num_lines_required_c < 3) + num_lines_required_c = 3; + + min_req_lb_entries_l = num_lines_required_l * pitch_l; + min_req_lb_entries_c = num_lines_required_c * pitch_c; + + if (min_req_lb_entries_l < LB_ENTRIES_TOTAL_NUMBER && + min_req_lb_entries_c <= LB_ENTRIES_TOTAL_NUMBER) + return true; + + return false; +} + +static const struct line_buffer_funcs funcs = { + .destroy = destroy, + .power_up = power_up, + .enable_power_gating = enable_power_gating, + .set_pixel_storage_depth = set_pixel_storage_depth, + .get_current_pixel_storage_depth = get_current_pixel_storage_depth, + .get_pixel_storage_depth = + dal_line_buffer_dce110_get_pixel_storage_depth, + .get_next_lower_pixel_storage_depth = NULL, + .get_max_num_of_supported_lines = + dal_line_buffer_dce110_get_max_num_of_supported_lines, + .reset_lb_on_vblank = NULL, + .set_vblank_irq = NULL, + .enable_alpha = enable_alpha, + .is_prefetch_supported = is_prefetch_supported +}; + +static bool construct( + struct line_buffer_dce110 *lb, + struct line_buffer_init_data *init_data) +{ + bool ret = true; + + if (!dal_line_buffer_dce110_construct(lb, init_data)) + return false; + + lb->caps = LB_PIXEL_DEPTH_24BPP; + lb->default_pixel_depth = LB_PIXEL_DEPTH_24BPP; + + switch (lb->controller_id) { + case CONTROLLER_ID_UNDERLAY0: + lb->lbx_memory_ctrl = mmLBV_MEMORY_CTRL; + lb->lbx_data_format = mmLBV_DATA_FORMAT; + lb->lbx_interrupt_mask = mmLBV_INTERRUPT_MASK; + lb->lbx_lb_sync_reset_sel = mmLBV_SYNC_RESET_SEL; + lb->crtcx_crtc_status_position = mmCRTCV_STATUS_POSITION; + lb->crtcx_crtc_status_frame_count = mmCRTCV_STATUS_FRAME_COUNT; + break; + default: + ret = false; + break; + } + + if (false == ret) + dal_line_buffer_destruct_base(&lb->base); + + lb->base.funcs = &funcs; + + return ret; +} + + +/****************************************************************************** + * non-static functions + *****************************************************************************/ + +struct line_buffer *dal_line_buffer_v_dce110_create( + struct line_buffer_init_data *init_data) +{ + struct line_buffer_dce110 *lb = dal_alloc(sizeof(*lb)); + + if (lb == NULL) + return NULL; + + if (construct(lb, init_data)) + return &lb->base; + + /* fail to construct */ + dal_free(lb); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.h new file mode 100644 index 000000000000..c9855b11a5ba --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/line_buffer_v_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_LINE_BUFFER_V_DCE110_H__ +#define __DAL_LINE_BUFFER_V_DCE110_H__ + +#include "line_buffer_dce110.h" + +struct line_buffer *dal_line_buffer_v_dce110_create( + struct line_buffer_init_data *init_data); + +#endif /* __DAL_LINE_BUFFER_V_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.c new file mode 100644 index 000000000000..d2065f75f660 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.c @@ -0,0 +1,650 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_ctrl_defs.h" +#include "include/controller_interface.h" +#include "include/bios_parser_interface.h" +#include "include/adapter_service_interface.h" +#include "include/logger_interface.h" +#include "include/isr_config_types.h" + +#include "../pipe_control.h" +#include "pipe_control_dce110.h" + +enum blender_type { + BLENDER_TYPE_NON_SINGLE_PIPE = 0, + BLENDER_TYPE_SB_SINGLE_PIPE, + BLENDER_TYPE_TB_SINGLE_PIPE +}; + +enum dc_memory_sleep_state { + DC_MEMORY_SLEEP_DISABLE = 0, + DC_MEMORY_LIGHT_SLEEP, + DC_MEMORY_DEEP_SLEEP, + DC_MEMORY_SHUTDOWN +}; + +enum { + DCE110_PIPE_UPDATE_PENDING_DELAY = 1000, + DCE110_PIPE_UPDATE_PENDING_CHECKCOUNT = 5000 +}; + +enum pc_regs_idx { + IDX_DCFE_CLOCK_CONTROL, + IDX_DCFE_MEM_PWR_CTRL, + IDX_DCFE_MEM_PWR_CTRL2, + IDX_BLND_SM_CONTROL2, + IDX_BLND_CONTROL, + IDX_BLND_GSL_CONTROL, + IDX_BLND_UPDATE, + IDX_BLND_V_UPDATE_LOCK, + IDX_BLND_REG_UPDATE_STATUS, + IDX_VMM_PTE_CONTROL, + IDX_CRTC_MASTER_UPDATE_LOCK, + IDX_DCFEV_DMIFV_CLOCK_CONTROL, + IDX_CRTC_HBLANK_START_END, + PC_REGS_IDX_SIZE +}; + +static uint32_t pc_regs[][PC_REGS_IDX_SIZE] = { + [CONTROLLER_ID_D0 - 1] = { + [IDX_DCFE_CLOCK_CONTROL] = mmDCFE0_DCFE_CLOCK_CONTROL, + [IDX_DCFE_MEM_PWR_CTRL] = mmDCFE0_DCFE_MEM_PWR_CTRL, + [IDX_DCFE_MEM_PWR_CTRL2] = mmDCFE0_DCFE_MEM_PWR_CTRL2, + [IDX_BLND_SM_CONTROL2] = mmBLND0_BLND_SM_CONTROL2, + [IDX_BLND_CONTROL] = mmBLND0_BLND_CONTROL, + [IDX_BLND_GSL_CONTROL] = mmCRTC0_CRTC_GSL_CONTROL, + [IDX_BLND_UPDATE] = mmBLND0_BLND_UPDATE, + [IDX_BLND_V_UPDATE_LOCK] = mmBLND0_BLND_V_UPDATE_LOCK, + [IDX_BLND_REG_UPDATE_STATUS] = mmBLND0_BLND_REG_UPDATE_STATUS, + [IDX_VMM_PTE_CONTROL] = mmDCP0_DVMM_PTE_CONTROL, + [IDX_CRTC_MASTER_UPDATE_LOCK] = mmCRTC0_CRTC_MASTER_UPDATE_LOCK, + [IDX_CRTC_HBLANK_START_END] = mmCRTC0_CRTC_H_BLANK_START_END + }, + [CONTROLLER_ID_D1 - 1] = { + [IDX_DCFE_CLOCK_CONTROL] = mmDCFE1_DCFE_CLOCK_CONTROL, + [IDX_DCFE_MEM_PWR_CTRL] = mmDCFE1_DCFE_MEM_PWR_CTRL, + [IDX_DCFE_MEM_PWR_CTRL2] = mmDCFE1_DCFE_MEM_PWR_CTRL2, + [IDX_BLND_SM_CONTROL2] = mmBLND1_BLND_SM_CONTROL2, + [IDX_BLND_CONTROL] = mmBLND1_BLND_CONTROL, + [IDX_BLND_GSL_CONTROL] = mmCRTC1_CRTC_GSL_CONTROL, + [IDX_BLND_UPDATE] = mmBLND1_BLND_UPDATE, + [IDX_BLND_V_UPDATE_LOCK] = mmBLND1_BLND_V_UPDATE_LOCK, + [IDX_BLND_REG_UPDATE_STATUS] = mmBLND1_BLND_REG_UPDATE_STATUS, + [IDX_VMM_PTE_CONTROL] = mmDCP1_DVMM_PTE_CONTROL, + [IDX_CRTC_MASTER_UPDATE_LOCK] = mmCRTC1_CRTC_MASTER_UPDATE_LOCK, + [IDX_CRTC_HBLANK_START_END] = mmCRTC1_CRTC_H_BLANK_START_END + }, + [CONTROLLER_ID_D2 - 1] = { + [IDX_DCFE_CLOCK_CONTROL] = mmDCFE2_DCFE_CLOCK_CONTROL, + [IDX_DCFE_MEM_PWR_CTRL] = mmDCFE2_DCFE_MEM_PWR_CTRL, + [IDX_DCFE_MEM_PWR_CTRL2] = mmDCFE2_DCFE_MEM_PWR_CTRL2, + [IDX_BLND_SM_CONTROL2] = mmBLND2_BLND_SM_CONTROL2, + [IDX_BLND_CONTROL] = mmBLND2_BLND_CONTROL, + [IDX_BLND_GSL_CONTROL] = mmCRTC2_CRTC_GSL_CONTROL, + [IDX_BLND_UPDATE] = mmBLND2_BLND_UPDATE, + [IDX_BLND_V_UPDATE_LOCK] = mmBLND2_BLND_V_UPDATE_LOCK, + [IDX_BLND_REG_UPDATE_STATUS] = mmBLND2_BLND_REG_UPDATE_STATUS, + [IDX_VMM_PTE_CONTROL] = mmDCP2_DVMM_PTE_CONTROL, + [IDX_CRTC_MASTER_UPDATE_LOCK] = mmCRTC2_CRTC_MASTER_UPDATE_LOCK, + [IDX_CRTC_HBLANK_START_END] = mmCRTC2_CRTC_H_BLANK_START_END + } +}; + +static bool pipe_control_dce110_construct( + struct pipe_control_dce110 *pc, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id); + +struct pipe_control *dal_pipe_control_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id controller_id) +{ + struct pipe_control_dce110 *pc = + dal_alloc(sizeof(struct pipe_control_dce110)); + if (!pc) + return NULL; + + if (pipe_control_dce110_construct(pc, ctx, + as, controller_id)) + return &pc->base; + + dal_free(pc); + ASSERT_CRITICAL(false); + return NULL; +} + +static void destroy(struct pipe_control **pc) +{ + dal_free(*pc); + *pc = NULL; +} +/* +static void set_update_lock(struct pipe_control *pc, bool lock) +{ + uint32_t value = 0; + + value = dal_read_reg(pc->ctx, pc->regs[IDX_BLND_UPDATE]); + + set_reg_field_value( + value, + lock, + BLND_UPDATE, + BLND_UPDATE_LOCK); + + dal_write_reg(pc->ctx, pc->regs[IDX_BLND_UPDATE], value); +} +*/ +static void enable_stereo_mixer( + struct pipe_control *pc, + const struct crtc_mixer_params *params) +{ + /*TODO*/ +} + +static void disable_stereo_mixer(struct pipe_control *pc) +{ + /*TODO*/ +} + +/** + ***************************************************************************** + * Function: enable_fe_clock + * + * @brief + * Enables DCFE clock + ***************************************************************************** + */ +static void enable_fe_clock(struct pipe_control *pc, bool enable) +{ + uint32_t value = 0; + + value = dal_read_reg(pc->ctx, pc->regs[IDX_DCFE_CLOCK_CONTROL]); + + set_reg_field_value( + value, + enable, + DCFE_CLOCK_CONTROL, + DCFE_CLOCK_ENABLE); + + dal_write_reg(pc->ctx, pc->regs[IDX_DCFE_CLOCK_CONTROL], value); +} + +static void enable_display_pipe_clock_gating( + struct pipe_control *pc, + bool clock_gating) +{ + /*TODO*/ +} + +#define FROM_PIPE_CONTROL(ptr)\ + (container_of(ptr, struct pipe_control_dce110, base)) + +static void init_pte(struct pipe_control *pc) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = pc->regs[IDX_VMM_PTE_CONTROL]; + value = dal_read_reg(pc->ctx, addr); + + set_reg_field_value( + value, + 0, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dal_write_reg(pc->ctx, addr, value); + + addr = mmDVMM_PTE_REQ; + value = dal_read_reg(pc->ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dal_write_reg(pc->ctx, addr, value); + } +} + +/** + ***************************************************************************** + * Function: enable_disp_power_gating + * + * @brief + * enable or disable power gating ,relevant for DCE6x and up + * + * @param [in] enum pipe_gating_control power_gating true - power down, + * false - power up + ***************************************************************************** + */ +static bool enable_disp_power_gating( + struct pipe_control *pc, + enum pipe_gating_control power_gating) +{ + if (FROM_PIPE_CONTROL(pc)->pipe_power_gating_support || + power_gating == PIPE_GATING_CONTROL_INIT) { + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (!(power_gating == PIPE_GATING_CONTROL_INIT && + pc->controller_id != CONTROLLER_ID_D0)) + bp_result = dal_bios_parser_enable_disp_power_gating( + pc->bp, pc->controller_id, cntl); + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + init_pte(pc); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; + } + + return false; +} + +/* this is a workaround for hw bug - it is a trigger on r/w */ + +static void trigger_write_crtc_h_blank_start_end( + struct pipe_control *pc) +{ + struct dal_context *dal_ctx = pc->ctx; + uint32_t value; + uint32_t addr; + + addr = pc->regs[IDX_CRTC_HBLANK_START_END]; + value = dal_read_reg(dal_ctx, addr); + dal_write_reg(dal_ctx, addr, value); +} + +static bool pipe_control_lock( + struct pipe_control *pc, + uint32_t control_mask, + bool lock) +{ + struct dal_context *dal_ctx = pc->ctx; + uint32_t addr = pc->regs[IDX_BLND_V_UPDATE_LOCK]; + uint32_t value = dal_read_reg(dal_ctx, addr); + bool need_to_wait = false; + + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SCL) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_SCL_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_BLND_V_UPDATE_LOCK); + need_to_wait = true; + } + + if (control_mask & PIPE_LOCK_CONTROL_MODE) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_V_UPDATE_LOCK_MODE); + + dal_write_reg(dal_ctx, addr, value); + + if (!lock && need_to_wait) { + uint8_t counter = 0; + const uint8_t counter_limit = 100; + const uint16_t delay_us = 1000; + + uint8_t pipe_pending; + + addr = pc->regs[IDX_BLND_REG_UPDATE_STATUS]; + + while (counter < counter_limit) { + value = dal_read_reg(dal_ctx, addr); + + pipe_pending = 0; + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + BLND_BLNDC_UPDATE_PENDING); + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + BLND_BLNDO_UPDATE_PENDING); + } + + if (control_mask & PIPE_LOCK_CONTROL_SCL) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDC_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDO_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDC_GRPH_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDO_GRPH_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) { + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDC_GRPH_SURF_UPDATE_PENDING); + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDO_GRPH_SURF_UPDATE_PENDING); + } + + if (pipe_pending == 0) + break; + + counter++; + dal_delay_in_microseconds(delay_us); + } + + if (counter == counter_limit) { + dal_logger_write( + dal_ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait for update exceeded (wait %d us)\n", + __func__, + counter * delay_us); + dal_logger_write( + dal_ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: control %d, remain value %x\n", + __func__, + control_mask, + value); + } else { + dal_logger_write( + dal_ctx->logger, + LOG_MAJOR_HW_TRACE, + LOG_MINOR_HW_TRACE_SET_MODE, + "%s: wait for %d\n", + __func__, + counter * delay_us); + } + } + + if (!lock && (control_mask & PIPE_LOCK_CONTROL_BLENDER)) + trigger_write_crtc_h_blank_start_end(pc); + + return true; +} + +static void set_blender_mode( + struct pipe_control *pc, + enum blender_mode mode) +{ + struct dal_context *dal_ctx = pc->ctx; + uint32_t value; + uint32_t addr = pc->regs[IDX_BLND_CONTROL]; + uint32_t blnd_mode; + uint32_t feedthrough = 0; + + switch (mode) { + case BLENDER_MODE_OTHER_PIPE: + feedthrough = 0; + blnd_mode = 1; + break; + case BLENDER_MODE_BLENDING: + feedthrough = 0; + blnd_mode = 2; + break; + case BLENDER_MODE_CURRENT_PIPE: + default: + feedthrough = 1; + blnd_mode = 0; + break; + } + + value = dal_read_reg(dal_ctx, addr); + + set_reg_field_value( + value, + feedthrough, + BLND_CONTROL, + BLND_FEEDTHROUGH_EN); + + set_reg_field_value( + value, + blnd_mode, + BLND_CONTROL, + BLND_MODE); + + dal_write_reg(dal_ctx, addr, value); +} + +static bool program_alpha_blending( + struct pipe_control *pc, + const struct alpha_mode_cfg *cfg) +{ + struct dal_context *dal_ctx = pc->ctx; + bool alpha_enable = false; + uint32_t value; + uint32_t addr = pc->regs[IDX_BLND_CONTROL]; + + value = dal_read_reg(dal_ctx, addr); + + if (cfg->flags.bits.MODE_IS_SET == 1) { + switch (cfg->mode) { + case ALPHA_MODE_PIXEL: + set_reg_field_value( + value, + 0, + BLND_CONTROL, + BLND_ALPHA_MODE); + alpha_enable = true; + break; + case ALPHA_MODE_PIXEL_AND_GLOBAL: + set_reg_field_value( + value, + 1, + BLND_CONTROL, + BLND_ALPHA_MODE); + alpha_enable = true; + break; + case ALPHA_MODE_GLOBAL: + set_reg_field_value( + value, + 2, + BLND_CONTROL, + BLND_ALPHA_MODE); + break; + default: + dal_logger_write( + dal_ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: Alpha mode is unknown\n", + __func__); + break; + } + } + + if (cfg->flags.bits.MODE_MULTIPLIED_IS_SET == 1) { + if (cfg->flags.bits.MULTIPLIED_MODE == 1) + set_reg_field_value( + value, + 1, + BLND_CONTROL, + BLND_MULTIPLIED_MODE); + else + set_reg_field_value( + value, + 0, + BLND_CONTROL, + BLND_MULTIPLIED_MODE); + } + + if (cfg->flags.bits.GLOBAL_ALPHA == 1) + set_reg_field_value( + value, + 1, + BLND_CONTROL, + BLND_GLOBAL_ALPHA); + + if (cfg->flags.bits.GLOBAL_ALPHA_GAIN == 1) + set_reg_field_value( + value, + 1, + BLND_CONTROL, + BLND_GLOBAL_GAIN); + + dal_write_reg(dal_ctx, addr, value); + + return alpha_enable; +} + +static const struct pipe_control_funcs pipe_control_dce110_funcs = { + .enable_stereo_mixer = enable_stereo_mixer, + .disable_stereo_mixer = disable_stereo_mixer, + .enable_fe_clock = enable_fe_clock, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .enable_disp_power_gating = enable_disp_power_gating, + .pipe_control_lock = pipe_control_lock, + .set_blender_mode = set_blender_mode, + .program_alpha_blending = program_alpha_blending, + .destroy = destroy, +}; + +static bool pipe_control_dce110_construct( + struct pipe_control_dce110 *pc, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id) +{ + struct pipe_control *pc_base = &pc->base; + + if (!as) + return false; + + switch (id) { + case CONTROLLER_ID_D0: + case CONTROLLER_ID_D1: + case CONTROLLER_ID_D2: + break; + default: + return false; + } + pc_base->ctx = ctx; + pc_base->bp = dal_adapter_service_get_bios_parser(as); + pc_base->regs = pc_regs[id - 1]; + pc_base->controller_id = id; + pc_base->funcs = &pipe_control_dce110_funcs; + + pc->pipe_power_gating_support = true; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.h new file mode 100644 index 000000000000..08f89450ac6e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_dce110.h @@ -0,0 +1,45 @@ +/* + * 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_PIPE_CONTROL_DCE110_H__ +#define __DAL_PIPE_CONTROL_DCE110_H__ + +#include "../pipe_control.h" + +struct pipe_control_dce110 { + struct pipe_control base; + bool pipe_power_gating_support; + /*TODO + * DCMemorySleepState + * LowPowerMemorySupport + */ +}; + +struct pipe_control *dal_pipe_control_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id controller_id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.c new file mode 100644 index 000000000000..ee67860c82b3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.c @@ -0,0 +1,605 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_ctrl_defs.h" +#include "include/controller_interface.h" +#include "include/bios_parser_interface.h" +#include "include/adapter_service_interface.h" +#include "include/logger_interface.h" + +#include "../pipe_control.h" +#include "pipe_control_v_dce110.h" + +enum blender_type { + BLENDER_TYPE_NON_SINGLE_PIPE = 0, + BLENDER_TYPE_SB_SINGLE_PIPE, + BLENDER_TYPE_TB_SINGLE_PIPE +}; + +enum { + DCE110_PIPE_UPDATE_PENDING_DELAY = 1000, + DCE110_PIPE_UPDATE_PENDING_CHECKCOUNT = 5000 +}; + +enum pc_regs_idx { + IDX_DCFE_CLOCK_CONTROL, + IDX_DCFE_MEM_PWR_CTRL, + IDX_DCFE_MEM_PWR_CTRL2, + IDX_BLND_SM_CONTROL2, + IDX_BLND_CONTROL, + IDX_BLND_GSL_CONTROL, + IDX_BLND_UPDATE, + IDX_BLND_V_UPDATE_LOCK, + IDX_BLND_REG_UPDATE_STATUS, + IDX_VMM_PTE_CONTROL, + IDX_VMM_PTE_CONTROL_C, + IDX_CRTC_MASTER_UPDATE_LOCK, + IDX_DCFE_DMIF_CLOCK_CONTROL, + IDX_DCFE_DMIF_MEM_PWR_CTRL, + PC_REGS_IDX_SIZE +}; + +static uint32_t pc_underlay_regs[PC_REGS_IDX_SIZE] = { + [IDX_DCFE_CLOCK_CONTROL] = mmDCFEV_CLOCK_CONTROL, + [IDX_DCFE_MEM_PWR_CTRL] = mmDCFEV_MEM_PWR_CTRL, + [IDX_DCFE_MEM_PWR_CTRL2] = mmDCFEV_MEM_PWR_CTRL2, + [IDX_BLND_SM_CONTROL2] = 0, + [IDX_BLND_CONTROL] = mmBLNDV_CONTROL, + [IDX_BLND_GSL_CONTROL] = 0, + [IDX_BLND_UPDATE] = mmBLNDV_UPDATE, + [IDX_BLND_V_UPDATE_LOCK] = mmBLNDV_V_UPDATE_LOCK, + [IDX_BLND_REG_UPDATE_STATUS] = 0, + [IDX_VMM_PTE_CONTROL] = mmUNP_DVMM_PTE_CONTROL, + [IDX_VMM_PTE_CONTROL_C] = mmUNP_DVMM_PTE_CONTROL_C, + [IDX_CRTC_MASTER_UPDATE_LOCK] = 0, + [IDX_DCFE_DMIF_CLOCK_CONTROL] = mmDCFEV_DMIFV_CLOCK_CONTROL, + [IDX_DCFE_DMIF_MEM_PWR_CTRL] = mmDCFEV_DMIFV_MEM_PWR_CTRL +}; + +static bool construct( + struct pipe_control_dce110 *pc, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id); + +struct pipe_control *dal_pipe_control_v_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id controller_id) +{ + struct pipe_control_dce110 *pc = + dal_alloc(sizeof(struct pipe_control_dce110)); + if (!pc) + return NULL; + + if (construct(pc, ctx, + as, controller_id)) + return &pc->base; + + dal_free(pc); + ASSERT_CRITICAL(false); + return NULL; +} + +static void destroy(struct pipe_control **pc) +{ + dal_free(*pc); + *pc = NULL; +} +/* +static void set_update_lock(struct pipe_control *pc, bool lock) +{ + uint32_t value = 0; + + value = dal_read_reg(pc->ctx, pc->regs[IDX_BLND_UPDATE]); + + set_reg_field_value( + value, + lock, + BLND_UPDATE, + BLND_UPDATE_LOCK); + + dal_write_reg(pc->ctx, pc->regs[IDX_BLND_UPDATE], value); +} +*/ +/** + ***************************************************************************** + * Function: enable_fe_clock + * + * @brief + * Enables DCFE clock + ***************************************************************************** + */ +static void enable_fe_clock(struct pipe_control *pc, bool enable) +{ + uint32_t value = 0; + + value = dal_read_reg(pc->ctx, pc->regs[IDX_DCFE_CLOCK_CONTROL]); + + set_reg_field_value( + value, + enable, + DCFEV_CLOCK_CONTROL, + DCFEV_CLOCK_ENABLE); + + dal_write_reg(pc->ctx, pc->regs[IDX_DCFE_CLOCK_CONTROL], value); +} + +static void enable_display_pipe_clock_gating( + struct pipe_control *pc, + bool enable) +{ + uint32_t addr = pc->regs[IDX_DCFE_CLOCK_CONTROL]; + uint32_t value = dal_read_reg(pc->ctx, addr); + uint8_t to_enable = enable ? 0 : 1; + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_G_UNP_GATE_DISABLE); + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_R_DCFEV_GATE_DISABLE); + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_G_SCLV_GATE_DISABLE); + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_G_COL_MAN_GATE_DISABLE); + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_G_PSCLV_GATE_DISABLE); + + set_reg_field_value( + value, + to_enable, + DCFEV_CLOCK_CONTROL, + DISPCLK_G_CRTC_GATE_DISABLE); + + dal_write_reg(pc->ctx, addr, value); + + /**** DMIFV *****/ + addr = pc->regs[IDX_DCFE_DMIF_CLOCK_CONTROL]; + value = dal_read_reg(pc->ctx, addr); + + set_reg_field_value( + value, + to_enable, + DCFEV_DMIFV_CLOCK_CONTROL, + DMIFV_SCLK_G_DMIFTRK_GATE_DIS); + + set_reg_field_value( + value, + to_enable, + DCFEV_DMIFV_CLOCK_CONTROL, + DMIFV_DISPCLK_G_DMIFVL_GATE_DIS); + + set_reg_field_value( + value, + to_enable, + DCFEV_DMIFV_CLOCK_CONTROL, + DMIFV_DISPCLK_G_DMIFVC_GATE_DIS); + + dal_write_reg(pc->ctx, addr, value); + + if (enable) { + uint32_t low_power_mode = 0; + + addr = pc->regs[IDX_DCFE_MEM_PWR_CTRL]; + value = dal_read_reg(pc->ctx, addr); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + SCLV_COEFF_MEM_PWR_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV0_MEM_PWR_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV1_MEM_PWR_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV2_MEM_PWR_FORCE); + + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_GAMMA_CORR_MEM_PWR_DIS); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + COL_MAN_INPUT_GAMMA_MEM_PWR_DIS); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + SCLV_COEFF_MEM_PWR_DIS); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV0_MEM_PWR_DIS); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV1_MEM_PWR_DIS); + set_reg_field_value( + value, + 0, + DCFEV_MEM_PWR_CTRL, + LBV2_MEM_PWR_DIS); + + dal_write_reg(pc->ctx, addr, value); + + addr = pc->regs[IDX_DCFE_MEM_PWR_CTRL2]; + value = dal_read_reg(pc->ctx, addr); + + set_reg_field_value( + value, + low_power_mode, + DCFEV_MEM_PWR_CTRL2, + COL_MAN_GAMMA_CORR_MEM_PWR_MODE_SEL); + set_reg_field_value( + value, + low_power_mode, + DCFEV_MEM_PWR_CTRL2, + COL_MAN_INPUT_GAMMA_MEM_PWR_MODE_SEL); + set_reg_field_value( + value, + low_power_mode, + DCFEV_MEM_PWR_CTRL2, + SCLV_COEFF_MEM_PWR_MODE_SEL); + set_reg_field_value( + value, + low_power_mode, + DCFEV_MEM_PWR_CTRL2, + LBV_MEM_PWR_MODE_SEL); + + dal_write_reg(pc->ctx, addr, value); + + addr = pc->regs[IDX_DCFE_DMIF_MEM_PWR_CTRL]; + value = dal_read_reg(pc->ctx, addr); + + set_reg_field_value( + value, + low_power_mode, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_SEL); + + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_LUMA_0_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_LUMA_1_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_LUMA_3_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_LUMA_4_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_CHROMA_0_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_CHROMA_1_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_CHROMA_2_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_CHROMA_3_FORCE); + set_reg_field_value( + value, + 0, + DCFEV_DMIFV_MEM_PWR_CTRL, + DMIFV_MEM_PWR_CHROMA_4_FORCE); + + dal_write_reg(pc->ctx, addr, value); + } +} + +#define FROM_PIPE_CONTROL(ptr)\ + (container_of(ptr, struct pipe_control_dce110, base)) + +static void init_pte(struct pipe_control *pc) +{ + /* per pipe setting */ + uint32_t addr = pc->regs[IDX_VMM_PTE_CONTROL]; + uint32_t value = dal_read_reg(pc->ctx, addr); + /* HW default 0: + 0: DVMM will fetch maximum possible number of PTEs per request. + 1: DVMM will fetch one PTE per request */ + set_reg_field_value( + value, + 0, + UNP_DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + /* linear PTE buffer to make maximum buffer */ + set_reg_field_value( + value, + 1, + UNP_DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + set_reg_field_value( + value, + 1, + UNP_DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + /*2D tiled Luma setting. this will be overrided later in + * updatePlane()*/ + set_reg_field_value(value, 6, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH); + /*2D tiled Luma setting. this will be overrided later in + * updatePlane()*/ + set_reg_field_value(value, 6, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + dal_write_reg(pc->ctx, addr, value); + + addr = pc->regs[IDX_VMM_PTE_CONTROL_C]; + /*2D tiled Chroma. this will be overrided later in updatePlane()*/ + set_reg_field_value(value, 5, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + dal_write_reg(pc->ctx, addr, value); +} + +/** + ***************************************************************************** + * Function: enable_disp_power_gating + * + * @brief + * enable or disable power gating ,relevant for DCE6x and up + * + * @param [in] enum pipe_gating_control power_gating true - power down, + * false - power up + ***************************************************************************** + */ +static bool enable_disp_power_gating( + struct pipe_control *pc, + enum pipe_gating_control power_gating) +{ + if (power_gating == PIPE_GATING_CONTROL_INIT) { + /* there is no need for underlay pipe to call VBIOS init, + * call once at initHW */ + init_pte(pc); + return true; + } + + if (FROM_PIPE_CONTROL(pc)->pipe_power_gating_support) { + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + + if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + bp_result = + dal_bios_parser_enable_disp_power_gating( + pc->bp, + pc->controller_id, + cntl); + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + init_pte(pc); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; + } + + return false; +} + +static bool pipe_control_lock( + struct pipe_control *pc, + uint32_t control_mask, + bool lock) +{ + struct dal_context *dal_ctx = pc->ctx; + uint32_t addr = pc->regs[IDX_BLND_V_UPDATE_LOCK]; + uint32_t value = dal_read_reg(dal_ctx, addr); + + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) + set_reg_field_value( + value, + lock, + BLNDV_V_UPDATE_LOCK, + BLND_DCP_GRPH_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SCL) + set_reg_field_value( + value, + lock, + BLNDV_V_UPDATE_LOCK, + BLND_SCL_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) + set_reg_field_value( + value, + lock, + BLNDV_V_UPDATE_LOCK, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) + set_reg_field_value( + value, + lock, + BLNDV_V_UPDATE_LOCK, + BLND_BLND_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_MODE) + set_reg_field_value( + value, + lock, + BLNDV_V_UPDATE_LOCK, + BLND_V_UPDATE_LOCK_MODE); + + dal_write_reg(dal_ctx, addr, value); + + return true; +} + +static void set_blender_mode( + struct pipe_control *pc, + enum blender_mode mode) +{ + struct dal_context *dal_ctx = pc->ctx; + uint32_t value; + uint32_t addr = pc->regs[IDX_BLND_CONTROL]; + uint32_t blnd_mode; + uint32_t feedthrough = 0; + + switch (mode) { + case BLENDER_MODE_OTHER_PIPE: + blnd_mode = 1; + break; + case BLENDER_MODE_BLENDING: + blnd_mode = 2; + break; + case BLENDER_MODE_CURRENT_PIPE: + default: + blnd_mode = 0; + break; + } + + value = dal_read_reg(dal_ctx, addr); + + set_reg_field_value( + value, + blnd_mode, + BLNDV_CONTROL, + BLND_MODE); + + set_reg_field_value( + value, + feedthrough, + BLNDV_CONTROL, + BLND_FEEDTHROUGH_EN); + + dal_write_reg(dal_ctx, addr, value); +} + +static bool program_alpha_blending( + struct pipe_control *pc, + const struct alpha_mode_cfg *cfg) +{ + return false; +} + +static const struct pipe_control_funcs pipe_control_v_dce110_funcs = { + .enable_stereo_mixer = NULL, + .disable_stereo_mixer = NULL, + .enable_fe_clock = enable_fe_clock, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .enable_disp_power_gating = enable_disp_power_gating, + .pipe_control_lock = pipe_control_lock, + .set_blender_mode = set_blender_mode, + .program_alpha_blending = program_alpha_blending, + .destroy = destroy, +}; + +static bool construct( + struct pipe_control_dce110 *pc, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id) +{ + struct pipe_control *pc_base = &pc->base; + + if (!as) + return false; + + switch (id) { + case CONTROLLER_ID_UNDERLAY0: + break; + default: + return false; + } + pc_base->ctx = ctx; + pc_base->bp = dal_adapter_service_get_bios_parser(as); + + pc_base->regs = pc_underlay_regs; + + pc_base->controller_id = id; + pc_base->funcs = &pipe_control_v_dce110_funcs; + + pc->pipe_power_gating_support = true; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.h new file mode 100644 index 000000000000..8e35f52d6145 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/pipe_control_v_dce110.h @@ -0,0 +1,41 @@ +/* + * 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_PIPE_CONTROL_V_DCE110_H__ +#define __DAL_PIPE_CONTROL_V_DCE110_H__ + +#include "../pipe_control.h" + +struct pipe_control_dce110 { + struct pipe_control base; + bool pipe_power_gating_support; +}; + +struct pipe_control *dal_pipe_control_v_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id controller_id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.c new file mode 100644 index 000000000000..b97efe057690 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.c @@ -0,0 +1,927 @@ +/* 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_id.h" +#include "include/bios_parser_interface.h" +#include "include/fixed31_32.h" +#include "include/logger_interface.h" + +#include "../scaler_filter.h" +#include "scaler_dce110.h" + +enum scl_regs_idx { + IDX_SCL_F_SHARP_CONTROL, + IDX_SCL_AUTOMATIC_MODE_CONTROL, + IDX_SCL_BYPASS_CONTROL, + IDX_SCL_UPDATE, + IDX_SCL_VERT_FILTER_CONTROL, + IDX_SCL_HORZ_FILTER_CONTROL, + IDX_SCL_ALU_CONTROL, + + IDX_SCL_HORZ_SCALE_RATIO, + IDX_SCL_VERT_SCALE_RATIO, + + IDX_SCL_HORZ_FILTER_INIT, + IDX_SCL_VERT_FILTER_INIT, + IDX_SCL_VERT_FILTER_INIT_BOT, + IDX_SCL_MANUAL_REPLICATE_CONTROL, + + IDX_SCL_COEF_RAM_SELECT, + IDX_SCL_COEF_RAM_TAP_DATA, + + IDX_SCL_TAP_CONTROL, + + IDX_SCL_OVERSCAN_LEFT_RIGHT, + IDX_SCL_OVERSCAN_TOP_BOTTOM, + + IDX_SCL_VIEWPORT_START, + IDX_SCL_VIEWPORT_SIZE, + IDX_SCL_MODE, + IDX_SCL_ROUND_OFFSET, + IDX_SCL_CONTROL, + + IDX_DCFE_MEM_PWR_CTRL, + IDX_DCFE_MEM_PWR_STATUS, + + SCL_REGS_IDX_SIZE +}; + +#define regs_for_scaler(id)\ +[CONTROLLER_ID_D ## id - 1] = {\ + [IDX_SCL_F_SHARP_CONTROL] = mmSCL ## id ## _SCL_F_SHARP_CONTROL,\ + [IDX_SCL_AUTOMATIC_MODE_CONTROL] =\ + mmSCL ## id ## _SCL_AUTOMATIC_MODE_CONTROL,\ + [IDX_SCL_BYPASS_CONTROL] = mmSCL ## id ## _SCL_BYPASS_CONTROL,\ + [IDX_SCL_UPDATE] = mmSCL ## id ## _SCL_UPDATE,\ + [IDX_SCL_VERT_FILTER_CONTROL] =\ + mmSCL ## id ## _SCL_VERT_FILTER_CONTROL,\ + [IDX_SCL_HORZ_FILTER_CONTROL] =\ + mmSCL ## id ## _SCL_HORZ_FILTER_CONTROL,\ + [IDX_SCL_ALU_CONTROL] = mmSCL ## id ## _SCL_ALU_CONTROL,\ + [IDX_SCL_HORZ_SCALE_RATIO] =\ + mmSCL ## id ## _SCL_HORZ_FILTER_SCALE_RATIO,\ + [IDX_SCL_VERT_SCALE_RATIO] =\ + mmSCL ## id ## _SCL_VERT_FILTER_SCALE_RATIO,\ + [IDX_SCL_HORZ_FILTER_INIT] = mmSCL ## id ## _SCL_HORZ_FILTER_INIT,\ + [IDX_SCL_VERT_FILTER_INIT] = mmSCL ## id ## _SCL_VERT_FILTER_INIT,\ + [IDX_SCL_VERT_FILTER_INIT_BOT] =\ + mmSCL ## id ## _SCL_VERT_FILTER_INIT_BOT,\ + [IDX_SCL_MANUAL_REPLICATE_CONTROL] =\ + mmSCL ## id ## _SCL_MANUAL_REPLICATE_CONTROL,\ + [IDX_SCL_COEF_RAM_SELECT] = mmSCL ## id ## _SCL_COEF_RAM_SELECT,\ + [IDX_SCL_COEF_RAM_TAP_DATA] = mmSCL ## id ## _SCL_COEF_RAM_TAP_DATA,\ + [IDX_SCL_TAP_CONTROL] = mmSCL ## id ## _SCL_TAP_CONTROL,\ + [IDX_SCL_OVERSCAN_LEFT_RIGHT] =\ + mmSCL ## id ## _EXT_OVERSCAN_LEFT_RIGHT,\ + [IDX_SCL_OVERSCAN_TOP_BOTTOM] =\ + mmSCL ## id ## _EXT_OVERSCAN_TOP_BOTTOM,\ + [IDX_SCL_VIEWPORT_START] = mmSCL ## id ## _VIEWPORT_START,\ + [IDX_SCL_VIEWPORT_SIZE] = mmSCL ## id ## _VIEWPORT_SIZE,\ + [IDX_SCL_MODE] = mmSCL ## id ## _SCL_MODE,\ + [IDX_SCL_ROUND_OFFSET] = mmSCL ## id ## _SCL_ROUND_OFFSET,\ + [IDX_SCL_CONTROL] = mmSCL ## id ## _SCL_CONTROL,\ + [IDX_DCFE_MEM_PWR_CTRL] = mmDCFE ## id ## _DCFE_MEM_PWR_CTRL,\ + [IDX_DCFE_MEM_PWR_STATUS] = mmDCFE ## id ## _DCFE_MEM_PWR_STATUS\ +} + +static const uint32_t scl_regs[][SCL_REGS_IDX_SIZE] = { + regs_for_scaler(0), + regs_for_scaler(1), + regs_for_scaler(2), +}; + +static void disable_enhanced_sharpness(struct scaler *scl) +{ + uint32_t value; + + value = dal_read_reg(scl->ctx, + scl->regs[IDX_SCL_F_SHARP_CONTROL]); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_SCALE_FACTOR); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_SCALE_FACTOR); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_F_SHARP_CONTROL], value); +} + +/** +* Function: +* void setup_scaling_configuration +* +* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps +* Input: data +* +* Output: + void +*/ +static bool setup_scaling_configuration( + struct scaler *scl, + const struct scaler_data *data) +{ + struct dal_context *dal_ctx = scl->ctx; + uint32_t addr; + uint32_t value; + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + scl->funcs->set_scaler_bypass(scl); + return false; + } + + { + addr = scl->regs[IDX_SCL_MODE]; + value = dal_read_reg(dal_ctx, addr); + + if (data->dal_pixel_format <= PIXEL_FORMAT_GRPH_END) + set_reg_field_value(value, 1, SCL_MODE, SCL_MODE); + else + set_reg_field_value(value, 2, SCL_MODE, SCL_MODE); + + set_reg_field_value(value, 1, SCL_MODE, SCL_PSCL_EN); + + dal_write_reg(dal_ctx, addr, value); + } + { + addr = scl->regs[IDX_SCL_TAP_CONTROL]; + value = dal_read_reg(dal_ctx, addr); + + set_reg_field_value(value, data->taps.h_taps - 1, + SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.v_taps - 1, + SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + + dal_write_reg(dal_ctx, addr, value); + } + { + addr = scl->regs[IDX_SCL_CONTROL]; + value = dal_read_reg(dal_ctx, addr); + /* 1 - Replaced out of bound pixels with edge */ + set_reg_field_value(value, 1, SCL_CONTROL, SCL_BOUNDARY_MODE); + + /* 1 - Replaced out of bound pixels with the edge pixel. */ + dal_write_reg(dal_ctx, addr, value); + } + + return true; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct scaler *scl, + const struct overscan_info *overscan) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + set_reg_field_value(overscan_left_right, overscan->left, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan->right, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, overscan->top, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan->bottom, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_OVERSCAN_LEFT_RIGHT], + overscan_left_right); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_OVERSCAN_TOP_BOTTOM], + overscan_top_bottom); +} + +static void program_two_taps_filter( + struct scaler *scl, + bool enable, + bool vertical) +{ + uint32_t addr; + uint32_t value; + /* 1: Hard coded 2 tap filter + * 0: Programmable 2 tap filter from coefficient RAM + */ + if (vertical) { + addr = scl->regs[IDX_SCL_VERT_FILTER_CONTROL]; + value = dal_read_reg(scl->ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_VERT_FILTER_CONTROL, + SCL_V_2TAP_HARDCODE_COEF_EN); + + } else { + addr = scl->regs[IDX_SCL_HORZ_FILTER_CONTROL]; + value = dal_read_reg(scl->ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_HORZ_FILTER_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN); + } + + dal_write_reg(scl->ctx, addr, value); +} + +enum { + DCE110_SCL_UPDATE_PENDING_DELAY = 1000, + DCE110_SCL_UPDATE_PENDING_CHECKCOUNT = 5000 +}; + +static void set_coeff_update_complete(struct scaler *scl) +{ + uint32_t value; + uint32_t addr = scl->regs[IDX_SCL_UPDATE]; + + value = dal_read_reg(scl->ctx, addr); + set_reg_field_value(value, 1, + SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dal_write_reg(scl->ctx, addr, value); +} + +static void program_filter( + struct scaler *scl, + enum ram_filter_type filter_type, + struct scaler_filter_params *scl_filter_params, + uint32_t *coeffs, + uint32_t coeffs_num) +{ + uint32_t phase = 0; + uint32_t array_idx = 0; + uint32_t pair = 0; + + uint32_t taps_pairs = (scl_filter_params->taps + 1) / 2; + uint32_t phases_to_program = scl_filter_params->phases / 2 + 1; + + uint32_t i; + uint32_t addr; + uint32_t select_addr; + uint32_t select; + uint32_t data; + /* We need to disable power gating on coeff memory to do programming */ + + uint32_t pwr_ctrl_orig; + uint32_t pwr_ctrl_off; + + addr = scl->regs[IDX_DCFE_MEM_PWR_CTRL]; + pwr_ctrl_orig = dal_read_reg(scl->ctx, addr); + pwr_ctrl_off = pwr_ctrl_orig; + set_reg_field_value( + pwr_ctrl_off, + 1, + DCFE_MEM_PWR_CTRL, + SCL_COEFF_MEM_PWR_DIS); + dal_write_reg(scl->ctx, addr, pwr_ctrl_off); + + addr = scl->regs[IDX_DCFE_MEM_PWR_STATUS]; + /* Wait to disable gating: */ + for (i = 0; + i < 10 && + get_reg_field_value( + dal_read_reg(scl->ctx, addr), + DCFE_MEM_PWR_STATUS, + SCL_COEFF_MEM_PWR_STATE); + i++) + dal_delay_in_microseconds(1); + + ASSERT(i < 10); + + select_addr = scl->regs[IDX_SCL_COEF_RAM_SELECT]; + select = dal_read_reg(scl->ctx, select_addr); + + set_reg_field_value( + select, + filter_type, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_FILTER_TYPE); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + data = 0; + + for (phase = 0; phase < phases_to_program; phase++) { + /* we always program N/2 + 1 phases, total phases N, but N/2-1 + * are just mirror phase 0 is unique and phase N/2 is unique + * if N is even + */ + + set_reg_field_value( + select, + phase, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + for (pair = 0; pair < taps_pairs; pair++) { + set_reg_field_value( + select, + pair, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + dal_write_reg(scl->ctx, select_addr, select); + + /* even tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + /* even tap data */ + set_reg_field_value( + data, + coeffs[array_idx], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + /* if we have odd number of taps and the last pair is + * here then we do not need to program + */ + if (scl_filter_params->taps % 2 && + pair == taps_pairs - 1) { + /* odd tap write disable */ + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + array_idx += 1; + } else { + /* odd tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + /* dbg_val: 0x1000 / sclFilterParams->taps; */ + set_reg_field_value( + data, + coeffs[array_idx + 1], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dal_write_reg( + scl->ctx, + scl->regs[IDX_SCL_COEF_RAM_TAP_DATA], + data); + } + } + + ASSERT(coeffs_num == array_idx); + + /* reset the power gating register */ + dal_write_reg( + scl->ctx, + scl->regs[IDX_DCFE_MEM_PWR_CTRL], + pwr_ctrl_orig); + + set_coeff_update_complete(scl); +} + +/* + * + * Populates an array with filter coefficients in 1.1.12 fixed point form +*/ +static bool get_filter_coefficients( + struct scaler *scl, + uint32_t taps, + uint32_t **data_tab, + uint32_t *data_size) +{ + uint32_t num = 0; + uint32_t i; + const struct fixed31_32 *filter = + dal_scaler_filter_get( + scl->filter, + data_tab, + &num); + uint32_t *data_row; + + if (!filter) { + BREAK_TO_DEBUGGER(); + return false; + } + data_row = *data_tab; + + for (i = 0; i < num; ++i) { + /* req. format sign fixed 1.1.12, the values are always between + * [-1; 1] + * + * Each phase is mirrored as follows : + * 0 : Phase 0 + * 1 : Phase 1 or Phase 64 - 1 / 128 - 1 + * N : Phase N or Phase 64 - N / 128 - N + * + * Convert from Fixed31_32 to 1.1.12 by using floor on value + * shifted by number of required fractional bits(12) + */ + struct fixed31_32 value = filter[i]; + + data_row[i] = + dal_fixed31_32_floor(dal_fixed31_32_shl(value, 12)) & + 0x3FFC; + } + *data_size = num; + + return true; +} + +static bool program_multi_taps_filter( + struct scaler *scl, + const struct scaler_data *data, + bool horizontal) +{ + struct scaler_filter_params filter_params; + enum ram_filter_type filter_type; + uint32_t src_size; + uint32_t dst_size; + + uint32_t *filter_data = NULL; + uint32_t filter_data_size = 0; + + /* 16 phases total for DCE11 */ + filter_params.phases = 16; + + if (horizontal) { + filter_params.taps = data->taps.h_taps; + filter_params.sharpness = data->h_sharpness; + filter_params.flags.bits.HORIZONTAL = 1; + + src_size = data->viewport.width; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.width), + data->ratios->horz)); + + filter_type = FILTER_TYPE_RGB_Y_HORIZONTAL; + } else { + filter_params.taps = data->taps.v_taps; + filter_params.sharpness = data->v_sharpness; + filter_params.flags.bits.HORIZONTAL = 0; + + src_size = data->viewport.height; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.height), + data->ratios->vert)); + + filter_type = FILTER_TYPE_RGB_Y_VERTICAL; + } + + /* 1. Generate the coefficients */ + if (!dal_scaler_filter_generate( + scl->filter, + &filter_params, + src_size, + dst_size)) + return false; + + /* 2. Convert coefficients to fixed point format 1.12 (note coeff. + * could be negative(!) and range is [ from -1 to 1 ]) */ + if (!get_filter_coefficients( + scl, + filter_params.taps, + &filter_data, + &filter_data_size)) + return false; + + /* 3. Program the filter */ + program_filter( + scl, + filter_type, + &filter_params, + filter_data, + filter_data_size); + + /* 4. Program the alpha if necessary */ + if (data->flags.bits.SHOULD_PROGRAM_ALPHA) { + if (horizontal) + filter_type = FILTER_TYPE_ALPHA_HORIZONTAL; + else + filter_type = FILTER_TYPE_ALPHA_VERTICAL; + + program_filter( + scl, + filter_type, + &filter_params, + filter_data, + filter_data_size); + } + + return true; +} + +static void program_viewport( + struct scaler *scl, + const struct rect *view_port) +{ + struct dal_context *dal_ctx = scl->ctx; + uint32_t value = 0; + uint32_t addr = 0; + + addr = scl->regs[IDX_SCL_VIEWPORT_START]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + view_port->x, + VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + view_port->y, + VIEWPORT_START, + VIEWPORT_Y_START); + dal_write_reg(dal_ctx, addr, value); + + addr = scl->regs[IDX_SCL_VIEWPORT_SIZE]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + view_port->height, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + view_port->width, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dal_write_reg(dal_ctx, addr, value); + + /* TODO: add stereo support */ +} + +static void calculate_inits( + struct scaler *scl, + const struct scaler_data *data, + struct scl_ratios_inits *inits) +{ + struct fixed31_32 h_init; + struct fixed31_32 v_init; + struct fixed31_32 v_init_bot; + + inits->bottom_enable = 0; + inits->h_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->horz) << 5; + inits->v_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->vert) << 5; + + h_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->horz, + dal_fixed31_32_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dal_fixed31_32_floor(h_init); + inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; + + v_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dal_fixed31_32_floor(v_init); + inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; + + if (data->flags.bits.INTERLACED) { + v_init_bot = + dal_fixed31_32_add( + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int( + data->taps.v_taps + 1)), + 2), + data->ratios->vert); + inits->v_init_bottom.integer = dal_fixed31_32_floor(v_init_bot); + inits->v_init_bottom.fraction = + dal_fixed31_32_u0d19(v_init_bot) << 5; + + inits->bottom_enable = 1; + } +} + +static void program_scl_ratios_inits( + struct scaler *scl, + struct scl_ratios_inits *inits) +{ + uint32_t addr = scl->regs[IDX_SCL_HORZ_SCALE_RATIO]; + uint32_t value = 0; + + set_reg_field_value( + value, + inits->h_int_scale_ratio, + SCL_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dal_write_reg(scl->ctx, addr, value); + + addr = scl->regs[IDX_SCL_VERT_SCALE_RATIO]; + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio, + SCL_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dal_write_reg(scl->ctx, addr, value); + + addr = scl->regs[IDX_SCL_HORZ_FILTER_INIT]; + value = 0; + set_reg_field_value( + value, + inits->h_init.integer, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + set_reg_field_value( + value, + inits->h_init.fraction, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + dal_write_reg(scl->ctx, addr, value); + + addr = scl->regs[IDX_SCL_VERT_FILTER_INIT]; + value = 0; + set_reg_field_value( + value, + inits->v_init.integer, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_INT); + set_reg_field_value( + value, + inits->v_init.fraction, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + dal_write_reg(scl->ctx, addr, value); + + if (inits->bottom_enable) { + addr = scl->regs[IDX_SCL_VERT_FILTER_INIT_BOT]; + value = 0; + set_reg_field_value( + value, + inits->v_init_bottom.integer, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_INT_BOT); + set_reg_field_value( + value, + inits->v_init_bottom.fraction, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_FRAC_BOT); + dal_write_reg(scl->ctx, addr, value); + } + + addr = scl->regs[IDX_SCL_AUTOMATIC_MODE_CONTROL]; + value = 0; + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_V_CALC_AUTO_RATIO_EN); + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_H_CALC_AUTO_RATIO_EN); + dal_write_reg(scl->ctx, addr, value); +} + +static bool set_scaler_wrapper( + struct scaler *scl, + const struct scaler_data *data) +{ + bool is_scaling_required; + struct dal_context *dal_ctx = scl->ctx; + + { + uint32_t addr = scl->regs[IDX_SCL_BYPASS_CONTROL]; + uint32_t value = dal_read_reg(scl->ctx, addr); + + set_reg_field_value( + value, + 0, + SCL_BYPASS_CONTROL, + SCL_BYPASS_MODE); + dal_write_reg(scl->ctx, addr, value); + } + + disable_enhanced_sharpness(scl); + + /* 3. Program overscan */ + program_overscan(scl, &data->overscan); + + /* 4. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(scl, data); + if (is_scaling_required) { + /* 5. Calculate and program ratio, filter initialization */ + struct scl_ratios_inits inits = { 0 }; + + calculate_inits(scl, data, &inits); + + program_scl_ratios_inits(scl, &inits); + + /* 6. Program vertical filters */ + if (data->taps.v_taps > 2) { + program_two_taps_filter(scl, false, true); + + if (!program_multi_taps_filter(scl, data, false)) { + dal_logger_write(dal_ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed vertical taps programming\n"); + return false; + } + } else + program_two_taps_filter(scl, true, true); + + /* 7. Program horizontal filters */ + if (data->taps.h_taps > 2) { + program_two_taps_filter(scl, false, false); + + if (!program_multi_taps_filter(scl, data, true)) { + dal_logger_write(dal_ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed horizontal taps programming\n"); + return false; + } + } else + program_two_taps_filter(scl, true, false); + } + + return true; +} + +static void set_scaler_bypass(struct scaler *scl) +{ + uint32_t sclv_mode; + + disable_enhanced_sharpness(scl); + + sclv_mode = dal_read_reg(scl->ctx, scl->regs[IDX_SCL_MODE]); + set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_MODE); + set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_PSCL_EN); + dal_write_reg(scl->ctx, scl->regs[IDX_SCL_MODE], sclv_mode); +} + +static bool is_scaling_enabled(struct scaler *scl) +{ + uint32_t value; + uint8_t scl_mode; + + value = dal_read_reg(scl->ctx, + scl->regs[IDX_SCL_MODE]); + + scl_mode = get_reg_field_value(value, SCL_MODE, SCL_MODE); + + return scl_mode == 0; +} + +static void get_viewport( + struct scaler *scl, + struct rect *current_view_port) +{ + uint32_t value_start; + uint32_t value_size; + + if (current_view_port == NULL) + return; + + value_start = dal_read_reg(scl->ctx, scl->regs[IDX_SCL_VIEWPORT_START]); + value_size = dal_read_reg(scl->ctx, scl->regs[IDX_SCL_VIEWPORT_SIZE]); + + current_view_port->x = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_X_START); + current_view_port->y = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_Y_START); + current_view_port->height = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + current_view_port->width = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); +} + + +/*****************************************/ +/* Constructor, Destructor, fcn pointers */ +/*****************************************/ + +static void destroy(struct scaler **scl) +{ + dal_free(*scl); + *scl = NULL; +} + +static const struct scaler_funcs scaler_funcs = { + .set_scaler_bypass = set_scaler_bypass, + .is_scaling_enabled = is_scaling_enabled, + .set_scaler_wrapper = set_scaler_wrapper, + .destroy = destroy, + .get_optimal_taps_number = dal_scaler_get_optimal_taps_number, + .get_next_lower_taps_number = dal_scaler_get_next_lower_taps_number, + .get_viewport = get_viewport, + .program_viewport = program_viewport, +}; + +static bool scaler_dce110_construct( + struct scaler *scl, + struct scaler_init_data *init_data) +{ + enum controller_id id; + + if (!dal_scaler_construct(scl, init_data)) + return false; + + id = init_data->id; + + scl->regs = scl_regs[id - 1]; + + scl->funcs = &scaler_funcs; + return true; +} + +struct scaler *dal_scaler_dce110_create( + struct scaler_init_data *init_data) +{ + struct scaler *scl = dal_alloc(sizeof(struct scaler)); + + if (!scl) + return NULL; + + if (!scaler_dce110_construct(scl, init_data)) + goto fail; + + return scl; +fail: + dal_free(scl); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.h new file mode 100644 index 000000000000..d8c409e7638a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_dce110.h @@ -0,0 +1,33 @@ +/* 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_DCE110_H__ +#define __DAL_SCALER_DCE110_H__ + +#include "../scaler.h" + +struct scaler *dal_scaler_dce110_create( + struct scaler_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.c new file mode 100644 index 000000000000..ef2105c6fae1 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.c @@ -0,0 +1,728 @@ +/* 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_id.h" +#include "include/bios_parser_interface.h" +#include "include/fixed31_32.h" +#include "include/logger_interface.h" +#include "include/set_mode_types.h" + +#include "../scaler_filter.h" +#include "scaler_v_dce110.h" + +enum scl_regs_idx { + IDX_SCL_AUTOMATIC_MODE_CONTROL, + IDX_SCL_UPDATE, + IDX_SCL_VERT_FILTER_CONTROL, + IDX_SCL_HORZ_FILTER_CONTROL, + IDX_SCL_ALU_CONTROL, + + IDX_SCL_HORZ_SCALE_RATIO, + IDX_SCL_VERT_SCALE_RATIO, + IDX_SCL_HORZ_SCALE_RATIO_C, + IDX_SCL_VERT_SCALE_RATIO_C, + + IDX_SCL_HORZ_FILTER_INIT, + IDX_SCL_VERT_FILTER_INIT, + IDX_SCL_VERT_FILTER_INIT_BOT, + IDX_SCL_MANUAL_REPLICATE_CONTROL, + + IDX_SCL_SCL_COEF_RAM_SELECT, + IDX_SCL_SCL_COEF_RAM_TAP_DATA, + + IDX_SCL_TAP_CONTROL, + + IDX_SCL_OVERSCAN_LEFT_RIGHT, + IDX_SCL_OVERSCAN_TOP_BOTTOM, + + IDX_SCL_VIEWPORT_START, + IDX_SCL_VIEWPORT_SIZE, + IDX_SCL_MODE, + IDX_SCL_ROUND_OFFSET, + IDX_SCL_CONTROL, + + IDX_SCL_VIEWPORT_START_C, + IDX_SCL_VIEWPORT_SIZE_C, + + SCL_REGS_IDX_SIZE +}; + +#define regs_for_underlay_scaler(id)\ +[CONTROLLER_ID_UNDERLAY ## id - CONTROLLER_ID_UNDERLAY0] = {\ + [IDX_SCL_AUTOMATIC_MODE_CONTROL] = mmSCLV_AUTOMATIC_MODE_CONTROL,\ + [IDX_SCL_UPDATE] = mmSCLV_UPDATE,\ + [IDX_SCL_VERT_FILTER_CONTROL] = mmSCLV_VERT_FILTER_CONTROL,\ + [IDX_SCL_HORZ_FILTER_CONTROL] = mmSCLV_HORZ_FILTER_CONTROL,\ + [IDX_SCL_ALU_CONTROL] = mmSCLV_ALU_CONTROL,\ + [IDX_SCL_HORZ_SCALE_RATIO] = mmSCLV_HORZ_FILTER_SCALE_RATIO,\ + [IDX_SCL_VERT_SCALE_RATIO] = mmSCLV_VERT_FILTER_SCALE_RATIO,\ + [IDX_SCL_HORZ_SCALE_RATIO_C] = mmSCLV_HORZ_FILTER_SCALE_RATIO_C,\ + [IDX_SCL_VERT_SCALE_RATIO_C] = mmSCLV_VERT_FILTER_SCALE_RATIO_C,\ + [IDX_SCL_HORZ_FILTER_INIT] = mmSCLV_HORZ_FILTER_INIT,\ + [IDX_SCL_VERT_FILTER_INIT] = mmSCLV_VERT_FILTER_INIT,\ + [IDX_SCL_VERT_FILTER_INIT_BOT] = mmSCLV_VERT_FILTER_INIT_BOT,\ + [IDX_SCL_MANUAL_REPLICATE_CONTROL] = mmSCLV_MANUAL_REPLICATE_CONTROL,\ + [IDX_SCL_SCL_COEF_RAM_SELECT] = mmSCLV_COEF_RAM_SELECT,\ + [IDX_SCL_SCL_COEF_RAM_TAP_DATA] = mmSCLV_COEF_RAM_TAP_DATA,\ + [IDX_SCL_TAP_CONTROL] = mmSCLV_TAP_CONTROL,\ + [IDX_SCL_OVERSCAN_LEFT_RIGHT] = mmSCLV_EXT_OVERSCAN_LEFT_RIGHT,\ + [IDX_SCL_OVERSCAN_TOP_BOTTOM] = mmSCLV_EXT_OVERSCAN_TOP_BOTTOM,\ + [IDX_SCL_VIEWPORT_START] = mmSCLV_VIEWPORT_START,\ + [IDX_SCL_VIEWPORT_SIZE] = mmSCLV_VIEWPORT_SIZE,\ + [IDX_SCL_MODE] = mmSCLV_MODE,\ + [IDX_SCL_ROUND_OFFSET] = mmSCL_ROUND_OFFSET,\ + [IDX_SCL_CONTROL] = mmSCLV_CONTROL,\ + [IDX_SCL_VIEWPORT_START_C] = mmSCLV_VIEWPORT_START_C,\ + [IDX_SCL_VIEWPORT_SIZE_C] = mmSCLV_VIEWPORT_SIZE_C,\ +} + +static const uint32_t scl_underlay_regs[][SCL_REGS_IDX_SIZE] = { + regs_for_underlay_scaler(0), +}; + +/* +***************************************************************************** +* Function: calculateViewport +* +* @brief +* Calculates all of the data required to set the viewport +* +* @param [in] pData: scaler settings data +* @param [out] pLumaVp: luma viewport information +* @param [out] pChromaVp: chroma viewport information +* @param [out] srcResCx2: source chroma resolution times 2 - for multi-taps +* +***************************************************************************** +*/ +static void calculate_viewport( + const struct scaler_data *scl_data, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + /*Do not set chroma vp for rgb444 pixel format*/ + luma_viewport->x = scl_data->viewport.x - scl_data->viewport.x % 2; + luma_viewport->y = scl_data->viewport.y - scl_data->viewport.y % 2; + luma_viewport->width = + scl_data->viewport.width - scl_data->viewport.width % 2; + luma_viewport->height = + scl_data->viewport.height - scl_data->viewport.height % 2; + + + if (scl_data->dal_pixel_format == PIXEL_FORMAT_422BPP16) { + luma_viewport->width += luma_viewport->width % 2; + + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->width = luma_viewport->width / 2; + } else if (scl_data->dal_pixel_format == PIXEL_FORMAT_420BPP12) { + luma_viewport->height += luma_viewport->height % 2; + luma_viewport->width += luma_viewport->width % 2; + /*for 420 video chroma is 1/4 the area of luma, scaled + *vertically and horizontally + */ + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->y = luma_viewport->y / 2; + chroma_viewport->height = luma_viewport->height / 2; + chroma_viewport->width = luma_viewport->width / 2; + } +} + + +static void program_viewport( + struct scaler *scl, + struct rect *luma_view_port, + struct rect *chroma_view_port) +{ + struct dal_context *dal_ctx = scl->ctx; + uint32_t value = 0; + uint32_t addr = 0; + + if (luma_view_port->width != 0 && luma_view_port->height != 0) { + addr = scl->regs[IDX_SCL_VIEWPORT_START]; + value = 0; + set_reg_field_value( + value, + luma_view_port->x, + SCLV_VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + luma_view_port->y, + SCLV_VIEWPORT_START, + VIEWPORT_Y_START); + dal_write_reg(dal_ctx, addr, value); + + addr = scl->regs[IDX_SCL_VIEWPORT_SIZE]; + value = 0; + set_reg_field_value( + value, + luma_view_port->height, + SCLV_VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + luma_view_port->width, + SCLV_VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dal_write_reg(dal_ctx, addr, value); + } + + if (chroma_view_port->width != 0 && chroma_view_port->height != 0) { + addr = scl->regs[IDX_SCL_VIEWPORT_START_C]; + value = 0; + set_reg_field_value( + value, + chroma_view_port->x, + SCLV_VIEWPORT_START_C, + VIEWPORT_X_START_C); + set_reg_field_value( + value, + chroma_view_port->y, + SCLV_VIEWPORT_START_C, + VIEWPORT_Y_START_C); + dal_write_reg(dal_ctx, addr, value); + + addr = scl->regs[IDX_SCL_VIEWPORT_SIZE_C]; + value = 0; + set_reg_field_value( + value, + chroma_view_port->height, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_HEIGHT_C); + set_reg_field_value( + value, + chroma_view_port->width, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_WIDTH_C); + dal_write_reg(dal_ctx, addr, value); + } + /* TODO: add stereo support */ +} + +/***************************************************************************** + * macro definitions + *****************************************************************************/ +#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_CONTROLLER,\ + "SCALER:%s()\n", __func__) + +/* Until and For MPO video play story, to reduce time for implementation, + * below limits are applied for now: 2_TAPS only + * Use auto-calculated filter values + * Following routines will be empty for now: + * + * programSclRatiosInits -- calcualate scaler ratio manually + * calculateInits --- calcualate scaler ratio manually + * programFilter -- multi-taps + * GetOptimalNumberOfTaps -- will hard coded to 2 TAPS + * GetNextLowerNumberOfTaps -- will hard coded to 2TAPS + * validateRequestedScaleRatio - used by GetOptimalNumberOfTaps internally + */ + +/** +* Function: +* void setup_scaling_configuration +* +* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps +* Input: data +* +* Output: + void +*/ +static bool setup_scaling_configuration( + struct scaler *scl, + const struct scaler_data *data) +{ + bool is_scaling_needed = false; + struct dal_context *dal_ctx = scl->ctx; + uint32_t value = 0; + + if (data->taps.h_taps + data->taps.v_taps > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN); + is_scaling_needed = true; + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + } + + if (data->taps.h_taps_c + data->taps.v_taps_c > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN_C); + is_scaling_needed = true; + } else if (data->dal_pixel_format != PIXEL_FORMAT_420BPP12 && + data->dal_pixel_format != PIXEL_FORMAT_422BPP16) { + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_MODE), + SCLV_MODE, + SCL_MODE_C); + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_PSCL_EN), + SCLV_MODE, + SCL_PSCL_EN_C); + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + } + dal_write_reg(dal_ctx, scl->regs[IDX_SCL_MODE], value); + + { + value = dal_read_reg(dal_ctx, + scl->regs[IDX_SCL_TAP_CONTROL]); + + set_reg_field_value(value, data->taps.h_taps - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.v_taps - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.h_taps_c - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS_C); + + set_reg_field_value(value, data->taps.v_taps_c - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS_C); + + dal_write_reg(dal_ctx, + scl->regs[IDX_SCL_TAP_CONTROL], value); + } + + { + /* we can ignore this register because we are ok with hw + * default 0 -- change to 1 according to dal2 code*/ + value = dal_read_reg(dal_ctx, + scl->regs[IDX_SCL_CONTROL]); + /* 0 - Replaced out of bound pixels with black pixel + * (or any other required color) */ + set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); + + /* 1 - Replaced out of bound pixels with the edge pixel. */ + dal_write_reg(dal_ctx, + scl->regs[IDX_SCL_CONTROL], value); + } + + return is_scaling_needed; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct scaler *scl, + const struct overscan_info *overscan) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + set_reg_field_value(overscan_left_right, overscan->left, + SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan->right, + SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, overscan->top, + SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan->bottom, + SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_OVERSCAN_LEFT_RIGHT], + overscan_left_right); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_OVERSCAN_TOP_BOTTOM], + overscan_top_bottom); +} +/* +static void setup_auto_scaling(struct scaler *scl) +{ + uint32_t value = 0; + set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, + SCL_V_CALC_AUTO_RATIO_EN); + set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, + SCL_H_CALC_AUTO_RATIO_EN); + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_AUTOMATIC_MODE_CONTROL], + value); +} +*/ +static void set_scaler_bypass(struct scaler *scl) +{ + uint32_t addr = scl->regs[IDX_SCL_MODE]; + uint32_t value = dal_read_reg(scl->ctx, addr); + + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + dal_write_reg(scl->ctx, addr, value); +} + +static bool is_scaling_enabled(struct scaler *scl) +{ + uint32_t value = dal_read_reg(scl->ctx, scl->regs[IDX_SCL_MODE]); + uint8_t scl_mode = get_reg_field_value(value, SCLV_MODE, SCL_MODE); + + return scl_mode == 0; +} + +static void program_two_taps_filter_horz( + struct scaler *scl, + bool hardcode_coff) +{ + uint32_t value = 0; + + if (hardcode_coff) + set_reg_field_value( + value, + 1, + SCLV_HORZ_FILTER_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_HORZ_FILTER_CONTROL], + value); +} + +static void program_two_taps_filter_vert( + struct scaler *scl, + bool hardcode_coff) +{ + uint32_t value = 0; + + if (hardcode_coff) + set_reg_field_value(value, 1, SCLV_VERT_FILTER_CONTROL, + SCL_V_2TAP_HARDCODE_COEF_EN); + + dal_write_reg(scl->ctx, + scl->regs[IDX_SCL_VERT_FILTER_CONTROL], + value); +} + + +enum { + DCE110_SCL_UPDATE_PENDING_DELAY = 1000, + DCE110_SCL_UPDATE_PENDING_CHECKCOUNT = 5000 +}; + +static void set_coeff_update_complete(struct scaler *scl) +{ + /*TODO: Until now, only scaler bypass, up-scaler 2 -TAPS coeff auto + * calculation are implemented. Coefficient RAM is not used + * Do not check this flag yet + */ + + /*uint32_t value; + uint32_t addr = scl->regs[IDX_SCL_UPDATE]; + + value = dal_read_reg(scl->ctx, addr); + set_reg_field_value(value, 0, + SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dal_write_reg(scl->ctx, addr, value);*/ +} + +static bool program_multi_taps_filter( + struct scaler *scl, + const struct scaler_data *data, + bool horizontal) +{ + struct dal_context *dal_context = scl->ctx; + + NOT_IMPLEMENTED(); + return false; +} + +static void get_viewport( + struct scaler *scl, + struct rect *viewport) +{ + uint32_t value = 0; + if (viewport != NULL) { + value = dal_read_reg(scl->ctx, + scl->regs[IDX_SCL_VIEWPORT_START]); + viewport->x = get_reg_field_value(value, + SCLV_VIEWPORT_START, VIEWPORT_X_START); + viewport->y = get_reg_field_value(value, + SCLV_VIEWPORT_START, VIEWPORT_Y_START); + + value = dal_read_reg(scl->ctx, + scl->regs[IDX_SCL_VIEWPORT_SIZE]); + viewport->height = get_reg_field_value(value, + SCLV_VIEWPORT_SIZE, VIEWPORT_HEIGHT); + viewport->width = get_reg_field_value(value, + SCLV_VIEWPORT_SIZE, VIEWPORT_WIDTH); + } +} + +enum scaler_validation_code get_optimal_taps_number( + struct scaler_validation_params *scaler_param, + struct scaling_tap_info *taps) +{ + /*TODO hard code to 2-TAPs for MPO video play story*/ + taps->h_taps = 2; + taps->v_taps = 2; + taps->h_taps_c = 2; + taps->v_taps_c = 2; + return SCALER_VALIDATION_OK; +} + +enum scaler_validation_code get_next_lower_taps_number( + struct scaler_validation_params *scaler_param, + struct scaling_tap_info *taps) +{ + /*TODO hard code to 2-TAPs for MPO video play story*/ + return SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; +} + +struct sclv_ratios_inits { + uint32_t chroma_enable; + uint32_t h_int_scale_ratio_luma; + uint32_t h_int_scale_ratio_chroma; + uint32_t v_int_scale_ratio_luma; + uint32_t v_int_scale_ratio_chroma; + struct init_int_and_frac h_init_luma; + struct init_int_and_frac h_init_chroma; + struct init_int_and_frac v_init_luma; + struct init_int_and_frac v_init_chroma; + struct init_int_and_frac h_init_lumabottom; + struct init_int_and_frac h_init_chromabottom; + struct init_int_and_frac v_init_lumabottom; + struct init_int_and_frac v_init_chromabottom; +}; + +static void calculate_inits( + struct scaler *scl, + const struct scaler_data *data, + struct sclv_ratios_inits *inits, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + if (data->dal_pixel_format == PIXEL_FORMAT_420BPP12 || + data->dal_pixel_format == PIXEL_FORMAT_422BPP16) + inits->chroma_enable = true; + + /* TODO: implement rest of this function properly */ + if (inits->chroma_enable) { + inits->h_int_scale_ratio_luma = 0x1000000; + inits->v_int_scale_ratio_luma = 0x1000000; + inits->h_int_scale_ratio_chroma = 0x800000; + inits->v_int_scale_ratio_chroma = 0x800000; + } +} + +static void program_scl_ratios_inits( + struct scaler *scl, + struct sclv_ratios_inits *inits) +{ + struct dal_context *ctx = scl->ctx; + uint32_t addr = scl->regs[IDX_SCL_HORZ_SCALE_RATIO]; + uint32_t value = dal_read_reg(ctx, addr); + + set_reg_field_value( + value, + inits->h_int_scale_ratio_luma, + SCLV_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dal_write_reg(ctx, addr, value); + + addr = scl->regs[IDX_SCL_VERT_SCALE_RATIO]; + value = dal_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->v_int_scale_ratio_luma, + SCLV_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dal_write_reg(ctx, addr, value); + + addr = scl->regs[IDX_SCL_HORZ_SCALE_RATIO_C]; + value = dal_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->h_int_scale_ratio_chroma, + SCLV_HORZ_FILTER_SCALE_RATIO_C, + SCL_H_SCALE_RATIO_C); + dal_write_reg(ctx, addr, value); + + addr = scl->regs[IDX_SCL_VERT_SCALE_RATIO_C]; + value = dal_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->v_int_scale_ratio_chroma, + SCLV_VERT_FILTER_SCALE_RATIO_C, + SCL_V_SCALE_RATIO_C); + dal_write_reg(ctx, addr, value); +} + +/* TODO: sync this one with DAL2 */ +static bool set_scaler_wrapper( + struct scaler *scl, + const struct scaler_data *data) +{ + bool is_scaling_required; + struct rect luma_viewport = {0}; + struct rect chroma_viewport = {0}; + struct dal_context *dal_ctx = scl->ctx; + + /* 1. Lock Scaler TODO: enable?*/ + /*set_scaler_update_lock(scl, true);*/ + + /* 2. Calculate viewport, viewport programming should happen after init + * calculations as they may require an adjustment in the viewport. + */ + + calculate_viewport(data, &luma_viewport, &chroma_viewport); + + /* 3. Program overscan */ + program_overscan(scl, &data->overscan); + + /* 4. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(scl, data); + + if (is_scaling_required) { + /* 5. Calculate and program ratio, filter initialization */ + + struct sclv_ratios_inits inits = { 0 }; + + calculate_inits( + scl, + data, + &inits, + &luma_viewport, + &chroma_viewport); + + program_scl_ratios_inits(scl, &inits); + + /*scaler coeff of 2-TAPS use hardware auto calculated value*/ + + /* 6. Program vertical filters */ + if (data->taps.v_taps > 2) { + program_two_taps_filter_vert(scl, false); + + if (!program_multi_taps_filter(scl, data, false)) { + dal_logger_write(dal_ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed vertical taps programming\n"); + return false; + } + } else + program_two_taps_filter_vert(scl, true); + + /* 7. Program horizontal filters */ + if (data->taps.h_taps > 2) { + program_two_taps_filter_horz(scl, false); + + if (!program_multi_taps_filter(scl, data, true)) { + dal_logger_write(dal_ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed horizontal taps programming\n"); + return false; + } + } else + program_two_taps_filter_horz(scl, true); + } + + /* 8. Program the viewport */ + if (data->flags.bits.SHOULD_PROGRAM_VIEWPORT) + program_viewport(scl, &luma_viewport, &chroma_viewport); + + /* 9. Unlock the Scaler TODO: enable?*/ + /* Every call to "set_scaler_update_lock(scl, TRUE)" + * must have a corresponding call to + * "set_scaler_update_lock(scl, FALSE)" */ + /*set_scaler_update_lock(scl, false);*/ + + /* TODO: investigate purpose/need of SHOULD_UNLOCK */ + if (data->flags.bits.SHOULD_UNLOCK == false) + set_coeff_update_complete(scl); + + return true; +} + +/*****************************************/ +/* Constructor, Destructor, fcn pointers */ +/*****************************************/ + +static void destroy(struct scaler **scl) +{ + dal_free(*scl); + *scl = NULL; +} + +static const struct scaler_funcs scaler_funcs = { + .set_scaler_bypass = set_scaler_bypass, + .is_scaling_enabled = is_scaling_enabled, + .set_scaler_wrapper = set_scaler_wrapper, + .destroy = destroy, + .get_optimal_taps_number = get_optimal_taps_number, + .get_next_lower_taps_number = get_next_lower_taps_number, + .get_viewport = get_viewport, +}; + +static bool scaler_v_dce110_construct( + struct scaler *scl, + struct scaler_init_data *init_data) +{ + enum controller_id id; + + if (!dal_scaler_construct(scl, init_data)) + return false; + + id = init_data->id; + + scl->regs = scl_underlay_regs[id - CONTROLLER_ID_UNDERLAY0]; + + scl->funcs = &scaler_funcs; + return true; +} + +struct scaler *dal_scaler_v_dce110_create( + struct scaler_init_data *init_data) +{ + struct scaler *scl = dal_alloc(sizeof(struct scaler)); + + if (!scl) + return NULL; + + if (!scaler_v_dce110_construct(scl, init_data)) + goto fail; + + return scl; +fail: + dal_free(scl); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.h new file mode 100644 index 000000000000..158f9bc8fece --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/scaler_v_dce110.h @@ -0,0 +1,33 @@ +/* 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_V_DCE110_H__ +#define __DAL_SCALER_V_DCE110_H__ + +#include "../scaler.h" + +struct scaler *dal_scaler_v_dce110_create( + struct scaler_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.c new file mode 100644 index 000000000000..60b3855f3428 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.c @@ -0,0 +1,574 @@ +/* + * 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/logger_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "surface_dce110.h" + +enum sf_regs_idx { + IDX_GRPH_CONTROL, + IDX_PRESCALE_GRPH_CONTROL, + + IDX_GRPH_X_START, + IDX_GRPH_Y_START, + IDX_GRPH_X_END, + IDX_GRPH_Y_END, + IDX_GRPH_PITCH, + + IDX_HW_ROTATION, + IDX_LB_DESKTOP_HEIGHT, + + IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + IDX_GRPH_PRIMARY_SURFACE_ADDRESS, + IDX_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + IDX_GRPH_SECONDARY_SURFACE_ADDRESS, + + IDX_GRPH_UPDATE, + IDX_GRPH_FLIP_CONTROL, + IDX_GRPH_ENABLE, + + SF_REGS_IDX_SIZE +}; + +#define regs_for_surface(id)\ +[CONTROLLER_ID_D ## id - 1] = {\ + [IDX_GRPH_CONTROL] = mmDCP ## id ## _GRPH_CONTROL,\ + [IDX_PRESCALE_GRPH_CONTROL] = mmDCP ## id ## _PRESCALE_GRPH_CONTROL,\ + [IDX_GRPH_X_START] = mmDCP ## id ## _GRPH_X_START,\ + [IDX_GRPH_Y_START] = mmDCP ## id ## _GRPH_Y_START,\ + [IDX_GRPH_X_END] = mmDCP ## id ## _GRPH_X_END,\ + [IDX_GRPH_Y_END] = mmDCP ## id ## _GRPH_Y_END,\ + [IDX_GRPH_PITCH] = mmDCP ## id ## _GRPH_PITCH,\ + [IDX_HW_ROTATION] = mmDCP ## id ## _HW_ROTATION,\ + [IDX_LB_DESKTOP_HEIGHT] = mmLB ## id ## _LB_DESKTOP_HEIGHT,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH] =\ + mmDCP ## id ## _GRPH_PRIMARY_SURFACE_ADDRESS_HIGH,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS] =\ + mmDCP ## id ## _GRPH_PRIMARY_SURFACE_ADDRESS,\ + [IDX_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH] =\ + mmDCP ## id ## _GRPH_SECONDARY_SURFACE_ADDRESS_HIGH,\ + [IDX_GRPH_SECONDARY_SURFACE_ADDRESS] =\ + mmDCP ## id ## _GRPH_SECONDARY_SURFACE_ADDRESS,\ + [IDX_GRPH_UPDATE] = mmDCP ## id ## _GRPH_UPDATE,\ + [IDX_GRPH_FLIP_CONTROL] = mmDCP ## id ## _GRPH_FLIP_CONTROL,\ + [IDX_GRPH_ENABLE] = mmDCP ## id ## _GRPH_ENABLE,\ +} + + +static const uint32_t sf_regs[][SF_REGS_IDX_SIZE] = { + regs_for_surface(0), + regs_for_surface(1), + regs_for_surface(2), +}; + + +/* +#define FROM_SURFACE(ptr) \ + container_of((ptr), struct surface_dce110, base) +*/ + +static void program_pixel_format( + struct surface *sf, + enum surface_pixel_format format) +{ + /*TODOunion GRPH_SWAP_CNTL grph_swap_cntl;*/ + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_BEGIN && + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + uint32_t value; + /*TODO: + grph_swap_cntl.u32All=0; + + if (format == PIXEL_FORMAT_GRPH_ARGB8888 || + format == PIXEL_FORMAT_GRPH_ABGR2101010 || + format == PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS || + format == PIXEL_FORMAT_GRPH_ABGR16161616F) { + grph_swap_cntl.bits.GRPH_RED_CROSSBAR = 2; + grph_swap_cntl.bits.GRPH_GREEN_CROSSBAR = 0; + grph_swap_cntl.bits.GRPH_BLUE_CROSSBAR = 2; + grph_swap_cntl.bits.GRPH_ALPHA_CROSSBAR = 0; + } else { + grph_swap_cntl.bits.GRPH_RED_CROSSBAR = 0; + grph_swap_cntl.bits.GRPH_GREEN_CROSSBAR = 0; + grph_swap_cntl.bits.GRPH_BLUE_CROSSBAR = 0; + grph_swap_cntl.bits.GRPH_ALPHA_CROSSBAR = 0; + } + dal_write_reg( + base->context, + mmGRPH_SWAP_CNTL+offset, + grph_swap_cntl.u32All);*/ + + + value = dal_read_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL]); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + set_reg_field_value( + value, 0, GRPH_CONTROL, GRPH_DEPTH); + set_reg_field_value( + value, 0, GRPH_CONTROL, GRPH_FORMAT); + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + set_reg_field_value( + value, 1, GRPH_CONTROL, GRPH_DEPTH); + set_reg_field_value( + value, 1, GRPH_CONTROL, GRPH_FORMAT); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: + set_reg_field_value( + value, 2, GRPH_CONTROL, GRPH_DEPTH); + set_reg_field_value( + value, 0, GRPH_CONTROL, GRPH_FORMAT); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + set_reg_field_value( + value, 2, GRPH_CONTROL, GRPH_DEPTH); + set_reg_field_value( + value, 1, GRPH_CONTROL, GRPH_FORMAT); + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + set_reg_field_value( + value, 3, GRPH_CONTROL, GRPH_DEPTH); + set_reg_field_value( + value, 0, GRPH_CONTROL, GRPH_FORMAT); + break; + default: + break; + } + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL], + value); + + /*TODO [hwentlan] MOVE THIS TO CONTROLLER GAMMA!!!!!*/ + value = dal_read_reg( + sf->ctx, + sf->regs[IDX_PRESCALE_GRPH_CONTROL]); + + if (format == SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F) { + set_reg_field_value( + value, 1, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_SELECT); + set_reg_field_value( + value, 1, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_R_SIGN); + set_reg_field_value( + value, 1, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_G_SIGN); + set_reg_field_value( + value, 1, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_B_SIGN); + } else { + set_reg_field_value( + value, 0, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_SELECT); + set_reg_field_value( + value, 0, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_R_SIGN); + set_reg_field_value( + value, 0, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_G_SIGN); + set_reg_field_value( + value, 0, PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_B_SIGN); + } + dal_write_reg( + sf->ctx, + sf->regs[IDX_PRESCALE_GRPH_CONTROL], + value); + } +} + +static void program_size_and_rotation( + struct surface *sf, + enum plane_rotation_angle rotation, + const union plane_size *plane_size) +{ + uint32_t value = 0; + union plane_size local_size = *plane_size; + + if (rotation == PLANE_ROTATION_ANGLE_90 || + rotation == PLANE_ROTATION_ANGLE_270) { + + uint32_t swap; + + swap = local_size.grph.surface_size.x; + local_size.grph.surface_size.x = + local_size.grph.surface_size.y; + local_size.grph.surface_size.y = swap; + + swap = local_size.grph.surface_size.width; + local_size.grph.surface_size.width = + local_size.grph.surface_size.height; + local_size.grph.surface_size.height = swap; + } + + set_reg_field_value(value, local_size.grph.surface_size.x, + GRPH_X_START, GRPH_X_START); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_START], + value); + + value = 0; + set_reg_field_value(value, local_size.grph.surface_size.y, + GRPH_Y_START, GRPH_Y_START); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_START], + value); + + value = 0; + set_reg_field_value(value, local_size.grph.surface_size.width, + GRPH_X_END, GRPH_X_END); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_END], + value); + + value = 0; + set_reg_field_value(value, local_size.grph.surface_size.height, + GRPH_Y_END, GRPH_Y_END); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_END], + value); + + value = 0; + set_reg_field_value(value, local_size.grph.surface_pitch, + GRPH_PITCH, GRPH_PITCH); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PITCH], + value); + + + value = 0; + switch (rotation) { + case PLANE_ROTATION_ANGLE_90: + set_reg_field_value(value, 3, + HW_ROTATION, GRPH_ROTATION_ANGLE); + break; + case PLANE_ROTATION_ANGLE_180: + set_reg_field_value(value, 2, + HW_ROTATION, GRPH_ROTATION_ANGLE); + break; + case PLANE_ROTATION_ANGLE_270: + set_reg_field_value(value, 1, + HW_ROTATION, GRPH_ROTATION_ANGLE); + break; + default: + set_reg_field_value(value, 0, + HW_ROTATION, GRPH_ROTATION_ANGLE); + break; + } + dal_write_reg( + sf->ctx, + sf->regs[IDX_HW_ROTATION], + value); +} + +static void program_tiling( + struct surface *sf, + const union plane_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + uint32_t value = 0; + + value = dal_read_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL]); + + set_reg_field_value(value, info->grph.NUM_BANKS, + GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->grph.BANK_WIDTH, + GRPH_CONTROL, GRPH_BANK_WIDTH); + + set_reg_field_value(value, info->grph.BANK_HEIGHT, + GRPH_CONTROL, GRPH_BANK_HEIGHT); + + set_reg_field_value(value, info->grph.TILE_ASPECT, + GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT); + + set_reg_field_value(value, info->grph.TILE_SPLIT, + GRPH_CONTROL, GRPH_TILE_SPLIT); + + set_reg_field_value(value, info->grph.TILE_MODE, + GRPH_CONTROL, GRPH_MICRO_TILE_MODE); + + set_reg_field_value(value, info->grph.PIPE_CONFIG, + GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->grph.ARRAY_MODE, + GRPH_CONTROL, GRPH_ARRAY_MODE); + + set_reg_field_value(value, 1, + GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); + + set_reg_field_value(value, 0, + GRPH_CONTROL, GRPH_Z); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL], + value); +} + +static void program_pri_addr( + struct surface *sf, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + + /*high register MUST be programmed first*/ + temp = address.high_part & +GRPH_PRIMARY_SURFACE_ADDRESS_HIGH__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_MASK; + + set_reg_field_value(value, temp, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH], + value); + + temp = 0; + value = 0; + temp = address.low_part >> + GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS__SHIFT; + + set_reg_field_value(value, temp, + GRPH_PRIMARY_SURFACE_ADDRESS, + GRPH_PRIMARY_SURFACE_ADDRESS); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS], + value); +} + +static void program_sec_addr( + struct surface *sf, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + /*high register MUST be programmed first*/ + temp = address.high_part & +GRPH_SECONDARY_SURFACE_ADDRESS_HIGH__GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_MASK; + + set_reg_field_value(value, temp, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH], + value); + + temp = 0; + value = 0; + temp = address.low_part >> + GRPH_SECONDARY_SURFACE_ADDRESS__GRPH_SECONDARY_SURFACE_ADDRESS__SHIFT; + + set_reg_field_value(value, temp, + GRPH_SECONDARY_SURFACE_ADDRESS, + GRPH_SECONDARY_SURFACE_ADDRESS); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_SECONDARY_SURFACE_ADDRESS], + value); +} + +/* +static void program_sec_video_surface_addr( + uint32_t offset, + const PHYSICAL_ADDRESS_LOC *address_luma, + const PHYSICAL_ADDRESS_LOC *address_chroma) +{ + NOT_IMPLEMENTED(); +} + +static void program_sec_video_surface_bottom_addr( + uint32_t offset, + const PHYSICAL_ADDRESS_LOC *address_luma, + const PHYSICAL_ADDRESS_LOC *address_chroma) +{ + NOT_IMPLEMENTED(); +} + +static void program_pri_video_surface_addr( + uint32_t offset, + const PHYSICAL_ADDRESS_LOC *address_luma, + const PHYSICAL_ADDRESS_LOC *address_chroma) +{ + NOT_IMPLEMENTED(); +} + +static void program_pri_video_surface_bottom_addr( + uint32_t offset, + const PHYSICAL_ADDRESS_LOC *address_luma, + const PHYSICAL_ADDRESS_LOC *address_chroma) +{ + NOT_IMPLEMENTED(); +} +*/ + +static void program_addr( + struct surface *sf, + const struct plane_address *addr) +{ + switch (addr->type) { + case PLN_ADDR_TYPE_GRAPHICS: + program_pri_addr( + sf, + addr->grph.addr); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + program_pri_addr( + sf, + addr->grph_stereo.left_addr); + program_sec_addr( + sf, + addr->grph_stereo.right_addr); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + case PLN_ADDR_TYPE_VIDEO_INTERLACED: + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE_STEREO: + case PLN_ADDR_TYPE_VIDEO_INTERLACED_STEREO: + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + } +} + +static void set_flip_control( + struct surface *sf, + bool immediate) +{ + uint32_t value = 0; + + value = dal_read_reg( + sf->ctx, + sf->regs[IDX_GRPH_FLIP_CONTROL]); + set_reg_field_value(value, 0, + GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_IMMEDIATE_EN); + set_reg_field_value(value, 0, + GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_H_RETRACE_EN); + if (immediate == true) + set_reg_field_value(value, 1, + GRPH_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_IMMEDIATE_EN); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_FLIP_CONTROL], + value); +} + +/*TODO: move to base class (isr.c) +static bool is_phy_addr_equal( + const PHYSICAL_ADDRESS_LOC *addr, + const PHYSICAL_ADDRESS_LOC *cached_addr) +{ + return false; +} +*/ + +static void enable(struct surface *sf) +{ + uint32_t value = 0; + + value = dal_read_reg(sf->ctx, sf->regs[IDX_GRPH_ENABLE]); + set_reg_field_value(value, 1, GRPH_ENABLE, GRPH_ENABLE); + dal_write_reg(sf->ctx, sf->regs[IDX_GRPH_ENABLE], value); +} + + +/*****************************************/ +/* Constructor, Destructor, fcn pointers */ +/*****************************************/ + +static void destroy(struct surface **sf) +{ + dal_free(*sf); + *sf = NULL; +} + +static const struct surface_funcs surface_funcs = { + .destroy = destroy, + .enable = enable, + .program_addr = program_addr, + .program_pixel_format = program_pixel_format, + .program_size_and_rotation = program_size_and_rotation, + .program_tiling = program_tiling, + .set_flip_control = set_flip_control +}; + +static bool surface_dce110_construct( + struct surface *sf, + struct surface_init_data *init_data) +{ + if (!dal_surface_construct(sf, init_data)) + return false; + + sf->regs = sf_regs[init_data->id - 1]; + + sf->funcs = &surface_funcs; + return true; +} + +struct surface *dal_surface_dce110_create( + struct surface_init_data *init_data) +{ + struct surface *sf = dal_alloc(sizeof(struct surface)); + + if (!sf) + return NULL; + + if (!surface_dce110_construct(sf, init_data)) + goto fail; + + return sf; +fail: + dal_free(sf); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.h new file mode 100644 index 000000000000..d3f273030b09 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/surface_dce110.h @@ -0,0 +1,35 @@ +/* + * 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_SURFACE__DCE110_H__ +#define __DAL_SURFACE__DCE110_H__ + +#include "../surface.h" + +struct surface *dal_surface_dce110_create( + struct surface_init_data *init_data); + + +#endif /*__DAL_SURFACE__DCE110_H__*/ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.c new file mode 100644 index 000000000000..245b68628f65 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.c @@ -0,0 +1,636 @@ +/* + * 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/logger_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "surface_v_dce110.h" + +enum sf_regs_idx { + IDX_GRPH_CONTROL, + IDX_GRPH_CONTROL_C, + IDX_GRPH_CONTROL_EXP, + IDX_PRESCALE_GRPH_CONTROL, + + IDX_GRPH_X_START_L, + IDX_GRPH_X_START_C, + IDX_GRPH_Y_START_L, + IDX_GRPH_Y_START_C, + IDX_GRPH_X_END_L, + IDX_GRPH_X_END_C, + IDX_GRPH_Y_END_L, + IDX_GRPH_Y_END_C, + IDX_GRPH_PITCH_L, + IDX_GRPH_PITCH_C, + + IDX_HW_ROTATION, + IDX_LB_DESKTOP_HEIGHT, + + IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + IDX_GRPH_PRIMARY_SURFACE_ADDRESS_L, + IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + IDX_GRPH_PRIMARY_SURFACE_ADDRESS_C, + IDX_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + IDX_GRPH_SECONDARY_SURFACE_ADDRESS, + + IDX_GRPH_UPDATE, + IDX_GRPH_ENABLE, + + SF_REGS_IDX_SIZE +}; + +#define regs_for_surface()\ +[0] = {\ + [IDX_GRPH_CONTROL] = mmUNP_GRPH_CONTROL,\ + [IDX_GRPH_CONTROL_C] = mmUNP_GRPH_CONTROL_C,\ + [IDX_GRPH_CONTROL_EXP] = mmUNP_GRPH_CONTROL_EXP,\ + [IDX_GRPH_X_START_L] = mmUNP_GRPH_X_START_L,\ + [IDX_GRPH_X_START_C] = mmUNP_GRPH_X_START_C,\ + [IDX_GRPH_Y_START_L] = mmUNP_GRPH_Y_START_L,\ + [IDX_GRPH_Y_START_C] = mmUNP_GRPH_Y_START_C,\ + [IDX_GRPH_X_END_L] = mmUNP_GRPH_X_END_L,\ + [IDX_GRPH_X_END_C] = mmUNP_GRPH_X_END_C,\ + [IDX_GRPH_Y_END_L] = mmUNP_GRPH_Y_END_L,\ + [IDX_GRPH_Y_END_C] = mmUNP_GRPH_Y_END_C,\ + [IDX_GRPH_PITCH_L] = mmUNP_GRPH_PITCH_L,\ + [IDX_GRPH_PITCH_C] = mmUNP_GRPH_PITCH_C,\ + [IDX_HW_ROTATION] = mmUNP_HW_ROTATION,\ + [IDX_LB_DESKTOP_HEIGHT] = mmLBV_DESKTOP_HEIGHT,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L] =\ + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS_L] =\ + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_L,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C] =\ + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C,\ + [IDX_GRPH_PRIMARY_SURFACE_ADDRESS_C] =\ + mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_C,\ + [IDX_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH] =\ + mmUNP_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_L,\ + [IDX_GRPH_SECONDARY_SURFACE_ADDRESS] =\ + mmUNP_GRPH_SECONDARY_SURFACE_ADDRESS_L,\ + [IDX_GRPH_UPDATE] = mmUNP_GRPH_UPDATE,\ + [IDX_GRPH_ENABLE] = mmUNP_GRPH_ENABLE,\ +} + +static const uint32_t sf_regs[][SF_REGS_IDX_SIZE] = { + regs_for_surface(), +}; + +/* +#define FROM_SURFACE(ptr) \ + container_of((ptr), struct surface_dce110, base) +*/ + +static void program_pixel_format( + struct surface *sf, + enum surface_pixel_format format) +{ + if (format >= SURFACE_PIXEL_FORMAT_VIDEO_444_BEGIN || + format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + uint32_t value; + uint32_t addr = sf->regs[IDX_GRPH_CONTROL]; + uint8_t grph_depth; + uint8_t grph_format; + + value = dal_read_reg(sf->ctx, addr); + + grph_depth = get_reg_field_value( + value, + UNP_GRPH_CONTROL, + GRPH_DEPTH); + grph_format = get_reg_field_value( + value, + UNP_GRPH_CONTROL, + GRPH_FORMAT); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + grph_depth = 1; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: + grph_depth = 2; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + grph_depth = 2; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + grph_depth = 3; + grph_format = 0; + break; + default: + break; + } + + set_reg_field_value( + value, + grph_depth, + UNP_GRPH_CONTROL, + GRPH_DEPTH); + set_reg_field_value( + value, + grph_format, + UNP_GRPH_CONTROL, + GRPH_FORMAT); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL], + value); + + addr = sf->regs[IDX_GRPH_CONTROL_EXP]; + value = dal_read_reg(sf->ctx, addr); + /* VIDEO FORMAT 0 */ + set_reg_field_value( + value, + 0, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + dal_write_reg(sf->ctx, addr, value); + } else { + /* Video 422 and 420 needs UNP_GRPH_CONTROL_EXP programmed */ + uint32_t value; + uint32_t addr = sf->regs[IDX_GRPH_CONTROL_EXP]; + uint8_t video_format; + + value = dal_read_reg(sf->ctx, addr); + + switch (format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + video_format = 2; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + video_format = 3; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCb: + video_format = 4; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_YCr: + video_format = 5; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CbY: + video_format = 6; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_422_CrY: + video_format = 7; + break; + default: + break; + } + + set_reg_field_value( + value, + video_format, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + + dal_write_reg(sf->ctx, addr, value); + } +} + + +static void program_size_and_rotation( + struct surface *sf, + enum plane_rotation_angle rotation, + const union plane_size *plane_size) +{ + uint32_t value = 0; + union plane_size local_size = *plane_size; + + if (rotation == PLANE_ROTATION_ANGLE_90 || + rotation == PLANE_ROTATION_ANGLE_270) { + + uint32_t swap; + + swap = local_size.video.luma_size.x; + local_size.video.luma_size.x = + local_size.video.luma_size.y; + local_size.video.luma_size.y = swap; + + swap = local_size.video.luma_size.width; + local_size.video.luma_size.width = + local_size.video.luma_size.height; + local_size.video.luma_size.height = swap; + + swap = local_size.video.chroma_size.x; + local_size.video.chroma_size.x = + local_size.video.chroma_size.y; + local_size.video.chroma_size.y = swap; + + swap = local_size.video.chroma_size.width; + local_size.video.chroma_size.width = + local_size.video.chroma_size.height; + local_size.video.chroma_size.height = swap; + } + + value = 0; + set_reg_field_value(value, local_size.video.luma_pitch, + UNP_GRPH_PITCH_L, GRPH_PITCH_L); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PITCH_L], + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_pitch, + UNP_GRPH_PITCH_C, GRPH_PITCH_C); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PITCH_C], + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_L, GRPH_X_START_L); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_START_L], + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_C, GRPH_X_START_C); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_START_C], + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_L, GRPH_Y_START_L); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_START_L], + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_C, GRPH_Y_START_C); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_START_C], + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.x + + local_size.video.luma_size.width, + UNP_GRPH_X_END_L, GRPH_X_END_L); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_END_L], + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.x + + local_size.video.chroma_size.width, + UNP_GRPH_X_END_C, GRPH_X_END_C); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_X_END_C], + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.y + + local_size.video.luma_size.height, + UNP_GRPH_Y_END_L, GRPH_Y_END_L); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_END_L], + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.y + + local_size.video.chroma_size.height, + UNP_GRPH_Y_END_C, GRPH_Y_END_C); + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_Y_END_C], + value); + + value = 0; + switch (rotation) { + case PLANE_ROTATION_ANGLE_90: + set_reg_field_value(value, 3, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case PLANE_ROTATION_ANGLE_180: + set_reg_field_value(value, 2, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case PLANE_ROTATION_ANGLE_270: + set_reg_field_value(value, 1, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + default: + set_reg_field_value(value, 0, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + } + dal_write_reg( + sf->ctx, + sf->regs[IDX_HW_ROTATION], + value); +} + +static void program_tiling( + struct surface *sf, + const union plane_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + uint32_t value = 0; + + value = dal_read_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL]); + + if (pixel_format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + + set_reg_field_value(value, info->grph.NUM_BANKS, + UNP_GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->grph.BANK_WIDTH, + UNP_GRPH_CONTROL, GRPH_BANK_WIDTH_L); + + set_reg_field_value(value, info->grph.BANK_HEIGHT, + UNP_GRPH_CONTROL, GRPH_BANK_HEIGHT_L); + + set_reg_field_value(value, info->grph.TILE_ASPECT, + UNP_GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT_L); + + set_reg_field_value(value, info->grph.TILE_MODE, + UNP_GRPH_CONTROL, GRPH_MICRO_TILE_MODE_L); + + set_reg_field_value(value, info->grph.TILE_SPLIT, + UNP_GRPH_CONTROL, GRPH_TILE_SPLIT_L); + + set_reg_field_value(value, info->grph.PIPE_CONFIG, + UNP_GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->grph.ARRAY_MODE, + UNP_GRPH_CONTROL, GRPH_ARRAY_MODE); + } else { + uint32_t value_chroma = 0; + + set_reg_field_value(value, info->video.NUM_BANKS, + UNP_GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->video.BANK_WIDTH_LUMA, + UNP_GRPH_CONTROL, GRPH_BANK_WIDTH_L); + + set_reg_field_value(value, info->video.BANK_HEIGHT_LUMA, + UNP_GRPH_CONTROL, GRPH_BANK_HEIGHT_L); + + set_reg_field_value(value, info->video.TILE_ASPECT_LUMA, + UNP_GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT_L); + + set_reg_field_value(value, info->video.TILE_MODE_LUMA, + UNP_GRPH_CONTROL, GRPH_MICRO_TILE_MODE_L); + + set_reg_field_value(value, info->video.TILE_SPLIT_LUMA, + UNP_GRPH_CONTROL, GRPH_TILE_SPLIT_L); + + set_reg_field_value(value, info->video.PIPE_CONFIG, + UNP_GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->video.ARRAY_MODE, + UNP_GRPH_CONTROL, GRPH_ARRAY_MODE); + + value_chroma = dal_read_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL_C]); + + set_reg_field_value(value_chroma, + info->video.BANK_WIDTH_CHROMA, + UNP_GRPH_CONTROL_C, GRPH_BANK_WIDTH_C); + + set_reg_field_value(value_chroma, + info->video.BANK_HEIGHT_CHROMA, + UNP_GRPH_CONTROL_C, GRPH_BANK_HEIGHT_C); + + set_reg_field_value(value_chroma, + info->video.TILE_ASPECT_CHROMA, + UNP_GRPH_CONTROL_C, GRPH_MACRO_TILE_ASPECT_C); + + set_reg_field_value(value_chroma, + info->video.TILE_MODE_CHROMA, + UNP_GRPH_CONTROL_C, GRPH_MICRO_TILE_MODE_C); + + set_reg_field_value(value_chroma, + info->video.TILE_SPLIT_CHROMA, + UNP_GRPH_CONTROL_C, GRPH_TILE_SPLIT_C); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL_C], + value_chroma); + } + + set_reg_field_value(value, 1, + UNP_GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); + + set_reg_field_value(value, 0, + UNP_GRPH_CONTROL, GRPH_Z); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_CONTROL], + value); +} + +static void program_pri_addr_l( + struct surface *sf, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + + /* high register MUST be programmed first */ + + temp = address.high_part & + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L], + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L__GRPH_PRIMARY_SURFACE_ADDRESS_L__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + GRPH_PRIMARY_SURFACE_ADDRESS_L); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS_L], + value); +} + +static void program_pri_addr_c( + struct surface *sf, + PHYSICAL_ADDRESS_LOC address) +{ + /* high register MUST be programmed first */ + uint32_t value = 0; + uint32_t temp = address.high_part & + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C], + value); + + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C__GRPH_PRIMARY_SURFACE_ADDRESS_C__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + GRPH_PRIMARY_SURFACE_ADDRESS_C); + + dal_write_reg( + sf->ctx, + sf->regs[IDX_GRPH_PRIMARY_SURFACE_ADDRESS_C], + value); +} + +static void program_addr( + struct surface *sf, + const struct plane_address *addr) +{ + switch (addr->type) { + case PLN_ADDR_TYPE_GRAPHICS: + program_pri_addr_l( + sf, + addr->grph.addr); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + program_pri_addr_l( + sf, + addr->video_progressive.luma_addr); + program_pri_addr_c( + sf, + addr->video_progressive.chroma_addr); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + case PLN_ADDR_TYPE_VIDEO_INTERLACED: + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE_STEREO: + case PLN_ADDR_TYPE_VIDEO_INTERLACED_STEREO: + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + } +} + +static void set_flip_control( + struct surface *sf, + bool immediate) +{ +} + + +static void enable(struct surface *sf) +{ + uint32_t value = 0; + + value = dal_read_reg(sf->ctx, sf->regs[IDX_GRPH_ENABLE]); + set_reg_field_value(value, 1, UNP_GRPH_ENABLE, GRPH_ENABLE); + dal_write_reg(sf->ctx, sf->regs[IDX_GRPH_ENABLE], value); +} + + +/*****************************************/ +/* Constructor, Destructor, fcn pointers */ +/*****************************************/ + +static void destroy(struct surface **sf) +{ + dal_free(*sf); + *sf = NULL; +} + +static const struct surface_funcs surface_funcs = { + .destroy = destroy, + .enable = enable, + .program_addr = program_addr, + .program_pixel_format = program_pixel_format, + .program_size_and_rotation = program_size_and_rotation, + .program_tiling = program_tiling, + .set_flip_control = set_flip_control +}; + +static bool surface_v_dce110_construct( + struct surface *sf, + struct surface_init_data *init_data) +{ + if (!dal_surface_construct(sf, init_data)) + return false; + + sf->regs = sf_regs[init_data->id - CONTROLLER_ID_UNDERLAY0]; + + sf->funcs = &surface_funcs; + return true; +} + +struct surface *dal_surface_v_dce110_create( + struct surface_init_data *init_data) +{ + struct surface *sf = dal_alloc(sizeof(struct surface)); + + if (!sf) + return NULL; + + if (!surface_v_dce110_construct(sf, init_data)) + goto fail; + + return sf; +fail: + dal_free(sf); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.h new file mode 100644 index 000000000000..039c1ece929b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/surface_v_dce110.h @@ -0,0 +1,34 @@ +/* + * 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_SURFACE_V_DCE110_H__ +#define __DAL_SURFACE_V_DCE110_H__ + +#include "../surface.h" + +struct surface *dal_surface_v_dce110_create( + struct surface_init_data *init_data); + +#endif /*__DAL_SURFACE_V_DCE110_H__*/ diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.c new file mode 100644 index 000000000000..639485ac4895 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.c @@ -0,0 +1,1122 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + + +#include "include/grph_object_id.h" +#include "include/adapter_service_interface.h" +#include "include/logger_interface.h" + +#include "timing_generator_dce110.h" +#include "../timing_generator.h" + +enum tg_regs_idx { + IDX_CRTC_UPDATE_LOCK, + IDX_CRTC_MASTER_UPDATE_LOCK, + IDX_CRTC_MASTER_UPDATE_MODE, + IDX_CRTC_H_TOTAL, + IDX_CRTC_V_TOTAL, + IDX_CRTC_H_BLANK_START_END, + IDX_CRTC_V_BLANK_START_END, + IDX_CRTC_H_SYNC_A, + IDX_CRTC_V_SYNC_A, + IDX_CRTC_H_SYNC_A_CNTL, + IDX_CRTC_V_SYNC_A_CNTL, + IDX_CRTC_INTERLACE_CONTROL, + IDX_CRTC_BLANK_CONTROL, + IDX_PIPE_PG_STATUS, + + IDX_CRTC_TEST_PATTERN_COLOR, + IDX_CRTC_TEST_PATTERN_CONTROL, + IDX_CRTC_TEST_PATTERN_PARAMETERS, + IDX_CRTC_FLOW_CONTROL, + IDX_CRTC_STATUS, + IDX_CRTC_STATUS_POSITION, + IDX_CRTC_STATUS_FRAME_COUNT, + IDX_CRTC_STEREO_CONTROL, + IDX_CRTC_STEREO_STATUS, + IDX_CRTC_STEREO_FORCE_NEXT_EYE, + IDX_CRTC_3D_STRUCTURE_CONTROL, + IDX_CRTC_DOUBLE_BUFFER_CONTROL, + IDX_CRTC_V_TOTAL_MIN, + IDX_CRTC_V_TOTAL_MAX, + IDX_CRTC_V_TOTAL_CONTROL, + IDX_CRTC_NOM_VERT_POSITION, + IDX_CRTC_STATIC_SCREEN_CONTROL, + IDX_CRTC_TRIGB_CNTL, + IDX_CRTC_FORCE_COUNT_CNTL, + IDX_CRTC_GSL_CONTROL, + + IDX_CRTC_CONTROL, + IDX_CRTC_START_LINE_CONTROL, + IDX_CRTC_COUNT_CONTROL, + + IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT, + IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM, + IDX_DCP_GSL_CONTROL, + IDX_GRPH_UPDATE, + + IDX_CRTC_VBI_END, + + IDX_BLND_UNDERFLOW_INTERRUPT, + TG_REGS_IDX_SIZE +}; + +#define regs_for_controller(id)\ +[CONTROLLER_ID_D ## id - 1] =\ +{[IDX_CRTC_UPDATE_LOCK] = mmCRTC ## id ## _CRTC_UPDATE_LOCK,\ +[IDX_CRTC_MASTER_UPDATE_LOCK] = mmCRTC ## id ## _CRTC_MASTER_UPDATE_LOCK,\ +[IDX_CRTC_MASTER_UPDATE_MODE] = mmCRTC ## id ## _CRTC_MASTER_UPDATE_MODE,\ +[IDX_CRTC_H_TOTAL] = mmCRTC ## id ## _CRTC_H_TOTAL,\ +[IDX_CRTC_V_TOTAL] = mmCRTC ## id ## _CRTC_V_TOTAL,\ +[IDX_CRTC_H_BLANK_START_END] = mmCRTC ## id ## _CRTC_H_BLANK_START_END,\ +[IDX_CRTC_V_BLANK_START_END] = mmCRTC ## id ## _CRTC_V_BLANK_START_END,\ +[IDX_CRTC_H_SYNC_A] = mmCRTC ## id ## _CRTC_H_SYNC_A,\ +[IDX_CRTC_V_SYNC_A] = mmCRTC ## id ## _CRTC_V_SYNC_A,\ +[IDX_CRTC_H_SYNC_A_CNTL] = mmCRTC ## id ## _CRTC_H_SYNC_A_CNTL,\ +[IDX_CRTC_V_SYNC_A_CNTL] = mmCRTC ## id ## _CRTC_V_SYNC_A_CNTL,\ +[IDX_CRTC_INTERLACE_CONTROL] = mmCRTC ## id ## _CRTC_INTERLACE_CONTROL,\ +[IDX_CRTC_BLANK_CONTROL] = mmCRTC ## id ## _CRTC_BLANK_CONTROL,\ +[IDX_PIPE_PG_STATUS] = mmPIPE ## id ## _PG_STATUS,\ +[IDX_CRTC_TEST_PATTERN_COLOR] = mmCRTC ## id ## _CRTC_TEST_PATTERN_COLOR,\ +[IDX_CRTC_TEST_PATTERN_CONTROL] = mmCRTC ## id ## _CRTC_TEST_PATTERN_CONTROL,\ +[IDX_CRTC_TEST_PATTERN_PARAMETERS] =\ +mmCRTC ## id ## _CRTC_TEST_PATTERN_PARAMETERS,\ +[IDX_CRTC_FLOW_CONTROL] = mmCRTC ## id ## _CRTC_FLOW_CONTROL,\ +[IDX_CRTC_STATUS] = mmCRTC ## id ## _CRTC_STATUS,\ +[IDX_CRTC_STATUS_POSITION] = mmCRTC ## id ## _CRTC_STATUS_POSITION,\ +[IDX_CRTC_STATUS_FRAME_COUNT] = mmCRTC ## id ## _CRTC_STATUS_FRAME_COUNT,\ +[IDX_CRTC_STEREO_CONTROL] = mmCRTC ## id ## _CRTC_STEREO_CONTROL,\ +[IDX_CRTC_STEREO_STATUS] = mmCRTC ## id ## _CRTC_STEREO_STATUS,\ +[IDX_CRTC_STEREO_FORCE_NEXT_EYE] = \ +mmCRTC ## id ## _CRTC_STEREO_FORCE_NEXT_EYE,\ +[IDX_CRTC_3D_STRUCTURE_CONTROL] = mmCRTC ## id ## _CRTC_3D_STRUCTURE_CONTROL,\ +[IDX_CRTC_DOUBLE_BUFFER_CONTROL] =\ +mmCRTC ## id ## _CRTC_DOUBLE_BUFFER_CONTROL,\ +[IDX_CRTC_V_TOTAL_MIN] = mmCRTC ## id ## _CRTC_V_TOTAL_MIN,\ +[IDX_CRTC_V_TOTAL_MAX] = mmCRTC ## id ## _CRTC_V_TOTAL_MAX,\ +[IDX_CRTC_V_TOTAL_CONTROL] = mmCRTC ## id ## _CRTC_V_TOTAL_CONTROL,\ +[IDX_CRTC_NOM_VERT_POSITION] = mmCRTC ## id ## _CRTC_NOM_VERT_POSITION,\ +[IDX_CRTC_STATIC_SCREEN_CONTROL] =\ +mmCRTC ## id ## _CRTC_STATIC_SCREEN_CONTROL,\ +[IDX_CRTC_TRIGB_CNTL] = mmCRTC ## id ## _CRTC_TRIGB_CNTL,\ +[IDX_CRTC_FORCE_COUNT_CNTL] = mmCRTC ## id ## _CRTC_FORCE_COUNT_NOW_CNTL,\ +[IDX_CRTC_GSL_CONTROL] = mmCRTC ## id ## _CRTC_GSL_CONTROL,\ +[IDX_CRTC_CONTROL] = mmCRTC ## id ## _CRTC_CONTROL,\ +[IDX_CRTC_START_LINE_CONTROL] = mmCRTC ## id ## _CRTC_START_LINE_CONTROL,\ +[IDX_CRTC_COUNT_CONTROL] = mmCRTC ## id ## _CRTC_COUNT_CONTROL,\ +[IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT] = mmSCL ## id ## _EXT_OVERSCAN_LEFT_RIGHT,\ +[IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM] = mmSCL ## id ## _EXT_OVERSCAN_TOP_BOTTOM,\ +[IDX_DCP_GSL_CONTROL] = mmDCP ## id ## _DCP_GSL_CONTROL,\ +[IDX_GRPH_UPDATE] = mmDCP ## id ## _GRPH_UPDATE,\ +[IDX_CRTC_VBI_END] = mmCRTC ## id ## _CRTC_VBI_END,\ +[IDX_BLND_UNDERFLOW_INTERRUPT] = mmBLND ## id ## _BLND_UNDERFLOW_INTERRUPT,\ +} + +static uint32_t tg_regs[][TG_REGS_IDX_SIZE] = { + regs_for_controller(0), + regs_for_controller(1), + regs_for_controller(2), +}; + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1) + +#define FROM_TIMING_GENERATOR(tg)\ + container_of(tg, struct timing_generator_dce110, base) + +/* get_scanout_position() return flags */ +#define DAL_CRTC_DRM_SCANOUTPOS_VALID (1 << 0) +#define DAL_CRTC_DRM_SCANOUTPOS_INVBL (1 << 1) +#define DAL_CRTC_DRM_SCANOUTPOS_ACCURATE (1 << 2) + + + +static void timing_generator_dce110_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + uint32_t address = tg->regs[IDX_CRTC_CONTROL]; + + regval = dal_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTC_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dal_write_reg(tg->ctx, address, regval); +} + +/** + * Enable CRTC + * Enable CRTC - call ASIC Control Object to enable Timing generator. + */ +static bool enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + /* 0 value is needed by DRR and is also suggested default value for CZ + */ + uint32_t value; + + value = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_MASTER_UPDATE_MODE]); + set_reg_field_value(value, 3, + CRTC_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); + dal_write_reg(tg->ctx, + tg->regs[IDX_CRTC_MASTER_UPDATE_MODE], value); + + result = dal_bios_parser_enable_crtc(tg->bp, tg->controller_id, true); + + return result == BP_RESULT_OK; +} + +/** + * blank_crtc + * Call ASIC Control Object to Blank CRTC. + */ +static bool blank_crtc( + struct timing_generator *tg, + enum color_space color_space) +{ + enum bp_result result = BP_RESULT_OK; + struct bp_blank_crtc_parameters params; + struct crtc_black_color black_color; + + dal_timing_generator_color_space_to_black_color( + color_space, + &black_color); + + dal_memset(¶ms, 0, sizeof(struct bp_blank_crtc_parameters)); + params.controller_id = tg->controller_id; + params.black_color_rcr = black_color.black_color_r_cr; + params.black_color_gy = black_color.black_color_g_y; + params.black_color_bcb = black_color.black_color_b_cb; + + result = dal_bios_parser_blank_crtc(tg->bp, ¶ms, true); + + { + uint32_t addr = tg->regs[IDX_CRTC_BLANK_CONTROL]; + uint32_t value; + uint8_t counter = 34; + + while (counter > 0) { + value = dal_read_reg(tg->ctx, addr); + + if (get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + break; + + dal_sleep_in_milliseconds(1); + counter--; + } + + if (counter == 0) { + dal_logger_write( + tg->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait for update exceeded\n", + __func__); + } + } + + return result == BP_RESULT_OK; +} + +/** + * unblank_crtc + * Call ASIC Control Object to UnBlank CRTC. + */ +static bool unblank_crtc( + struct timing_generator *tg, + enum color_space color_space) +{ + enum bp_result result; + struct bp_blank_crtc_parameters bp_params; + struct crtc_black_color value; + + dal_timing_generator_color_space_to_black_color( + color_space, + &value); + + dal_memset(&bp_params, 0, + sizeof(struct bp_blank_crtc_parameters)); + bp_params.controller_id = tg->controller_id; + bp_params.black_color_rcr = value.black_color_r_cr; + bp_params.black_color_gy = value.black_color_g_y; + bp_params.black_color_bcb = value.black_color_b_cb; + + result = dal_bios_parser_blank_crtc(tg->bp, &bp_params, false); + + return result == BP_RESULT_OK; +} + +/** + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool is_in_vertical_blank(struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + + addr = tg->regs[IDX_CRTC_STATUS]; + value = dal_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + +/** + ***************************************************************************** + * Function: is_counter_moving + * + * @brief + * check if the timing generator is currently going + * + * @return + * true if currently going, false if currently paused or stopped. + * + ***************************************************************************** + */ +static bool is_counter_moving(struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value_1 = 0; + uint32_t field_1 = 0; + uint32_t value_2 = 0; + uint32_t field_2 = 0; + + addr = tg->regs[IDX_CRTC_STATUS_POSITION]; + value_1 = dal_read_reg(tg->ctx, addr); + value_2 = dal_read_reg(tg->ctx, addr); + + field_1 = get_reg_field_value( + value_1, CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + field_2 = get_reg_field_value( + value_2, CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + + if (field_1 == field_2) { + field_1 = get_reg_field_value( + value_1, CRTC_STATUS_POSITION, CRTC_VERT_COUNT); + field_2 = get_reg_field_value( + value_2, CRTC_STATUS_POSITION, CRTC_VERT_COUNT); + return field_1 != field_2; + } + + return true; +} + +/** + ***************************************************************************** + * Function: disable_stereo + * + * @brief + * Disables active stereo on controller + * Frame Packing need to be disabled in vBlank or when CRTC not running + ***************************************************************************** + */ +#if 0 +@TODOSTEREO +static void disable_stereo(struct timing_generator *tg) +{ + uint32_t addr = tg->regs[IDX_CRTC_3D_STRUCTURE_CONTROL]; + uint32_t value = 0; + uint32_t test = 0; + uint32_t field = 0; + uint32_t struc_en = 0; + uint32_t struc_stereo_sel_ovr = 0; + + value = dal_read_reg(tg->ctx, addr); + struc_en = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_EN); + + struc_stereo_sel_ovr = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_STEREO_SEL_OVR); + + /* + * When disabling Frame Packing in 2 step mode, we need to program both + * registers at the same frame + * Programming it in the beginning of VActive makes sure we are ok + */ + + if (struc_en != 0 && struc_stereo_sel_ovr == 0) { + tg->funcs->wait_for_vblank(tg); + tg->funcs->wait_for_vactive(tg); + } + + value = 0; + dal_write_reg(tg->ctx, addr, value); + + + addr = tg->regs[IDX_CRTC_STEREO_CONTROL]; + dal_write_reg(tg->ctx, addr, value); +} +#endif + +/** + * disable_crtc - call ASIC Control Object to disable Timing generator. + */ +static bool disable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + result = dal_bios_parser_enable_crtc(tg->bp, tg->controller_id, false); + + /* Need to make sure stereo is disabled according to the DCE5.0 spec */ + + /* + * @TODOSTEREO call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + + return result == BP_RESULT_OK; +} +/** +* program_pixel_repetition +* Programs Pixel Repetition Count field - DxCRTC_COUNT_CONTROL +* (CEA video formats with native pixel clock rates below 25 MHz require +* pixel-repetition in order to be carried across TMDS link; 720x480i, 720x576i +* CEA video formats timings shall always be pixel-repeated. +*/ +static void program_pixel_repetition( + struct timing_generator *tg, + uint32_t repeat_cnt) +{ + uint32_t regval; + + ASSERT((repeat_cnt > 0) && (repeat_cnt < 10)); + + regval = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_COUNT_CONTROL]); + + set_reg_field_value(regval, (repeat_cnt - 1), CRTC_COUNT_CONTROL, + CRTC_HORZ_REPETITION_COUNT); + + dal_write_reg(tg->ctx, + tg->regs[IDX_CRTC_COUNT_CONTROL], regval); +} + +/** +* program_horz_count_by_2 +* Programs DxCRTC_HORZ_COUNT_BY2_EN - 1 for DVI 30bpp mode, 0 otherwise +* +*/ +static void program_horz_count_by_2( + struct timing_generator *tg, + const struct hw_crtc_timing *timing) +{ + uint32_t regval; + + regval = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_COUNT_CONTROL]); + + set_reg_field_value(regval, 0, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + if (timing->flags.HORZ_COUNT_BY_TWO) + set_reg_field_value(regval, 1, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + dal_write_reg(tg->ctx, + tg->regs[IDX_CRTC_COUNT_CONTROL], regval); +} + +/** + * program_timing_generator + * Program CRTC Timing Registers - DxCRTC_H_*, DxCRTC_V_*, Pixel repetition. + * Call ASIC Control Object to program Timings. + */ +static bool program_timing_generator( + struct timing_generator *tg, + struct hw_crtc_timing *hw_crtc_timing) +{ + enum bp_result result; + struct bp_hw_crtc_timing_parameters bp_params; + uint32_t regval; + + dal_memset(&bp_params, 0, sizeof(struct bp_hw_crtc_timing_parameters)); + + /* Due to an asic bug we need to apply the Front Porch workaround prior + * to programming the timing. + */ + dal_timing_generator_apply_front_porch_workaround(tg, hw_crtc_timing); + + bp_params.controller_id = tg->controller_id; + + bp_params.h_total = hw_crtc_timing->h_total; + bp_params.h_addressable = + hw_crtc_timing->h_addressable; + bp_params.v_total = hw_crtc_timing->v_total; + bp_params.v_addressable = hw_crtc_timing->v_addressable; + + bp_params.h_sync_start = hw_crtc_timing->h_sync_start; + bp_params.h_sync_width = hw_crtc_timing->h_sync_width; + bp_params.v_sync_start = hw_crtc_timing->v_sync_start; + bp_params.v_sync_width = hw_crtc_timing->v_sync_width; + + /* Set overscan */ + bp_params.h_overscan_left = + hw_crtc_timing->h_overscan_left; + bp_params.h_overscan_right = + hw_crtc_timing->h_overscan_right; + bp_params.v_overscan_top = hw_crtc_timing->v_overscan_top; + bp_params.v_overscan_bottom = + hw_crtc_timing->v_overscan_bottom; + + /* Set flags */ + if (hw_crtc_timing->flags.HSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.HSYNC_POSITIVE_POLARITY = 1; + + if (hw_crtc_timing->flags.VSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.VSYNC_POSITIVE_POLARITY = 1; + + if (hw_crtc_timing->flags.INTERLACED == 1) + bp_params.flags.INTERLACE = 1; + + if (hw_crtc_timing->flags.HORZ_COUNT_BY_TWO == 1) + bp_params.flags.HORZ_COUNT_BY_TWO = 1; + + result = dal_bios_parser_program_crtc_timing(tg->bp, &bp_params); + + program_pixel_repetition(tg, hw_crtc_timing->flags.PIXEL_REPETITION); + + program_horz_count_by_2(tg, hw_crtc_timing); + + + regval = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_START_LINE_CONTROL]); + + if (dal_timing_generator_get_vsynch_and_front_porch_size(hw_crtc_timing) <= 3) { + set_reg_field_value(regval, 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + set_reg_field_value(regval, 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value(regval, 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + set_reg_field_value(regval, 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + dal_write_reg(tg->ctx, + tg->regs[IDX_CRTC_START_LINE_CONTROL], regval); + + /* Enable stereo - only when we need to pack 3D frame. Other types + * of stereo handled in explicit call */ + + /* TODOSTEREO + if (hw_crtc_timing->flags.PACK_3D_FRAME) { + struct crtc_stereo_parameters stereo_params = { false }; + stereo_params.PROGRAM_STEREO = true; + stereo_params.PROGRAM_POLARITY = true; + stereo_params.FRAME_PACKED = true; + stereo_params.RIGHT_EYE_POLARITY = + hw_crtc_timing->flags.RIGHT_EYE_3D_POLARITY; + tg->funcs->enable_stereo(tg, &stereo_params); + }*/ + + return result == BP_RESULT_OK; +} + +/** + ***************************************************************************** + * Function: program_drr + * + * @brief + * Program dynamic refresh rate registers m_DxCRTC_V_TOTAL_*. + * + * @param [in] pHwCrtcTiming: point to HwCrtcTiming struct + ***************************************************************************** + */ +static void program_drr( + struct timing_generator *tg, + const struct hw_ranged_timing *timing) +{ + /* register values */ + uint32_t v_total_min = 0; + uint32_t v_total_max = 0; + uint32_t v_total_cntl = 0; + uint32_t static_screen_cntl = 0; + + uint32_t addr = 0; + + addr = tg->regs[IDX_CRTC_V_TOTAL_MIN]; + v_total_min = dal_read_reg(tg->ctx, addr); + + addr = tg->regs[IDX_CRTC_V_TOTAL_MAX]; + v_total_max = dal_read_reg(tg->ctx, addr); + + addr = tg->regs[IDX_CRTC_V_TOTAL_CONTROL]; + v_total_cntl = dal_read_reg(tg->ctx, addr); + + addr = tg->regs[IDX_CRTC_STATIC_SCREEN_CONTROL]; + static_screen_cntl = dal_read_reg(tg->ctx, addr); + + if (timing != NULL) { + /* Set Static Screen trigger events + * If CRTC_SET_V_TOTAL_MIN_MASK_EN is set, use legacy event mask + * register + */ + if (get_reg_field_value( + v_total_cntl, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK_EN)) { + set_reg_field_value(v_total_cntl, + /* TODO: add implementation + translate_to_dce_static_screen_events( + timing->control.event_mask.u_all), + */ 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + } else { + set_reg_field_value(static_screen_cntl, + /* TODO: add implementation + translate_to_dce_static_screen_events( + timing->control.event_mask.u_all), + */ 0, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + } + + /* Number of consecutive static screen frames before interrupt + * is triggered. 0 is an invalid setting, which means we should + * leaving HW setting unchanged. */ + if (timing->control.static_frame_count != 0) { + set_reg_field_value( + static_screen_cntl, + timing->control.static_frame_count, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_FRAME_COUNT); + } + + /* This value is reduced by 1 based on the register definition + * of the VTOTAL value: + * CRTC_V_TOTAL should be set to Vertical total minus one. (E.g. + * for 525 lines, set to 524 = 0x20C) + */ + set_reg_field_value(v_total_min, + timing->vertical_total_min, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + set_reg_field_value(v_total_max, + timing->vertical_total_max, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + + /* set VTotalControl value according to ranged timing control. + */ + + if (timing->vertical_total_min != 0) { + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + } + if (timing->vertical_total_max != 0) { + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + } + set_reg_field_value(v_total_cntl, + timing->control.force_lock_on_event, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + timing->control.lock_to_master_vsync, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + set_reg_field_value(static_screen_cntl, + 0, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + set_reg_field_value(v_total_min, + 0, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + set_reg_field_value(v_total_max, + 0, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + } + + addr = tg->regs[IDX_CRTC_V_TOTAL_MIN]; + dal_write_reg(tg->ctx, addr, v_total_min); + + addr = tg->regs[IDX_CRTC_V_TOTAL_MAX]; + dal_write_reg(tg->ctx, addr, v_total_max); + + addr = tg->regs[IDX_CRTC_V_TOTAL_CONTROL]; + dal_write_reg(tg->ctx, addr, v_total_cntl); + + addr = tg->regs[IDX_CRTC_STATIC_SCREEN_CONTROL]; + dal_write_reg(tg->ctx, addr, static_screen_cntl); +} + +/* + * get_vblank_counter + * + * @brief + * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which + * holds the counter of frames. + * + * @param + * struct timing_generator *tg - [in] timing generator which controls the + * desired CRTC + * + * @return + * Counter of frames, which should equal to number of vblanks. + */ +static uint32_t get_vblank_counter(struct timing_generator *tg) +{ + uint32_t addr = tg->regs[IDX_CRTC_STATUS_FRAME_COUNT]; + uint32_t value = dal_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + + +/** + ***************************************************************************** + * Function: get_crtc_scanoutpos + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] vpos, hpos + ***************************************************************************** + */ +static uint32_t get_crtc_scanoutpos( + struct timing_generator *tg, + int32_t *vbl, + int32_t *position) +{ + /* @TODO: Update the implementation once caller is updated + * WARNING!! This function is returning the whole register value + * because the caller is expecting it instead of proper vertical and + * horizontal position. This should be a temporary implementation + * until the caller is updated. */ + + + *vbl = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_V_BLANK_START_END]); + + *position = dal_read_reg(tg->ctx, + tg->regs[IDX_CRTC_STATUS_POSITION]); + + /* @TODO: return value should indicate if current + * crtc is inside vblank*/ + return 0; +} + +static void set_lock_master(struct timing_generator *tg, bool lock) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t addr = tg->regs[IDX_CRTC_MASTER_UPDATE_LOCK]; + uint32_t value = dal_read_reg(dal_ctx, addr); + + set_reg_field_value( + value, + lock ? 1 : 0, + CRTC_MASTER_UPDATE_LOCK, + MASTER_UPDATE_LOCK); + + dal_write_reg(dal_ctx, addr, value); +} + +/* TODO: is it safe to assume that mask/shift of Primary and Underlay + * are the same? + * For example: today CRTC_H_TOTAL == CRTCV_H_TOTAL but is it always + * guaranteed? */ +static void program_blanking( + struct timing_generator *tg, + const struct hw_crtc_timing *timing) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = tg->regs[IDX_CRTC_H_TOTAL]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTC_H_TOTAL, + CRTC_H_TOTAL); + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_V_TOTAL]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_H_BLANK_START_END]; + value = dal_read_reg(dal_ctx, addr); + + tmp = timing->h_total - + (timing->h_sync_start + timing->h_overscan_left); + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_overscan_left + timing->h_overscan_right; + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_V_BLANK_START_END]; + value = dal_read_reg(dal_ctx, addr); + + tmp = timing->v_total - (timing->v_sync_start + timing->v_overscan_top); + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_overscan_top + + timing->v_overscan_bottom; + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dal_write_reg(dal_ctx, addr, value); +} + +static void set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum crtc_color_depth color_depth) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t value; + uint32_t addr; + + /* TODO: add support for other test patterns */ + switch (test_pattern) { + default: + value = 0; + addr = tg->regs[IDX_CRTC_TEST_PATTERN_PARAMETERS]; + + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_TEST_PATTERN_CONTROL]; + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + /* add color depth translation here */ + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + dal_write_reg(dal_ctx, addr, value); + break; + } /* switch() */ +} + +static void enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct hw_crtc_timing *timing) +{ + uint32_t addr = tg->regs[IDX_CRTC_START_LINE_CONTROL]; + uint32_t value = dal_read_reg(tg->ctx, addr); + + if (enable && FROM_TIMING_GENERATOR(tg)->advanced_request_enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if (dal_timing_generator_get_vsynch_and_front_porch_size(timing) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dal_write_reg(tg->ctx, addr, value); +} + +/*****************************************/ +/* Constructor, Destructor, Fcn Pointers */ +/*****************************************/ + +static void destroy(struct timing_generator **tg) +{ + dal_free(FROM_TIMING_GENERATOR(*tg)); + *tg = NULL; +} + +static const struct timing_generator_funcs timing_generator_dce110_funcs = { + .blank_crtc = blank_crtc, + .did_triggered_reset_occur = NULL, + .disable_crtc = disable_crtc, + .disable_reset_trigger = NULL, + .disable_stereo = NULL, + .enable_advanced_request = enable_advanced_request, + .enable_crtc = enable_crtc, + .enable_reset_trigger = NULL, + .enable_stereo = NULL, + .force_stereo_next_eye = NULL, + .get_crtc_position = NULL, + .get_crtc_timing = NULL, + .get_current_frame_number = NULL, + .get_global_swap_lock_setup = NULL, + .get_io_sequence = NULL, + .get_stereo_status = NULL, + .is_counter_moving = is_counter_moving, + .is_in_vertical_blank = is_in_vertical_blank, + .is_test_pattern_enabled = NULL, + .set_lock_graph_surface_registers = NULL, + .set_lock_master = set_lock_master, + .set_lock_timing_registers = NULL, + .program_blanking = program_blanking, + .program_drr = program_drr, + .program_flow_control = NULL, + .program_timing_generator = program_timing_generator, + .program_vbi_end_signal = NULL, + .reprogram_timing = NULL, + .reset_stereo_3d_phase = NULL, + .set_early_control = timing_generator_dce110_set_early_control, + .set_horizontal_sync_composite = NULL, + .set_horizontal_sync_polarity = NULL, + .set_test_pattern = set_test_pattern, + .set_vertical_sync_polarity = NULL, + .setup_global_swap_lock = NULL, + .unblank_crtc = unblank_crtc, + .validate_timing = dal_timing_generator_validate_timing, + .destroy = destroy, + .wait_for_vactive = + dal_timing_generator_wait_for_vactive, + .force_triggered_reset_now = + NULL, + .wait_for_vblank = + dal_timing_generator_wait_for_vblank, + .get_crtc_scanoutpos = get_crtc_scanoutpos, + .get_vblank_counter = get_vblank_counter, +}; + +static bool timing_generator_dce110_construct(struct timing_generator *tg, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id) +{ + if (!as) + return false; + + switch (id) { + case CONTROLLER_ID_D0: + case CONTROLLER_ID_D1: + case CONTROLLER_ID_D2: + break; + default: + return false; + } + + if (!dal_timing_generator_construct(tg, id)) + return false; + + tg->ctx = ctx; + tg->bp = dal_adapter_service_get_bios_parser(as); + tg->regs = tg_regs[id-1]; + tg->funcs = &timing_generator_dce110_funcs; + tg->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg->min_h_blank = 56; + tg->min_h_front_porch = 4; + tg->min_h_back_porch = 4; + + return true; +} + +struct timing_generator *dal_timing_generator_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id) +{ + struct timing_generator_dce110 *tg = + dal_alloc(sizeof(struct timing_generator_dce110)); + + if (!tg) + return NULL; + + if (timing_generator_dce110_construct(&tg->base, ctx, + as, id)) + return &tg->base; + + BREAK_TO_DEBUGGER(); + dal_free(tg); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.h new file mode 100644 index 000000000000..b8fcbd4db888 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_dce110.h @@ -0,0 +1,45 @@ + + +/* + * 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_TIMING_GENERATOR_DCE110_H__ +#define __DAL_TIMING_GENERATOR_DCE110_H__ + +#include "include/grph_object_id.h" +#include "../timing_generator.h" + +struct timing_generator_dce110 { + struct timing_generator base; + enum sync_source cached_gsl_group; + bool advanced_request_enable; +}; + +struct timing_generator *dal_timing_generator_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.c new file mode 100644 index 000000000000..94c0d83f00cc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.c @@ -0,0 +1,507 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + + +#include "include/grph_object_id.h" +#include "include/adapter_service_interface.h" + +#include "timing_generator_v_dce110.h" +#include "../timing_generator.h" + +enum tg_regs_idx { + IDX_CRTC_UPDATE_LOCK, + IDX_CRTC_MASTER_UPDATE_LOCK, + IDX_CRTC_MASTER_UPDATE_MODE, + IDX_CRTC_H_TOTAL, + IDX_CRTC_V_TOTAL, + IDX_CRTC_H_BLANK_START_END, + IDX_CRTC_V_BLANK_START_END, + IDX_CRTC_H_SYNC_A, + IDX_CRTC_V_SYNC_A, + IDX_CRTC_H_SYNC_A_CNTL, + IDX_CRTC_V_SYNC_A_CNTL, + IDX_CRTC_INTERLACE_CONTROL, + IDX_CRTC_BLANK_CONTROL, + IDX_PIPE_PG_STATUS, + + IDX_CRTC_TEST_PATTERN_COLOR, + IDX_CRTC_TEST_PATTERN_CONTROL, + IDX_CRTC_TEST_PATTERN_PARAMETERS, + IDX_CRTC_FLOW_CONTROL, + IDX_CRTC_STATUS, + IDX_CRTC_STATUS_POSITION, + IDX_CRTC_STATUS_FRAME_COUNT, + IDX_CRTC_STEREO_CONTROL, + IDX_CRTC_STEREO_STATUS, + IDX_CRTC_STEREO_FORCE_NEXT_EYE, + IDX_CRTC_3D_STRUCTURE_CONTROL, + IDX_CRTC_DOUBLE_BUFFER_CONTROL, + IDX_CRTC_V_TOTAL_MIN, + IDX_CRTC_V_TOTAL_MAX, + IDX_CRTC_V_TOTAL_CONTROL, + IDX_CRTC_NOM_VERT_POSITION, + IDX_CRTC_STATIC_SCREEN_CONTROL, + IDX_CRTC_TRIGB_CNTL, + IDX_CRTC_FORCE_COUNT_CNTL, + IDX_CRTC_GSL_CONTROL, + + IDX_CRTC_CONTROL, + IDX_CRTC_START_LINE_CONTROL, + IDX_CRTC_COUNT_CONTROL, + + IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT, + IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM, + IDX_DCP_GSL_CONTROL, + IDX_GRPH_UPDATE, + + IDX_CRTC_VBI_END, + + IDX_BLND_UNDERFLOW_INTERRUPT, + IDX_CRTC_BLACK_COLOR, + TG_REGS_IDX_SIZE +}; + +#define regs_for_underlay_controller()\ +{[IDX_CRTC_UPDATE_LOCK] = 0,\ +[IDX_CRTC_MASTER_UPDATE_LOCK] = 0,\ +[IDX_CRTC_MASTER_UPDATE_MODE] = 0,\ +[IDX_CRTC_H_TOTAL] = mmCRTCV_H_TOTAL,\ +[IDX_CRTC_V_TOTAL] = mmCRTCV_V_TOTAL,\ +[IDX_CRTC_H_BLANK_START_END] = mmCRTCV_H_BLANK_START_END,\ +[IDX_CRTC_V_BLANK_START_END] = mmCRTCV_V_BLANK_START_END,\ +[IDX_CRTC_H_SYNC_A] = 0,\ +[IDX_CRTC_V_SYNC_A] = 0,\ +[IDX_CRTC_H_SYNC_A_CNTL] = 0,\ +[IDX_CRTC_V_SYNC_A_CNTL] = 0,\ +[IDX_CRTC_INTERLACE_CONTROL] = 0,\ +[IDX_CRTC_BLANK_CONTROL] = mmCRTCV_BLANK_CONTROL,\ +[IDX_PIPE_PG_STATUS] = 0,\ +[IDX_CRTC_TEST_PATTERN_COLOR] = mmCRTCV_TEST_PATTERN_COLOR,\ +[IDX_CRTC_TEST_PATTERN_CONTROL] = mmCRTCV_TEST_PATTERN_CONTROL,\ +[IDX_CRTC_TEST_PATTERN_PARAMETERS] = mmCRTCV_TEST_PATTERN_PARAMETERS,\ +[IDX_CRTC_FLOW_CONTROL] = 0,\ +[IDX_CRTC_STATUS] = 0,\ +[IDX_CRTC_STATUS_POSITION] = 0,\ +[IDX_CRTC_STATUS_FRAME_COUNT] = 0,\ +[IDX_CRTC_STEREO_CONTROL] = 0,\ +[IDX_CRTC_STEREO_STATUS] = 0,\ +[IDX_CRTC_STEREO_FORCE_NEXT_EYE] = 0,\ +[IDX_CRTC_3D_STRUCTURE_CONTROL] = 0,\ +[IDX_CRTC_DOUBLE_BUFFER_CONTROL] = 0,\ +[IDX_CRTC_V_TOTAL_MIN] = 0,\ +[IDX_CRTC_V_TOTAL_MAX] = 0,\ +[IDX_CRTC_V_TOTAL_CONTROL] = 0,\ +[IDX_CRTC_NOM_VERT_POSITION] = 0,\ +[IDX_CRTC_STATIC_SCREEN_CONTROL] = 0,\ +[IDX_CRTC_TRIGB_CNTL] = 0,\ +[IDX_CRTC_FORCE_COUNT_CNTL] = 0,\ +[IDX_CRTC_GSL_CONTROL] = 0,\ +[IDX_CRTC_CONTROL] = mmCRTCV_CONTROL,\ +[IDX_CRTC_START_LINE_CONTROL] = mmCRTCV_START_LINE_CONTROL,\ +[IDX_CRTC_COUNT_CONTROL] = 0,\ +[IDX_MODE_EXT_OVERSCAN_LEFT_RIGHT] = 0,\ +[IDX_MODE_EXT_OVERSCAN_TOP_BOTTOM] = 0,\ +[IDX_DCP_GSL_CONTROL] = 0,\ +[IDX_GRPH_UPDATE] = 0,\ +[IDX_CRTC_VBI_END] = 0,\ +[IDX_BLND_UNDERFLOW_INTERRUPT] = 0,\ +[IDX_CRTC_BLACK_COLOR] = mmCRTCV_BLACK_COLOR,\ +} + +static uint32_t tg_underlay_regs[][TG_REGS_IDX_SIZE] = { + regs_for_underlay_controller(), +}; + +static void set_lock_master(struct timing_generator *tg, bool lock) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t addr = tg->regs[IDX_CRTC_MASTER_UPDATE_LOCK]; + uint32_t value = dal_read_reg(dal_ctx, addr); + + set_reg_field_value( + value, + lock ? 1 : 0, + CRTCV_MASTER_UPDATE_LOCK, + MASTER_UPDATE_LOCK); + + dal_write_reg(dal_ctx, addr, value); +} + +static void program_blanking( + struct timing_generator *tg, + const struct hw_crtc_timing *timing) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = tg->regs[IDX_CRTC_H_TOTAL]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTCV_H_TOTAL, + CRTC_H_TOTAL); + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_V_TOTAL]; + value = dal_read_reg(dal_ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTCV_V_TOTAL, + CRTC_V_TOTAL); + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_H_BLANK_START_END]; + value = dal_read_reg(dal_ctx, addr); + + tmp = timing->h_total - + (timing->h_sync_start + timing->h_overscan_left); + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_overscan_left + timing->h_overscan_right; + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_V_BLANK_START_END]; + value = dal_read_reg(dal_ctx, addr); + + tmp = timing->v_total - (timing->v_sync_start + timing->v_overscan_top); + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_overscan_top + + timing->v_overscan_bottom; + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dal_write_reg(dal_ctx, addr, value); +} + +static void set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum crtc_color_depth color_depth) +{ + struct dal_context *dal_ctx = tg->ctx; + uint32_t value; + uint32_t addr; + + /* TODO: add support for other test patterns */ + switch (test_pattern) { + default: + value = 0; + addr = tg->regs[IDX_CRTC_TEST_PATTERN_PARAMETERS]; + + set_reg_field_value( + value, + 6, + CRTCV_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 6, + CRTCV_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + + dal_write_reg(dal_ctx, addr, value); + + addr = tg->regs[IDX_CRTC_TEST_PATTERN_CONTROL]; + value = 0; + + set_reg_field_value( + value, + 1, + CRTCV_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + 0, + CRTCV_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 1, + CRTCV_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + /* add color depth translation here */ + set_reg_field_value( + value, + 1, + CRTCV_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + dal_write_reg(dal_ctx, addr, value); + break; + } /* switch() */ +} + +static int get_vsync_and_front_porch_size( + const struct hw_crtc_timing *timing) +{ + int32_t front_porch = (int32_t) ( + timing->v_sync_start + - timing->v_addressable + - timing->v_overscan_bottom + + timing->flags.INTERLACED); + + return timing->v_sync_width + front_porch; +} + +static void enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct hw_crtc_timing *timing) +{ + uint32_t addr = tg->regs[IDX_CRTC_START_LINE_CONTROL]; + uint32_t value = dal_read_reg(tg->ctx, addr); + uint32_t start_line_position; + + if (enable) { + if (get_vsync_and_front_porch_size(timing) <= 3) + start_line_position = 3; + else + start_line_position = 4; + } else + start_line_position = 2; + + set_reg_field_value( + value, + start_line_position, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + set_reg_field_value( + value, + enable ? 1 : 0, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + + dal_write_reg(tg->ctx, addr, value); +} + +/*****************************************/ +/* Constructor, Destructor, Fcn Pointers */ +/*****************************************/ + +static void destroy(struct timing_generator **tg) +{ + dal_free(*tg); + *tg = NULL; +} + +static bool disable_crtc(struct timing_generator *tg) +{ + uint32_t addr = tg->regs[IDX_CRTC_CONTROL]; + uint32_t value = dal_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 0, + CRTCV_CONTROL, + CRTC_DISABLE_POINT_CNTL); + + set_reg_field_value( + value, + 0, + CRTCV_CONTROL, + CRTC_MASTER_EN); + + dal_write_reg(tg->ctx, addr, value); + + return true; +} + +static bool blank_crtc( + struct timing_generator *tg, + enum color_space color_space) +{ + struct crtc_black_color black_color; + uint32_t addr = tg->regs[IDX_CRTC_BLACK_COLOR]; + uint32_t value = dal_read_reg(tg->ctx, addr); + + dal_timing_generator_color_space_to_black_color( + color_space, + &black_color); + + set_reg_field_value( + value, + black_color.black_color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color.black_color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color.black_color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dal_write_reg(tg->ctx, addr, value); + + addr = tg->regs[IDX_CRTC_BLANK_CONTROL]; + value = dal_read_reg(tg->ctx, addr); + set_reg_field_value( + value, + 1, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 1, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dal_write_reg(tg->ctx, addr, value); + + return true; +} + +static const struct timing_generator_funcs timing_generator_dce110_funcs = { + .blank_crtc = blank_crtc, + .did_triggered_reset_occur = NULL, + .disable_crtc = disable_crtc, + .disable_reset_trigger = NULL, + .disable_stereo = NULL, + .enable_advanced_request = enable_advanced_request, + .enable_crtc = NULL, + .enable_reset_trigger = NULL, + .enable_stereo = NULL, + .force_stereo_next_eye = NULL, + .get_crtc_position = NULL, + .get_crtc_timing = NULL, + .get_current_frame_number = NULL, + .get_global_swap_lock_setup = NULL, + .get_io_sequence = NULL, + .get_stereo_status = NULL, + .is_counter_moving = NULL, + .is_in_vertical_blank = NULL, + .is_test_pattern_enabled = NULL, + .set_lock_graph_surface_registers = NULL, + .set_lock_master = set_lock_master, + .set_lock_timing_registers = NULL, + .program_blanking = program_blanking, + .program_drr = NULL, + .program_flow_control = NULL, + .program_timing_generator = NULL, + .program_vbi_end_signal = NULL, + .reprogram_timing = NULL, + .reset_stereo_3d_phase = NULL, + .set_early_control = NULL, + .set_horizontal_sync_composite = NULL, + .set_horizontal_sync_polarity = NULL, + .set_test_pattern = set_test_pattern, + .set_vertical_sync_polarity = NULL, + .setup_global_swap_lock = NULL, + .unblank_crtc = NULL, + .validate_timing = NULL, + .destroy = destroy, + .wait_for_vactive = NULL, + .force_triggered_reset_now = NULL, + .wait_for_vblank = NULL, + .get_crtc_scanoutpos = NULL, + .get_vblank_counter = NULL, +}; + +static bool construct(struct timing_generator *tg, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id) +{ + if (!as) + return false; + + switch (id) { + case CONTROLLER_ID_UNDERLAY0: + break; + default: + return false; + } + + if (!dal_timing_generator_construct(tg, id)) + return false; + + tg->ctx = ctx; + tg->bp = dal_adapter_service_get_bios_parser(as); + + tg->regs = tg_underlay_regs[id - CONTROLLER_ID_UNDERLAY0]; + + tg->funcs = &timing_generator_dce110_funcs; + return true; +} + +struct timing_generator *dal_timing_generator_v_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id) +{ + struct timing_generator *tg = dal_alloc(sizeof(*tg)); + + if (!tg) + return NULL; + + if (construct(tg, ctx, as, id)) + return tg; + + BREAK_TO_DEBUGGER(); + dal_free(tg); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.h new file mode 100644 index 000000000000..370bb3251f40 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/timing_generator_v_dce110.h @@ -0,0 +1,39 @@ + + +/* + * 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_TIMING_GENERATOR_V_DCE110_H__ +#define __DAL_TIMING_GENERATOR_V_DCE110_H__ + +#include "include/grph_object_id.h" +#include "../timing_generator.h" + +struct timing_generator *dal_timing_generator_v_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.c b/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.c new file mode 100644 index 000000000000..6a50023a08b6 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.c @@ -0,0 +1,140 @@ +/* + * 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 "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "include/grph_object_id.h" +#include "include/adapter_service_interface.h" + +#include "../vga.h" + +static bool vga_dce110_construct( + struct vga *vga, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id); + +struct vga *dal_vga_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id) +{ + struct vga *vga; + + if (!as) + return NULL; + + vga = dal_alloc(sizeof(struct vga)); + + if (!vga) + return NULL; + + if (vga_dce110_construct(vga, ctx, as, id)) + return vga; + + dal_free(vga); + return NULL; +} + +static void disable_vga(struct vga *vga); +static void destroy(struct vga **vga); +static const struct vga_funcs vga_funcs = { + .destroy = destroy, + .disable_vga = disable_vga, +}; + +static bool vga_dce110_construct( + struct vga *vga, + struct dal_context *ctx, + struct adapter_service *as, + enum controller_id id) +{ + vga->bp = dal_adapter_service_get_bios_parser(as); + + switch (id) { + case CONTROLLER_ID_D0: + vga->vga_control = mmD1VGA_CONTROL; + break; + + case CONTROLLER_ID_D1: + vga->vga_control = mmD2VGA_CONTROL; + break; + + case CONTROLLER_ID_D2: + vga->vga_control = mmD3VGA_CONTROL; + break; + + case CONTROLLER_ID_D3: + vga->vga_control = mmD4VGA_CONTROL; + break; + + case CONTROLLER_ID_D4: + vga->vga_control = mmD5VGA_CONTROL; + break; + + case CONTROLLER_ID_D5: + vga->vga_control = mmD6VGA_CONTROL; + break; + + case CONTROLLER_ID_UNDERLAY0: + vga->vga_control = 0;/* dal_reg_r/w will filter out addr=0 */ + break; + + default: + ASSERT_CRITICAL(false); /* Invalid ControllerId */ + return false; + } + + vga->ctx = ctx; + vga->funcs = &vga_funcs; + return true; +} + +/** + * disable_vga + * Turn OFF VGA Mode and Timing - DxVGA_CONTROL + * VGA Mode and VGA Timing is used by VBIOS on CRT Monitors; + */ +static void disable_vga(struct vga *vga) +{ + uint32_t addr = vga->vga_control; + uint32_t value = dal_read_reg(vga->ctx, addr); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dal_write_reg(vga->ctx, addr, value); +} + +static void destroy(struct vga **vga) +{ + dal_free(*vga); + *vga = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.h b/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.h new file mode 100644 index 000000000000..666190bbf4e2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/dce110/vga_dce110.h @@ -0,0 +1,36 @@ +/* + * 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_VGA_DCE110_H__ +#define __DAL_VGA_DCE110_H__ + +#include "../vga.h" + +struct vga *dal_vga_dce110_create( + struct adapter_service *as, + struct dal_context *ctx, + enum controller_id id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/fbc.c b/drivers/gpu/drm/amd/dal/controller/fbc.c new file mode 100644 index 000000000000..b5e0a57508f7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/fbc.c @@ -0,0 +1,159 @@ +/* + * 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/adapter_service_interface.h" + +#include "fbc.h" + +#ifdef CONFIG_DRM_AMD_DAL_DCE11_0 +#include "dce110/fbc_dce110.h" +#endif + +bool dal_fbc_get_max_supported_fbc_size( + struct fbc *fbc, + struct fbc_max_resolution_supported *out_max_res, + bool is_max_dynamic_size) +{ + if (NULL == out_max_res) + return false; + + /* this is HW limit. */ + out_max_res->source_view_width = FBC_MAX_X; + out_max_res->source_view_height = FBC_MAX_Y; + + if (is_max_dynamic_size != true) { + /* this is SW preferred limited on embedded display. */ + if ((fbc->embedded_panel_h_size != 0 && + fbc->embedded_panel_v_size != 0)) { + out_max_res->source_view_width = + fbc->embedded_panel_h_size; + out_max_res->source_view_height = + fbc->embedded_panel_v_size; + } + } + + return true; +} + +bool dal_fbc_is_source_bigger_than_epanel_size( + struct fbc *fbc, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (fbc->embedded_panel_h_size != 0 && + fbc->embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (fbc->embedded_panel_h_size * fbc->embedded_panel_v_size))) + return true; + + return false; +} + +bool dal_fbc_is_fbc_attached_to_controller_id( + struct fbc *fbc, + enum controller_id controller_id) +{ + return controller_id == fbc->attached_controller_id; +} + +uint32_t dal_fbc_align_to_chunks_number_per_line( + struct fbc *fbc, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +void dal_fbc_store_compressed_surface_address( + struct fbc *fbc, + struct fbc_compressed_surface_info *params) +{ + fbc->compr_surface_address.quad_part = + params->compressed_surface_address.quad_part; + fbc->allocated_size = params->allocated_size; + fbc->options.bits.FB_POOL = params->allocation_flags.FB_POOL; + fbc->options.bits.DYNAMIC_ALLOC = + params->allocation_flags.DYNAMIC_ALLOC; + + if (fbc->allocated_size == 0) { + fbc->options.bits.FBC_SUPPORT = false; + fbc->options.bits.LPT_SUPPORT = false; + } + if ((fbc->options.bits.FB_POOL == 0) || + (fbc->options.bits.FB_POOL == 1 && + fbc->allocated_size < fbc->preferred_requested_size)) + fbc->options.bits.LPT_SUPPORT = false; +} + +struct fbc *dal_fbc_create(struct fbc_init_data *data) +{ + if (!data->as) + return NULL; + + switch (dal_adapter_service_get_dce_version(data->as)) { +#ifdef CONFIG_DRM_AMD_DAL_DCE11_0 + case DCE_VERSION_11_0: + return dal_fbc_dce110_create(data); +#endif + default: + return NULL; + } +} + +bool dal_fbc_construct(struct fbc *fbc, struct fbc_init_data *data) +{ + struct embedded_panel_info panel_info; + + if (!data->context) + return false; + + fbc->context = data->context; + fbc->as = data->as; + fbc->embedded_panel_h_size = 0; + fbc->embedded_panel_v_size = 0; + fbc->memory_bus_width = dal_adapter_service_get_asic_vram_bit_width( + fbc->as); + fbc->compr_surface_address.quad_part = 0; + fbc->allocated_size = 0; + fbc->preferred_requested_size = 0; + fbc->min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + fbc->options.raw = 0; + fbc->banks_num = 0; + fbc->raw_size = 0; + fbc->channel_interleave_size = 0; + fbc->dram_channels_num = 0; + fbc->lpt_channels_num = 0; + fbc->attached_controller_id = CONTROLLER_ID_UNDEFINED; + + if (dal_adapter_service_get_embedded_panel_info(fbc->as, &panel_info)) { + fbc->embedded_panel_h_size = + panel_info.lcd_timing.horizontal_addressable; + fbc->embedded_panel_v_size = + panel_info.lcd_timing.vertical_addressable; + } + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/fbc.h b/drivers/gpu/drm/amd/dal/controller/fbc.h new file mode 100644 index 000000000000..df04be97e9c7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/fbc.h @@ -0,0 +1,136 @@ +/* + * 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_FBC_H__ +#define __DAL_FBC_H__ + +#include "include/grph_object_id.h" + +#include "fbc_types.h" + +struct fbc; + +struct fbc_funcs { + void (*power_up_fbc)(struct fbc *fbc); + void (*disable_fbc)(struct fbc *fbc); + void (*enable_fbc)( + struct fbc *fbc, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + bool (*is_fbc_enabled_in_hw)( + struct fbc *fbc, + enum controller_id *fbc_mapped_crtc_id); + bool (*is_lpt_enabled_in_hw)(struct fbc *fbc); + void (*set_fbc_invalidation_triggers)( + struct fbc *fbc, + uint32_t fbc_trigger); + bool (*get_required_compressed_surface_size)( + struct fbc *fbc, + struct fbc_input_info *info, + struct fbc_requested_compressed_size *size); + void (*program_compressed_surface_address_and_pitch)( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params); + void (*disable_lpt)(struct fbc *fbc); + void (*enable_lpt)( + struct fbc *fbc, + uint32_t paths_num, + enum controller_id controller_id); + void (*program_lpt_control)( + struct fbc *fbc, + struct compr_addr_and_pitch_params *params); + uint32_t (*controller_idx)(struct fbc *fbc, enum controller_id id); + void (*destroy)(struct fbc **fbc); +}; + +struct fbc { + const struct fbc_funcs *funcs; + struct dal_context *context; + + union { + uint32_t raw; + struct { + uint32_t FBC_SUPPORT:1; + uint32_t FB_POOL:1; + uint32_t DYNAMIC_ALLOC:1; + uint32_t LPT_SUPPORT:1; + uint32_t LPT_MC_CONFIG:1; + uint32_t DUMMY_BACKEND:1; + uint32_t CLK_GATING_DISABLED:1; + + } bits; + } options; + + struct adapter_service *as; + + union fbc_physical_address compr_surface_address; + + uint32_t embedded_panel_h_size; + uint32_t embedded_panel_v_size; + uint32_t memory_bus_width; + uint32_t banks_num; + uint32_t raw_size; + uint32_t channel_interleave_size; + uint32_t dram_channels_num; + + uint32_t allocated_size; + uint32_t preferred_requested_size; + uint32_t lpt_channels_num; + enum fbc_compress_ratio min_compress_ratio; + + enum controller_id attached_controller_id; +}; + +struct fbc_init_data { + struct adapter_service *as; + struct dal_context *context; +}; + +struct fbc *dal_fbc_create(struct fbc_init_data *data); +bool dal_fbc_construct(struct fbc *fbc, struct fbc_init_data *data); + +uint32_t dal_fbc_align_to_chunks_number_per_line( + struct fbc *fbc, + uint32_t pixels); + +bool dal_fbc_is_source_bigger_than_epanel_size( + struct fbc *fbc, + uint32_t source_view_width, + uint32_t source_view_height); + +bool dal_fbc_is_fbc_attached_to_controller_id( + struct fbc *fbc, + enum controller_id controller_id); + +bool dal_fbc_get_max_supported_fbc_size( + struct fbc *fbc, + struct fbc_max_resolution_supported *out_max_res, + bool is_max_dynamic_size); + +void dal_fbc_store_compressed_surface_address( + struct fbc *fbc, + struct fbc_compressed_surface_info *params); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/fbc_types.h b/drivers/gpu/drm/amd/dal/controller/fbc_types.h new file mode 100644 index 000000000000..31a5b120a441 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/fbc_types.h @@ -0,0 +1,104 @@ +/* + * 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_FBC_TYPES_H__ +#define __DAL_FBC_TYPES_H__ + +enum fbc_compress_ratio { + FBC_COMPRESS_RATIO_INVALID = 0, + FBC_COMPRESS_RATIO_1TO1 = 1, + FBC_COMPRESS_RATIO_2TO1 = 2, + FBC_COMPRESS_RATIO_4TO1 = 4, + FBC_COMPRESS_RATIO_8TO1 = 8, +}; + +union fbc_physical_address { + struct { + uint32_t low_part; + int32_t high_part; + } addr; + int64_t quad_part; +}; + +struct compr_addr_and_pitch_params { + enum controller_id controller_id; + uint32_t source_view_width; + uint32_t source_view_height; +}; + +struct fbc_lpt_config { + uint32_t mem_channels_num; + uint32_t banks_num; + uint32_t chan_interleave_size; + uint32_t row_size; +}; + +struct fbc_input_info { + bool dynamic_fbc_buffer_alloc; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t active_targets_num; + struct fbc_lpt_config lpt_config; +}; + +struct fbc_requested_compressed_size { + uint32_t prefered_size; + uint32_t prefered_size_alignment; + uint32_t min_size; + uint32_t min_size_alignment; + union { + struct { + /*Above prefered_size must be allocated in FB pool */ + uint32_t PREFERED_MUST_BE_FRAME_BUFFER_POOL:1; + /*Above min_size must be allocated in FB pool */ + uint32_t MIN_MUST_BE_FRAME_BUFFER_POOL:1; + } flags; + uint32_t bits; + }; +}; + +struct fbc_compressed_surface_info { + union fbc_physical_address compressed_surface_address; + uint32_t allocated_size; + union { + struct { + uint32_t FB_POOL:1; /*Allocated in FB Pool */ + uint32_t DYNAMIC_ALLOC:1; /*Dynamic allocation */ + } allocation_flags; + uint32_t bits; + }; +}; + +enum fbc_hw_max_resolution_supported { + FBC_MAX_X = 3840, + FBC_MAX_Y = 2400 +}; + +struct fbc_max_resolution_supported { + uint32_t source_view_width; + uint32_t source_view_height; +}; + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/formatter.c b/drivers/gpu/drm/amd/dal/controller/formatter.c new file mode 100644 index 000000000000..e3325d8df31c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/formatter.c @@ -0,0 +1,42 @@ +/* + * 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 "formatter.h" + +bool dal_formatter_construct( + struct formatter *fmt, + struct formatter_init_data *init_data) +{ + if (!init_data) + return false; + + if (!init_data->ctx) + return false; + + fmt->ctx = init_data->ctx; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/formatter.h b/drivers/gpu/drm/amd/dal/controller/formatter.h new file mode 100644 index 000000000000..2c2ece18d001 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/formatter.h @@ -0,0 +1,77 @@ +/* + * 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_FORMATTER_H__ +#define __DAL_FORMATTER_H__ + +#include "include/signal_types.h" +#include "include/grph_csc_types.h" +#include "include/grph_object_id.h" +#include "include/formatter_types.h" + +enum fmt_stereo_action { + FMT_STEREO_ACTION_ENABLE = 0, + FMT_STEREO_ACTION_DISABLE, + FMT_STEREO_ACTION_UPDATE_POLARITY +}; + +struct formatter; + +struct formatter_funcs { + void (*program_bit_depth_reduction)( + struct formatter *fmt, + const struct bit_depth_reduction_params *params); + void (*program_clamping_and_pixel_encoding)( + struct formatter *fmt, + const struct clamping_and_pixel_encoding_params + *params); + void (*set_dyn_expansion)( + struct formatter *fmt, + enum color_space color_space, + enum color_depth color_depth, + enum signal_type signal); + void (*setup_stereo_polarity)( + struct formatter *fmt, + enum fmt_stereo_action action, + bool right_eye_polarity); + void (*destroy)(struct formatter **fmt); +}; + +struct formatter { + const struct formatter_funcs *funcs; + const uint32_t *regs; + struct dal_context *ctx; +}; + +struct formatter_init_data { + enum controller_id id; + struct dal_context *ctx; +}; + +bool dal_formatter_construct( + struct formatter *fmt, + struct formatter_init_data *init_data); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.c b/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.c new file mode 100644 index 000000000000..85a636d35812 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.c @@ -0,0 +1,841 @@ +/* + * 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/fixed31_32.h" + +#include "graphics_and_video_gamma.h" + +#define DIVIDER 10000 + +/* S2D13 value in [-3.00...0.9999] */ +#define S2D13_MIN (-3 * DIVIDER) +#define S2D13_MAX (3 * DIVIDER) + +/** +* convert_float_matrix +* This converts a double into HW register spec defined format S2D13. +* @param : +* @return None +*/ +void dal_controller_convert_float_matrix( + uint16_t *matrix, + struct fixed31_32 *flt, + uint32_t buffer_size) +{ + const struct fixed31_32 min_2_13 = + dal_fixed31_32_from_fraction(S2D13_MIN, DIVIDER); + const struct fixed31_32 max_2_13 = + dal_fixed31_32_from_fraction(S2D13_MAX, DIVIDER); + uint32_t i; + + for (i = 0; i < buffer_size; ++i) { + uint32_t reg_value = + dal_controller_float_to_hw_setting( + dal_fixed31_32_clamp( + flt[i], + min_2_13, + max_2_13), + 2, + 13); + + matrix[i] = (uint16_t)reg_value; + } +} + +struct fixed31_32 dal_controller_translate_from_linear_space( + struct fixed31_32 arg, + struct fixed31_32 a0, + struct fixed31_32 a1, + struct fixed31_32 a2, + struct fixed31_32 a3, + struct fixed31_32 gamma) +{ + const struct fixed31_32 one = dal_fixed31_32_from_int(1); + + if (dal_fixed31_32_le(arg, dal_fixed31_32_neg(a0))) + return dal_fixed31_32_sub( + a2, + dal_fixed31_32_mul( + dal_fixed31_32_add( + one, + a3), + dal_fixed31_32_pow( + dal_fixed31_32_neg(arg), + dal_fixed31_32_recip(gamma)))); + else if (dal_fixed31_32_le(a0, arg)) + return dal_fixed31_32_sub( + dal_fixed31_32_mul( + dal_fixed31_32_add( + one, + a3), + dal_fixed31_32_pow( + arg, + dal_fixed31_32_recip(gamma))), + a2); + else + return dal_fixed31_32_mul( + arg, + a1); +} + +static bool build_custom_float( + struct fixed31_32 value, + const struct custom_float_format *format, + bool *negative, + uint32_t *mantissa, + uint32_t *exponenta) +{ + uint32_t exp_offset = (1 << (format->exponenta_bits - 1)) - 1; + + const struct fixed31_32 mantissa_constant_plus_max_fraction = + dal_fixed31_32_from_fraction( + (1LL << (format->mantissa_bits + 1)) - 1, + 1LL << format->mantissa_bits); + + struct fixed31_32 mantiss; + + if (dal_fixed31_32_eq( + value, + dal_fixed31_32_zero)) { + *negative = false; + *mantissa = 0; + *exponenta = 0; + return true; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_zero)) { + *negative = format->sign; + value = dal_fixed31_32_neg(value); + } else { + *negative = false; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shl(value, 1); + ++i; + } while (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)); + + --i; + + if (exp_offset <= i) { + *mantissa = 0; + *exponenta = 0; + return true; + } + + *exponenta = exp_offset - i; + } else if (dal_fixed31_32_le( + mantissa_constant_plus_max_fraction, + value)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shr(value, 1); + ++i; + } while (dal_fixed31_32_lt( + mantissa_constant_plus_max_fraction, + value)); + + *exponenta = exp_offset + i - 1; + } else { + *exponenta = exp_offset; + } + + mantiss = dal_fixed31_32_sub( + value, + dal_fixed31_32_one); + + if (dal_fixed31_32_lt( + mantiss, + dal_fixed31_32_zero) || + dal_fixed31_32_lt( + dal_fixed31_32_one, + mantiss)) + mantiss = dal_fixed31_32_zero; + else + mantiss = dal_fixed31_32_shl( + mantiss, + format->mantissa_bits); + + *mantissa = dal_fixed31_32_floor(mantiss); + + return true; +} + +static bool setup_custom_float( + const struct custom_float_format *format, + bool negative, + uint32_t mantissa, + uint32_t exponenta, + uint32_t *result) +{ + uint32_t i = 0; + uint32_t j = 0; + + uint32_t value = 0; + + /* verification code: + * once calculation is ok we can remove it */ + + const uint32_t mantissa_mask = + (1 << (format->mantissa_bits + 1)) - 1; + + const uint32_t exponenta_mask = + (1 << (format->exponenta_bits + 1)) - 1; + + if (mantissa & ~mantissa_mask) { + BREAK_TO_DEBUGGER(); + mantissa = mantissa_mask; + } + + if (exponenta & ~exponenta_mask) { + BREAK_TO_DEBUGGER(); + exponenta = exponenta_mask; + } + + /* end of verification code */ + + while (i < format->mantissa_bits) { + uint32_t mask = 1 << i; + + if (mantissa & mask) + value |= mask; + + ++i; + } + + while (j < format->exponenta_bits) { + uint32_t mask = 1 << j; + + if (exponenta & mask) + value |= mask << i; + + ++j; + } + + if (negative && format->sign) + value |= 1 << (i + j); + + *result = value; + + return true; +} + +bool dal_controller_convert_to_custom_float_format( + struct fixed31_32 value, + const struct custom_float_format *format, + uint32_t *result) +{ + uint32_t mantissa; + uint32_t exponenta; + bool negative; + + return build_custom_float( + value, format, &negative, &mantissa, &exponenta) && + setup_custom_float( + format, negative, mantissa, exponenta, result); +} + +bool dal_controller_convert_to_custom_float_format_ex( + struct fixed31_32 value, + const struct custom_float_format *format, + struct custom_float_value *result) +{ + return build_custom_float( + value, format, + &result->negative, &result->mantissa, &result->exponenta) && + setup_custom_float( + format, result->negative, result->mantissa, result->exponenta, + &result->value); +} + +bool dal_controller_build_hw_curve_configuration( + const struct curve_config *curve_config, + struct gamma_curve *gamma_curve, + struct curve_points *curve_points, + struct fixed31_32 *points, + uint32_t *number_of_points) +{ + const int8_t max_regions_number = ARRAY_SIZE(curve_config->segments); + + int8_t i; + + uint8_t segments_calculation[8] = { 0 }; + + struct fixed31_32 region1 = dal_fixed31_32_zero; + struct fixed31_32 region2; + struct fixed31_32 increment; + + uint32_t index = 0; + uint32_t segments = 0; + uint32_t max_number; + + bool result = false; + + if (!number_of_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + max_number = *number_of_points; + + i = 0; + + while (i != max_regions_number) { + gamma_curve[i].offset = 0; + gamma_curve[i].segments_num = 0; + + ++i; + } + + i = 0; + + while (i != max_regions_number) { + /* number should go in uninterruptible sequence */ + if (curve_config->segments[i] == -1) + break; + + ASSERT(curve_config->segments[i] >= 0); + + segments += (1 << curve_config->segments[i]); + + ++i; + } + + if (segments <= max_number) { + int32_t divisor; + uint32_t offset = curve_config->offset; + int8_t begin = curve_config->begin; + int32_t region_number = 0; + + i = begin; + + while ((index < max_number) && + (region_number < max_regions_number) && (i <= 1)) { + int32_t j = 0; + + segments = curve_config->segments[region_number]; + + if (segments == -1) { + if (i > 0) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i - 1); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i - 1)); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + } + + break; + } + + if (i > -1) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i + 1); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i + 1)); + } + + divisor = 1 << segments; + + gamma_curve[region_number].offset = offset; + gamma_curve[region_number].segments_num = segments; + + offset += divisor; + + ++segments_calculation[segments]; + + increment = dal_fixed31_32_div_int( + dal_fixed31_32_sub( + region2, + region1), + divisor); + + points[index] = region1; + + ++index; + ++region_number; + + while ((index < max_number) && (j < divisor - 1)) { + region1 = dal_fixed31_32_add( + region1, + increment); + + points[index] = region1; + + ++index; + ++j; + } + + ++i; + } + + points[index] = region1; + + *number_of_points = index; + + result = true; + } + + curve_points[0].x = points[0]; + curve_points[0].offset = dal_fixed31_32_zero; + + curve_points[1].x = points[index - 1]; + curve_points[1].offset = dal_fixed31_32_zero; + + curve_points[2].x = points[index]; + curve_points[2].offset = dal_fixed31_32_zero; + + return result; +} + +void dal_controller_build_evenly_distributed_points( + struct fixed31_32 *points, + uint32_t coefficient, + uint32_t max_value) +{ + uint32_t i = 0; + uint32_t numerator = 0; + uint32_t denominator = max_value - 1; + + while (i != max_value) { + points[i] = dal_fixed31_32_from_fraction( + numerator, + denominator); + + numerator += coefficient; + ++i; + } +} + +void dal_controller_normalize_oem_gamma( + const struct regamma_ramp *gamma, + struct pwl_float_data *oem_regamma) +{ + uint32_t i; + + /* find OEM maximum */ + + const uint16_t max_driver = 0xFFFF; + const uint16_t max_os = 0xFF00; + + uint16_t norma = max_os; + + i = 0; + + do { + if ((gamma->gamma[i] > max_os) || + (gamma->gamma[i + RGB_256X3X16] > max_os) || + (gamma->gamma[i + 2 * RGB_256X3X16] > max_os)) { + norma = max_driver; + break; + } + + ++i; + } while (i != RGB_256X3X16); + + /* normalize gamma */ + + i = 0; + + do { + oem_regamma[i].r = dal_fixed31_32_from_fraction( + gamma->gamma[i], norma); + oem_regamma[i].g = dal_fixed31_32_from_fraction( + gamma->gamma[i + RGB_256X3X16], norma); + oem_regamma[i].b = dal_fixed31_32_from_fraction( + gamma->gamma[i + 2 * RGB_256X3X16], norma); + + ++i; + } while (i != RGB_256X3X16); +} + +void dal_controller_build_regamma_coefficients( + const struct regamma_lut *regamma, + bool is_degamma_srgb, + struct gamma_coefficients *coefficients) +{ + /* sRGB should apply 2.4 */ + static const int32_t numerator01[3] = { 31308, 31308, 31308 }; + static const int32_t numerator02[3] = { 12920, 12920, 12920 }; + static const int32_t numerator03[3] = { 55, 55, 55 }; + static const int32_t numerator04[3] = { 55, 55, 55 }; + static const int32_t numerator05[3] = { 2400, 2400, 2400 }; + + /* Non-sRGB should apply 2.2 */ + static const int32_t numerator11[3] = { 180000, 180000, 180000 }; + static const int32_t numerator12[3] = { 4500, 4500, 4500 }; + static const int32_t numerator13[3] = { 99, 99, 99 }; + static const int32_t numerator14[3] = { 99, 99, 99 }; + static const int32_t numerator15[3] = { 2200, 2200, 2200 }; + + const int32_t *numerator1; + const int32_t *numerator2; + const int32_t *numerator3; + const int32_t *numerator4; + const int32_t *numerator5; + + uint32_t i = 0; + + if (!regamma->features.bits.GAMMA_RAMP_ARRAY) { + numerator1 = regamma->gamma_coeff.a0; + numerator2 = regamma->gamma_coeff.a1; + numerator3 = regamma->gamma_coeff.a2; + numerator4 = regamma->gamma_coeff.a3; + numerator5 = regamma->gamma_coeff.gamma; + } else if (is_degamma_srgb) { + numerator1 = numerator01; + numerator2 = numerator02; + numerator3 = numerator03; + numerator4 = numerator04; + numerator5 = numerator05; + } else { + numerator1 = numerator11; + numerator2 = numerator12; + numerator3 = numerator13; + numerator4 = numerator14; + numerator5 = numerator15; + } + + do { + coefficients->a0[i] = dal_fixed31_32_from_fraction( + numerator1[i], 10000000); + coefficients->a1[i] = dal_fixed31_32_from_fraction( + numerator2[i], 1000); + coefficients->a2[i] = dal_fixed31_32_from_fraction( + numerator3[i], 1000); + coefficients->a3[i] = dal_fixed31_32_from_fraction( + numerator4[i], 1000); + coefficients->user_gamma[i] = dal_fixed31_32_from_fraction( + numerator5[i], 1000); + + ++i; + } while (i != ARRAY_SIZE(regamma->gamma_coeff.a0)); +} + +void dal_controller_prepare_yuv_ideal( + bool b601, + struct fixed31_32 *matrix) +{ + static const int32_t matrix_1[] = { + 25578516, 50216016, 9752344, 6250000, + -14764391, -28985609, 43750000, 50000000, + 43750000, -36635164, -7114836, 50000000 + }; + + static const int32_t matrix_2[] = { + 18187266, 61183125, 6176484, 6250000, + -10025059, -33724941, 43750000, 50000000, + 43750000, -39738379, -4011621, 50000000 + }; + + const int32_t *matrix_x = b601 ? matrix_1 : matrix_2; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_x[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_1)); +} + +void dal_controller_prepare_tv_rgb_ideal( + struct fixed31_32 *matrix) +{ + static const int32_t matrix_[] = { + 85546875, 0, 0, 6250000, + 0, 85546875, 0, 6250000, + 0, 0, 85546875, 6250000 + }; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_)); +} + +void dal_controller_prepare_srgb_ideal( + struct fixed31_32 *matrix) +{ + static const int32_t matrix_[] = { + 100000000, 0, 0, 0, + 0, 100000000, 0, 0, + 0, 0, 100000000, 0 + }; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_)); +} + +static void calculate_adjustments_common( + const struct fixed31_32 *ideal_matrix, + const struct csc_adjustments *adjustments, + struct fixed31_32 *matrix) +{ + const struct fixed31_32 sin_hue = + dal_fixed31_32_sin(adjustments->hue); + const struct fixed31_32 cos_hue = + dal_fixed31_32_cos(adjustments->hue); + + const struct fixed31_32 multiplier = + dal_fixed31_32_mul( + adjustments->contrast, + adjustments->saturation); + + matrix[0] = dal_fixed31_32_mul( + ideal_matrix[0], + adjustments->contrast); + + matrix[1] = dal_fixed31_32_mul( + ideal_matrix[1], + adjustments->contrast); + + matrix[2] = dal_fixed31_32_mul( + ideal_matrix[2], + adjustments->contrast); + + matrix[4] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_add( + dal_fixed31_32_mul( + ideal_matrix[8], + sin_hue), + dal_fixed31_32_mul( + ideal_matrix[4], + cos_hue))); + + matrix[5] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_add( + dal_fixed31_32_mul( + ideal_matrix[9], + sin_hue), + dal_fixed31_32_mul( + ideal_matrix[5], + cos_hue))); + + matrix[6] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_add( + dal_fixed31_32_mul( + ideal_matrix[10], + sin_hue), + dal_fixed31_32_mul( + ideal_matrix[6], + cos_hue))); + + matrix[7] = ideal_matrix[7]; + + matrix[8] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_sub( + dal_fixed31_32_mul( + ideal_matrix[8], + cos_hue), + dal_fixed31_32_mul( + ideal_matrix[4], + sin_hue))); + + matrix[9] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_sub( + dal_fixed31_32_mul( + ideal_matrix[9], + cos_hue), + dal_fixed31_32_mul( + ideal_matrix[5], + sin_hue))); + + matrix[10] = dal_fixed31_32_mul( + multiplier, + dal_fixed31_32_sub( + dal_fixed31_32_mul( + ideal_matrix[10], + cos_hue), + dal_fixed31_32_mul( + ideal_matrix[6], + sin_hue))); + + matrix[11] = ideal_matrix[11]; +} + +void dal_controller_calculate_adjustments( + const struct fixed31_32 *ideal_matrix, + const struct csc_adjustments *adjustments, + struct fixed31_32 *matrix) +{ + calculate_adjustments_common(ideal_matrix, adjustments, matrix); + + matrix[3] = dal_fixed31_32_add( + ideal_matrix[3], + dal_fixed31_32_mul( + adjustments->brightness, + dal_fixed31_32_from_fraction(86, 100))); +} + +void dal_controller_calculate_adjustments_y_only( + const struct fixed31_32 *ideal_matrix, + const struct csc_adjustments *adjustments, + struct fixed31_32 *matrix) +{ + calculate_adjustments_common(ideal_matrix, adjustments, matrix); + + matrix[3] = dal_fixed31_32_add( + ideal_matrix[3], + adjustments->brightness); +} + +static inline struct fixed31_32 fixed31_32_clamp( + struct fixed31_32 value, + int32_t min_numerator, + int32_t max_numerator, + int32_t denominator) +{ + return dal_fixed31_32_clamp( + value, + dal_fixed31_32_from_fraction( + min_numerator, + denominator), + dal_fixed31_32_from_fraction( + max_numerator, + denominator)); +} + +void dal_controller_setup_reg_format( + struct fixed31_32 *coefficients, + uint16_t *reg_values) +{ + enum { + LENGTH = 12, + DENOMINATOR = 10000 + }; + + static const int32_t min_numerator[] = { + -3 * DENOMINATOR, + -DENOMINATOR + }; + + static const int32_t max_numerator[] = { + DENOMINATOR, + DENOMINATOR + }; + + static const uint8_t integer_bits[] = { 2, 0 }; + + uint32_t i = 0; + + do { + const uint32_t index = (i % 4) == 3; + + reg_values[i] = dal_controller_float_to_hw_setting( + fixed31_32_clamp(coefficients[(i + 8) % LENGTH], + min_numerator[index], + max_numerator[index], + DENOMINATOR), + integer_bits[index], 13); + + ++i; + } while (i != LENGTH); +} + +uint16_t dal_controller_float_to_hw_setting( + struct fixed31_32 arg, + uint8_t integer_bits, + uint8_t fractional_bits) +{ + int32_t numerator; + int32_t divisor = 1 << fractional_bits; + + uint16_t result; + + uint16_t d = (uint16_t)dal_fixed31_32_floor( + dal_fixed31_32_abs( + arg)); + + if (d <= (uint16_t)(1 << integer_bits) - (1 / (uint16_t)divisor)) + numerator = (uint16_t)dal_fixed31_32_floor( + dal_fixed31_32_mul_int( + arg, + divisor)); + else { + numerator = dal_fixed31_32_floor( + dal_fixed31_32_sub( + dal_fixed31_32_from_int( + 1LL << integer_bits), + dal_fixed31_32_recip( + dal_fixed31_32_from_int( + divisor)))); + } + + if (numerator >= 0) + result = (uint16_t)numerator; + else + result = (uint16_t)( + (1 << (integer_bits + fractional_bits + 1)) + numerator); + + if ((result != 0) && dal_fixed31_32_lt( + arg, dal_fixed31_32_zero)) + result |= 1 << (integer_bits + fractional_bits); + + return result; +} diff --git a/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.h b/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.h new file mode 100644 index 000000000000..e15e3a66e9df --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/graphics_and_video_gamma.h @@ -0,0 +1,231 @@ +/* + * 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_GRAPHICS_AND_VIDEO_GAMMA_H__ +#define __DAL_GRAPHICS_AND_VIDEO_GAMMA_H__ + +#include "include/csc_common_types.h" +#include "internal_types_wide_gamut.h" + +struct fixed31_32; + +enum channel_name { + CHANNEL_NAME_RED, + CHANNEL_NAME_GREEN, + CHANNEL_NAME_BLUE +}; + +struct custom_float_format { + uint32_t mantissa_bits; + uint32_t exponenta_bits; + bool sign; +}; + +struct custom_float_value { + uint32_t mantissa; + uint32_t exponenta; + uint32_t value; + bool negative; +}; + +struct hw_x_point { + uint32_t custom_float_x; + uint32_t custom_float_x_adjusted; + struct fixed31_32 x; + struct fixed31_32 adjusted_x; + struct fixed31_32 regamma_y_red; + struct fixed31_32 regamma_y_green; + struct fixed31_32 regamma_y_blue; + +}; + +/* Hardware capabilities */ +#define MAX_REGIONS_NUMBER 16 + +struct curve_config { + uint32_t offset; + int8_t segments[MAX_REGIONS_NUMBER]; + int8_t begin; +}; + +struct gamma_curve { + uint32_t offset; + uint32_t segments_num; +}; + +struct curve_points { + struct fixed31_32 x; + struct fixed31_32 y; + struct fixed31_32 offset; + struct fixed31_32 slope; + + uint32_t custom_float_x; + uint32_t custom_float_y; + uint32_t custom_float_offset; + uint32_t custom_float_slope; +}; + +struct pwl_float_data { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; +}; + +struct pwl_float_data_ex { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; + struct fixed31_32 delta_r; + struct fixed31_32 delta_g; + struct fixed31_32 delta_b; +}; + +enum hw_point_position { + /* hw point sits between left and right sw points */ + HW_POINT_POSITION_MIDDLE, + /* hw point lays left from left (smaller) sw point */ + HW_POINT_POSITION_LEFT, + /* hw point lays stays from right (bigger) sw point */ + HW_POINT_POSITION_RIGHT +}; + +struct gamma_point { + int32_t left_index; + int32_t right_index; + enum hw_point_position pos; + struct fixed31_32 coeff; +}; + +struct pixel_gamma_point { + struct gamma_point r; + struct gamma_point g; + struct gamma_point b; +}; + +struct gamma_coefficients { + struct fixed31_32 a0[3]; + struct fixed31_32 a1[3]; + struct fixed31_32 a2[3]; + struct fixed31_32 a3[3]; + struct fixed31_32 user_gamma[3]; + struct fixed31_32 user_contrast; + struct fixed31_32 user_brightness; +}; + +struct csc_adjustments { + struct fixed31_32 contrast; + struct fixed31_32 saturation; + struct fixed31_32 brightness; + struct fixed31_32 hue; +}; + +struct fixed31_32 dal_controller_translate_from_linear_space( + struct fixed31_32 arg, + struct fixed31_32 a0, + struct fixed31_32 a1, + struct fixed31_32 a2, + struct fixed31_32 a3, + struct fixed31_32 gamma); + +static inline struct fixed31_32 dal_controller_translate_from_linear_space_ex( + struct fixed31_32 arg, + struct gamma_coefficients *coeff, + uint32_t color_index) +{ + return dal_controller_translate_from_linear_space( + arg, + coeff->a0[color_index], + coeff->a1[color_index], + coeff->a2[color_index], + coeff->a3[color_index], + coeff->user_gamma[color_index]); +} + +bool dal_controller_convert_to_custom_float_format( + struct fixed31_32 value, + const struct custom_float_format *format, + uint32_t *result); + +bool dal_controller_convert_to_custom_float_format_ex( + struct fixed31_32 value, + const struct custom_float_format *format, + struct custom_float_value *result); + +bool dal_controller_build_hw_curve_configuration( + const struct curve_config *curve_config, + struct gamma_curve *gamma_curve, + struct curve_points *curve_points, + struct fixed31_32 *points, + uint32_t *number_of_points); + +void dal_controller_build_evenly_distributed_points( + struct fixed31_32 *points, + uint32_t coefficient, + uint32_t max_value); + +void dal_controller_normalize_oem_gamma( + const struct regamma_ramp *gamma, + struct pwl_float_data *oem_regamma); + +void dal_controller_build_regamma_coefficients( + const struct regamma_lut *regamma, + bool is_degamma_srgb, + struct gamma_coefficients *coefficients); + +void dal_controller_prepare_yuv_ideal( + bool b601, + struct fixed31_32 *matrix); + +void dal_controller_prepare_tv_rgb_ideal( + struct fixed31_32 *matrix); + +void dal_controller_prepare_srgb_ideal( + struct fixed31_32 *matrix); + +void dal_controller_calculate_adjustments( + const struct fixed31_32 *ideal_matrix, + const struct csc_adjustments *adjustments, + struct fixed31_32 *matrix); + +void dal_controller_calculate_adjustments_y_only( + const struct fixed31_32 *ideal_matrix, + const struct csc_adjustments *adjustments, + struct fixed31_32 *matrix); + +void dal_controller_setup_reg_format( + struct fixed31_32 *coefficients, + uint16_t *reg_values); + +uint16_t dal_controller_float_to_hw_setting( + struct fixed31_32 arg, + uint8_t integer_bits, + uint8_t fractional_bits); + +void dal_controller_convert_float_matrix( + uint16_t *matrix, + struct fixed31_32 *flt, + uint32_t buffer_size); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/grph_gamma.c b/drivers/gpu/drm/amd/dal/controller/grph_gamma.c new file mode 100644 index 000000000000..b729fefd4e8f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/grph_gamma.c @@ -0,0 +1,1841 @@ +/* + * 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/fixed31_32.h" +#include "include/adapter_service_interface.h" + +#include "grph_gamma.h" + +static bool set_gamma_ramp( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + /* there was some implementation, + * but grph_gamma_dce80 have own implementation */ + BREAK_TO_DEBUGGER(); + return false; +} + +static bool set_gamma_ramp_legacy( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + BREAK_TO_DEBUGGER(); + return false; +} + +static void set_legacy_mode( + struct grph_gamma *gg, + bool is_legacy) +{ + BREAK_TO_DEBUGGER(); +} + +static void program_prescale_legacy( + struct grph_gamma *gg, + enum pixel_format pixel_format) +{ + BREAK_TO_DEBUGGER(); +} + +static bool setup_distribution_points( + struct grph_gamma *gg) +{ + /* there was some implementation, + * but grph_gamma_dce80 have own implementation */ + BREAK_TO_DEBUGGER(); + return false; +} + +static void program_black_offsets( + struct grph_gamma *gg, + struct dev_c_lut16 *offset) +{ + BREAK_TO_DEBUGGER(); +} + +static void program_white_offsets( + struct grph_gamma *gg, + struct dev_c_lut16 *offset) +{ + BREAK_TO_DEBUGGER(); +} + +static void set_lut_inc( + struct grph_gamma *gg, + uint8_t inc, + bool is_float, + bool is_signed) +{ + BREAK_TO_DEBUGGER(); +} + +static void select_lut( + struct grph_gamma *gg) +{ + BREAK_TO_DEBUGGER(); +} + +static void destroy( + struct grph_gamma **ptr) +{ + BREAK_TO_DEBUGGER(); +} + +static bool find_software_points( + struct grph_gamma *gg, + struct fixed31_32 hw_point, + enum channel_name channel, + uint32_t *index_to_start, + uint32_t *index_left, + uint32_t *index_right, + enum hw_point_position *pos) +{ + const uint32_t max_number = RGB_256X3X16 + gg->extra_points; + + struct fixed31_32 left, right; + + uint32_t i = *index_to_start; + + while (i < max_number) { + if (channel == CHANNEL_NAME_RED) { + left = gg->axis_x_256[i].r; + + if (i < max_number - 1) + right = gg->axis_x_256[i + 1].r; + else + right = gg->axis_x_256[max_number - 1].r; + } else if (channel == CHANNEL_NAME_GREEN) { + left = gg->axis_x_256[i].g; + + if (i < max_number - 1) + right = gg->axis_x_256[i + 1].g; + else + right = gg->axis_x_256[max_number - 1].g; + } else { + left = gg->axis_x_256[i].b; + + if (i < max_number - 1) + right = gg->axis_x_256[i + 1].b; + else + right = gg->axis_x_256[max_number - 1].b; + } + + if (dal_fixed31_32_le(left, hw_point) && + dal_fixed31_32_le(hw_point, right)) { + *index_to_start = i; + *index_left = i; + + if (i < max_number - 1) + *index_right = i + 1; + else + *index_right = max_number - 1; + + *pos = HW_POINT_POSITION_MIDDLE; + + return true; + } else if ((i == *index_to_start) && + dal_fixed31_32_le(hw_point, left)) { + *index_to_start = i; + *index_left = i; + *index_right = i; + + *pos = HW_POINT_POSITION_LEFT; + + return true; + } else if ((i == max_number - 1) && + dal_fixed31_32_le(right, hw_point)) { + *index_to_start = i; + *index_left = i; + *index_right = i; + + *pos = HW_POINT_POSITION_RIGHT; + + return true; + } + + ++i; + } + + return false; +} + +static bool find_software_points_dx( + struct grph_gamma *gg, + struct fixed31_32 hw_point, + enum channel_name channel, + uint32_t *index_to_start, + uint32_t *index_left, + uint32_t *index_right, + enum hw_point_position *pos) +{ + const uint32_t max_number = DX_GAMMA_RAMP_MAX + gg->extra_points; + + struct fixed31_32 left, right; + + uint32_t i = *index_to_start; + + while (i < max_number) { + if (channel == CHANNEL_NAME_RED) { + left = gg->axis_x_1025[i].r; + + if (i < DX_GAMMA_RAMP_MAX - 1) + right = gg->axis_x_1025[i + 1].r; + else + right = gg->axis_x_1025[DX_GAMMA_RAMP_MAX-1].r; + } else if (channel == CHANNEL_NAME_GREEN) { + left = gg->axis_x_1025[i].g; + + if (i < DX_GAMMA_RAMP_MAX - 1) + right = gg->axis_x_1025[i + 1].g; + else + right = gg->axis_x_1025[DX_GAMMA_RAMP_MAX-1].g; + } else { + left = gg->axis_x_1025[i].b; + + if (i < DX_GAMMA_RAMP_MAX - 1) + right = gg->axis_x_1025[i + 1].b; + else + right = gg->axis_x_1025[DX_GAMMA_RAMP_MAX-1].b; + } + + if (dal_fixed31_32_le(left, hw_point) && + dal_fixed31_32_le(hw_point, right)) { + *index_to_start = i; + *index_left = i; + + if (i < DX_GAMMA_RAMP_MAX - 1) + *index_right = i + 1; + else + *index_right = DX_GAMMA_RAMP_MAX - 1; + + *pos = HW_POINT_POSITION_MIDDLE; + + return true; + } else if ((i == *index_to_start) && + dal_fixed31_32_le(hw_point, left)) { + *index_to_start = i; + *index_left = i; + *index_right = i; + + *pos = HW_POINT_POSITION_LEFT; + + return true; + } else if ((i == max_number - 1) && + dal_fixed31_32_le(right, hw_point)) { + *index_to_start = i; + *index_left = i; + *index_right = i; + + *pos = HW_POINT_POSITION_RIGHT; + + return true; + } + + ++i; + } + + return false; +} + +static bool build_custom_gamma_mapping_coefficients_worker( + struct grph_gamma *gg, + struct pixel_gamma_point *coeff, + enum channel_name channel, + uint32_t number_of_points, + enum pixel_format pixel_format) +{ + uint32_t i = 0; + + while (i <= number_of_points) { + struct fixed31_32 coord_x; + + uint32_t index_to_start = 0; + uint32_t index_left = 0; + uint32_t index_right = 0; + + enum hw_point_position hw_pos; + + struct gamma_point *point; + + struct fixed31_32 left_pos; + struct fixed31_32 right_pos; + + if (pixel_format == PIXEL_FORMAT_FP16) + coord_x = gg->coordinates_x[i].adjusted_x; + else if (channel == CHANNEL_NAME_RED) + coord_x = gg->coordinates_x[i].regamma_y_red; + else if (channel == CHANNEL_NAME_GREEN) + coord_x = gg->coordinates_x[i].regamma_y_green; + else + coord_x = gg->coordinates_x[i].regamma_y_blue; + + if (!find_software_points( + gg, coord_x, channel, + &index_to_start, &index_left, &index_right, &hw_pos)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_left >= RGB_256X3X16 + gg->extra_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_right >= RGB_256X3X16 + gg->extra_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (channel == CHANNEL_NAME_RED) { + point = &coeff[i].r; + + left_pos = gg->axis_x_256[index_left].r; + right_pos = gg->axis_x_256[index_right].r; + } else if (channel == CHANNEL_NAME_GREEN) { + point = &coeff[i].g; + + left_pos = gg->axis_x_256[index_left].g; + right_pos = gg->axis_x_256[index_right].g; + } else { + point = &coeff[i].b; + + left_pos = gg->axis_x_256[index_left].b; + right_pos = gg->axis_x_256[index_right].b; + } + + if (hw_pos == HW_POINT_POSITION_MIDDLE) + point->coeff = dal_fixed31_32_div( + dal_fixed31_32_sub( + coord_x, + left_pos), + dal_fixed31_32_sub( + right_pos, + left_pos)); + else if (hw_pos == HW_POINT_POSITION_LEFT) + point->coeff = gg->x_min; + else if (hw_pos == HW_POINT_POSITION_RIGHT) + point->coeff = gg->x_max2; + else { + BREAK_TO_DEBUGGER(); + return false; + } + + point->left_index = index_left; + point->right_index = index_right; + point->pos = hw_pos; + + ++i; + } + + return true; +} + +static inline bool build_custom_gamma_mapping_coefficients( + struct grph_gamma *gg, + enum channel_name channel, + uint32_t number_of_points, + enum pixel_format pixel_format) +{ + return build_custom_gamma_mapping_coefficients_worker( + gg, gg->coeff128, channel, number_of_points, pixel_format); +} + +static inline bool build_oem_custom_gamma_mapping_coefficients( + struct grph_gamma *gg, + enum channel_name channel, + uint32_t number_of_points, + enum pixel_format pixel_format) +{ + return build_custom_gamma_mapping_coefficients_worker( + gg, gg->coeff128_oem, channel, number_of_points, pixel_format); +} + +static bool build_custom_dx_gamma_mapping_coefficients( + struct grph_gamma *gg, + enum channel_name channel, + uint32_t number_of_points, + enum pixel_format pixel_format) +{ + uint32_t i = 0; + + while (i <= number_of_points) { + struct fixed31_32 coord_x; + + uint32_t index_to_start = 0; + uint32_t index_left = 0; + uint32_t index_right = 0; + + enum hw_point_position hw_pos; + + struct gamma_point *point; + + struct fixed31_32 left_pos; + struct fixed31_32 right_pos; + + if (pixel_format == PIXEL_FORMAT_FP16) + coord_x = gg->coordinates_x[i].adjusted_x; + else if (channel == CHANNEL_NAME_RED) + coord_x = gg->coordinates_x[i].regamma_y_red; + else if (channel == CHANNEL_NAME_GREEN) + coord_x = gg->coordinates_x[i].regamma_y_green; + else + coord_x = gg->coordinates_x[i].regamma_y_blue; + + if (!find_software_points_dx( + gg, coord_x, channel, + &index_to_start, &index_left, &index_right, &hw_pos)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_left >= DX_GAMMA_RAMP_MAX + gg->extra_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_right >= DX_GAMMA_RAMP_MAX + gg->extra_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (channel == CHANNEL_NAME_RED) { + point = &gg->coeff128_dx[i].r; + + left_pos = gg->axis_x_1025[index_left].r; + right_pos = gg->axis_x_1025[index_right].r; + } else if (channel == CHANNEL_NAME_GREEN) { + point = &gg->coeff128_dx[i].g; + + left_pos = gg->axis_x_1025[index_left].g; + right_pos = gg->axis_x_1025[index_right].g; + } else { + point = &gg->coeff128_dx[i].b; + + left_pos = gg->axis_x_1025[index_left].b; + right_pos = gg->axis_x_1025[index_right].b; + } + + if (hw_pos == HW_POINT_POSITION_MIDDLE) + point->coeff = dal_fixed31_32_div( + dal_fixed31_32_sub( + coord_x, + left_pos), + dal_fixed31_32_sub( + right_pos, + left_pos)); + else if (hw_pos == HW_POINT_POSITION_LEFT) + point->coeff = gg->x_min; + else if (hw_pos == HW_POINT_POSITION_RIGHT) + point->coeff = gg->x_max2; + else { + BREAK_TO_DEBUGGER(); + return false; + } + + point->left_index = index_left; + point->right_index = index_right; + point->pos = hw_pos; + + ++i; + } + + return true; +} + +static struct fixed31_32 calculate_mapped_value( + struct grph_gamma *gg, + struct pwl_float_data *rgb, + const struct pixel_gamma_point *coeff, + enum channel_name channel, + uint32_t max_index) +{ + const struct gamma_point *point; + + struct fixed31_32 result; + + if (channel == CHANNEL_NAME_RED) + point = &coeff->r; + else if (channel == CHANNEL_NAME_GREEN) + point = &coeff->g; + else + point = &coeff->b; + + if ((point->left_index < 0) || (point->left_index > max_index)) { + BREAK_TO_DEBUGGER(); + return dal_fixed31_32_zero; + } + + if ((point->right_index < 0) || (point->right_index > max_index)) { + BREAK_TO_DEBUGGER(); + return dal_fixed31_32_zero; + } + + if (point->pos == HW_POINT_POSITION_MIDDLE) + if (channel == CHANNEL_NAME_RED) + result = dal_fixed31_32_add( + dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + rgb[point->right_index].r, + rgb[point->left_index].r)), + rgb[point->left_index].r); + else if (channel == CHANNEL_NAME_GREEN) + result = dal_fixed31_32_add( + dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + rgb[point->right_index].g, + rgb[point->left_index].g)), + rgb[point->left_index].g); + else + result = dal_fixed31_32_add( + dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + rgb[point->right_index].b, + rgb[point->left_index].b)), + rgb[point->left_index].b); + else if (point->pos == HW_POINT_POSITION_LEFT) { + BREAK_TO_DEBUGGER(); + result = gg->x_min; + } else { + BREAK_TO_DEBUGGER(); + result = gg->x_max1; + } + + return result; +} + +static inline struct fixed31_32 calculate_regamma_user_mapped_value( + struct grph_gamma *gg, + const struct pixel_gamma_point *coeff, + enum channel_name channel, + uint32_t max_index) +{ + return calculate_mapped_value( + gg, gg->rgb_oem, coeff, channel, max_index); +} + +static inline struct fixed31_32 calculate_user_mapped_value( + struct grph_gamma *gg, + const struct pixel_gamma_point *coeff, + enum channel_name channel, + uint32_t max_index) +{ + return calculate_mapped_value( + gg, gg->rgb_user, coeff, channel, max_index); +} + +static inline struct fixed31_32 calculate_oem_mapped_value( + struct grph_gamma *gg, + uint32_t index, + enum channel_name channel, + uint32_t max_index) +{ + return calculate_regamma_user_mapped_value( + gg, gg->coeff128_oem + index, channel, max_index); +} + +static void scale_oem_gamma( + struct grph_gamma *gg, + const struct regamma_ramp *regamma_ramp) +{ + const uint16_t max_driver = 0xFFFF; + const uint16_t max_os = 0xFF00; + + uint16_t scale = max_os; + + uint32_t i; + + struct pwl_float_data *rgb = gg->rgb_oem; + struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1; + + /* find OEM maximum */ + + i = 0; + + do { + if ((regamma_ramp->gamma[i] > max_os) || + (regamma_ramp->gamma[i + RGB_256X3X16] > max_os) || + (regamma_ramp->gamma[i + 2 * RGB_256X3X16] > max_os)) { + scale = max_driver; + break; + } + + ++i; + } while (i != RGB_256X3X16); + + /* scale */ + + i = 0; + + do { + rgb->r = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + regamma_ramp->gamma[i]), + scale); + rgb->g = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + regamma_ramp->gamma[i + RGB_256X3X16]), + scale); + rgb->b = dal_fixed31_32_div_int( + dal_fixed31_32_from_int( + regamma_ramp->gamma[i + 2 * RGB_256X3X16]), + scale); + + ++rgb; + ++i; + } while (i != RGB_256X3X16); + + /* add 3 extra points, 2 physical plus 1 virtual */ + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider1); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider1); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider1); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider2); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider2); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider2); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider3); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider3); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider3); +} + +static inline void copy_rgb_regamma_to_coordinates_x( + struct grph_gamma *gg) +{ + struct hw_x_point *coords = gg->coordinates_x; + const struct pwl_float_data_ex *rgb_regamma = gg->rgb_regamma; + + uint32_t i = 0; + + while (i <= gg->hw_points_num) { + coords->regamma_y_red = rgb_regamma->r; + coords->regamma_y_green = rgb_regamma->g; + coords->regamma_y_blue = rgb_regamma->b; + + ++coords; + ++rgb_regamma; + ++i; + } +} + +static bool calculate_interpolated_hardware_curve( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + struct pwl_result_data *rgb_resulted = gg->rgb_resulted; + + const struct pixel_gamma_point *coeff; + uint32_t max_entries = gg->extra_points - 1; + + uint32_t i = 0; + + if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) { + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_RED, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_GREEN, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_BLUE, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + coeff = gg->coeff128; + max_entries += RGB_256X3X16; + } else if (gamma_ramp->type == GAMMA_RAMP_DXGI_1) { + if (!build_custom_dx_gamma_mapping_coefficients( + gg, CHANNEL_NAME_RED, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_dx_gamma_mapping_coefficients( + gg, CHANNEL_NAME_GREEN, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_dx_gamma_mapping_coefficients( + gg, CHANNEL_NAME_BLUE, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + coeff = gg->coeff128_dx; + max_entries += DX_GAMMA_RAMP_MAX; + } else { + BREAK_TO_DEBUGGER(); + return false; + } + + while (i <= gg->hw_points_num) { + rgb_resulted->red = calculate_user_mapped_value( + gg, coeff, CHANNEL_NAME_RED, max_entries); + rgb_resulted->green = calculate_user_mapped_value( + gg, coeff, CHANNEL_NAME_GREEN, max_entries); + rgb_resulted->blue = calculate_user_mapped_value( + gg, coeff, CHANNEL_NAME_BLUE, max_entries); + + ++coeff; + ++rgb_resulted; + ++i; + } + + return true; +} + +static void map_standard_regamma_hw_to_x_user( + struct grph_gamma *gg, + enum gamma_ramp_type type, + const struct gamma_parameters *params) +{ + struct pwl_result_data *rgb_resulted = gg->rgb_resulted; + const struct pwl_float_data_ex *rgb_regamma = gg->rgb_regamma; + + uint32_t i = 0; + + while (i <= gg->hw_points_num) { + rgb_resulted->red = rgb_regamma->r; + rgb_resulted->green = rgb_regamma->g; + rgb_resulted->blue = rgb_regamma->b; + + ++rgb_resulted; + ++rgb_regamma; + ++i; + } +} + +static bool map_regamma_hw_to_x_user_improved_1( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + bool result; + + if (params->regamma.features.bits.GAMMA_RAMP_ARRAY) { + const uint32_t max_entries = + RGB_256X3X16 + gg->extra_points - 1; + + const struct pixel_gamma_point *coeff = gg->coeff128; + struct pwl_result_data *rgb_resulted = gg->rgb_resulted; + + uint32_t i = 0; + + scale_oem_gamma(gg, ¶ms->regamma.regamma_ramp); + + copy_rgb_regamma_to_coordinates_x(gg); + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_RED, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_GREEN, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_BLUE, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + while (i <= gg->hw_points_num) { + rgb_resulted->red = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_RED, max_entries); + rgb_resulted->green = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_GREEN, max_entries); + rgb_resulted->blue = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_BLUE, max_entries); + + ++coeff; + ++rgb_resulted; + ++i; + } + } else + map_standard_regamma_hw_to_x_user(gg, gamma_ramp->type, params); + + result = gg->funcs->set_gamma_ramp_legacy(gg, gamma_ramp, params); + + gg->funcs->set_legacy_mode(gg, true); + + /* set bypass */ + gg->funcs->program_prescale_legacy(gg, PIXEL_FORMAT_UNINITIALIZED); + + return result; +} + +static bool map_regamma_hw_to_x_user_improved_2( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + /* setup to spare calculated ideal regamma values */ + if (params->regamma.features.bits.GAMMA_RAMP_ARRAY) { + const uint32_t max_entries = + RGB_256X3X16 + gg->extra_points - 1; + + const struct pixel_gamma_point *coeff = gg->coeff128; + struct hw_x_point *coords = gg->coordinates_x; + + uint32_t i = 0; + + scale_oem_gamma(gg, ¶ms->regamma.regamma_ramp); + + copy_rgb_regamma_to_coordinates_x(gg); + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_RED, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_GREEN, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_BLUE, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + while (i <= gg->hw_points_num) { + coords->regamma_y_red = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_RED, max_entries); + coords->regamma_y_green = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_GREEN, max_entries); + coords->regamma_y_blue = + calculate_regamma_user_mapped_value(gg, coeff, + CHANNEL_NAME_BLUE, max_entries); + + ++coeff; + ++coords; + ++i; + } + } else { + copy_rgb_regamma_to_coordinates_x(gg); + } + + return calculate_interpolated_hardware_curve(gg, gamma_ramp, params); +} + +static bool map_regamma_hw_to_x_user_old_1( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + bool result; + + map_standard_regamma_hw_to_x_user(gg, gamma_ramp->type, params); + + result = gg->funcs->set_gamma_ramp_legacy(gg, gamma_ramp, params); + + gg->funcs->set_legacy_mode(gg, true); + + /* set bypass */ + gg->funcs->program_prescale_legacy(gg, PIXEL_FORMAT_UNINITIALIZED); + + return result; +} + +static bool map_regamma_hw_to_x_user_old_2( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + copy_rgb_regamma_to_coordinates_x(gg); + + return calculate_interpolated_hardware_curve(gg, gamma_ramp, params); +} + +bool dal_grph_gamma_map_regamma_hw_to_x_user( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + bool legacy_and_regamma = + params->selected_gamma_lut == + GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA; + + if (params->regamma.features.bits.APPLY_DEGAMMA) + /* correct solution used by some vendors*/ + if (legacy_and_regamma) + return map_regamma_hw_to_x_user_improved_1( + gg, gamma_ramp, params); + else + return map_regamma_hw_to_x_user_improved_2( + gg, gamma_ramp, params); + else + /* alternative solution used by other vendors */ + if (legacy_and_regamma) + return map_regamma_hw_to_x_user_old_1( + gg, gamma_ramp, params); + else + return map_regamma_hw_to_x_user_old_2( + gg, gamma_ramp, params); +} + + +void dal_grph_gamma_scale_rgb256x3x16( + struct grph_gamma *gg, + bool use_palette, + const struct gamma_ramp_rgb256x3x16 *gamma) +{ + const uint16_t max_driver = 0xFFFF; + const uint16_t max_os = 0xFF00; + + uint16_t scaler = max_os; + + uint32_t i; + + struct dev_c_lut *palette = gg->saved_palette; + + struct pwl_float_data *rgb = gg->rgb_user; + struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1; + + i = 0; + + do { + if ((gamma->red[i] > max_os) || + (gamma->green[i] > max_os) || + (gamma->blue[i] > max_os)) { + scaler = max_driver; + break; + } + ++i; + } while (i != RGB_256X3X16); + + i = 0; + + if (use_palette) + do { + rgb->r = dal_fixed31_32_from_fraction( + gamma->red[palette->red], scaler); + rgb->g = dal_fixed31_32_from_fraction( + gamma->green[palette->green], scaler); + rgb->b = dal_fixed31_32_from_fraction( + gamma->blue[palette->blue], scaler); + + ++palette; + ++rgb; + ++i; + } while (i != RGB_256X3X16); + else + do { + rgb->r = dal_fixed31_32_from_fraction( + gamma->red[i], scaler); + rgb->g = dal_fixed31_32_from_fraction( + gamma->green[i], scaler); + rgb->b = dal_fixed31_32_from_fraction( + gamma->blue[i], scaler); + + ++rgb; + ++i; + } while (i != RGB_256X3X16); + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider1); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider1); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider1); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider2); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider2); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider2); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, gg->divider3); + rgb->g = dal_fixed31_32_mul(rgb_last->g, gg->divider3); + rgb->b = dal_fixed31_32_mul(rgb_last->b, gg->divider3); +} + +void dal_grph_gamma_scale_dx( + struct grph_gamma *gg, + enum pixel_format pixel_format, + const struct dxgi_rgb *gamma) +{ + /* TODO remove all "dx" functions */ +} + +bool dal_grph_gamma_build_regamma_curve( + struct grph_gamma *gg, + const struct gamma_parameters *params) +{ + struct pwl_float_data_ex *rgb = gg->rgb_regamma; + + uint32_t i; + + if (!params->regamma.features.bits.GAMMA_RAMP_ARRAY && + params->regamma.features.bits.APPLY_DEGAMMA) { + struct gamma_coefficients coeff; + + struct hw_x_point *coord_x = gg->coordinates_x; + + dal_controller_build_regamma_coefficients( + ¶ms->regamma, + params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB, + &coeff); + + /* Use gg->coordinates_x to retrieve coordinates chosen + * base on given user curve (future task). + * The x values are exponentially distributed and currently + * it is hard-coded, the user curve shape is ignored. + * The future task is to recalculate gg->coordinates_x + * based on input/user curve, + * translation from 256/1025 to 128 pwl points. + */ + + i = 0; + + while (i != gg->hw_points_num + 1) { + rgb->r = dal_controller_translate_from_linear_space_ex( + coord_x->adjusted_x, &coeff, 0); + rgb->g = dal_controller_translate_from_linear_space_ex( + coord_x->adjusted_x, &coeff, 1); + rgb->b = dal_controller_translate_from_linear_space_ex( + coord_x->adjusted_x, &coeff, 2); + + ++coord_x; + ++rgb; + ++i; + } + } else { + const uint32_t max_entries = + RGB_256X3X16 + gg->extra_points - 1; + + /* interpolate between 256 input points and output 185 points */ + + scale_oem_gamma(gg, ¶ms->regamma.regamma_ramp); + + if (!build_oem_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_RED, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_oem_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_GREEN, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_oem_custom_gamma_mapping_coefficients( + gg, CHANNEL_NAME_BLUE, gg->hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + i = 0; + + while (i != gg->hw_points_num + 1) { + rgb->r = calculate_oem_mapped_value( + gg, i, CHANNEL_NAME_RED, max_entries); + rgb->g = calculate_oem_mapped_value( + gg, i, CHANNEL_NAME_GREEN, max_entries); + rgb->b = calculate_oem_mapped_value( + gg, i, CHANNEL_NAME_BLUE, max_entries); + ++rgb; + ++i; + } + } + + return true; +} + +void dal_grph_gamma_build_new_custom_resulted_curve( + struct grph_gamma *gg, + const struct gamma_parameters *params) +{ + struct pwl_result_data *rgb = gg->rgb_resulted; + struct pwl_result_data *rgb_plus_1 = rgb + 1; + + uint32_t i; + + i = 0; + + while (i != gg->hw_points_num + 1) { + rgb->red = dal_fixed31_32_clamp( + rgb->red, gg->x_min, gg->x_max1); + rgb->green = dal_fixed31_32_clamp( + rgb->green, gg->x_min, gg->x_max1); + rgb->blue = dal_fixed31_32_clamp( + rgb->blue, gg->x_min, gg->x_max1); + + ++rgb; + ++i; + } + + rgb = gg->rgb_resulted; + + i = 1; + + while (i != gg->hw_points_num + 1) { + if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dal_fixed31_32_sub( + rgb_plus_1->red, + rgb->red); + rgb->delta_green = dal_fixed31_32_sub( + rgb_plus_1->green, + rgb->green); + rgb->delta_blue = dal_fixed31_32_sub( + rgb_plus_1->blue, + rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } +} + +bool dal_grph_gamma_rebuild_curve_configuration_magic( + struct grph_gamma *gg) +{ + const struct fixed31_32 magic_number = + dal_fixed31_32_from_fraction(249, 1000); + + struct fixed31_32 y_r; + struct fixed31_32 y_g; + struct fixed31_32 y_b; + + struct fixed31_32 y1_min; + struct fixed31_32 y2_max; + struct fixed31_32 y3_max; + + y_r = gg->rgb_resulted[0].red; + y_g = gg->rgb_resulted[0].green; + y_b = gg->rgb_resulted[0].blue; + + y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b)); + + gg->arr_points[0].x = gg->coordinates_x[0].adjusted_x; + gg->arr_points[0].y = y1_min; + gg->arr_points[0].slope = dal_fixed31_32_div( + gg->arr_points[0].y, + gg->arr_points[0].x); + + gg->arr_points[1].x = dal_fixed31_32_add( + gg->coordinates_x[gg->hw_points_num - 1].adjusted_x, + magic_number); + + gg->arr_points[2].x = gg->arr_points[1].x; + + y_r = gg->rgb_resulted[gg->hw_points_num - 1].red; + y_g = gg->rgb_resulted[gg->hw_points_num - 1].green; + y_b = gg->rgb_resulted[gg->hw_points_num - 1].blue; + + y2_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + gg->arr_points[1].y = y2_max; + + y_r = gg->rgb_resulted[gg->hw_points_num].red; + y_g = gg->rgb_resulted[gg->hw_points_num].green; + y_b = gg->rgb_resulted[gg->hw_points_num].blue; + + y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + gg->arr_points[2].y = y3_max; + + gg->arr_points[2].slope = dal_fixed31_32_one; + + return true; +} + +bool dal_grph_gamma_convert_to_custom_float( + struct grph_gamma *gg) +{ + struct custom_float_format fmt; + + struct pwl_result_data *rgb = gg->rgb_resulted; + + uint32_t i = 0; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[0].x, + &fmt, + &gg->arr_points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[0].offset, + &fmt, + &gg->arr_points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[0].slope, + &fmt, + &gg->arr_points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 10; + fmt.sign = false; + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[1].x, + &fmt, + &gg->arr_points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[1].y, + &fmt, + &gg->arr_points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + gg->arr_points[2].slope, + &fmt, + &gg->arr_points[2].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 12; + fmt.sign = true; + + while (i != gg->hw_points_num) { + if (!dal_controller_convert_to_custom_float_format( + rgb->red, + &fmt, + &rgb->red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + rgb->green, + &fmt, + &rgb->green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + rgb->blue, + &fmt, + &rgb->blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + rgb->delta_red, + &fmt, + &rgb->delta_red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + rgb->delta_green, + &fmt, + &rgb->delta_green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + rgb->delta_blue, + &fmt, + &rgb->delta_blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + ++rgb; + ++i; + } + + return true; +} + +static bool round_custom_float_6_12( + struct hw_x_point *x) +{ + struct custom_float_format fmt; + + struct custom_float_value value; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!dal_controller_convert_to_custom_float_format_ex( + x->x, &fmt, &value)) + return false; + + x->adjusted_x = x->x; + + if (value.mantissa) { + BREAK_TO_DEBUGGER(); + + return false; + } + + return true; +} + +bool dal_grph_gamma_build_hw_curve_configuration( + const struct curve_config *curve_config, + struct gamma_curve *gamma_curve, + struct curve_points *curve_points, + struct hw_x_point *points, + uint32_t *number_of_points) +{ + const int8_t max_regions_number = ARRAY_SIZE(curve_config->segments); + + int8_t i; + + uint8_t segments_calculation[8] = { 0 }; + + struct fixed31_32 region1 = dal_fixed31_32_zero; + struct fixed31_32 region2; + struct fixed31_32 increment; + + uint32_t index = 0; + uint32_t segments = 0; + uint32_t max_number; + + bool result = false; + + if (!number_of_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + max_number = *number_of_points; + + i = 0; + + while (i != max_regions_number) { + gamma_curve[i].offset = 0; + gamma_curve[i].segments_num = 0; + + ++i; + } + + i = 0; + + while (i != max_regions_number) { + /* number should go in uninterruptible sequence */ + if (curve_config->segments[i] == -1) + break; + + ASSERT(curve_config->segments[i] >= 0); + + segments += (1 << curve_config->segments[i]); + + ++i; + } + + if (segments > max_number) { + BREAK_TO_DEBUGGER(); + } else { + int32_t divisor; + uint32_t offset = 0; + int8_t begin = curve_config->begin; + int32_t region_number = 0; + + i = begin; + + while ((index < max_number) && + (region_number < max_regions_number) && + (i <= 1)) { + int32_t j = 0; + + segments = curve_config->segments[region_number]; + divisor = 1 << segments; + + if (segments == -1) { + if (i > 0) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i - 1); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i - 1)); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + } + + break; + } + + if (i > -1) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i + 1); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i + 1)); + } + + gamma_curve[region_number].offset = offset; + gamma_curve[region_number].segments_num = segments; + + offset += divisor; + + ++segments_calculation[segments]; + + increment = dal_fixed31_32_div_int( + dal_fixed31_32_sub( + region2, + region1), + divisor); + + points[index].x = region1; + + round_custom_float_6_12(points + index); + + ++index; + ++region_number; + + while ((index < max_number) && (j < divisor - 1)) { + region1 = dal_fixed31_32_add( + region1, + increment); + + points[index].x = region1; + points[index].adjusted_x = region1; + + ++index; + ++j; + } + + ++i; + } + + points[index].x = region1; + + round_custom_float_6_12(points + index); + + *number_of_points = index; + + result = true; + } + + curve_points[0].x = points[0].adjusted_x; + curve_points[0].offset = dal_fixed31_32_zero; + + curve_points[1].x = points[index - 1].adjusted_x; + curve_points[1].offset = dal_fixed31_32_zero; + + curve_points[2].x = points[index].adjusted_x; + curve_points[2].offset = dal_fixed31_32_zero; + + return result; +} + +static void build_evenly_distributed_points( + struct gamma_pixel *points, + uint32_t numberof_points, + struct fixed31_32 max_value, + struct fixed31_32 divider1, + struct fixed31_32 divider2, + struct fixed31_32 divider3) +{ + struct gamma_pixel *p = points; + struct gamma_pixel *p_last = p + numberof_points - 1; + + uint32_t i = 0; + + do { + struct fixed31_32 value = dal_fixed31_32_div_int( + dal_fixed31_32_mul_int(max_value, i), + numberof_points - 1); + + p->r = value; + p->g = value; + p->b = value; + + ++p; + ++i; + } while (i != numberof_points); + + p->r = dal_fixed31_32_div(p_last->r, divider1); + p->g = dal_fixed31_32_div(p_last->g, divider1); + p->b = dal_fixed31_32_div(p_last->b, divider1); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, divider2); + p->g = dal_fixed31_32_div(p_last->g, divider2); + p->b = dal_fixed31_32_div(p_last->b, divider2); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, divider3); + p->g = dal_fixed31_32_div(p_last->g, divider3); + p->b = dal_fixed31_32_div(p_last->b, divider3); +} + +void dal_grph_gamma_convert_256_lut_entries_to_gxo_format( + const struct gamma_ramp_rgb256x3x16 *lut, + struct dev_c_lut16 *gamma) +{ + uint32_t i = 0; + + ASSERT(lut); + ASSERT(gamma); + + do { + gamma->red = lut->red[i]; + gamma->green = lut->green[i]; + gamma->blue = lut->blue[i]; + + ++gamma; + ++i; + } while (i != MAX_LUT_ENTRY); +} + +void dal_grph_gamma_convert_udx_gamma_entries_to_gxo_format( + const struct gamma_ramp_dxgi_1 *lut, + struct dev_c_lut16 *gamma) +{ + /* TODO here we deal with DXGI gamma table, + * originally, values was expressed as 'float', + * now values expressed as 'dal_fixed20_12'. */ +} + +bool dal_grph_gamma_set_palette( + struct grph_gamma *gg, + const struct dev_c_lut *palette, + uint32_t start, + uint32_t length, + enum pixel_format surface_pixel_format) +{ + uint32_t i; + + if (((start + length) > MAX_LUT_ENTRY) || (NULL == palette)) { + BREAK_TO_DEBUGGER(); + /* wrong input */ + return false; + } + + for (i = start; i < start + length; i++) { + gg->saved_palette[i] = palette[i]; + gg->saved_palette[i] = palette[i]; + gg->saved_palette[i] = palette[i]; + } + + return dal_grph_gamma_set_default_gamma(gg, surface_pixel_format); +} + +bool dal_grph_gamma_set_default_gamma( + struct grph_gamma *gg, + enum pixel_format surface_pixel_format) +{ + uint32_t i; + + struct dev_c_lut16 *gamma16 = NULL; + struct gamma_parameters *params = NULL; + + gamma16 = dal_alloc(sizeof(struct dev_c_lut16) * MAX_LUT_ENTRY); + + if (!gamma16) + return false; + + params = dal_alloc(sizeof(*params)); + + if (!params) { + dal_free(gamma16); + return false; + } + + for (i = 0; i < MAX_LUT_ENTRY; i++) { + gamma16[i].red = gamma16[i].green = + gamma16[i].blue = (uint16_t) (i << 8); + } + + params->surface_pixel_format = surface_pixel_format; + params->regamma_adjust_type = GRAPHICS_REGAMMA_ADJUST_HW; + params->degamma_adjust_type = GRAPHICS_DEGAMMA_ADJUST_HW; + params->selected_gamma_lut = GRAPHICS_GAMMA_LUT_REGAMMA; + params->disable_adjustments = false; + + params->regamma.features.value = 0; + + params->regamma.features.bits.GAMMA_RAMP_ARRAY = 0; + params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB = 1; + params->regamma.features.bits.OVERLAY_DEGAMMA_SRGB = 1; + + for (i = 0; i < 3; i++) { + params->regamma.gamma_coeff.a0[i] = 31308; + params->regamma.gamma_coeff.a1[i] = 12920; + params->regamma.gamma_coeff.a2[i] = 55; + params->regamma.gamma_coeff.a3[i] = 55; + params->regamma.gamma_coeff.gamma[i] = 2400; + + } + + gg->funcs->program_lut_gamma(gg, gamma16, params); + + dal_free(gamma16); + dal_free(params); + + return true; +} +static const struct grph_gamma_funcs grph_gamma_funcs = { + .set_gamma_ramp = set_gamma_ramp, + .set_gamma_ramp_legacy = set_gamma_ramp_legacy, + .set_legacy_mode = set_legacy_mode, + .program_prescale_legacy = program_prescale_legacy, + .setup_distribution_points = setup_distribution_points, + .program_black_offsets = program_black_offsets, + .program_white_offsets = program_white_offsets, + .set_lut_inc = set_lut_inc, + .select_lut = select_lut, + .destroy = destroy, +}; +bool dal_grph_gamma_construct( + struct grph_gamma *gg, + struct grph_gamma_init_data *init_data) +{ + if (!init_data) + return false; + + gg->funcs = &grph_gamma_funcs; + gg->regs = NULL; + gg->ctx = init_data->ctx; + + gg->hw_points_num = 128; + gg->coordinates_x = NULL; + gg->rgb_resulted = NULL; + gg->rgb_regamma = NULL; + gg->coeff128 = NULL; + gg->coeff128_oem = NULL; + gg->coeff128_dx = NULL; + gg->axis_x_256 = NULL; + gg->axis_x_1025 = NULL; + gg->rgb_oem = NULL; + gg->rgb_user = NULL; + gg->extra_points = 3; + gg->use_half_points = false; + gg->x_max1 = dal_fixed31_32_one; + gg->x_max2 = dal_fixed31_32_from_int(2); + gg->x_min = dal_fixed31_32_zero; + gg->divider1 = dal_fixed31_32_from_fraction(3, 2); + gg->divider2 = dal_fixed31_32_from_int(2); + gg->divider3 = dal_fixed31_32_from_fraction(5, 2); + + gg->rgb_user = dal_alloc( + sizeof(struct pwl_float_data) * + (DX_GAMMA_RAMP_MAX + gg->extra_points)); + if (!gg->rgb_user) + goto failure_1; + + gg->rgb_oem = dal_alloc( + sizeof(struct pwl_float_data) * + (DX_GAMMA_RAMP_MAX + gg->extra_points)); + if (!gg->rgb_oem) + goto failure_2; + + gg->rgb_resulted = dal_alloc( + sizeof(struct pwl_result_data) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->rgb_resulted) + goto failure_3; + + gg->rgb_regamma = dal_alloc( + sizeof(struct pwl_float_data_ex) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->rgb_regamma) + goto failure_4; + + gg->coordinates_x = dal_alloc( + sizeof(struct hw_x_point) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->coordinates_x) + goto failure_5; + + gg->axis_x_256 = dal_alloc( + sizeof(struct gamma_pixel) * + (MAX_LUT_ENTRY + gg->extra_points)); + if (!gg->axis_x_256) + goto failure_6; + + gg->axis_x_1025 = dal_alloc( + sizeof(struct gamma_pixel) * + (DX_GAMMA_RAMP_MAX + gg->extra_points)); + if (!gg->axis_x_1025) + goto failure_7; + + gg->coeff128 = dal_alloc( + sizeof(struct pixel_gamma_point) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->coeff128) + goto failure_8; + + gg->coeff128_oem = dal_alloc( + sizeof(struct pixel_gamma_point) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->coeff128_oem) + goto failure_9; + + gg->coeff128_dx = dal_alloc( + sizeof(struct pixel_gamma_point) * + (MAX_NUMBEROF_ENTRIES + gg->extra_points)); + if (!gg->coeff128_dx) + goto failure_10; + + /* init palette */ + { + uint32_t i = 0; + + do { + gg->saved_palette[i].red = (uint8_t)i; + gg->saved_palette[i].green = (uint8_t)i; + gg->saved_palette[i].blue = (uint8_t)i; + + ++i; + } while (i != MAX_LUT_ENTRY); + } + + build_evenly_distributed_points( + gg->axis_x_256, MAX_LUT_ENTRY, gg->x_max1, + gg->divider1, gg->divider2, gg->divider3); + + build_evenly_distributed_points( + gg->axis_x_1025, DX_GAMMA_RAMP_MAX, gg->x_max1, + gg->divider1, gg->divider2, gg->divider3); + + return true; + +failure_10: + dal_free(gg->coeff128_oem); +failure_9: + dal_free(gg->coeff128); +failure_8: + dal_free(gg->axis_x_1025); +failure_7: + dal_free(gg->axis_x_256); +failure_6: + dal_free(gg->coordinates_x); +failure_5: + dal_free(gg->rgb_regamma); +failure_4: + dal_free(gg->rgb_resulted); +failure_3: + dal_free(gg->rgb_oem); +failure_2: + dal_free(gg->rgb_user); +failure_1: + return false; +} + +void dal_grph_gamma_destruct( + struct grph_gamma *gg) +{ + dal_free(gg->coeff128_dx); + dal_free(gg->coeff128_oem); + dal_free(gg->coeff128); + dal_free(gg->axis_x_1025); + dal_free(gg->axis_x_256); + dal_free(gg->coordinates_x); + dal_free(gg->rgb_regamma); + dal_free(gg->rgb_resulted); + dal_free(gg->rgb_oem); + dal_free(gg->rgb_user); +} diff --git a/drivers/gpu/drm/amd/dal/controller/grph_gamma.h b/drivers/gpu/drm/amd/dal/controller/grph_gamma.h new file mode 100644 index 000000000000..4f4c644022f6 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/grph_gamma.h @@ -0,0 +1,208 @@ +/* + * 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_GRPH_GAMMA_H__ +#define __DAL_GRPH_GAMMA_H__ + +#include "grph_gamma_types.h" +#include "graphics_and_video_gamma.h" +#include "lut_and_gamma_types.h" + +#define MAX_PWL_ENTRY 128 +#define MAX_NUMBEROF_ENTRIES 256 + +struct pwl_result_data { + struct fixed31_32 red; + struct fixed31_32 green; + struct fixed31_32 blue; + + struct fixed31_32 delta_red; + struct fixed31_32 delta_green; + struct fixed31_32 delta_blue; + + uint32_t red_reg; + uint32_t green_reg; + uint32_t blue_reg; + + uint32_t delta_red_reg; + uint32_t delta_green_reg; + uint32_t delta_blue_reg; +}; + +struct gamma_pixel { + struct fixed31_32 r; + struct fixed31_32 g; + struct fixed31_32 b; +}; + +struct grph_gamma; + +struct grph_gamma_funcs { + bool (*set_gamma_ramp)( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params); + bool (*set_gamma_ramp_legacy)( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params); + void (*set_legacy_mode)( + struct grph_gamma *gg, + bool is_legacy); + void (*program_prescale_legacy)( + struct grph_gamma *gg, + enum pixel_format pixel_format); + bool (*setup_distribution_points)( + struct grph_gamma *gg); + void (*program_black_offsets)( + struct grph_gamma *gg, + struct dev_c_lut16 *offset); + void (*program_white_offsets)( + struct grph_gamma *gg, + struct dev_c_lut16 *offset); + void (*program_lut_gamma)( + struct grph_gamma *gg, + const struct dev_c_lut16 *gamma, + const struct gamma_parameters *params); + void (*set_lut_inc)( + struct grph_gamma *gg, + uint8_t inc, + bool is_float, + bool is_signed); + void (*select_lut)( + struct grph_gamma *gg); + void (*destroy)( + struct grph_gamma **ptr); +}; + +struct grph_gamma { + const struct grph_gamma_funcs *funcs; + const uint32_t *regs; + struct gamma_curve arr_curve_points[MAX_REGIONS_NUMBER]; + struct curve_points arr_points[3]; + uint32_t hw_points_num; + struct hw_x_point *coordinates_x; + struct pwl_result_data *rgb_resulted; + + /* re-gamma curve */ + struct pwl_float_data_ex *rgb_regamma; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 256 */ + struct pixel_gamma_point *coeff128; + struct pixel_gamma_point *coeff128_oem; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 1025 */ + struct pixel_gamma_point *coeff128_dx; + /* evenly distributed points, gamma 256 software points 0-255 */ + struct gamma_pixel *axis_x_256; + /* evenly distributed points, gamma 1025 software points 0-1025 */ + struct gamma_pixel *axis_x_1025; + /* OEM supplied gamma for regamma LUT */ + struct pwl_float_data *rgb_oem; + /* user supplied gamma */ + struct pwl_float_data *rgb_user; + struct dev_c_lut saved_palette[RGB_256X3X16]; + uint32_t extra_points; + bool use_half_points; + struct fixed31_32 x_max1; + struct fixed31_32 x_max2; + struct fixed31_32 x_min; + struct fixed31_32 divider1; + struct fixed31_32 divider2; + struct fixed31_32 divider3; + struct dal_context *ctx; +}; + +struct grph_gamma_init_data { + struct dal_context *ctx; + struct adapter_service *as; + enum controller_id id; +}; + +struct adapter_service; + +bool dal_grph_gamma_construct( + struct grph_gamma *gg, + struct grph_gamma_init_data *init_data); + +void dal_grph_gamma_destruct( + struct grph_gamma *gg); + +bool dal_grph_gamma_map_regamma_hw_to_x_user( + struct grph_gamma *gg, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params); + +void dal_grph_gamma_scale_rgb256x3x16( + struct grph_gamma *gg, + bool use_palette, + const struct gamma_ramp_rgb256x3x16 *gamma); + +void dal_grph_gamma_scale_dx( + struct grph_gamma *gg, + enum pixel_format pixel_format, + const struct dxgi_rgb *gamma); + +bool dal_grph_gamma_build_regamma_curve( + struct grph_gamma *gg, + const struct gamma_parameters *params); + +void dal_grph_gamma_build_new_custom_resulted_curve( + struct grph_gamma *gg, + const struct gamma_parameters *params); + +bool dal_grph_gamma_rebuild_curve_configuration_magic( + struct grph_gamma *gg); + +bool dal_grph_gamma_convert_to_custom_float( + struct grph_gamma *gg); + +bool dal_grph_gamma_build_hw_curve_configuration( + const struct curve_config *curve_config, + struct gamma_curve *gamma_curve, + struct curve_points *curve_points, + struct hw_x_point *points, + uint32_t *number_of_points); + +void dal_grph_gamma_convert_256_lut_entries_to_gxo_format( + const struct gamma_ramp_rgb256x3x16 *lut, + struct dev_c_lut16 *gamma); + +void dal_grph_gamma_convert_udx_gamma_entries_to_gxo_format( + const struct gamma_ramp_dxgi_1 *lut, + struct dev_c_lut16 *gamma); + +bool dal_grph_gamma_set_palette( + struct grph_gamma *gg, + const struct dev_c_lut *palette, + uint32_t start, + uint32_t length, + enum pixel_format surface_pixel_format); + +bool dal_grph_gamma_set_default_gamma( + struct grph_gamma *gg, + enum pixel_format surface_pixel_format); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/grph_gamma_types.h b/drivers/gpu/drm/amd/dal/controller/grph_gamma_types.h new file mode 100644 index 000000000000..ef29c6054f11 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/grph_gamma_types.h @@ -0,0 +1,130 @@ +/* + * 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_GRPH_GAMMA_TYPES_H__ +#define __DAL_GRPH_GAMMA_TYPES_H__ + +#include "include/fixed32_32.h" +#include "include/fixed31_32.h" +#include "include/hw_sequencer_types.h" + +#include "include/csc_common_types.h" + +struct dev_c_lut { + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +struct dev_c_lut16 { + uint16_t red; + uint16_t green; + uint16_t blue; +}; + +enum gamma_ramp_type { + GAMMA_RAMP_UNINITIALIZED = 0, + GAMMA_RAMP_DEFAULT, + GAMMA_RAMP_RBG256X3X16, + GAMMA_RAMP_DXGI_1, +}; + +struct gamma_ramp_rgb256x3x16 { + uint16_t red[RGB_256X3X16]; + uint16_t green[RGB_256X3X16]; + uint16_t blue[RGB_256X3X16]; +}; + +struct dxgi_rgb { + struct fixed32_32 red; + struct fixed32_32 green; + struct fixed32_32 blue; +}; + +struct gamma_ramp_dxgi_1 { + struct dxgi_rgb scale; + struct dxgi_rgb offset; + struct dxgi_rgb gamma_curve[DX_GAMMA_RAMP_MAX]; +}; + +struct gamma_ramp { + enum gamma_ramp_type type; + union { + struct gamma_ramp_rgb256x3x16 gamma_ramp_rgb256x3x16; + struct gamma_ramp_dxgi_1 gamma_ramp_dxgi1; + }; + uint32_t size; +}; + +struct gamma_pwl_integer { + struct dev_c_lut16 lut_base[128]; + struct dev_c_lut16 lut_delta[128]; +}; + +enum graphics_degamma_adjust { + GRAPHICS_DEGAMMA_ADJUST_BYPASS = 0, + GRAPHICS_DEGAMMA_ADJUST_HW, /*without adjustments */ + GRAPHICS_DEGAMMA_ADJUST_SW /* use adjustments */ +}; + +enum graphics_regamma_adjust { + GRAPHICS_REGAMMA_ADJUST_BYPASS = 0, + GRAPHICS_REGAMMA_ADJUST_HW, /* without adjustments */ + GRAPHICS_REGAMMA_ADJUST_SW /* use adjustments */ +}; + +enum graphics_gamma_lut { + GRAPHICS_GAMMA_LUT_LEGACY = 0, /* use only legacy LUT */ + GRAPHICS_GAMMA_LUT_REGAMMA, /* use only regamma LUT */ + GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA /* use legacy & regamma LUT's */ +}; + +union gamma_flag { + struct { + uint32_t config_is_changed:1; + uint32_t both_pipe_req:1; + uint32_t regamma_update:1; + uint32_t gamma_update:1; + uint32_t reserved:28; + } bits; + uint32_t u_all; +}; + +struct gamma_parameters { + union gamma_flag flag; + enum pixel_format surface_pixel_format; /*OS surface pixel format*/ + struct regamma_lut regamma; + + enum graphics_regamma_adjust regamma_adjust_type; + enum graphics_degamma_adjust degamma_adjust_type; + + enum graphics_gamma_lut selected_gamma_lut; + + bool disable_adjustments; + + /* here we grow with parameters if necessary */ +}; + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/internal_types_wide_gamut.h b/drivers/gpu/drm/amd/dal/controller/internal_types_wide_gamut.h new file mode 100644 index 000000000000..0f1e831bac32 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/internal_types_wide_gamut.h @@ -0,0 +1,90 @@ +/* + * 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_INTERNAL_TYPES_WIDE_GAMUT_H__ +#define __DAL_INTERNAL_TYPES_WIDE_GAMUT_H__ + +enum wide_gamut_color_mode { + /* 00 - BITS2:0 Bypass */ + WIDE_GAMUT_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + WIDE_GAMUT_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 02 - hard coded coefficient YCbCr601 */ + /* 03 - hard coded coefficient YCbCr709 */ + /* 04 - programmable OUTPUT CSC coefficient */ + WIDE_GAMUT_COLOR_MODE_GRAPHICS_OUTPUT_CSC, + /* 05 - programmable shared registers COMM_MATRIXB_TRANS coefficient */ + WIDE_GAMUT_COLOR_MODE_GRAPHICS_MATRIX_B, + /* 00 - BITS6:4 Bypass */ + WIDE_GAMUT_COLOR_MODE_OVERLAY_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + WIDE_GAMUT_COLOR_MODE_OVERLAY_PREDEFINED, + /* 02 - hard coded coefficient YCbCr601 */ + /* 03 - hard coded coefficient YCbCr709 */ + /* 04 - programmable OUTPUT CSC coefficient */ + WIDE_GAMUT_COLOR_MODE_OVERLAY_OUTPUT_CSC, + /* 05 - programmable shared registers COMM_MATRIXB_TRANS coefficient */ + WIDE_GAMUT_COLOR_MODE_OVERLAY_MATRIX_B +}; + +enum wide_gamut_degamma_mode { + /* 00 - BITS1:0 Bypass */ + WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_BYPASS, + /* 0x1 - PWL gamma ROM A */ + WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_PWL_ROM_A, + /* 0x2 - PWL gamma ROM B */ + WIDE_GAMUT_DEGAMMA_MODE_GRAPHICS_PWL_ROM_B, + /* 00 - BITS5:4 Bypass */ + WIDE_GAMUT_DEGAMMA_MODE_OVL_BYPASS, + /* 0x1 - PWL gamma ROM A */ + WIDE_GAMUT_DEGAMMA_MODE_OVL_PWL_ROM_A, + /* 0x2 - PWL gamma ROM B */ + WIDE_GAMUT_DEGAMMA_MODE_OVL_PWL_ROM_B, +}; + +enum wide_gamut_regamma_mode { + /* 0x0 - BITS2:0 Bypass */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS, + /* 0x1 - Fixed curve sRGB 2.4 */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24, + /* 0x2 - Fixed curve xvYCC 2.22 */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22, + /* 0x3 - Programmable control A */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A, + /* 0x4 - Programmable control B */ + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B, + /* 0x0 - BITS6:4 Bypass */ + WIDE_GAMUT_REGAMMA_MODE_OVL_BYPASS, + /* 0x1 - Fixed curve sRGB 2.4 */ + WIDE_GAMUT_REGAMMA_MODE_OVL_SRGB24, + /* 0x2 - Fixed curve xvYCC 2.22 */ + WIDE_GAMUT_REGAMMA_MODE_OVL_XYYCC22, + /* 0x3 - Programmable control A */ + WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_A, + /* 0x4 - Programmable control B */ + WIDE_GAMUT_REGAMMA_MODE_OVL_MATRIX_B +}; + +#endif /* __DAL_INTERNAL_TYPES_WIDE_GAMUT_H__ */ diff --git a/drivers/gpu/drm/amd/dal/controller/line_buffer.c b/drivers/gpu/drm/amd/dal/controller/line_buffer.c new file mode 100644 index 000000000000..3641f1fe9415 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/line_buffer.c @@ -0,0 +1,222 @@ +/* + * 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/logger_interface.h" +#include "include/line_buffer_interface.h" +#include "include/adapter_service_interface.h" +#include "line_buffer.h" + +void dal_line_buffer_destroy(struct line_buffer **lb) +{ + if (lb == NULL || *lb == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + (*lb)->funcs->destroy(lb); + + *lb = NULL; +} + +void dal_line_buffer_destruct_base(struct line_buffer *lb) +{ + +} + +bool dal_line_buffer_construct_base( + struct line_buffer *lb, + struct line_buffer_init_data *init_data + ) +{ + struct dal_context *dal_context = init_data->dal_context; + + if (init_data == NULL || init_data->as == NULL) + return false; + lb->dal_context = init_data->dal_context; + lb->size = dal_adapter_service_get_line_buffer_size(init_data->as); + + lb->power_gating = dal_adapter_service_is_feature_supported( + FEATURE_POWER_GATING_LB_PORTION); + dal_logger_write(dal_context->logger, + LOG_MAJOR_LINE_BUFFER, + LOG_MINOR_LINE_BUFFER_POWERGATING, + "LB Partial Power Gating option: %s\n", + (lb->power_gating == true ? "Enabled" : "Disabled")); + + return true; +} + +enum lb_pixel_depth dal_line_buffer_display_bpp_to_lb_depth( + uint32_t disp_bpp) +{ + switch (disp_bpp) { + case 18: + return LB_PIXEL_DEPTH_18BPP; + case 24: + return LB_PIXEL_DEPTH_24BPP; + case 36: + case 42: + case 48: + return LB_PIXEL_DEPTH_36BPP; + case 30: + return LB_PIXEL_DEPTH_30BPP; + default: + break; + + } + return LB_PIXEL_DEPTH_30BPP; +} + +void dal_line_buffer_power_up(struct line_buffer *lb) +{ + lb->funcs->power_up(lb); +} + +uint32_t dal_line_buffer_get_size(struct line_buffer *lb) +{ + return lb->size; +} + +void dal_line_buffer_program_interleave_mode( + struct line_buffer *lb, + enum controller_id idx, + bool interleave) +{ + /* TODO: never called */ +} + +void dal_line_buffer_reset_on_vblank( + struct line_buffer *lb, + enum controller_id idx) +{ + lb->funcs->reset_lb_on_vblank(lb, idx); +} + +bool dal_line_buffer_enable_power_gating( + struct line_buffer *lb, + enum controller_id idx, + struct lb_config_data *lb_config) +{ + /* TODO:check if need here controller_id*/ + return lb->funcs->enable_power_gating(lb, idx, lb_config); +} + +bool dal_line_buffer_set_pixel_storage_depth( + struct line_buffer *lb, + enum lb_pixel_depth depth) +{ + return lb->funcs->set_pixel_storage_depth(lb, depth); +} + +bool dal_line_buffer_get_current_pixel_storage_depth( + struct line_buffer *lb, + enum lb_pixel_depth *lower_depth) +{ + return lb->funcs->get_current_pixel_storage_depth(lb, lower_depth); +} + +bool dal_line_buffer_get_pixel_storage_depth( + struct line_buffer *lb, + uint32_t display_bpp, + enum lb_pixel_depth *depth) +{ + return lb->funcs->get_pixel_storage_depth(lb, display_bpp, depth); +} + +bool dal_line_buffer_get_next_lower_pixel_storage_depth( + struct line_buffer *lb, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth) +{ + return lb->funcs->get_next_lower_pixel_storage_depth( + lb, display_bpp, depth, lower_depth); +} + +bool dal_line_buffer_get_max_num_of_supported_lines( + struct line_buffer *lb, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines) +{ + return lb->funcs->get_max_num_of_supported_lines( + lb, depth, pixel_width, lines); +} + +void dal_line_buffer_set_vblank_irq( + struct line_buffer *lb, + bool enable) +{ + lb->funcs->set_vblank_irq(lb, enable); +} + +void dal_line_buffer_enable_alpha( + struct line_buffer *lb, + bool enable) +{ + lb->funcs->enable_alpha(lb, enable); +} + +bool dal_line_buffer_is_prefetch_supported( + struct line_buffer *lb, + struct lb_config_data *lb_config) +{ + return lb->funcs->is_prefetch_supported(lb, lb_config); +} + +bool dal_line_buffer_base_is_prefetch_supported( + struct line_buffer *lb, + struct lb_config_data *lb_config) +{ + return false; +} + +uint32_t dal_line_buffer_base_calculate_pitch( + enum lb_pixel_depth depth, + uint32_t width) +{ + uint32_t pitch = 0; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pitch = (width + 7) >> 3; + break; + + case LB_PIXEL_DEPTH_24BPP: + pitch = ((width + 7) / 8) * 683; + pitch = (pitch + 511) >> 9; + break; + + case LB_PIXEL_DEPTH_30BPP: + pitch = ((width + 7) / 8) * 854; + pitch = (pitch + 511) >> 9; + break; + + case LB_PIXEL_DEPTH_36BPP: + pitch = (width + 3) >> 2; + break; + } + return pitch; +} diff --git a/drivers/gpu/drm/amd/dal/controller/line_buffer.h b/drivers/gpu/drm/amd/dal/controller/line_buffer.h new file mode 100644 index 000000000000..21f0e0ff7772 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/line_buffer.h @@ -0,0 +1,102 @@ +/* + * 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_LINE_BUFFER_H__ +#define __DAL_LINE_BUFFER_H__ + +#include "include/line_buffer_interface.h" + +struct line_buffer_funcs { + void (*destroy)(struct line_buffer **lb); + void (*power_up)(struct line_buffer *lb); + bool (*enable_power_gating)( + struct line_buffer *lb, + enum controller_id idx, + struct lb_config_data *lb_config); + bool (*set_pixel_storage_depth)( + struct line_buffer *lb, + enum lb_pixel_depth depth); + bool (*get_current_pixel_storage_depth)( + struct line_buffer *lb, + enum lb_pixel_depth *lower_depth); + bool (*get_pixel_storage_depth)( + struct line_buffer *lb, + uint32_t display_bpp, + enum lb_pixel_depth *depth); + bool (*get_next_lower_pixel_storage_depth)( + struct line_buffer *lb, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth); + bool (*get_max_num_of_supported_lines)( + struct line_buffer *lb, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines); + void (*reset_lb_on_vblank)( + struct line_buffer *lb, + enum controller_id idx); + void (*set_vblank_irq)( + struct line_buffer *lb, + bool enable); + void (*enable_alpha)( + struct line_buffer *lb, + bool enable); + bool (*is_prefetch_supported)( + struct line_buffer *lb, + struct lb_config_data *lb_config); +}; + +struct line_buffer { + const struct line_buffer_funcs *funcs; + uint32_t size; + struct dal_context *dal_context; + bool power_gating; +}; + +struct line_buffer_init_data { + struct dal_context *dal_context; + struct adapter_service *as; + enum controller_id id; +}; + +bool dal_line_buffer_construct_base( + struct line_buffer *lb, + struct line_buffer_init_data *init_data); +void dal_line_buffer_destruct_base(struct line_buffer *lb); +void dal_line_buffer_destroy(struct line_buffer **lb); + +enum lb_pixel_depth dal_line_buffer_display_bpp_to_lb_depth( + uint32_t disp_bpp); + +bool dal_line_buffer_base_is_prefetch_supported( + struct line_buffer *lb, + struct lb_config_data *lb_config); + +uint32_t dal_line_buffer_base_calculate_pitch( + enum lb_pixel_depth depth, + uint32_t width); + +#endif /* __DAL_LINE_BUFFER_H__ */ diff --git a/drivers/gpu/drm/amd/dal/controller/lut_and_gamma_types.h b/drivers/gpu/drm/amd/dal/controller/lut_and_gamma_types.h new file mode 100644 index 000000000000..47313bd9624d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/lut_and_gamma_types.h @@ -0,0 +1,33 @@ +/* + * 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_LUT_AND_GAMMA_TYPES_H__ +#define __DAL_LUT_AND_GAMMA_TYPES_H__ + +enum { + MAX_LUT_ENTRY = 256 +}; + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/pipe_control.h b/drivers/gpu/drm/amd/dal/controller/pipe_control.h new file mode 100644 index 000000000000..a2b787876a0c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/pipe_control.h @@ -0,0 +1,67 @@ +/* + * 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_PIPE_CONTROL_H__ +#define __DAL_PIPE_CONTROL_H__ + +struct pipe_control; + +struct pipe_control_funcs { + void (*enable_stereo_mixer)( + struct pipe_control *pc, + const struct crtc_mixer_params *params); + void (*disable_stereo_mixer)(struct pipe_control *pc); + void (*enable_fe_clock)(struct pipe_control *pc, bool enable); + void (*disable_fe_clock)(struct pipe_control *pc); + void (*enable_display_pipe_clock_gating)( + struct pipe_control *pc, + bool clock_gating); + bool (*enable_disp_power_gating)( + struct pipe_control *pc, + enum pipe_gating_control power_gating); + bool (*pipe_control_lock)( + struct pipe_control *pc, + uint32_t control_mask, + bool lock); + void (*set_blender_mode)( + struct pipe_control *pc, + enum blender_mode mode); + bool (*program_alpha_blending)( + struct pipe_control *pc, + const struct alpha_mode_cfg *cfg); + void (*destroy)(struct pipe_control **pc); +}; + +struct pipe_control { + enum controller_id controller_id; + const struct pipe_control_funcs *funcs; + struct bios_parser *bp; + uint32_t *regs; + struct dal_context *ctx; +}; + +bool dal_pipe_control_construct(struct pipe_control *pc); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/scaler.c b/drivers/gpu/drm/amd/dal/controller/scaler.c new file mode 100644 index 000000000000..b8358f7bcbe5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/scaler.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 "scaler.h" + +#define UP_SCALER_RATIO_MAX 16000 +#define DOWN_SCALER_RATIO_MAX 250 +#define SCALER_RATIO_DIVIDER 1000 + +static const struct scaler_taps_and_ratio downscaling_data[] = { + { 4, 4, 660, 1000 }, + { 6, 5, 510, 659 }, + { 6, 6, 460, 509 }, + { 8, 6, 360, 459 }, + { 10, 6, 250, 359 } +}; + +static const struct scaler_taps downscaling_data_fallback[] = { + { 6, 6 }, + { 6, 5 }, + { 4, 4 }, + { 4, 3 }, + { 4, 2 }, + { 2, 2 } +}; + +static const struct scaler_taps upscaling_data[] = { + { 4, 4 }, + { 4, 3 }, + { 4, 2 }, + { 2, 2 } +}; + +bool dal_scaler_construct( + struct scaler *scl, + struct scaler_init_data *init_data) +{ + if (!init_data) + return false; + + if (!init_data->bp) + return false; + + scl->bp = init_data->bp; + scl->id = init_data->id; + scl->ctx = init_data->dal_ctx; + return true; +} + +static bool validate_requested_scale_ratio(uint32_t src, uint32_t dst); +static bool get_taps_number( + enum scaling_type type, + uint32_t ratio, + bool horizontal, + uint32_t *taps); +static enum scaling_type get_scaling_type( + uint32_t src_size, + uint32_t dst_size); + +enum scaler_validation_code dal_scaler_get_optimal_taps_number( + struct scaler_validation_params *params, + struct scaling_tap_info *taps) +{ + struct view *src_view = ¶ms->source_view; + struct view *dst_view = ¶ms->dest_view; + enum scaling_type h_type; + enum scaling_type v_type; + uint32_t h_ratio; + uint32_t v_ratio; + + /* Validate the parameters */ + if (src_view->width == 0 || dst_view->width == 0) + return SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; + + if (src_view->height == 0 || dst_view->height == 0) + return SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; + + /* There are 9 cases and we grouped this into 3 groups : no scale, + * upscale, downscale + * 0 Hor & vert no scale + + * 1. Hor & vert upscale + * 2. Hor down scale, vert up scale + * 3. Hor upscale , vert down scale + * 4. Hor up scale, vert no scale + * 5. Hor no scale, vert up scale + + * 6. Hor & vert down scale + * 7. Hor no scale, vert down scale + * 8. Hor down scale, vert no scale + + * The hw could do down scale up to 4:1 and upscale could be an + * unlimited ratio. + * However the mode validation goes through this code path, but + * when we need to generate the coefficients the same restrictions + * should be applied to the ratio . + * Then following limitations are applied : + * For coefficient generation for upscale ratio 1:4 versa hw could do + * unlimited ratio. + * Hw places the restriction in down scale case because it could do 4:1, + * but coefficients generation could do 6:1. + * We could enlarge the coefficient generation range for upscale case if + * we really need this because it would be related to + * add more sharpness tables + * + */ + + if (!validate_requested_scale_ratio(src_view->width, + dst_view->width)) + return SCALER_VALIDATION_SCALING_RATIO_NOT_SUPPORTED; + + if (!validate_requested_scale_ratio(src_view->height, + dst_view->height)) + return SCALER_VALIDATION_SCALING_RATIO_NOT_SUPPORTED; + + h_type = get_scaling_type(src_view->width, dst_view->width); + v_type = get_scaling_type(src_view->height, dst_view->height); + + h_ratio = dst_view->width * SCALER_RATIO_DIVIDER + / src_view->width; + v_ratio = dst_view->height * SCALER_RATIO_DIVIDER + / src_view->height; + + if (!get_taps_number(h_type, h_ratio, true, &taps->h_taps)) + return SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; + + if (!get_taps_number(v_type, v_ratio, false, &taps->v_taps)) + return SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; + + return SCALER_VALIDATION_OK; +} + +enum scaler_validation_code dal_scaler_get_next_lower_taps_number( + struct scaler_validation_params *params, + struct scaling_tap_info *taps) +{ + enum scaler_validation_code code = + SCALER_VALIDATION_INVALID_INPUT_PARAMETERS; + uint32_t array_size = sizeof(downscaling_data_fallback) + / sizeof(downscaling_data_fallback[0]); + + uint32_t i; + + /* we should loop through the predefined array to find lower taps + * configuration + * we apply the lower taps in down scale and upscale cases identically + */ + for (i = 0; i < array_size; ++i) { + /* find vtaps smaller than we have in parameter and return to + * the caller */ + if (taps->v_taps > downscaling_data_fallback[i].v_tap) { + /* override is allowed when there is scaling on X */ + if (taps->h_taps > 1) + taps->h_taps = + downscaling_data_fallback[i].h_tap; + + taps->v_taps = downscaling_data_fallback[i].v_tap; + code = SCALER_VALIDATION_OK; + break; + } + } + + return code; +} + +static bool validate_requested_scale_ratio(uint32_t src, uint32_t dst) +{ + uint32_t ratio = dst * SCALER_RATIO_DIVIDER / src; + + if (dst > src) { + /* ratio bigger than max allowed? + * acc.to coefficient generation capability + */ + if (ratio > UP_SCALER_RATIO_MAX) + return false; + } else { + if (ratio < DOWN_SCALER_RATIO_MAX) + return false; + } + return true; +} + +static bool get_taps_number( + enum scaling_type type, + uint32_t ratio, + bool horizontal, + uint32_t *taps) +{ + if (!taps) + return false; + + if (type == SCALING_TYPE_NO_SCALING) + *taps = 1; + else if (type == SCALING_TYPE_UPSCALING) { + if (horizontal) + *taps = upscaling_data[0].h_tap; + else + *taps = upscaling_data[0].v_tap; + } else { + uint32_t size_of_array = ARRAY_SIZE(downscaling_data); + uint32_t i; + + for (i = 0; i < size_of_array; ++i) { + if (ratio >= downscaling_data[i].lo_ratio + && ratio <= downscaling_data[i].hi_ratio) { + if (horizontal) + *taps = downscaling_data[i].h_tap; + else + *taps = downscaling_data[i].v_tap; + + return true; + } + } + + if (horizontal) + *taps = downscaling_data[0].h_tap; + else + *taps = downscaling_data[0].v_tap; + } + + return true; +} + +static enum scaling_type get_scaling_type( + uint32_t src_size, + uint32_t dst_size) +{ + enum scaling_type type = SCALING_TYPE_NO_SCALING; + + if (dst_size > src_size) + type = SCALING_TYPE_UPSCALING; + else if (dst_size < src_size) + type = SCALING_TYPE_DOWNSCALING; + else + ASSERT(dst_size == src_size); + + return type; + +} + +bool dal_scaler_update_viewport( + struct scaler *scl, + const struct rect *view_port, + bool is_fbc_attached) +{ + bool program_req = false; + struct rect current_view_port; + + if (view_port == NULL) + return program_req; + + scl->funcs->get_viewport(scl, ¤t_view_port); + + if (current_view_port.x != view_port->x || + current_view_port.y != view_port->y || + current_view_port.height != view_port->height || + current_view_port.width != view_port->width) + program_req = true; + + if (program_req) { + /*underlay viewport is programmed with scaler + *program_viewport function pointer is not exposed*/ + if (scl->funcs->program_viewport != NULL) + scl->funcs->program_viewport(scl, view_port); + } + + return program_req; +} diff --git a/drivers/gpu/drm/amd/dal/controller/scaler.h b/drivers/gpu/drm/amd/dal/controller/scaler.h new file mode 100644 index 000000000000..1070a51c849e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/scaler.h @@ -0,0 +1,105 @@ +/* + * 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_H__ +#define __DAL_SCALER_H__ + +#include "include/bios_parser_interface.h" +#include "include/scaler_types.h" +#include "scaler_filter.h" + +struct scaler; + +struct scaler_funcs { + enum scaler_validation_code (*get_optimal_taps_number)( + struct scaler_validation_params *params, + struct scaling_tap_info *taps); + enum scaler_validation_code (*get_next_lower_taps_number)( + struct scaler_validation_params *params, + struct scaling_tap_info *taps); + void (*set_scaler_bypass)(struct scaler *scl); + bool (*is_scaling_enabled)(struct scaler *scl); + bool (*set_scaler_wrapper)( + struct scaler *scl, + const struct scaler_data *data); + void (*get_viewport)( + struct scaler *scl, + struct rect *current_view_port); + void (*program_viewport)( + struct scaler *scl, + const struct rect *view_port); + void (*destroy)(struct scaler **scl); +}; + +struct scaler_init_data { + struct bios_parser *bp; + struct dal_context *dal_ctx; + enum controller_id id; +}; + +struct scaler { + const struct scaler_funcs *funcs; + struct bios_parser *bp; + enum controller_id id; + const uint32_t *regs; + struct scaler_filter *filter; + struct dal_context *ctx; +}; + +enum scaling_type { + SCALING_TYPE_NO_SCALING = 0, + SCALING_TYPE_UPSCALING, + SCALING_TYPE_DOWNSCALING +}; + +struct scaler_taps_and_ratio { + uint32_t h_tap; + uint32_t v_tap; + uint32_t lo_ratio; + uint32_t hi_ratio; +}; + +struct scaler_taps { + uint32_t h_tap; + uint32_t v_tap; +}; + +bool dal_scaler_construct( + struct scaler *scl, + struct scaler_init_data *init_data); + +enum scaler_validation_code dal_scaler_get_optimal_taps_number( + struct scaler_validation_params *params, + struct scaling_tap_info *taps); +enum scaler_validation_code dal_scaler_get_next_lower_taps_number( + struct scaler_validation_params *params, + struct scaling_tap_info *taps); + +bool dal_scaler_update_viewport( + struct scaler *scl, + const struct rect *view_port, + bool is_fbc_attached); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/scaler_filter.c b/drivers/gpu/drm/amd/dal/controller/scaler_filter.c new file mode 100644 index 000000000000..f8910959a5bd --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/scaler_filter.c @@ -0,0 +1,1978 @@ +/* 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/fixed31_32.h" + +#include "scaler_filter.h" + +enum { + DOWN_DB_SCALES = 8, + DOWN_DB_POINTS = 11, + + UP_DB_SCALES = 1, + UP_DB_POINTS = 7, + + MIN_SHARPNESS = -50, + MAX_SHARPNESS = 50, + + CONST_DIVIDER = 10000000, + + MAX_HOR_DOWNSCALE = 1666000, /* 1:6 */ + MAX_VER_DOWNSCALE = 1666000, /* 1:6 */ + + MAX_HOR_UPSCALE = 160000000, /* 16:1 */ + MAX_VER_UPSCALE = 160000000, /* 16:1 */ + + THRESHOLDRATIOLOW = 8000000, /* 0.8 */ + THRESHOLDRATIOUP = 10000000, /* 1.0 */ + + DOWN_DB_FUZZY = -120411996, /* -12.041200 */ + DOWN_DB_FLAT = -60205998, /* -6.020600 */ + DOWN_DB_SHARP = -10000000, /* -1.000000 */ + + UP_DB_FUZZY = -60205998, /* -6.020600 */ + UP_DB_FLAT = 0, + UP_DB_SHARP = 60205998 /* 6.020600 */ +}; + +static inline struct fixed31_32 max_hor_downscale(void) +{ + return dal_fixed31_32_from_fraction(MAX_HOR_DOWNSCALE, CONST_DIVIDER); +} + +static inline struct fixed31_32 max_ver_downscale(void) +{ + return dal_fixed31_32_from_fraction(MAX_VER_DOWNSCALE, CONST_DIVIDER); +} + +static inline struct fixed31_32 max_hor_upscale(void) +{ + return dal_fixed31_32_from_fraction(MAX_HOR_UPSCALE, CONST_DIVIDER); +} + +static inline struct fixed31_32 max_ver_upscale(void) +{ + return dal_fixed31_32_from_fraction(MAX_VER_UPSCALE, CONST_DIVIDER); +} + +static inline struct fixed31_32 threshold_ratio_low(void) +{ + return dal_fixed31_32_from_fraction(THRESHOLDRATIOLOW, CONST_DIVIDER); +} + +static inline struct fixed31_32 threshold_ratio_up(void) +{ + return dal_fixed31_32_from_fraction(THRESHOLDRATIOUP, CONST_DIVIDER); +} + +static inline struct fixed31_32 down_db_fuzzy(void) +{ + return dal_fixed31_32_from_fraction(DOWN_DB_FUZZY, CONST_DIVIDER); +} + +static inline struct fixed31_32 down_db_flat(void) +{ + return dal_fixed31_32_from_fraction(DOWN_DB_FLAT, CONST_DIVIDER); +} + +static inline struct fixed31_32 down_db_sharp(void) +{ + return dal_fixed31_32_from_fraction(DOWN_DB_SHARP, CONST_DIVIDER); +} + +static inline struct fixed31_32 up_db_fuzzy(void) +{ + return dal_fixed31_32_from_fraction(UP_DB_FUZZY, CONST_DIVIDER); +} + +static inline struct fixed31_32 up_db_flat(void) +{ + return dal_fixed31_32_from_fraction(UP_DB_FLAT, CONST_DIVIDER); +} + +static inline struct fixed31_32 up_db_sharp(void) +{ + return dal_fixed31_32_from_fraction(UP_DB_SHARP, CONST_DIVIDER); +} + +static const int32_t + downscaling_db_table[][DOWN_DB_SCALES + 1][DOWN_DB_POINTS] = { + /* 3 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 14302719, 14302719, 14302719, + 10000000, 99999, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14302339, 14302339, 14302339, + 10000000, 4452010, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14302760, 14302760, 14302760, + 10000000, 7826979, 5258399, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14302819, 14302819, 14302819, + 10000000, 8669400, 7414469, + 4422729, 99999, 99999, + 99999, 99999 + }, + { + 14302730, 14302730, 12791190, + 10000000, 9045640, 8180170, + 6477950, 4599249, 2019010, + 99999, 99999 + }, + { + 14302699, 14302699, 12067849, + 10000000, 9236029, 8541280, + 7252740, 6021010, 4820120, + 3511950, 1769340 + }, + { + 14302710, 14302710, 11783510, + 10000000, 9325690, 8704419, + 7595670, 6583020, 5652850, + 4749999, 3847680 + }, + { + 14302920, 14302920, 11709250, + 10000000, 9345560, 8754609, + 7692559, 6738259, 5878239, + 5057529, 4264070 + } + }, + /* 4 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 14308999, 14308999, 14308999, + 10000000, 99999, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14308999, 14308999, 14308999, + 10000000, 6311039, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14308999, 14308999, 14308999, + 10000000, 8526669, 6832849, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 14308999, 14308999, 12110630, + 10000000, 9117940, 8230940, + 6320130, 3719770, 99999, + 99999, 99999 + }, + { + 14308999, 14308999, 11474980, + 10000000, 9370139, 8771979, + 7601270, 6440780, 5249999, + 3887520, 2039040 + }, + { + 14308999, 13084859, 11179579, + 10000000, 9495180, 9016919, + 8134520, 7311699, 6560329, + 5845720, 5155519 + }, + { + 14308999, 12576600, 11048669, + 10000000, 9550499, 9132360, + 8368729, 7679399, 7073119, + 6520900, 6015530 + }, + { + 14308999, 12448530, 11007410, + 10000000, 9566799, 9165279, + 8435800, 7785279, 7215780, + 6701470, 6240640 + } + }, + /* 5 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 8971139, 8971139, 8971139, + 10000000, 99999, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 9466379, 9466379, 9466379, + 10000000, 5648760, 3834280, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 15000000, 15000000, 14550110, + 10000000, 7121120, 5994579, + 4314630, 2606149, 99999, + 99999, 99999 + }, + { + 15000000, 15000000, 13047469, + 10000000, 8368809, 7343569, + 5970299, 4924620, 4029389, + 3171139, 2276369 + }, + { + 15000000, 14157199, 11897679, + 10000000, 9166659, 8444600, + 7287240, 6374719, 5615460, + 4949580, 4350199 + }, + { + 15000000, 12877819, 11224579, + 10000000, 9488620, 9016109, + 8203780, 7500000, 6883730, + 6326839, 5818459 + }, + { + 14733040, 12233200, 10939040, + 10000000, 9608929, 9250000, + 8623390, 8076940, 7606369, + 7177749, 6785169 + }, + { + 14627330, 12046170, 10862360, + 10000000, 9639260, 9312710, + 8737679, 8242470, 7815709, + 7432209, 7082970 + } + }, + /* 6 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 8231559, 8231559, 8231559, + 10000000, 99999, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 8353310, 8353310, 8353310, + 10000000, 5504879, 4310710, + 870359, 99999, 99999, + 99999, 99999 + }, + { + 8643479, 8643479, 8643479, + 10000000, 6483510, 5768150, + 4630779, 3580690, 2501940, + 1015309, 99999 + }, + { + 15000000, 15000000, 13493930, + 10000000, 7516040, 6802409, + 5824409, 5080109, 4454280, + 3896749, 3386510 + }, + { + 15000000, 14055930, 12321079, + 10000000, 8872389, 8090410, + 7035570, 6281229, 5676810, + 5165010, 4717260 + }, + { + 15000000, 12915290, 11311399, + 10000000, 9460610, 8988440, + 8202149, 7548679, 6999999, + 6510639, 6065719 + }, + { + 14310129, 12140829, 10901659, + 10000000, 9635019, 9307180, + 8740929, 8263260, 7858849, + 7499330, 7170130 + }, + { + 13815449, 11911309, 10801299, + 10000000, 9669629, 9380580, + 8878319, 8452050, 8097199, + 7785030, 7504299 + } + }, + /* 7 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 10616660, 10616660, 10616660, + 10000000, 2646020, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 10099999, 10099999, 10099999, + 10000000, 4936839, 4112670, + 2729740, 896539, 99999, + 99999, 99999 + }, + { + 8345860, 8345860, 8345860, + 10000000, 6034079, 5371739, + 4466759, 3763799, 3155870, + 2588019, 2026730 + }, + { + 9298499, 9298499, 13768420, + 10000000, 7174239, 6524270, + 5670250, 5052099, 4549089, + 4115279, 3722150 + }, + { + 15000000, 14116940, 12563209, + 10000000, 8542140, 7782419, + 6865050, 6239479, 5758860, + 5351870, 4992800 + }, + { + 15000000, 12913750, 11306079, + 10000000, 9452580, 8969209, + 8168810, 7538409, 7029479, + 6603180, 6227809 + }, + { + 14390859, 11862809, 10757420, + 10000000, 9688709, 9404249, + 8904439, 8472480, 8099079, + 7765330, 7459110 + }, + { + 13752900, 11554559, 10637769, + 10000000, 9736120, 9499999, + 9079759, 8718389, 8408790, + 8134469, 7886120 + } + }, + /* 8 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 11277090, 11277090, 11277090, + 10000000, 2949059, 99999, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 11196039, 11196039, 11196039, + 10000000, 4627540, 4018869, + 3018769, 2000000, 250770, + 99999, 99999 + }, + { + 10878369, 10878369, 10878369, + 10000000, 5657230, 5118110, + 4372630, 3809120, 3337709, + 2919510, 2535369 + }, + { + 9090089, 9090089, 13961290, + 10000000, 6929969, 6334999, + 5569829, 5019649, 4584150, + 4208610, 3876540 + }, + { + 15000000, 14173229, 12732659, + 10000000, 8267070, 7575380, + 6764540, 6218209, 5803539, + 5454990, 5146239 + }, + { + 15000000, 12928279, 11292259, + 10000000, 9447429, 8954229, + 8141599, 7516989, 7039459, + 6649519, 6316819 + }, + { + 14661350, 11638879, 10665880, + 10000000, 9722669, 9464690, + 9013469, 8613470, 8266339, + 7949870, 7663450 + }, + { + 13861900, 11311980, 10543940, + 10000000, 9772019, 9565100, + 9198870, 8881340, 8609200, + 8365769, 8147500 + } + }, + /* 9 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { 10099999, 10099999, 10099999, + 10000000, 2939159, 1526470, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 11726609, 11726609, 11726609, + 10000000, 4329420, 3805609, + 3030480, 2363760, 1732099, + 980139, 99999 + }, + { + 10949269, 10949269, 10949269, + 10000000, 5452589, 4946640, + 4277969, 3790729, 3392640, + 3048950, 2750000 + }, + { + 8830279, 8830279, 14084529, + 10000000, 6743149, 6182519, + 5482980, 5000000, 4622060, + 4303340, 4022600 + }, + { + 9709150, 14111399, 12800760, + 10000000, 7989749, 7445629, + 6741260, 6241980, 5857459, + 5534989, 5255370 + }, + { + 15000000, 12830289, 11489900, + 10000000, 9302089, 8767340, + 8025540, 7500000, 7100800, + 6768149, 6481850 + }, + { + 14873609, 11576000, 10650579, + 10000000, 9731360, 9483649, + 9054650, 8680559, 8358049, + 8066400, 7802420 + }, + { + 12981410, 11185950, 10491620, + 10000000, 9795730, 9611030, + 9286710, 9007279, 8768100, + 8553469, 8361340 + } + }, + /* 10 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 8993279, 8993279, 8993279, + 10000000, 2921360, 1905619, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 9064850, 9064850, 9064850, + 10000000, 4095619, 3655839, + 3021000, 2500000, 2031680, + 1566990, 1055440 + }, + { + 11043460, 11043460, 11043460, + 10000000, 5287479, 4816150, + 4208439, 3769229, 3418970, + 3117449, 2850320 + }, + { + 8651900, 8651900, 14169909, + 10000000, 6596950, 6071490, + 5423219, 4980779, 4644620, + 4362219, 4114899 + }, + { + 9246050, 14055370, 12832759, + 10000000, 7831320, 7369570, + 6731680, 6262450, 5897690, + 5592269, 5328789 + }, + { + 15000000, 12770450, 11642129, + 10000000, 9120929, 8601920, + 7946630, 7490440, 7140589, + 6847490, 6593719 + }, + { + 14062479, 11541219, 10644329, + 10000000, 9736120, 9495139, + 9080520, 8724340, 8419489, + 8146359, 7899820 + }, + { + 12507469, 11102950, 10457479, + 10000000, 9811149, 9641249, + 9344969, 9090980, 8875219, + 8684499, 8513180 + } + }, + /* 11 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 10099509, 10099509, 10099509, + 10000000, 2788810, 2054850, + 99999, 99999, 99999, + 99999, 99999 + }, + { + 8872069, 8872069, 8872069, + 10000000, 3929649, 3522840, + 2963230, 2527720, 2157579, + 1823610, 1500000 + }, + { + 10099999, 10099999, 10099999, + 10000000, 5155599, 4712319, + 4154500, 3759450, 3448629, + 3183139, 2948490 + }, + { + 10511649, 10511649, 14216580, + 10000000, 6445930, 5988820, + 5401239, 4988409, 4673399, + 4410479, 4181599 + }, + { + 9170889, 14003310, 12949769, + 10000000, 7684900, 7250000, + 6670129, 6255810, 5934680, + 5664110, 5427970 + }, + { + 15000000, 12763030, 11734730, + 10000000, 8958870, 8478559, + 7893459, 7489529, 7179200, + 6917790, 6688359 + }, + { + 14634610, 11491880, 10619130, + 10000000, 9744859, 9509819, + 9102900, 8760340, 8463050, + 8202620, 7968729 + }, + { + 12415319, 10980290, 10405089, + 10000000, 9831910, 9680110, + 9413710, 9184579, 8987190, + 8813819, 8655819 + } + }, + /* 12 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 10832400, 10832400, 10832400, + 10000000, 2700819, 2115820, + 750000, 99999, 99999, + 99999, 99999 + }, + { + 10747549, 10747549, 10747549, + 10000000, 3781630, 3415020, + 2914879, 2537429, 2221180, + 1943199, 1688420 + }, + { + 11630790, 11630790, 11630790, + 10000000, 5047429, 4631519, + 4113860, 3750000, 3469760, + 3229379, 3016360 + }, + { + 10780229, 10780229, 10780229, + 10000000, 6340010, 5935009, + 5387600, 4995940, 4695929, + 4446829, 4231610 + }, + { + 9055669, 13968739, 13037070, + 10000000, 7556660, 7149490, + 6625509, 6250000, 5958870, + 5713790, 5500869 + }, + { + 14614900, 12760740, 11806739, + 10000000, 8824530, 8388419, + 7857400, 7489010, 7206150, + 6968010, 6759889 + }, + { + 14894100, 11451840, 10598870, + 10000000, 9750000, 9521099, + 9122239, 8784019, 8494700, + 8243309, 8018680 + }, + { + 12298769, 10886880, 10367530, + 10000000, 9846829, 9708030, + 9464049, 9252949, 9072539, + 8910980, 8766649 + } + }, + /* 13 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 10099999, 10099999, 10099999, + 10000000, 2574490, 2099110, + 1194889, 99999, 99999, + 99999, 99999 + }, + { + 10099999, 10099999, 10099999, + 10000000, 3679780, 3332070, + 2869139, 2530030, 2251899, + 2010450, 1793050 + }, + { + 9306690, 9306690, 9306690, + 10000000, 4964010, 4573009, + 4082309, 3742089, 3481810, + 3262990, 3070969 + }, + { + 10099999, 10099999, 10099999, + 10000000, 6217889, 5843269, + 5353810, 5000000, 4730190, + 4499999, 4301390 + }, + { + 8819990, 13964320, 13098440, + 10000000, 7454770, 7075160, + 6591439, 6250000, 5983970, + 5760229, 5564339 + }, + { + 14432849, 12727780, 11847709, + 10000000, 8695709, 8322049, + 7842620, 7500000, 7234820, + 7010849, 6814730 + }, + { + 15000000, 11440130, 10620100, + 10000000, 9742270, 9508739, + 9110010, 8782560, 8510140, + 8276290, 8069980 + }, + { + 12039999, 10825289, 10341939, + 10000000, 9858080, 9729740, + 9505100, 9310669, 9144560, + 8996559, 8862569 + } + }, + /* 14 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 9289590, 9289590, 9289590, + 10000000, 2485270, 2084970, + 1362659, 250000, 99999, + 99999, 99999 + }, + { + 9484500, 9484500, 9484500, + 10000000, 3593840, 3263100, + 2833609, 2519409, 2267650, + 2050379, 1856749 + }, + { + 9237130, 9237130, 9237130, + 10000000, 4898909, 4527629, + 4057880, 3734529, 3490320, + 3287230, 3111050 + }, + { + 9543399, 9543399, 9543399, + 10000000, 6110230, 5772359, + 5328080, 5007240, 4757330, + 4545379, 4359109 + }, + { + 9032660, 9032660, 9032660, + 10000000, 7373520, 7016940, + 6565740, 6250000, 6002650, + 5794939, 5610830 + }, + { + 14351329, 12697319, 11875350, + 10000000, 8606730, 8275989, + 7833449, 7510430, 7257339, + 7043970, 6857690 + }, + { + 13286800, 11436090, 10643019, + 10000000, 9729470, 9491149, + 9096930, 8778640, 8519319, + 8299450, 8104829 + }, + { + 11838380, 10778709, 10322740, + 10000000, 9866499, 9746059, + 9535790, 9354810, 9200339, + 9063839, 8940430 + } + }, + /* 15 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 9193199, 9193199, 9193199, + 10000000, 2400999, 2042409, + 1450179, 789309, 99999, + 99999, 99999 + }, + { + 10755189, 10755189, 10755189, + 10000000, 3532319, 3212479, + 2803660, 2510200, 2278629, + 2078720, 1899970 + }, + { + 8732669, 8732669, 8732669, + 10000000, 4821290, 4483030, + 4045079, 3737959, 3505080, + 3311960, 3143329 + }, + { + 9450280, 9450280, 9450280, + 10000000, 6040880, 5718960, + 5302609, 5004199, 4771710, + 4575310, 4404180 + }, + { + 10520930, 10520930, 10520930, + 10000000, 7298259, 6975160, + 6552690, 6250000, 6018469, + 5822089, 5648869 + }, + { + 14320160, 12683949, 11917040, + 10000000, 8541300, 8228710, + 7812070, 7509459, 7272909, + 7072560, 6895729 + }, + { + 15000000, 11434819, 10650700, + 10000000, 9723110, 9480339, + 9083300, 8771640, 8524850, + 8317480, 8135899 + }, + { + 11750520, 10722860, 10299190, + 10000000, 9875990, 9763770, + 9567070, 9397709, 9252669, + 9124029, 9008929 + } + }, + /* 16 tap downscaling */ + { + { + 60209999, 40000000, 20000000, + 0, -10000000, -20000000, + -40000000, -60209999, -80000000, + -100000000, -120410003 + }, + { + 10612260, 10612260, 10612260, + 10000000, 2308720, 1999289, + 1495770, 1009820, 315460, + 99999, 99999 + }, + { + 9394969, 9394969, 9394969, + 10000000, 3462660, 3162190, + 2780120, 2508420, 2295179, + 2109449, 1943989 + }, + { + 10609409, 10609409, 10609409, + 10000000, 4749999, 4447000, + 4039109, 3746300, 3522360, + 3336620, 3177059 + }, + { + 9435039, 9435039, 9435039, + 10000000, 5978109, 5675160, + 5282300, 5000000, 4782429, + 4598149, 4438050 + }, + { + 10592620, 10592620, 10592620, + 10000000, 7244589, 6940630, + 6537730, 6250000, 6027920, + 5842260, 5680159 + }, + { + 14282959, 12678509, 11963449, + 10000000, 8484349, 8181620, + 7785459, 7500000, 7281309, + 7095699, 6932809 + }, + { + 15000000, 11434919, 10673819, + 10000000, 9708179, 9456859, + 9060000, 8760929, 8529940, + 8338279, 8172209 + }, + { + 11690390, 10668220, 10277210, + 10000000, 9884750, 9780330, + 9597110, 9439319, 9304260, + 9183580, 9075019 + } + } +}; + +static const int32_t upscaling_db_table[][UP_DB_SCALES+1][UP_DB_POINTS] = { + /* 3 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 14302920, 14302920, 11709250, + 10000000, + 8754609, 7692559, 6738259 + } + }, + /* 4 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 14308999, 12448530, 11007410, + 10000000, 9165279, 8435800, + 7785279 + } + }, + /* 5 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 14627330, 12046170, 10862360, + 10000000, + 9312710, 8737679, 8242470 + } + }, + /* 6 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 13815449, 11911309, 10801299, + 10000000, + 9380580, 8878319, 8452050 + } + }, + /* 7 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 13752900, 11554559, 10637769, + 10000000, + 9499999, 9079759, 8718389 + } + }, + /* 8 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 13861900, 11311980, 10543940, + 10000000, + 9565100, 9198870, 8881340 + } + }, + /* 9 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 12981410, 11185950, 10491620, + 10000000, + 9611030, 9286710, 9007279 + } + }, + /* 10 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 12507469, 11102950, 10457479, + 10000000, + 9641249, 9344969, 9090980 + } + }, + /* 11 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 12415319, 10980290, 10405089, + 10000000, + 9680110, 9413710, 9184579 + } + }, + /* 12 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 12298769, 10886880, 10367530, + 10000000, + 9708030, 9464049, 9252949 + } + }, + /* 13 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 12039999, 10825289, 10341939, + 10000000, + 9729740, 9505100, 9310669 + } + }, + /* 14 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 11838380, 10778709, 10322740, + 10000000, + 9746059, 9535790, 9354810 + } + }, + /* 15 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 11750520, 10722860, 10299190, + 10000000, + 9763770, 9567070, 9397709 + } + }, + /* 16 tap upscaling */ + { + { + 60209999, 40000000, 20000000, 0, + -20000000, -40000000, -60209999 + }, + { + 11690390, 10668220, 10277210, + 10000000, + 9780330, 9597110, 9439319 + } + } +}; + +static bool allocate_3d_storage( + struct fixed31_32 ****ptr, + int32_t numberof_tables, + int32_t numberof_rows, + int32_t numberof_columns) +{ + int32_t indexof_table = 0; + int32_t indexof_row = 0; + + struct fixed31_32 ***tables = dal_alloc( + numberof_tables * sizeof(struct fixed31_32 **)); + + if (!tables) { + BREAK_TO_DEBUGGER(); + return false; + } + + while (indexof_table != numberof_tables) { + struct fixed31_32 **rows = dal_alloc( + numberof_rows * sizeof(struct fixed31_32 *)); + + if (!rows) { + BREAK_TO_DEBUGGER(); + --indexof_table; + goto failure; + } + + tables[indexof_table] = rows; + + while (indexof_row != numberof_rows) { + struct fixed31_32 *columns = dal_alloc( + numberof_columns * sizeof(struct fixed31_32)); + + if (!columns) { + BREAK_TO_DEBUGGER(); + --indexof_row; + goto failure; + } + + rows[indexof_row] = columns; + + ++indexof_row; + } + + indexof_row = 0; + + ++indexof_table; + } + + *ptr = tables; + + return true; + +failure: + + while (indexof_table >= 0) { + while (indexof_row >= 0) { + dal_free(tables[indexof_table][indexof_row]); + + --indexof_row; + } + + indexof_row = numberof_rows - 1; + + dal_free(tables[indexof_table]); + + --indexof_table; + } + + dal_free(tables); + + return false; +} + +static void destroy_3d_storage( + struct fixed31_32 ****ptr, + uint32_t numberof_tables, + uint32_t numberof_rows) +{ + struct fixed31_32 ***tables = *ptr; + + uint32_t indexof_table = 0; + + if (!tables) + return; + + while (indexof_table != numberof_tables) { + uint32_t indexof_row = 0; + + while (indexof_row != numberof_rows) { + dal_free(tables[indexof_table][indexof_row]); + + ++indexof_row; + }; + + dal_free(tables[indexof_table]); + + ++indexof_table; + }; + + dal_free(tables); + + *ptr = NULL; +} + +static bool create_downscaling_table( + struct scaler_filter *filter) +{ + const int32_t numberof_tables = + ARRAY_SIZE(downscaling_db_table); + const int32_t numberof_rows = + ARRAY_SIZE(downscaling_db_table[0]); + const int32_t numberof_columns = + ARRAY_SIZE(downscaling_db_table[0][0]); + + int32_t indexof_table = 0; + + if (!allocate_3d_storage(&filter->downscaling_table, + numberof_tables, numberof_rows, numberof_columns)) { + BREAK_TO_DEBUGGER(); + return false; + } + + while (indexof_table != numberof_tables) { + struct fixed31_32 **table = + filter->downscaling_table[indexof_table]; + + int32_t indexof_row = 0; + + while (indexof_row != numberof_rows) { + struct fixed31_32 *row = table[indexof_row]; + + int32_t indexof_column = 0; + + while (indexof_column != numberof_columns) { + row[indexof_column] = +dal_fixed31_32_from_fraction( + downscaling_db_table[indexof_table][indexof_row][indexof_column], + CONST_DIVIDER); + + ++indexof_column; + } + + ++indexof_row; + } + + ++indexof_table; + } + + return true; +} + +static inline void destroy_downscaling_table( + struct scaler_filter *filter) +{ + destroy_3d_storage( + &filter->downscaling_table, + ARRAY_SIZE(downscaling_db_table), + ARRAY_SIZE(downscaling_db_table[0])); +} + +static bool create_upscaling_table( + struct scaler_filter *filter) +{ + const int32_t numberof_tables = + ARRAY_SIZE(upscaling_db_table); + const int32_t numberof_rows = + ARRAY_SIZE(upscaling_db_table[0]); + const int32_t numberof_columns = + ARRAY_SIZE(upscaling_db_table[0][0]); + + int32_t indexof_table = 0; + + if (!allocate_3d_storage(&filter->upscaling_table, + numberof_tables, numberof_rows, numberof_columns)) { + BREAK_TO_DEBUGGER(); + return false; + } + + while (indexof_table != numberof_tables) { + struct fixed31_32 **table = + filter->upscaling_table[indexof_table]; + + int32_t indexof_row = 0; + + while (indexof_row != numberof_rows) { + struct fixed31_32 *row = table[indexof_row]; + + int32_t indexof_column = 0; + + while (indexof_column != numberof_columns) { + row[indexof_column] = +dal_fixed31_32_from_fraction( + upscaling_db_table[indexof_table][indexof_row][indexof_column], + CONST_DIVIDER); + + ++indexof_column; + } + + ++indexof_row; + } + + ++indexof_table; + } + + return true; +} + +static inline void destroy_upscaling_table( + struct scaler_filter *filter) +{ + destroy_3d_storage( + &filter->upscaling_table, + ARRAY_SIZE(upscaling_db_table), + ARRAY_SIZE(upscaling_db_table[0])); +} + +static bool same_filter_required( + struct scaler_filter *filter, + const struct scaler_filter_params *params, + uint32_t src_size, + uint32_t dst_size) +{ + if (!filter->src_size) + return false; + if (!filter->dst_size) + return false; + if (filter->src_size != src_size) + return false; + if (filter->dst_size != dst_size) + return false; + if (filter->params.taps != params->taps) + return false; + if (filter->params.phases != params->phases) + return false; + if (filter->params.sharpness != params->sharpness) + return false; + + return true; +} + +/* + * @brief + * (scale_max - scale_min) + * result = scale_min + (value - value_min) * ----------------------- + * (value_max - value_min) + */ + +static struct fixed31_32 interpolate( + struct fixed31_32 value, + struct fixed31_32 value_min, + struct fixed31_32 value_max, + struct fixed31_32 scale_min, + struct fixed31_32 scale_max) +{ + return dal_fixed31_32_add( + scale_min, + dal_fixed31_32_div( + dal_fixed31_32_mul( + dal_fixed31_32_sub( + value, + value_min), + dal_fixed31_32_sub( + scale_max, + scale_min)), + dal_fixed31_32_sub( + value_max, + value_min))); +} + +static bool map_sharpness( + struct scaler_filter *filter, + const struct scaler_filter_params *params, + uint32_t src_size, + uint32_t dst_size, + struct fixed31_32 *attenuation, + struct fixed31_32 *decibels_at_nyquist) +{ + struct fixed31_32 ratio = dal_fixed31_32_from_fraction( + dst_size, + src_size); + + const struct fixed31_32 sharp_flat = + dal_fixed31_32_from_fraction(MIN_SHARPNESS + MAX_SHARPNESS, 2); + + struct fixed31_32 sharp_max = + dal_fixed31_32_from_int(MAX_SHARPNESS); + struct fixed31_32 sharp_min = + dal_fixed31_32_from_int(MIN_SHARPNESS); + + uint32_t index = params->taps - 3; + + struct fixed31_32 ratio_low; + struct fixed31_32 ratio_up; + + struct fixed31_32 db_min; + struct fixed31_32 db_flat; + struct fixed31_32 db_max; + struct fixed31_32 db_value; + + uint32_t i0; + uint32_t i1; + uint32_t row0; + uint32_t row1; + + int32_t sharp = params->sharpness; + + if (sharp < MIN_SHARPNESS) + sharp = MIN_SHARPNESS; + else if (sharp > MAX_SHARPNESS) + sharp = MAX_SHARPNESS; + + if (params->flags.bits.HORIZONTAL) { + if (dal_fixed31_32_lt(ratio, max_hor_downscale())) { + BREAK_TO_DEBUGGER(); + return false; + } else if (dal_fixed31_32_lt( + max_hor_upscale(), ratio)) { + BREAK_TO_DEBUGGER(); + return false; + } + } else { + if (dal_fixed31_32_lt(ratio, max_ver_downscale())) { + BREAK_TO_DEBUGGER(); + return false; + } else if (dal_fixed31_32_lt( + max_ver_upscale(), ratio)) { + BREAK_TO_DEBUGGER(); + return false; + } + } + + if (dst_size >= src_size) { + if (sharp < 0) { + db_max = up_db_flat(); + db_min = up_db_fuzzy(); + + sharp_max = sharp_flat; + } else { + db_max = up_db_sharp(); + db_min = up_db_flat(); + + sharp_min = sharp_flat; + } + + db_value = interpolate( + dal_fixed31_32_from_int(sharp), + sharp_min, sharp_max, + db_min, db_max); + + i0 = 0; + + while (dal_fixed31_32_lt( + db_value, filter->upscaling_table[index][0][i0]) && + (i0 < UP_DB_POINTS - 1)) + ++i0; + + i1 = i0 + 1; + + if (i0 == UP_DB_POINTS - 1) + i1 = i0--; + + sharp_max = filter->upscaling_table[index][1][i0]; + sharp_min = filter->upscaling_table[index][1][i1]; + + db_max = filter->upscaling_table[index][0][i0]; + db_min = filter->upscaling_table[index][0][i1]; + + *attenuation = interpolate( + db_value, + db_max, db_min, + sharp_max, sharp_min); + + *decibels_at_nyquist = db_value; + + return true; + } else if ((5 * dst_size) < (src_size << 2)) { + if (sharp < 0) { + db_max = down_db_flat(); + db_min = down_db_fuzzy(); + + sharp_max = sharp_flat; + } else { + db_max = down_db_sharp(); + db_min = down_db_flat(); + + sharp_min = sharp_flat; + } + + db_value = interpolate( + dal_fixed31_32_from_int(sharp), + sharp_min, sharp_max, + db_min, db_max); + } else { + struct fixed31_32 db_value_min = + filter->downscaling_table[index][0][0]; + + struct fixed31_32 db_value_max = + filter->downscaling_table[index][0][DOWN_DB_POINTS - 1]; + + db_min = interpolate( + ratio, + threshold_ratio_low(), threshold_ratio_up(), + down_db_fuzzy(), up_db_fuzzy()); + + db_flat = interpolate( + ratio, + threshold_ratio_low(), threshold_ratio_up(), + down_db_flat(), up_db_flat()); + + db_max = interpolate( + ratio, + threshold_ratio_low(), threshold_ratio_up(), + down_db_sharp(), up_db_sharp()); + + if (sharp < 0) { + db_max = db_flat; + + db_value = interpolate( + dal_fixed31_32_from_int(sharp), + sharp_min, dal_fixed31_32_zero, + db_min, db_max); + } else { + db_min = db_flat; + + db_value = interpolate( + dal_fixed31_32_from_int(sharp), + dal_fixed31_32_zero, sharp_max, + db_min, db_max); + } + + if (dal_fixed31_32_lt(db_value_min, db_value)) + db_value = db_value_min; + else if (dal_fixed31_32_lt(db_value, db_value_max)) + db_value = db_value_max; + } + + i1 = 0; + + while (dal_fixed31_32_lt(db_value, + filter->downscaling_table[index][0][i1]) && + (i1 < DOWN_DB_POINTS - 1)) + ++i1; + + if (i1 == 0) + i0 = i1++; + else + i0 = i1 - 1; + + row0 = dal_fixed31_32_round( + dal_fixed31_32_mul_int(ratio, DOWN_DB_SCALES)); + + if (dal_fixed31_32_lt( + dal_fixed31_32_from_fraction(row0, DOWN_DB_SCALES), ratio)) { + row1 = row0 + 1; + + if (row1 > DOWN_DB_SCALES) { + row1 = DOWN_DB_SCALES; + row0 = row1 - 1; + } + } else { + row1 = row0--; + + if (row0 < 1) { + row0 = 1; + row1 = 2; + } + } + + ratio_low = dal_fixed31_32_from_fraction(row0, DOWN_DB_SCALES); + ratio_up = dal_fixed31_32_from_fraction(row1, DOWN_DB_SCALES); + + sharp_max = interpolate( + ratio, + ratio_low, ratio_up, + filter->downscaling_table[index][row0][i0], + filter->downscaling_table[index][row1][i0]); + + sharp_min = interpolate( + ratio, + ratio_low, ratio_up, + filter->downscaling_table[index][row0][i1], + filter->downscaling_table[index][row1][i1]); + + db_max = filter->downscaling_table[index][0][i0]; + db_min = filter->downscaling_table[index][0][i1]; + + *attenuation = interpolate( + db_value, + db_max, db_min, + sharp_max, sharp_min); + + *decibels_at_nyquist = db_value; + + return true; +} + +static inline struct fixed31_32 lanczos( + struct fixed31_32 x, + struct fixed31_32 a2) +{ + return dal_fixed31_32_mul( + dal_fixed31_32_sinc(x), + dal_fixed31_32_sinc( + dal_fixed31_32_mul(x, a2))); +} + +static bool generate_filter( + struct scaler_filter *filter, + const struct scaler_filter_params *params, + struct fixed31_32 attenuation, + struct fixed31_32 *ringing) +{ + uint32_t n = params->phases * params->taps; + + uint32_t coefficients_quantity = n; + uint32_t coefficients_sum_quantity = params->phases; + + uint32_t i; + uint32_t i_limit; + uint32_t j; + uint32_t m; + + struct fixed31_32 attenby2; + + struct fixed31_32 a_max = dal_fixed31_32_zero; + struct fixed31_32 a_min = dal_fixed31_32_zero; + + if (filter->coefficients_quantity < coefficients_quantity) { + if (filter->coefficients) { + dal_free(filter->coefficients); + + filter->coefficients = NULL; + filter->coefficients_quantity = 0; + } + + filter->coefficients = dal_alloc( + coefficients_quantity * sizeof(struct fixed31_32)); + + if (!filter->coefficients) { + BREAK_TO_DEBUGGER(); + return false; + } + + filter->coefficients_quantity = coefficients_quantity; + } + + i = 0; + + while (i != filter->coefficients_quantity) { + filter->coefficients[i] = dal_fixed31_32_zero; + + ++i; + } + + if (filter->coefficients_sum_quantity < coefficients_sum_quantity) { + if (filter->coefficients_sum) { + dal_free(filter->coefficients_sum); + + filter->coefficients_sum = NULL; + filter->coefficients_sum_quantity = 0; + } + + filter->coefficients_sum = dal_alloc( + coefficients_sum_quantity * sizeof(struct fixed31_32)); + + if (!filter->coefficients_sum) { + BREAK_TO_DEBUGGER(); + return false; + } + + filter->coefficients_sum_quantity = coefficients_sum_quantity; + } + + i = 0; + + while (i != filter->coefficients_sum_quantity) { + filter->coefficients_sum[i] = dal_fixed31_32_zero; + + ++i; + } + + m = 0; + + attenby2 = dal_fixed31_32_div_int( + dal_fixed31_32_mul_int(attenuation, params->taps), 2); + + i = 1; + + while (i <= params->taps) { + j = 0; + + while (j != params->phases) { + struct fixed31_32 x = dal_fixed31_32_mul( + dal_fixed31_32_pi, + dal_fixed31_32_from_fraction( + (int64_t)(m << 1) - n, n)); + + uint32_t index = + (params->taps - i) * params->phases + j; + + filter->coefficients[index] = lanczos(x, attenby2); + + ++m; + + ++j; + } + + ++i; + } + + i = 0; + + while (i != params->phases) { + filter->coefficients_sum[i] = dal_fixed31_32_zero; + + m = i; + + j = 0; + + while (j != params->taps) { + filter->coefficients_sum[i] = + dal_fixed31_32_add( + filter->coefficients_sum[i], + filter->coefficients[m]); + + m += params->phases; + + ++j; + } + + ++i; + } + + i = 0; + + while (i != params->phases) { + m = i; + + j = 0; + + while (j != params->taps) { + filter->coefficients[m] = + dal_fixed31_32_div( + filter->coefficients[m], + filter->coefficients_sum[i]); + + m += params->phases; + + ++j; + } + + ++i; + } + + i = 0; + i_limit = (params->phases >> 1) + 1; + + while (i != i_limit) { + m = i; + + j = 0; + + while (j != params->taps) { + struct fixed31_32 tmp = filter->coefficients[m]; + + filter->filter[i * params->taps + j] = tmp; + + if (dal_fixed31_32_lt( + tmp, dal_fixed31_32_zero) && + dal_fixed31_32_lt(tmp, a_min)) + a_min = tmp; + else if (dal_fixed31_32_lt( + dal_fixed31_32_zero, tmp) && + dal_fixed31_32_lt(a_max, tmp)) + a_max = tmp; + + m += params->phases; + + ++j; + } + + ++i; + } + + if (dal_fixed31_32_eq(a_min, dal_fixed31_32_zero)) + *ringing = dal_fixed31_32_from_int(100); + else + *ringing = dal_fixed31_32_min( + dal_fixed31_32_abs( + dal_fixed31_32_div(a_max, a_min)), + dal_fixed31_32_from_int(100)); + + return true; +} + +static bool construct_scaler_filter( + struct scaler_filter *filter) +{ + filter->src_size = 0; + filter->dst_size = 0; + filter->filter = NULL; + filter->integer_filter = NULL; + filter->filter_size_allocated = 0; + filter->filter_size_effective = 0; + filter->coefficients = NULL; + filter->coefficients_quantity = 0; + filter->coefficients_sum = NULL; + filter->coefficients_sum_quantity = 0; + filter->downscaling_table = NULL; + filter->upscaling_table = NULL; + + if (!create_downscaling_table(filter)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!create_upscaling_table(filter)) { + BREAK_TO_DEBUGGER(); + destroy_downscaling_table(filter); + return false; + } + + return true; +} + +static void destruct_scaler_filter( + struct scaler_filter *filter) +{ + if (filter->coefficients_sum) + dal_free(filter->coefficients_sum); + + if (filter->coefficients) + dal_free(filter->coefficients); + + if (filter->integer_filter) + dal_free(filter->integer_filter); + + if (filter->filter) + dal_free(filter->filter); + + destroy_upscaling_table(filter); + + destroy_downscaling_table(filter); +} + +struct scaler_filter *dal_scaler_filter_create(void) +{ + struct scaler_filter *filter = + dal_alloc(sizeof(struct scaler_filter)); + + if (!filter) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (construct_scaler_filter(filter)) + return filter; + + BREAK_TO_DEBUGGER(); + + dal_free(filter); + + return NULL; +} + +bool dal_scaler_filter_generate( + struct scaler_filter *filter, + const struct scaler_filter_params *params, + uint32_t src_size, + uint32_t dst_size) +{ + uint32_t filter_size_required; + + struct fixed31_32 attenuation; + struct fixed31_32 decibels_at_nyquist; + struct fixed31_32 ringing; + + if (!params) { + BREAK_TO_DEBUGGER(); + return false; + } + + if ((params->taps < 3) || (params->taps > 16)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!src_size || !dst_size) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (same_filter_required(filter, params, src_size, dst_size)) + return true; + + filter_size_required = + params->taps * ((params->phases >> 1) + 1); + + if (filter_size_required > filter->filter_size_allocated) { + if (filter->filter) { + dal_free(filter->filter); + + filter->filter = 0; + filter->filter_size_allocated = 0; + } + + filter->filter = dal_alloc( + filter_size_required * sizeof(struct fixed31_32)); + + if (!filter->filter) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (filter->integer_filter) { + dal_free(filter->integer_filter); + + filter->integer_filter = 0; + } + + filter->integer_filter = dal_alloc( + filter_size_required * sizeof(uint32_t)); + + if (!filter->integer_filter) { + BREAK_TO_DEBUGGER(); + return false; + } + + filter->filter_size_allocated = filter_size_required; + } + + filter->filter_size_effective = filter_size_required; + + if (!map_sharpness(filter, params, src_size, dst_size, + &attenuation, &decibels_at_nyquist)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!generate_filter(filter, params, attenuation, &ringing)) { + BREAK_TO_DEBUGGER(); + return false; + } + + filter->params = *params; + filter->src_size = src_size; + filter->dst_size = dst_size; + + return true; +} + +const struct fixed31_32 *dal_scaler_filter_get( + const struct scaler_filter *filter, + uint32_t **data, + uint32_t *number) +{ + if (!number) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + if (!data) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + *number = filter->filter_size_effective; + *data = filter->integer_filter; + + return filter->filter; +} + +void dal_scaler_filter_destroy( + struct scaler_filter **filter) +{ + if (!filter || !*filter) { + BREAK_TO_DEBUGGER(); + return; + } + + destruct_scaler_filter(*filter); + + dal_free(*filter); + + *filter = NULL; +} diff --git a/drivers/gpu/drm/amd/dal/controller/scaler_filter.h b/drivers/gpu/drm/amd/dal/controller/scaler_filter.h new file mode 100644 index 000000000000..33dc86ddd6cc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/scaler_filter.h @@ -0,0 +1,70 @@ +/* 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_FILTER_H__ +#define __DAL_SCALER_FILTER_H__ + +struct scaler_filter_params { + uint32_t taps; /* 3...16 */ + uint32_t phases; + int32_t sharpness; /* -50...50 */ + union { + struct { + uint32_t HORIZONTAL:1; + uint32_t RESERVED:31; + } bits; + uint32_t value; + } flags; +}; + +struct q31_32; + +struct scaler_filter { + struct scaler_filter_params params; + uint32_t src_size; + uint32_t dst_size; + struct fixed31_32 *filter; + uint32_t *integer_filter; + uint32_t filter_size_allocated; + uint32_t filter_size_effective; + struct fixed31_32 *coefficients; + uint32_t coefficients_quantity; + struct fixed31_32 *coefficients_sum; + uint32_t coefficients_sum_quantity; + struct fixed31_32 ***downscaling_table; + struct fixed31_32 ***upscaling_table; +}; + +bool dal_scaler_filter_generate( + struct scaler_filter *filter, + const struct scaler_filter_params *params, + uint32_t src_size, + uint32_t dst_size); + +const struct fixed31_32 *dal_scaler_filter_get( + const struct scaler_filter *filter, + uint32_t **data, + uint32_t *number); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/surface.c b/drivers/gpu/drm/amd/dal/controller/surface.c new file mode 100644 index 000000000000..b7572d618c44 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/surface.c @@ -0,0 +1,77 @@ +/* + * 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 "surface.h" + +bool dal_surface_construct( + struct surface *sf, + struct surface_init_data *init_data) +{ + if (!init_data) + return false; + + sf->id = init_data->id; + sf->ctx = init_data->dal_ctx; + return true; +} + +bool dal_surface_program_flip_and_addr( + struct surface *sf, + const struct plane_addr_flip_info *flip_info) +{ + sf->funcs->set_flip_control( + sf, + flip_info->flip_immediate == 1); + + sf->funcs->program_addr(sf, + &flip_info->address_info.address); + + return true; +} + +bool dal_surface_program_config( + struct surface *sf, + const struct plane_surface_config *configs) +{ + sf->funcs->enable(sf); + + sf->funcs->program_tiling( + sf, + &configs->tiling_info, + configs->format); + + sf->funcs->program_size_and_rotation( + sf, + configs->rotation, + &configs->plane_size); + + sf->funcs->program_pixel_format( + sf, + configs->format); + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/surface.h b/drivers/gpu/drm/amd/dal/controller/surface.h new file mode 100644 index 000000000000..a328e3843a76 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/surface.h @@ -0,0 +1,87 @@ +/* + * 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_SURFACE_H__ +#define __DAL_SURFACE_H__ + +#include "include/plane_types.h" +#include "include/grph_object_id.h" + +struct surface; + +struct surface_funcs { + void (*program_pixel_format)( + struct surface *sf, + enum surface_pixel_format format); + + void (*program_size_and_rotation)( + struct surface *sf, + enum plane_rotation_angle rotation, + const union plane_size *plane_size); + + void (*program_tiling)( + struct surface *sf, + const union plane_tiling_info *info, + const enum surface_pixel_format pixel_format); + + void (*program_addr)( + struct surface *sf, + const struct plane_address *addr); + + void (*set_flip_control)( + struct surface *sf, + bool immediate); + + void (*enable)(struct surface *sf); + + void (*destroy)(struct surface **sf); +}; + +struct surface_init_data { + struct dal_context *dal_ctx; + enum controller_id id; +}; + +struct surface { + const struct surface_funcs *funcs; + enum controller_id id; + const uint32_t *regs; + struct dal_context *ctx; +}; + +bool dal_surface_construct( + struct surface *sf, + struct surface_init_data *init_data); + +bool dal_surface_program_flip_and_addr( + struct surface *sf, + const struct plane_addr_flip_info *info); + +bool dal_surface_program_config( + struct surface *sf, + const struct plane_surface_config *configs); + + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/timing_generator.c b/drivers/gpu/drm/amd/dal/controller/timing_generator.c new file mode 100644 index 000000000000..e8e4837a9b19 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/timing_generator.c @@ -0,0 +1,300 @@ +/* + * 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 "timing_generator.h" +#include "crtc_overscan_color.h" + +enum black_color_format { + BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ + BLACK_COLOR_FORMAT_RGB_LIMITED, + BLACK_COLOR_FORMAT_YUV_TV, + BLACK_COLOR_FORMAT_YUV_CV, + BLACK_COLOR_FORMAT_YUV_SUPER_AA, + + BLACK_COLOR_FORMAT_COUNT +}; + +bool dal_timing_generator_construct( + struct timing_generator *tg, + enum controller_id id) +{ + tg->controller_id = id; + return true; +} + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +/** + ***************************************************************************** + * Function: force_triggered_reset_now + * + * @brief + * Complete operation for CRTC triggered reset: + * 1. Enables TimingGenerator triggered reset + * 2. Wait until reset occurs + * 3. Disables TimingGenerator triggered reset + * + * @param [in] pTriggerParams: triggering data (IO source + trigger edge) + * + * @return + * true if CRTC was succefully reset, false otherwise + ***************************************************************************** + */ + +bool dal_timing_generator_force_triggered_reset_now( + struct timing_generator *tg, + const struct trigger_params *trigger_params) +{ + bool success = false; + uint32_t i; + + /* Enable CRTC triggered reset */ + if (!tg->funcs->enable_reset_trigger(tg, trigger_params)) + return success; + + /* To avoid endless loop we wait at most frames for the reset to + * occur */ + for (i = 0; i < NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET; i++) { + /* Make sure CRTC is running */ + if (!tg->funcs->is_counter_moving(tg)) + break; + + if (tg->funcs->did_triggered_reset_occur(tg)) { + success = true; + break; + } + + /* Wait one frame */ + tg->funcs->wait_for_vactive(tg); + tg->funcs->wait_for_vblank(tg); + } + + ASSERT(success); + + /* We are done (reset occured or failed to reset) - need to disable + * reset trigger + */ + tg->funcs->disable_reset_trigger(tg); + return success; +} + +/** +* Wait till we are in VActive (anywhere in VActive) +* Note: for now use use quick easy polling. +*/ +void dal_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + /* To prevent infinite loop for checking IsInVerticalBlank, we will + * check IsCounterMoving for every 100 call to IsVerticalBlank and break + * if counter are stopped + */ + uint32_t i = 0; + + while (tg->funcs->is_in_vertical_blank(tg)) + if (i++ % 100 == 0) + if (!tg->funcs->is_counter_moving(tg)) + break; +} + +/** +* Wait till we are in VBlank +* Note: for now use use quick easy polling. +*/ +void dal_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* To prevent infinite loop for checking IsInVerticalBlank, we will + * check IsCounterMoving for every 100 call to IsVerticalBlank and break + * if counter are stopped + */ + uint32_t i = 0; + + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (tg->funcs->is_in_vertical_blank(tg)) + if (i++ % 100 == 0) + if (!tg->funcs->is_counter_moving(tg)) + break; + + while (!tg->funcs->is_in_vertical_blank(tg)) + if (i++ % 100 == 0) + if (!tg->funcs->is_counter_moving(tg)) + break; +} + +static const struct crtc_black_color black_color_format[] = { + /* BlackColorFormat_RGB_FullRange */ + {0, 0, 0}, + /* BlackColorFormat_RGB_Limited */ + {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_RGB_LIMITED_RANGE, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_RGB_LIMITED_RANGE, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_RGB_LIMITED_RANGE}, + /* BlackColorFormat_YUV_TV */ + {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4TV, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4TV, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4TV}, + /* BlackColorFormat_YUV_CV */ + {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4CV, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4CV, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4CV}, + /* BlackColorFormat_YUV_SuperAA */ + {CRTC_OVERSCAN_COLOR_BLACK_COLOR_B_CB_YUV_4SUPERAA, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_G_Y_YUV_4SUPERAA, + CRTC_OVERSCAN_COLOR_BLACK_COLOR_R_CR_YUV_4SUPERAA} +}; + +void dal_timing_generator_color_space_to_black_color( + enum color_space colorspace, + struct crtc_black_color *black_color) +{ + switch (colorspace) { + case COLOR_SPACE_YPBPR601: + *black_color = black_color_format[BLACK_COLOR_FORMAT_YUV_TV]; + break; + + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YCBCR709: + *black_color = black_color_format[BLACK_COLOR_FORMAT_YUV_CV]; + break; + + case COLOR_SPACE_N_MVPU_SUPER_AA: + /* In crossfire SuperAA mode, the slave overscan data is forced + * to 0 in the pixel mixer on the master. As a result, we need + * to adjust the blank color so that after blending the + * master+slave, it will appear black + */ + *black_color = + black_color_format[BLACK_COLOR_FORMAT_YUV_SUPER_AA]; + break; + + case COLOR_SPACE_SRGB_LIMITED_RANGE: + *black_color = + black_color_format[BLACK_COLOR_FORMAT_RGB_LIMITED]; + break; + + default: + /* fefault is sRGB black (full range). */ + *black_color = + black_color_format[BLACK_COLOR_FORMAT_RGB_FULLRANGE]; + /* default is sRGB black 0. */ + break; + } +} + +/** +* apply_front_porch_workaround +* +* This is a workaround for a bug that has existed since R5xx and has not been +* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. +*/ +void dal_timing_generator_apply_front_porch_workaround( + struct timing_generator *tg, + struct hw_crtc_timing *timing) +{ + if (timing->flags.INTERLACED == 1) { + if ((timing->v_sync_start - timing->v_addressable) < 2) + timing->v_sync_start = timing->v_addressable + 2; + } else { + if ((timing->v_sync_start - timing->v_addressable) < 1) + timing->v_sync_start = timing->v_addressable + 1; + } +} + +int32_t dal_timing_generator_get_vsynch_and_front_porch_size( + const struct hw_crtc_timing *timing) +{ + int32_t front_porch = timing->v_sync_start - timing->v_addressable - + timing->v_overscan_bottom + timing->flags.INTERLACED; + + int32_t synch_width = timing->v_sync_width; + + return synch_width + front_porch; +} + +/** +* dal_timing_generator_validate_timing +* The timing generators support a maximum display size of is 8192 x 8192 pixels, +* including both active display and blanking periods. Check H Total and V Total. +*/ +bool dal_timing_generator_validate_timing( + struct timing_generator *tg, + const struct hw_crtc_timing *hw_crtc_timing, + enum signal_type signal) +{ + uint32_t h_blank; + uint32_t h_front_porch; + uint32_t h_back_porch; + + ASSERT(hw_crtc_timing != NULL); + + if (!hw_crtc_timing) + return false; + + /* Check maximum number of pixels supported by Timing Generator + * (Currently will never fail, in order to fail needs display which + * needs more than 8192 horizontal and + * more than 8192 vertical total pixels) + */ + if (hw_crtc_timing->h_total > tg->max_h_total || + hw_crtc_timing->v_total > tg->max_v_total) + return false; + + h_blank = (hw_crtc_timing->h_total - hw_crtc_timing->h_addressable - + hw_crtc_timing->h_overscan_right - + hw_crtc_timing->h_overscan_left) * + hw_crtc_timing->flags.PIXEL_REPETITION; + + if (h_blank < tg->min_h_blank) + return false; + + h_front_porch = (hw_crtc_timing->h_sync_start - + hw_crtc_timing->h_addressable - + hw_crtc_timing->h_overscan_right) * + hw_crtc_timing->flags.PIXEL_REPETITION; + + if (h_front_porch < tg->min_h_front_porch) + return false; + + h_back_porch = h_blank - (hw_crtc_timing->h_sync_start - + hw_crtc_timing->h_addressable - + hw_crtc_timing->h_overscan_right - + hw_crtc_timing->h_sync_width) * + hw_crtc_timing->flags.PIXEL_REPETITION; + + if (h_back_porch < tg->min_h_back_porch) + return false; + + if (hw_crtc_timing->flags.PIXEL_REPETITION > 1) { + if (signal != SIGNAL_TYPE_HDMI_TYPE_A) + /* Pixel Repetition is ONLY allowed on HDMI */ + return false; + } + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/timing_generator.h b/drivers/gpu/drm/amd/dal/controller/timing_generator.h new file mode 100644 index 000000000000..281818a76096 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/timing_generator.h @@ -0,0 +1,227 @@ +/* + * 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_TIMING_GENERATOR_H__ +#define __DAL_TIMING_GENERATOR_H__ + +#include "include/signal_types.h" +#include "include/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/timing_generator_types.h" +#include "include/grph_csc_types.h" + +#define LEFT_EYE_3D_PRIMARY_SURFACE 1 +#define RIGHT_EYE_3D_PRIMARY_SURFACE 0 + +enum test_pattern_dyn_range { + TEST_PATTERN_DYN_RANGE_VESA = 0, + TEST_PATTERN_DYN_RANGE_CEA +}; + +enum test_pattern_mode { + TEST_PATTERN_MODE_COLORSQUARES_RGB = 0, + TEST_PATTERN_MODE_COLORSQUARES_YCBCR601, + TEST_PATTERN_MODE_COLORSQUARES_YCBCR709, + TEST_PATTERN_MODE_VERTICALBARS, + TEST_PATTERN_MODE_HORIZONTALBARS, + TEST_PATTERN_MODE_SINGLERAMP_RGB, + TEST_PATTERN_MODE_DUALRAMP_RGB +}; + +enum test_pattern_color_format { + TEST_PATTERN_COLOR_FORMAT_BPC_6 = 0, + TEST_PATTERN_COLOR_FORMAT_BPC_8, + TEST_PATTERN_COLOR_FORMAT_BPC_10, + TEST_PATTERN_COLOR_FORMAT_BPC_12 +}; + +enum controller_dp_test_pattern { + CONTROLLER_DP_TEST_PATTERN_D102 = 0, + CONTROLLER_DP_TEST_PATTERN_SYMBOLERROR, + CONTROLLER_DP_TEST_PATTERN_PRBS7, + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES, + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS, + CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS, + CONTROLLER_DP_TEST_PATTERN_COLORRAMP, + CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, + CONTROLLER_DP_TEST_PATTERN_RESERVED_8, + CONTROLLER_DP_TEST_PATTERN_RESERVED_9, + CONTROLLER_DP_TEST_PATTERN_RESERVED_A, + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA +}; + +enum { + STUTTER_MODE_NO_ADVANCED_REQUEST = 0x100 +}; + +struct timing_generator; + +struct timing_generator_funcs { + bool (*validate_timing)( + struct timing_generator *tg, + const struct hw_crtc_timing *hw_crtc_timing, + enum signal_type signal); + void (*set_lock_timing_registers)( + struct timing_generator *tg, + bool lock); + void (*set_lock_graph_surface_registers)( + struct timing_generator *tg, + bool lock); + void (*unlock_graph_surface_registers)(struct timing_generator *tg); + void (*set_lock_master)(struct timing_generator *tg, bool lock); + bool (*enable_crtc)(struct timing_generator *tg); + bool (*disable_crtc)(struct timing_generator *tg); + bool (*program_timing_generator)( + struct timing_generator *tg, + struct hw_crtc_timing *hw_crtc_timing); + void (*reprogram_timing)( + struct timing_generator *tg, + const struct hw_crtc_timing *ref_timing, + const struct hw_crtc_timing *new_timing); + void (*get_crtc_timing)( + struct timing_generator *tg, + struct hw_crtc_timing *hw_crtc_timing); + bool (*blank_crtc)( + struct timing_generator *tg, + enum color_space color_space); + bool (*unblank_crtc)( + struct timing_generator *tg, + enum color_space color_space); + bool (*program_flow_control)( + struct timing_generator *tg, + enum sync_source source); + void (*set_early_control)( + struct timing_generator *tg, + uint32_t early_cntl); + void (*enable_stereo)( + struct timing_generator *tg, + const struct crtc_stereo_parameters *stereo_params); + void (*disable_stereo)(struct timing_generator *tg); + bool (*get_stereo_status)( + struct timing_generator *tg, + struct crtc_stereo_status *stereo_status); + void (*force_stereo_next_eye)( + struct timing_generator *tg, + bool right_eye); + void (*reset_stereo_3d_phase)(struct timing_generator *tg); + void (*program_drr)( + struct timing_generator *tg, + const struct hw_ranged_timing *timing); + void (*enable_advanced_request)( + struct timing_generator *tg, + bool enable, + const struct hw_crtc_timing *timing); + void (*set_vertical_sync_polarity)( + struct timing_generator *tg, + uint32_t positive_polarity); + void (*set_horizontal_sync_polarity)( + struct timing_generator *tg, + uint32_t positive_polarity); + void (*set_horizontal_sync_composite)( + struct timing_generator *tg, + uint32_t composite); + bool (*enable_reset_trigger)( + struct timing_generator *tg, + const struct trigger_params *trigger_params); + void (*disable_reset_trigger)(struct timing_generator *tg); + bool (*did_triggered_reset_occur)(struct timing_generator *tg); + void (*set_test_pattern)( + struct timing_generator *tg, + enum controller_dp_test_pattern test_pattern, + enum crtc_color_depth color_depth); + bool (*is_test_pattern_enabled)(struct timing_generator *tg); + bool (*is_in_vertical_blank)(struct timing_generator *tg); + bool (*is_counter_moving)(struct timing_generator *tg); + void (*get_crtc_position)( + struct timing_generator *tg, + struct crtc_position *crtc_position); + uint32_t (*get_current_frame_number)(struct timing_generator *tg); + void (*setup_global_swap_lock)( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params); + void (*get_global_swap_lock_setup)( + struct timing_generator *tg, + struct dcp_gsl_params *gsl_params); + bool (*get_io_sequence)( + struct timing_generator *tg, + enum io_register_sequence sequence, + struct io_reg_sequence *io_reg_sequence); + void (*program_vbi_end_signal)( + struct timing_generator *tg, + const struct vbi_end_signal_setup *setup); + void (*program_blanking)( + struct timing_generator *tg, + const struct hw_crtc_timing *timing); + void (*destroy)(struct timing_generator **tg); + bool (*force_triggered_reset_now)( + struct timing_generator *tg, + const struct trigger_params *trigger_params); + void (*wait_for_vactive)(struct timing_generator *tg); + void (*wait_for_vblank)(struct timing_generator *tg); + uint32_t (*get_crtc_scanoutpos)( + struct timing_generator *tg, + int32_t *vpos, + int32_t *hpos); + uint32_t (*get_vblank_counter)(struct timing_generator *tg); +}; + +struct timing_generator { + const struct timing_generator_funcs *funcs; + uint32_t *regs; + struct bios_parser *bp; + enum controller_id controller_id; + struct dal_context *ctx; + uint32_t max_h_total; + uint32_t max_v_total; + + uint32_t min_h_blank; + uint32_t min_h_front_porch; + uint32_t min_h_back_porch; +}; + +bool dal_timing_generator_force_triggered_reset_now( + struct timing_generator *tg, + const struct trigger_params *trigger_params); +void dal_timing_generator_wait_for_vactive(struct timing_generator *tg); +void dal_timing_generator_wait_for_vblank(struct timing_generator *tg); +void dal_timing_generator_color_space_to_black_color( + enum color_space colorspace, + struct crtc_black_color *black_color); +void dal_timing_generator_apply_front_porch_workaround( + struct timing_generator *tg, + struct hw_crtc_timing *timing); +int32_t dal_timing_generator_get_vsynch_and_front_porch_size( + const struct hw_crtc_timing *timing); + +bool dal_timing_generator_validate_timing( + struct timing_generator *tg, + const struct hw_crtc_timing *hw_crtc_timing, + enum signal_type signal); + +bool dal_timing_generator_construct( + struct timing_generator *tg, + enum controller_id id); + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/vga.h b/drivers/gpu/drm/amd/dal/controller/vga.h new file mode 100644 index 000000000000..232e23d2a593 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/vga.h @@ -0,0 +1,45 @@ +/* + * 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_VGA_H__ +#define __DAL_VGA_H__ + +#include "include/bios_parser_interface.h" + +struct vga; + +struct vga_funcs { + void (*disable_vga)(struct vga *vga); + void (*destroy)(struct vga **vga); +}; + +struct vga { + const struct vga_funcs *funcs; + uint32_t vga_control; + struct bios_parser *bp; + struct dal_context *ctx; +}; + +#endif diff --git a/drivers/gpu/drm/amd/dal/controller/video_gamma.c b/drivers/gpu/drm/amd/dal/controller/video_gamma.c new file mode 100644 index 000000000000..1c53df11b5eb --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/video_gamma.c @@ -0,0 +1,1007 @@ +/* + * 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/fixed31_32.h" + +#include "video_gamma.h" + +#define X_MIN dal_fixed31_32_zero +#define X_MAX1 dal_fixed31_32_one +#define X_MAX2 dal_fixed31_32_one + +static void setup_config( + struct curve_config *config, + uint32_t *hw_points_num); +static bool build_oem_regamma( + const struct overlay_gamma_parameters *data, + uint32_t points_num, + struct gamma_work_item *item); +static bool translate_gamma_parameter( + int32_t ovl_gamma_cont, + struct fixed31_32 gamma_result[3], + struct fixed31_32 *contrast_result, + struct fixed31_32 *brightness_result); +static void generate_gamma( + struct gamma_sample *gamma_sample, + struct fixed31_32 contrast, + struct fixed31_32 brightness, + struct fixed31_32 gamma); +static bool find_software_points( + struct fixed31_32 hw_point, + const struct fixed31_32 *x, + uint32_t points_num, + uint32_t *index_to_start, + uint32_t *index_left, + uint32_t *index_right, + enum hw_point_position *position); +static bool build_resulted_regamma( + struct gamma_work_item *item, + uint32_t points_num); +static void build_resulted_curve( + const struct overlay_gamma_parameters *parameters, + uint32_t points_num, + struct pwl_float_data_ex *data); +static bool build_curve_configuration( + const struct pwl_float_data_ex *data, + struct fixed31_32 *x, + struct curve_points *points, + uint32_t points_num); +static bool convert_to_custom_float( + const struct pwl_float_data_ex *data, + struct curve_points *points, + uint32_t points_num, + uint32_t *base, + uint32_t *delta); +static struct fixed31_32 calculate_user_mapped_value( + const struct pixel_gamma_point_ex *coeff, + const struct gamma_sample *gamma_sample, + enum channel_name name, + uint32_t max_index); +static struct fixed31_32 calculate_user_mapped_value_ex( + const struct gamma_point *point, + enum channel_name name, + struct pwl_float_data *oem_regamma, + uint32_t max_index); + +bool dal_video_gamma_build_resulted_gamma( + struct video_gamma *vg, + const struct overlay_gamma_parameters *data, + uint32_t *lut, + uint32_t *lut_delta, + uint32_t *points_num) +{ + struct curve_config config = { 0 }; + + struct gamma_work_item *item = + dal_alloc(sizeof(struct gamma_work_item)); + + if (item == NULL) + return false; + + setup_config(&config, points_num); + + /* build hw regamma curve */ + if (!dal_controller_build_hw_curve_configuration( + &config, + item->curve, + item->points, + item->x_axis, + points_num)) { + dal_free(item); + return false; + } + + if (!build_oem_regamma(data, *points_num, item)) { + dal_free(item); + return false; + } + + /* tranlate index to gamma */ + if (!translate_gamma_parameter(data->ovl_gamma_cont, + item->coeff.user_gamma, &item->coeff.user_contrast, + &item->coeff.user_brightness)) { + BREAK_TO_DEBUGGER(); + /* The pData->ovlgammacont is wrong , please, check it + * we can still continue, but gamma index is wrong , we set the + * default gamma */ + } + + generate_gamma(item->gamma_sample, item->coeff.user_gamma[0], + item->coeff.user_contrast, item->coeff.user_brightness); + + if (!build_resulted_regamma(item, *points_num)) { + dal_free(item); + return false; + } + + build_resulted_curve(data, *points_num, item->resulted); + + build_curve_configuration(item->resulted, item->x_axis, item->points, + *points_num); + + if (!convert_to_custom_float(item->resulted, item->points, + *points_num, lut, lut_delta)) { + dal_free(item); + return false; + } + + vg->funcs->regamma_config_regions_and_segments( + vg, item->points, item->curve); + + dal_free(item); + + return true; +} + +static bool build_custom_gamma_mapping_coefficients( + const struct fixed31_32 *x, + const struct pwl_float_data *xhw, + struct pixel_gamma_point_ex *coeff128, + enum channel_name channel, + uint32_t points_num) +{ + uint32_t i; + + for (i = 0; i <= points_num; ++i) { + struct fixed31_32 coord_x; + uint32_t index_to_start = 0; + uint32_t index_left = 0; + uint32_t index_right = 0; + enum hw_point_position position; + struct gamma_point *point; + + if (channel == CHANNEL_NAME_RED) + coord_x = xhw[i].r; + else if (channel == CHANNEL_NAME_GREEN) + coord_x = xhw[i].g; + else + coord_x = xhw[i].b; + + if (!find_software_points( + coord_x, + x, + MAX_GAMMA_256X3X16, + &index_to_start, + &index_left, + &index_right, + &position)) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + if (index_left >= MAX_GAMMA_256X3X16) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + if (index_right >= MAX_GAMMA_256X3X16) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + if (channel == CHANNEL_NAME_RED) + point = &coeff128[i].r; + else if (channel == CHANNEL_NAME_GREEN) + point = &coeff128[i].g; + else + point = &coeff128[i].b; + + { + struct fixed31_32 left_pos = x[index_left]; + struct fixed31_32 right_pos = x[index_right]; + + switch (position) { + case HW_POINT_POSITION_MIDDLE: + point->coeff = dal_fixed31_32_div( + dal_fixed31_32_sub( + coord_x, + left_pos), + dal_fixed31_32_sub( + right_pos, + left_pos)); + point->left_index = index_left; + point->right_index = index_right; + point->pos = position; + break; + + case HW_POINT_POSITION_LEFT: + point->coeff = X_MIN; + point->left_index = index_left; + point->right_index = index_right; + point->pos = position; + break; + + case HW_POINT_POSITION_RIGHT: + point->coeff = X_MAX2; + point->left_index = index_left; + point->right_index = index_right; + point->pos = position; + break; + + default: + /* invalid parameters or bug */ + BREAK_TO_DEBUGGER(); + return false; + } + } + } + + return true; +} + +static bool build_oem_custom_gamma_mapping_coefficients( + const struct fixed31_32 *x, + const struct fixed31_32 *xhw, + struct gamma_point *coeffs, + uint32_t points_num) +{ + uint32_t i; + + for (i = 0; i <= points_num; ++i) { + struct fixed31_32 coord_x = xhw[i]; + + uint32_t index_to_start = 0; + uint32_t index_left = 0; + uint32_t index_right = 0; + + enum hw_point_position hw_position; + + if (!find_software_points( + coord_x, + x, + MAX_GAMMA_256X3X16, + &index_to_start, + &index_left, + &index_right, + &hw_position)) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + if (index_left >= MAX_GAMMA_256X3X16) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + if (index_right >= MAX_GAMMA_256X3X16) { + BREAK_TO_DEBUGGER(); /* invalid parameters or bug */ + return false; + } + + { + struct fixed31_32 left_pos = x[index_left]; + struct fixed31_32 right_pos = x[index_right]; + + struct gamma_point *coeff = &coeffs[i]; + + switch (hw_position) { + case HW_POINT_POSITION_MIDDLE: + coeff->coeff = + dal_fixed31_32_div( + dal_fixed31_32_sub( + coord_x, + left_pos), + dal_fixed31_32_sub( + right_pos, + left_pos)); + coeff->left_index = index_left; + coeff->right_index = index_right; + coeff->pos = hw_position; + break; + + case HW_POINT_POSITION_LEFT: + coeff->coeff = X_MIN; + coeff->left_index = index_left; + coeff->right_index = index_right; + coeff->pos = hw_position; + break; + + case HW_POINT_POSITION_RIGHT: + coeff->coeff = X_MAX2; + coeff->left_index = index_left; + coeff->right_index = index_right; + coeff->pos = hw_position; + break; + + default: + /* invalid parameters or bug */ + BREAK_TO_DEBUGGER(); + return false; + } + } + } + + return true; +} + +/***************************************************************************** +* Function: build_oem_regamma +* +* This programs overlay PWL transunit matrix. +* +* @param [in] data overlay_gamma_cont -parameters for overlay gamma +* +* @return none +* +*****************************************************************************/ +static bool build_oem_regamma( + const struct overlay_gamma_parameters *data, + uint32_t points_num, + struct gamma_work_item *item) +{ + bool is_degamma_srgb = + data->regamma.features.bits.OVERLAY_DEGAMMA_SRGB == 1; + /* Create evenly distributed software points */ + dal_controller_build_evenly_distributed_points( + item->axis_x256, 1, 256); + + dal_controller_build_regamma_coefficients( + &data->regamma, is_degamma_srgb, &item->coeff); + + if (data->regamma.features.bits.GAMMA_RAMP_ARRAY == 0) { + uint32_t i; + + for (i = 0; i <= points_num; ++i) { + /* for gamma array these values are same because a0-a3 + * for ideals are the same */ + item->regamma[i].r = + dal_controller_translate_from_linear_space( + item->x_axis[i], + item->coeff.a0[0], + item->coeff.a1[0], + item->coeff.a2[0], + item->coeff.a3[0], + item->coeff.user_gamma[0]); + + item->regamma[i].g = + dal_controller_translate_from_linear_space( + item->x_axis[i], + item->coeff.a0[1], + item->coeff.a1[1], + item->coeff.a2[1], + item->coeff.a3[1], + item->coeff.user_gamma[1]); + + item->regamma[i].b = + dal_controller_translate_from_linear_space( + item->x_axis[i], + item->coeff.a0[2], + item->coeff.a1[2], + item->coeff.a2[2], + item->coeff.a3[2], + item->coeff.user_gamma[2]); + } + } else { + const struct fixed31_32 *xhw; + uint32_t i; + /* normalize oem gamma to 0-1 */ + dal_controller_normalize_oem_gamma(&data->regamma.regamma_ramp, + item->oem_regamma); + + if (data->regamma.features.bits.APPLY_DEGAMMA == 1) { + uint32_t i; + /* Build ideal regamma if using new implementation. */ + for (i = 0; i <= points_num; ++i) + item->y_axis[i] = + dal_controller_translate_from_linear_space( + item->x_axis[i], + item->coeff.a0[0], + item->coeff.a1[0], + item->coeff.a2[0], + item->coeff.a3[0], + item->coeff.user_gamma[0]); + + /* Use ideal regamma Y as X if using new solution. */ + xhw = item->y_axis; + } else + xhw = item->x_axis; + + if (!build_oem_custom_gamma_mapping_coefficients( + item->axis_x256, xhw, item->coeff_oem128, points_num)) { + BREAK_TO_DEBUGGER(); + return false; + } + + /* calculate oem regamma for our x hw points */ + for (i = 0; i <= points_num; ++i) { + item->regamma[i].r = calculate_user_mapped_value_ex( + &item->coeff_oem128[i], + CHANNEL_NAME_RED, + item->oem_regamma, + MAX_GAMMA_256X3X16); + + item->regamma[i].g = calculate_user_mapped_value_ex( + &item->coeff_oem128[i], + CHANNEL_NAME_GREEN, + item->oem_regamma, + MAX_GAMMA_256X3X16); + + item->regamma[i].b = calculate_user_mapped_value_ex( + &item->coeff_oem128[i], + CHANNEL_NAME_BLUE, + item->oem_regamma, + MAX_GAMMA_256X3X16); + } + } + + return true; +} + +static void setup_config( + struct curve_config *config, + uint32_t *hw_points_num) +{ + const uint8_t index = 8; + uint8_t i; + + if (!hw_points_num) + return; + + *hw_points_num = 0; + + config->offset = 185; + /* we are staring from offset 185 because grph gamma uses 185 entries + * we setup regions and segment configuration hard coded here + * and the other code uses it as runtime parameters */ + config->segments[0] = 0; /* region 0, 1 segment */ + config->segments[1] = 0; /* region 1, 1 segments */ + config->segments[2] = 2; /* region 2, 4 segments */ + config->segments[3] = 2; /* region 3, 4 segments */ + config->segments[4] = 2; /* region 4, 4 segments */ + config->segments[5] = 3; /* region 5, 8 segments */ + config->segments[6] = 4; /* region 6, 16 segments */ + config->segments[7] = 5; /* region 7, 32 segments */ + config->segments[index] = 0; /* region 8, 1 segments */ + config->segments[9] = -1; /* region 9, undefined */ + config->segments[10] = -1; /* region 10, undefined */ + config->segments[11] = -1; /* region 11, undefined */ + config->segments[12] = -1; /* region 12, undefined */ + config->segments[13] = -1; /* region 13, undefined */ + config->segments[14] = -1; /* region 14, undefined */ + config->segments[15] = -1; /* region 15, undefined */ + + for (i = 0; i <= index; ++i) + *hw_points_num += 1 << config->segments[i]; + + config->begin = (int8_t)(-index); +} + +struct gamma_values { + uint32_t index; + uint32_t gamma; + uint32_t contrast; + uint32_t brightness; +}; + +/***************************************************************************** +* Function: translate_gamma_parameter +* +* This programs overlay PWL transunit matrix. +* +* @return none +* +*****************************************************************************/ +static bool translate_gamma_parameter( + int32_t ovl_gamma_cont, + struct fixed31_32 gamma_result[3], + struct fixed31_32 *contrast_result, + struct fixed31_32 *brightness_result) +{ + static const struct gamma_values gammas[] = { + /*index gamma contrast brightness*/ + { 7, 8500, 10000, 0 }, { 6, 10000, 10000, 0 }, { 5, 11000, 10000, 0 }, + { 4, 12000, 10000, 0 }, { 3, 14500, 10000, 0 }, { 2, 17000, 10000, 0 }, + { 1, 22000, 10000, 0 }, { 0, 25000, 10000, 0 } }; + + struct fixed31_32 gamma = dal_fixed31_32_from_fraction(24, 10); + + uint32_t contrast = 1; + uint32_t brightness = 0; + + const uint32_t divider = 10000; + + const struct gamma_values *p; + + bool is_found = false; + + for (p = gammas; p < &gammas[ARRAY_SIZE(gammas)]; ++p) { + if (p->index == ovl_gamma_cont) { + gamma = dal_fixed31_32_from_int(p->gamma); + contrast = p->contrast; + brightness = p->brightness; + is_found = true; + + break; + } + } + + gamma_result[0] = dal_fixed31_32_div_int(gamma, divider); + gamma_result[1] = gamma_result[0]; + gamma_result[2] = gamma_result[0]; + + *contrast_result = dal_fixed31_32_from_fraction(contrast, divider); + *brightness_result = dal_fixed31_32_from_fraction(brightness, divider); + + return is_found; +} + +static void generate_gamma( + struct gamma_sample *gamma_sample, + struct fixed31_32 contrast, + struct fixed31_32 brightness, + struct fixed31_32 gamma) +{ + const struct fixed31_32 clamp_lut16 = + dal_fixed31_32_from_int(65535); + + const struct fixed31_32 min_gamma_value = dal_fixed31_32_half; + const struct fixed31_32 max_gamma_value = + dal_fixed31_32_from_fraction(7, 2); + const struct fixed31_32 def_gamma_value = dal_fixed31_32_one; + + const struct fixed31_32 min_brightness_value = + dal_fixed31_32_from_fraction(-2, 10); + const struct fixed31_32 max_brightness_value = + dal_fixed31_32_from_fraction(2, 10); + const struct fixed31_32 def_brightness_value = dal_fixed31_32_zero; + + const struct fixed31_32 min_contrast_value = dal_fixed31_32_half; + const struct fixed31_32 max_contrast_value = + dal_fixed31_32_from_fraction(3, 2); + const struct fixed31_32 def_contrast_value = dal_fixed31_32_one; + + uint32_t i; + + if (dal_fixed31_32_lt(contrast, min_contrast_value) || + dal_fixed31_32_lt(max_contrast_value, contrast)) + contrast = def_contrast_value; + + if (dal_fixed31_32_lt(brightness, min_brightness_value) || + dal_fixed31_32_lt(max_brightness_value, brightness)) + brightness = def_brightness_value; + + if (dal_fixed31_32_lt(gamma, min_gamma_value) || + dal_fixed31_32_lt(max_gamma_value, gamma)) + gamma = def_gamma_value; + + for (i = 0; i < MAX_GAMMA_256X3X16; ++i) { + struct fixed31_32 value; + + gamma_sample[i].point_x = + dal_fixed31_32_from_fraction(i, MAX_GAMMA_256X3X16 - 1); + + value = + dal_fixed31_32_mul( + dal_fixed31_32_pow( + gamma_sample[i].point_x, + dal_fixed31_32_recip(gamma)), + clamp_lut16); + + value = dal_fixed31_32_clamp( + value, + dal_fixed31_32_zero, + clamp_lut16); + + value = dal_fixed31_32_add( + dal_fixed31_32_mul(value, contrast), + dal_fixed31_32_mul(brightness, clamp_lut16)); + + value = dal_fixed31_32_clamp( + value, + dal_fixed31_32_zero, + clamp_lut16); + + gamma_sample[i].point_y = + dal_fixed31_32_div(value, clamp_lut16); + } +} + +static bool build_resulted_regamma( + struct gamma_work_item *item, + uint32_t points_num) +{ + uint32_t i; + + if (!build_custom_gamma_mapping_coefficients(item->axis_x256, + item->regamma, item->coeff_128, CHANNEL_NAME_RED, + points_num)) + return false; + + if (!build_custom_gamma_mapping_coefficients(item->axis_x256, + item->regamma, item->coeff_128, CHANNEL_NAME_GREEN, + points_num)) + return false; + + if (!build_custom_gamma_mapping_coefficients(item->axis_x256, + item->regamma, item->coeff_128, CHANNEL_NAME_BLUE, + points_num)) + return false; + + for (i = 0; i < points_num; ++i) { + item->resulted[i].r = calculate_user_mapped_value( + &item->coeff_128[i], item->gamma_sample, + CHANNEL_NAME_RED, MAX_GAMMA_256X3X16); + + item->resulted[i].g = calculate_user_mapped_value( + &item->coeff_128[i], item->gamma_sample, + CHANNEL_NAME_GREEN, MAX_GAMMA_256X3X16); + + item->resulted[i].b = calculate_user_mapped_value( + &item->coeff_128[i], item->gamma_sample, + CHANNEL_NAME_BLUE, MAX_GAMMA_256X3X16); + } + + return true; +} + +static bool find_software_points( + struct fixed31_32 hw_point, + const struct fixed31_32 *x, + uint32_t points_num, + uint32_t *index_to_start, + uint32_t *index_left, + uint32_t *index_right, + enum hw_point_position *position) +{ + uint32_t i; + + for (i = *index_to_start; i < points_num; ++i) { + struct fixed31_32 left = x[i]; + + struct fixed31_32 right; + + if (i < points_num - 1) + right = x[i + 1]; + else + right = x[points_num - 1]; + + if (dal_fixed31_32_le(left, hw_point) && + dal_fixed31_32_le(hw_point, right)) { + *index_to_start = i; + *index_left = i; + + if (i < points_num - 1) + *index_right = i + 1; + else + *index_right = points_num - 1; + + *position = HW_POINT_POSITION_MIDDLE; + + return true; + } else if ((i == *index_to_start) && + dal_fixed31_32_le(hw_point, left)) { + *index_left = i; + *index_right = i; + + *position = HW_POINT_POSITION_LEFT; + + return true; + } else if ((i == points_num - 1) && + dal_fixed31_32_le(right, hw_point)) { + *index_to_start = i; + *index_left = i; + *index_right = i; + + *position = HW_POINT_POSITION_RIGHT; + + return true; + } + } + + return false; +} + +static void build_resulted_curve( + const struct overlay_gamma_parameters *parameters, + uint32_t points_num, + struct pwl_float_data_ex *data) +{ + uint32_t i; + + for (i = 0; i < points_num; ++i) { + if (dal_fixed31_32_lt(X_MAX1, data[i].r) && + (i < points_num - 1)) { + data[i].r = X_MAX1; + } else if (dal_fixed31_32_lt(data[i].r, X_MIN)) { + data[i].r = X_MIN; + } + + if (dal_fixed31_32_lt(X_MAX1, data[i].g) && + (i < points_num - 1)) { + data[i].g = X_MAX1; + } else if (dal_fixed31_32_lt(data[i].g, X_MIN)) { + data[i].g = X_MIN; + } + + if (dal_fixed31_32_lt(X_MAX1, data[i].b) && + (i < points_num - 1)) + data[i].b = X_MAX1; + else if (dal_fixed31_32_lt(data[i].b, X_MIN)) + data[i].b = X_MIN; + } + + for (i = 1; i < points_num; ++i) { + if (dal_fixed31_32_lt(data[i].r, data[i - 1].r)) + data[i].r = data[i - 1].r; + + data[i - 1].delta_r = + dal_fixed31_32_sub( + data[i].r, + data[i - 1].r); + + if (dal_fixed31_32_lt(data[i].g, data[i - 1].g)) + data[i].g = data[i - 1].g; + + data[i - 1].delta_g = + dal_fixed31_32_sub( + data[i].g, + data[i - 1].g); + + if (dal_fixed31_32_lt(data[i].b, data[i - 1].b)) + data[i].b = data[i - 1].b; + + data[i - 1].delta_b = + dal_fixed31_32_sub( + data[i].b, + data[i - 1].b); + } +} + +static bool build_curve_configuration( + const struct pwl_float_data_ex *data, + struct fixed31_32 *x, + struct curve_points *points, + uint32_t points_num) +{ + struct fixed31_32 magic_number = + dal_fixed31_32_from_fraction(249, 1000); + + points[0].x = x[0]; + points[0].y = data[0].r; + points[0].slope = dal_fixed31_32_div( + points[0].y, + points[0].x); + + points[1].x = dal_fixed31_32_add( + x[points_num - 1], + magic_number); + points[2].x = dal_fixed31_32_add( + x[points_num], + magic_number); + + points[1].y = dal_fixed31_32_one; + + points[2].y = data[points_num].r; + + points[2].slope = dal_fixed31_32_one; + + return true; +} + +static bool convert_to_custom_float( + const struct pwl_float_data_ex *data, + struct curve_points *points, + uint32_t points_num, + uint32_t *base, + uint32_t *delta) +{ + struct custom_float_format format; + uint32_t i; + + format.exponenta_bits = 6; + format.mantissa_bits = 12; + format.sign = true; + + if (!dal_controller_convert_to_custom_float_format(points[0].x, &format, + &points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + if (!dal_controller_convert_to_custom_float_format(points[0].offset, + &format, &points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + if (!dal_controller_convert_to_custom_float_format(points[0].slope, + &format, &points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + format.mantissa_bits = 10; + format.sign = false; + + if (!dal_controller_convert_to_custom_float_format(points[1].x, &format, + &points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + if (!dal_controller_convert_to_custom_float_format(points[1].y, &format, + &points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + if (!dal_controller_convert_to_custom_float_format(points[2].slope, + &format, &points[2].custom_float_slope)) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + format.mantissa_bits = 12; + format.sign = true; + + for (i = 0; i < points_num; ++i) { + if (!dal_controller_convert_to_custom_float_format(data[i].r, + &format, &base[i])) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + + if (!dal_controller_convert_to_custom_float_format( + data[i].delta_r, &format, &delta[i])) { + BREAK_TO_DEBUGGER(); /* invalid calculation */ + return false; + } + } + + return true; +} + +/* + ***************************************************************************** + * Function: calculate_user_mapped_value + * + * calculated new y value based on pre-calculated coeff. and index. + * + * @param [in ] coeff surface type from OS + * @param [in ] name if 256 colors mode + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static struct fixed31_32 calculate_user_mapped_value( + const struct pixel_gamma_point_ex *coeff, + const struct gamma_sample *gamma_sample, + enum channel_name name, + uint32_t max_index) +{ + const struct gamma_point *point; + struct fixed31_32 hw_point; + + if (name == CHANNEL_NAME_RED) + point = &coeff->r; + else if (name == CHANNEL_NAME_GREEN) + point = &coeff->g; + else + point = &coeff->b; + + if ((point->right_index < 0) || (point->right_index > max_index)) + BREAK_TO_DEBUGGER(); /* invalid calculation */ + + if ((point->left_index < 0) || (point->left_index > max_index)) + BREAK_TO_DEBUGGER(); /* invalid calculation */ + + if (point->pos == HW_POINT_POSITION_MIDDLE) { + hw_point = dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + gamma_sample[point->right_index].point_y, + gamma_sample[point->left_index].point_y)); + + hw_point = dal_fixed31_32_add( + hw_point, + gamma_sample[point->left_index].point_y); + } else if (point->pos == HW_POINT_POSITION_LEFT) { + hw_point = X_MIN; + + BREAK_TO_DEBUGGER(); /* invalid calculation */ + } else { + hw_point = X_MAX1; + + BREAK_TO_DEBUGGER(); /* invalid calculation */ + } + + return hw_point; +} + +static struct fixed31_32 calculate_user_mapped_value_ex( + const struct gamma_point *point, + enum channel_name name, + struct pwl_float_data *oem_regamma, + uint32_t max_index) +{ + struct fixed31_32 hw_point; + + if ((point->right_index < 0) || (point->right_index > max_index)) + BREAK_TO_DEBUGGER(); /* invalid calculation */ + + if ((point->left_index < 0) || (point->left_index > max_index)) + BREAK_TO_DEBUGGER(); /* invalid calculation */ + + if (point->pos == HW_POINT_POSITION_MIDDLE) { + if (name == CHANNEL_NAME_RED) { + hw_point = dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + oem_regamma[point->right_index].r, + oem_regamma[point->left_index].r)); + + hw_point = dal_fixed31_32_add( + hw_point, + oem_regamma[point->left_index].r); + } else if (name == CHANNEL_NAME_GREEN) { + hw_point = dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + oem_regamma[point->right_index].g, + oem_regamma[point->left_index].g)); + + hw_point = dal_fixed31_32_add( + hw_point, + oem_regamma[point->left_index].g); + } else { + hw_point = dal_fixed31_32_mul( + point->coeff, + dal_fixed31_32_sub( + oem_regamma[point->right_index].b, + oem_regamma[point->left_index].b)); + + hw_point = dal_fixed31_32_add( + hw_point, + oem_regamma[point->left_index].b); + } + } else if (point->pos == HW_POINT_POSITION_LEFT) { + hw_point = X_MIN; + + BREAK_TO_DEBUGGER(); /* invalid calculation */ + } else { + hw_point = X_MAX1; + + BREAK_TO_DEBUGGER(); /* invalid calculation */ + } + + return hw_point; +} + +bool dal_video_gamma_construct( + struct video_gamma *vg, + struct video_gamma_init_data *init_data) +{ + if (!init_data) + return false; + + vg->ctx = init_data->ctx; + return true; +} diff --git a/drivers/gpu/drm/amd/dal/controller/video_gamma.h b/drivers/gpu/drm/amd/dal/controller/video_gamma.h new file mode 100644 index 000000000000..39c13d94bec2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/controller/video_gamma.h @@ -0,0 +1,114 @@ +/* + * 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_VIDEO_GAMMA_H__ +#define __DAL_VIDEO_GAMMA_H__ + +#include "include/grph_object_id.h" +#include "include/video_gamma_types.h" + +#include "graphics_and_video_gamma.h" + +/* overlay could use 1/2 of max 256 points which are hardwire capabilities */ +#define MAX_GAMMA_OVERLAY_POINTS 128 +#define MAX_GAMMA_256X3X16 256 /* number of points used by OS */ +/* begin point, end point, end + 1 point for slope */ +#define CONFIG_POINTS_NUMBER 3 + +struct gamma_sample { + struct fixed31_32 point_x; + struct fixed31_32 point_y; +}; + +struct pixel_gamma_point_ex { + struct gamma_point r; + struct gamma_point g; + struct gamma_point b; +}; + +struct gamma_work_item { + struct gamma_coefficients coeff; + struct gamma_sample gamma_sample[MAX_GAMMA_256X3X16]; + + struct fixed31_32 x_axis[MAX_GAMMA_OVERLAY_POINTS]; + struct fixed31_32 y_axis[MAX_GAMMA_OVERLAY_POINTS]; + + struct gamma_point gamma_points[MAX_GAMMA_OVERLAY_POINTS]; + struct pwl_float_data regamma[MAX_GAMMA_OVERLAY_POINTS]; + struct pwl_float_data_ex resulted[MAX_GAMMA_OVERLAY_POINTS]; + struct gamma_curve curve[MAX_REGIONS_NUMBER]; + struct curve_points points[CONFIG_POINTS_NUMBER]; + + struct gamma_point coeff_oem128[MAX_GAMMA_OVERLAY_POINTS]; + struct pwl_float_data oem_regamma[MAX_GAMMA_256X3X16]; + struct fixed31_32 axis_x256[MAX_GAMMA_256X3X16]; + + struct pixel_gamma_point_ex coeff_128[MAX_GAMMA_OVERLAY_POINTS]; +}; + +struct video_gamma; +struct overlay_gamma_parameters; + +bool dal_video_gamma_build_resulted_gamma( + struct video_gamma *vg, + const struct overlay_gamma_parameters *data, + uint32_t *lut, + uint32_t *lut_delta, + uint32_t *entries_num); + +struct video_gamma_funcs { + bool (*set_overlay_pwl_adjustment)( + struct video_gamma *vg, + const struct overlay_gamma_parameters *data); + void (*regamma_config_regions_and_segments)( + struct video_gamma *vg, + const struct curve_points *points, + const struct gamma_curve *curve); + void (*set_legacy_mode)( + struct video_gamma *vg, + bool is_legacy); + bool (*set_overlay_gamma)( + struct video_gamma *vg, + const struct overlay_gamma_parameters *data); + void (*destroy)(struct video_gamma **vg); +}; + +struct video_gamma { + const struct video_gamma_funcs *funcs; + const uint32_t *regs; + struct dal_context *ctx; +}; + +struct video_gamma_init_data { + struct adapter_service *as; + struct dal_context *ctx; + enum controller_id id; +}; + +bool dal_video_gamma_construct( + struct video_gamma *vg, + struct video_gamma_init_data *init_data); + +#endif -- cgit v1.2.3