Responsible for programming the audio encoder in the display path. Signed-off-by: Harry Wentland <harry.wentland@xxxxxxx> Reviewed-by: Alex Deucher <alexander.deucher@xxxxxxx> --- drivers/gpu/drm/amd/dal/dc/audio/Makefile | 22 + drivers/gpu/drm/amd/dal/dc/audio/audio.h | 195 ++ drivers/gpu/drm/amd/dal/dc/audio/audio_base.c | 470 +++++ .../gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c | 453 +++++ .../gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h | 42 + .../amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c | 1930 ++++++++++++++++++++ .../amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h | 47 + drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c | 771 ++++++++ drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h | 285 +++ 9 files changed, 4215 insertions(+) create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/Makefile create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/audio.h create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/audio_base.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c create mode 100644 drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h diff --git a/drivers/gpu/drm/amd/dal/dc/audio/Makefile b/drivers/gpu/drm/amd/dal/dc/audio/Makefile new file mode 100644 index 000000000000..0999372cecf0 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the 'audio' sub-component of DAL. +# It provides the control and status of HW adapter resources, +# that are global for the ASIC and sharable between pipes. + +AUDIO = audio_base.o hw_ctx_audio.o + +AMD_DAL_AUDIO = $(addprefix $(AMDDALPATH)/dc/audio/,$(AUDIO)) + +AMD_DAL_FILES += $(AMD_DAL_AUDIO) + + +############################################################################### +# DCE 11x +############################################################################### +ifdef CONFIG_DRM_AMD_DAL_DCE11_0 +AUDIO_DCE11 = audio_dce110.o hw_ctx_audio_dce110.o + +AMD_DAL_AUDIO_DCE11 = $(addprefix $(AMDDALPATH)/dc/audio/dce110/,$(AUDIO_DCE11)) + +AMD_DAL_FILES += $(AMD_DAL_AUDIO_DCE11) +endif diff --git a/drivers/gpu/drm/amd/dal/dc/audio/audio.h b/drivers/gpu/drm/amd/dal/dc/audio/audio.h new file mode 100644 index 000000000000..ad2dc18ef37b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/audio.h @@ -0,0 +1,195 @@ +/* + * 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_AUDIO_H__ +#define __DAL_AUDIO_H__ + +#include "include/audio_interface.h" +#include "hw_ctx_audio.h" +#include "include/link_service_types.h" + +/***** only for hook functions *****/ +/** + *which will be overwritten by derived audio object. + *audio hw context object is independent object + */ + +struct audio; + +struct audio_funcs { + /* + *get_object_id + *get_object_type + *enumerate_input_signals + *enumerate_output_signals + *is_input_signal_supported + *is_output_signal_supported + *set_object_properties + *get_object_properties + */ + + void (*destroy)(struct audio **audio); + /*power_up + *power_down + *release_hw_base + */ + + /* setup audio */ + enum audio_result (*setup)( + struct audio *audio, + struct audio_output *output, + struct audio_info *info); + + enum audio_result (*enable_output)( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal); + + enum audio_result (*disable_output)( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal); + + /*enable_azalia_audio_jack_presence + * disable_azalia_audio_jack_presence + */ + + enum audio_result (*unmute)( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal); + + enum audio_result (*mute)( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal); + + /* SW initialization that cannot be done in constructor. This will + * be done is audio_power_up but is not in audio_interface. It is only + * called by power_up + */ + enum audio_result (*initialize)( + struct audio *audio); + + /* enable channel splitting mapping */ + void (*enable_channel_splitting_mapping)( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable); + + /* get current multi channel split. */ + enum audio_result (*get_channel_splitting_mapping)( + struct audio *audio, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping); + + /* set payload value for the unsolicited response */ + void (*set_unsolicited_response_payload)( + struct audio *audio, + enum audio_payload payload); + + /* Update audio wall clock source */ + void (*setup_audio_wall_dto)( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info); + + /* options and features supported by Audio */ + struct audio_feature_support (*get_supported_features)( + struct audio *audio); + + /* + *check_audio_bandwidth + *write_reg + *read_reg + *enable_gtc_embedding_with_group + *disable_gtc_embedding + *register_interrupt + *unregister_interrupt + *process_interrupt + *create_hw_ctx + *getHwCtx + *setHwCtx + *handle_interrupt + */ +}; + +struct audio { + /* hook functions. they will be overwritten by specific ASIC */ + const struct audio_funcs *funcs; + /* TODO: static struct audio_funcs funcs;*/ + + /*external structures - get service from external*/ + struct graphics_object_id id; + struct adapter_service *adapter_service; + /* audio HW context */ + struct hw_ctx_audio *hw_ctx; + struct dc_context *ctx; + /* audio supports input and output signals */ + uint32_t input_signals; + uint32_t output_signals; +}; + +/* - functions defined by audio.h will be used by audio component only. + * but audio.c also implements some function defined by dal\include + */ + +/* graphics_object_base implemention + * 1.input_signals and output_signals are moved + * into audio object. + * + * 2.Get the Graphics Object ID + * + * Outside audio: + * use dal_graphics_object_id_get_audio_id + * Within audio: + * use audio->go_base.id + * + * 3. Get the Graphics Object Type + * + * use object_id.type + * not function implemented. + * 4. Common Graphics Object Properties + * use object id ->go_properties.multi_path + * not function implemented. + */ + +bool dal_audio_construct_base( + struct audio *audio, + const struct audio_init_data *init_data); + +void dal_audio_destruct_base( + struct audio *audio); + +void dal_audio_release_hw_base( + struct audio *audio); + +#endif /* __DAL_AUDIO__ */ + + + diff --git a/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c new file mode 100644 index 000000000000..bfd6725d9b91 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/audio_base.c @@ -0,0 +1,470 @@ +/* + * 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 "dm_services.h" + +#include "include/logger_interface.h" + +#include "audio.h" +#include "hw_ctx_audio.h" + +#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) +#include "dce110/audio_dce110.h" +#include "dce110/hw_ctx_audio_dce110.h" +#endif + +/***** static function : only used within audio.c *****/ + +/* stub for hook functions */ +static void destroy( + struct audio **audio) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +static enum audio_result setup( + struct audio *audio, + struct audio_output *output, + struct audio_info *info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static enum audio_result enable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static enum audio_result disable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static enum audio_result unmute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static enum audio_result mute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static enum audio_result initialize( + struct audio *audio) +{ + /*DCE specific, must be implemented in derived. Implemeentaion of + *initialize will create audio hw context. create_hw_ctx + */ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +static void enable_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* get current multi channel split. */ +static enum audio_result get_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return AUDIO_RESULT_OK; +} + +/* set payload value for the unsolicited response */ +static void set_unsolicited_response_payload( + struct audio *audio, + enum audio_payload payload) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* update audio wall clock source */ +static void setup_audio_wall_dto( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +static struct audio_feature_support get_supported_features(struct audio *audio) +{ + /*DCE specific, must be implemented in derived*/ + struct audio_feature_support features; + + dm_memset(&features, 0, sizeof(features)); + + features.ENGINE_DIGA = 1; + features.ENGINE_DIGB = 1; + + return features; +} + +static const struct audio_funcs audio_funcs = { + .destroy = destroy, + .setup = setup, + .enable_output = enable_output, + .disable_output = disable_output, + .unmute = unmute, + .mute = mute, + .initialize = initialize, + .enable_channel_splitting_mapping = + enable_channel_splitting_mapping, + .get_channel_splitting_mapping = + get_channel_splitting_mapping, + .set_unsolicited_response_payload = + set_unsolicited_response_payload, + .setup_audio_wall_dto = setup_audio_wall_dto, + .get_supported_features = get_supported_features, +}; + +/***** SCOPE : declare in audio.h. use within dal-audio. *****/ + +bool dal_audio_construct_base( + struct audio *audio, + const struct audio_init_data *init_data) +{ + enum signal_type signals = SIGNAL_TYPE_HDMI_TYPE_A; + + ASSERT(init_data->as != NULL); + + /* base hook functions */ + audio->funcs = &audio_funcs; + + /*setup pointers to get service from dal service compoenents*/ + audio->adapter_service = init_data->as; + + audio->ctx = init_data->ctx; + + /* save audio endpoint number to identify object creating */ + audio->id = init_data->audio_stream_id; + + /* Fill supported signals. !!! be aware that android definition is + * already shift to vector. + */ + signals |= SIGNAL_TYPE_DISPLAY_PORT; + signals |= SIGNAL_TYPE_DISPLAY_PORT_MST; + signals |= SIGNAL_TYPE_EDP; + signals |= SIGNAL_TYPE_DISPLAY_PORT; + signals |= SIGNAL_TYPE_WIRELESS; + + /* Audio supports same set for input and output signals */ + audio->input_signals = signals; + audio->output_signals = signals; + + return true; +} + +/* except hw_ctx, no other hw need reset. so do nothing */ +void dal_audio_destruct_base( + struct audio *audio) +{ +} + +/* Enumerate Graphics Object supported Input/Output Signal Types */ +uint32_t dal_audio_enumerate_input_signals( + struct audio *audio) +{ + return audio->input_signals; +} + +uint32_t dal_audio_enumerate_output_signals( + struct audio *audio) +{ + return audio->output_signals; +} + +/* Check if signal supported by GraphicsObject */ +bool dal_audio_is_input_signal_supported( + struct audio *audio, + enum signal_type signal) +{ + return (signal & audio->output_signals) != 0; +} + +bool dal_audio_is_output_signal_supported( + struct audio *audio, + enum signal_type signal) +{ + return (signal & audio->input_signals) != 0; +} + +/***** SCOPE : declare in dal\include *****/ + +/* audio object creator triage. memory allocate and release will be + * done within dal_audio_create_dcexx + */ +struct audio *dal_audio_create( + const struct audio_init_data *init_data) +{ + struct adapter_service *as; + + if (init_data->as == NULL) + return NULL; + + as = init_data->as; + switch (dal_adapter_service_get_dce_version(as)) { +#if defined(CONFIG_DRM_AMD_DAL_DCE10_0) + case DCE_VERSION_10_0: + return dal_audio_create_dce110(init_data); +#endif +#if defined(CONFIG_DRM_AMD_DAL_DCE11_0) + case DCE_VERSION_11_0: + return dal_audio_create_dce110(init_data); +#endif + default: + BREAK_TO_DEBUGGER(); + return NULL; + } + + return NULL; +} + +/* audio object creator triage. + * memory for "struct audio dal_audio_create_dce8x" allocate + * will happens within dal_audio_dce8x. memory allocate is done + * with dal_audio_create_dce8x. memory release is initiated by + * dal_audio_destroy. It will call hook function which will finially + * used destroy() of dal_audio_dce8x. therefore, no memroy allocate + *and release happen physcially at audio base object. + */ +void dal_audio_destroy( + struct audio **audio) +{ + if (!audio || !*audio) { + BREAK_TO_DEBUGGER(); + return; + } + + (*audio)->funcs->destroy(audio); + + *audio = NULL; +} + +const struct graphics_object_id dal_audio_get_graphics_object_id( + const struct audio *audio) +{ + return audio->id; +} + +/* enable azalia audio endpoint. This function call hw_ctx directly + *not overwitten at audio level. + */ +enum audio_result dal_audio_enable_azalia_audio_jack_presence( + struct audio *audio, + enum engine_id engine_id) +{ + audio->hw_ctx->funcs->enable_azalia_audio(audio->hw_ctx, engine_id); + return AUDIO_RESULT_OK; +} + +/* disable azalia audio endpoint. This function call hw_ctx directly + *not overwitten at audio level. + */ +enum audio_result dal_audio_disable_azalia_audio_jack_presence( + struct audio *audio, + enum engine_id engine_id) +{ + audio->hw_ctx->funcs->disable_azalia_audio(audio->hw_ctx, engine_id); + return AUDIO_RESULT_OK; +} + +/* get audio bandwidth information. This function call hw_ctx directly + *not overwitten at audio level. + */ +void dal_audio_check_audio_bandwidth( + struct audio *audio, + const struct audio_crtc_info *info, + uint32_t channel_count, + enum signal_type signal, + union audio_sample_rates *sample_rates) +{ + dal_hw_ctx_audio_check_audio_bandwidth( + audio->hw_ctx, info, channel_count, signal, sample_rates); +} + +/* DP Audio register write access. This function call hw_ctx directly + * not overwitten at audio level. + */ + +/*assign GTC group and enable GTC value embedding*/ +void dal_audio_enable_gtc_embedding_with_group( + struct audio *audio, + uint32_t group_num, + uint32_t audio_latency) +{ + audio->hw_ctx->funcs->enable_gtc_embedding_with_group( + audio->hw_ctx, group_num, audio_latency); +} + +/* disable GTC value embedding */ +void dal_audio_disable_gtc_embedding( + struct audio *audio) +{ + audio->hw_ctx->funcs->disable_gtc_embedding(audio->hw_ctx); +} + +/* perform power up sequence (boot up, resume, recovery) */ +enum audio_result dal_audio_power_up( + struct audio *audio) +{ + return audio->funcs->initialize(audio); +} + +/* perform power down (shut down, stand by) */ +enum audio_result dal_audio_power_down( + struct audio *audio) +{ + return AUDIO_RESULT_OK; +} + +/* setup audio */ +enum audio_result dal_audio_setup( + struct audio *audio, + struct audio_output *output, + struct audio_info *info) +{ + return audio->funcs->setup(audio, output, info); +} + +/* enable audio */ +enum audio_result dal_audio_enable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + return audio->funcs->enable_output(audio, engine_id, signal); +} + +/* disable audio */ +enum audio_result dal_audio_disable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + return audio->funcs->disable_output(audio, engine_id, signal); +} + +/* unmute audio */ +enum audio_result dal_audio_unmute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + return audio->funcs->unmute(audio, engine_id, signal); +} + +/* mute audio */ +enum audio_result dal_audio_mute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + return audio->funcs->mute(audio, engine_id, signal); +} + +/* Enable multi channel split */ +void dal_audio_enable_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable) +{ + audio->funcs->enable_channel_splitting_mapping( + audio, engine_id, signal, audio_mapping, enable); +} + +/* get current multi channel split. */ +enum audio_result dal_audio_get_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping) +{ + return audio->funcs->get_channel_splitting_mapping( + audio, engine_id, audio_mapping); +} + +/* set payload value for the unsolicited response */ +void dal_audio_set_unsolicited_response_payload( + struct audio *audio, + enum audio_payload payload) +{ + audio->funcs->set_unsolicited_response_payload(audio, payload); +} + +/* update audio wall clock source */ +void dal_audio_setup_audio_wall_dto( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + audio->funcs->setup_audio_wall_dto(audio, signal, crtc_info, pll_info); +} + +struct audio_feature_support dal_audio_get_supported_features( + struct audio *audio) +{ + return audio->funcs->get_supported_features(audio); +} diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c new file mode 100644 index 000000000000..1aa0c1e61b32 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.c @@ -0,0 +1,453 @@ +/* + * 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 "dm_services.h" + +#include "include/logger_interface.h" + +#include "audio_dce110.h" + +/***** static functions *****/ + +static void destruct(struct audio_dce110 *audio) +{ + /*release memory allocated for hw_ctx -- allocated is initiated + *by audio_dce110 power_up + *audio->base->hw_ctx = NULL is done within hw-ctx->destroy + */ + if (audio->base.hw_ctx) + audio->base.hw_ctx->funcs->destroy(&(audio->base.hw_ctx)); + + /* reset base_audio_block */ + dal_audio_destruct_base(&audio->base); +} + +static void destroy(struct audio **ptr) +{ + struct audio_dce110 *audio = NULL; + + audio = container_of(*ptr, struct audio_dce110, base); + + destruct(audio); + + /* release memory allocated for audio_dce110*/ + dm_free((*ptr)->ctx, audio); + *ptr = NULL; +} + + +/* The inital call of hook function comes from audio object level. + *The passing object handle "struct audio *audio" point to base object + *already.There is not need to get base object from audio_dce110. + */ + +/** +* setup +* +* @brief +* setup Audio HW block, to be called by dal_audio_setup +* +*/ +static enum audio_result setup( + struct audio *audio, + struct audio_output *output, + struct audio_info *info) +{ + switch (output->signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + /*setup HDMI audio engine*/ + audio->hw_ctx->funcs->enable_afmt_clock( + audio->hw_ctx, + output->engine_id, + true); + audio->hw_ctx->funcs->setup_hdmi_audio( + audio->hw_ctx, output->engine_id, &output->crtc_info); + + audio->hw_ctx->funcs->setup_azalia( + audio->hw_ctx, + output->engine_id, + output->signal, + &output->crtc_info, + &output->pll_info, + info); + break; + + case SIGNAL_TYPE_WIRELESS: + /* setup Azalia block for Wireless Display - This + is different than for wired + displays because there is no + DIG to program.*/ + /*TODO: + audio->hw_ctx->funcs->setup_azalia_for_vce( + audio->hw_ctx, + audio->signal, + audio->crtc_info, + info); + */ + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + /* setup DP audio engine will be done at enable output */ + + /* setup Azalia block*/ + audio->hw_ctx->funcs->setup_azalia( + audio->hw_ctx, + output->engine_id, + output->signal, + &output->crtc_info, + &output->pll_info, + info); + + break; + default: + return AUDIO_RESULT_ERROR; + } + + return AUDIO_RESULT_OK; +} + +/** +* enable_output +* +* @brief +* enable Audio HW block, to be called by dal_audio_enable_output +*/ +static enum audio_result enable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + /* enable audio output */ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: { + /* enable AFMT clock before enable audio*/ + audio->hw_ctx->funcs->enable_afmt_clock( + audio->hw_ctx, engine_id, true); + /* setup DP audio engine */ + audio->hw_ctx->funcs->setup_dp_audio( + audio->hw_ctx, engine_id); + /* enabl DP audio packets will be done at unblank */ + audio->hw_ctx->funcs->enable_dp_audio( + audio->hw_ctx, engine_id); + } + break; + case SIGNAL_TYPE_WIRELESS: + /* route audio to VCE block */ + audio->hw_ctx->funcs->setup_vce_audio(audio->hw_ctx); + break; + default: + return AUDIO_RESULT_ERROR; + } + return AUDIO_RESULT_OK; +} + +/** +* disable_output +* +* @brief +* disable Audio HW block, to be called by dal_audio_disable_output +* +*/ +static enum audio_result disable_output( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_WIRELESS: + /* disable HDMI audio */ + audio->hw_ctx-> + funcs->disable_azalia_audio( + audio->hw_ctx, engine_id); + audio->hw_ctx-> + funcs->enable_afmt_clock( + audio->hw_ctx, engine_id, + false); + + break; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: { + /* disable DP audio */ + audio->hw_ctx->funcs->disable_dp_audio( + audio->hw_ctx, engine_id); + audio->hw_ctx->funcs->disable_azalia_audio( + audio->hw_ctx, engine_id); + audio->hw_ctx->funcs->enable_afmt_clock( + audio->hw_ctx, engine_id, false); + } + break; + default: + return AUDIO_RESULT_ERROR; + } + + return AUDIO_RESULT_OK; +} + +/** +* unmute +* +* @brief +* unmute audio, to be called by dal_audio_unmute +* +*/ +static enum audio_result unmute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + /* unmute Azalia audio */ + audio->hw_ctx->funcs->unmute_azalia_audio( + audio->hw_ctx, engine_id); + break; + case SIGNAL_TYPE_WIRELESS: + /*Do nothing for wireless display*/ + break; + default: + return AUDIO_RESULT_ERROR; + } + return AUDIO_RESULT_OK; +} + +/** +* mute +* +* @brief +* mute audio, to be called by dal_audio_nmute +* +*/ +static enum audio_result mute( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + /* mute Azalia audio */ + audio->hw_ctx->funcs->mute_azalia_audio( + audio->hw_ctx, engine_id); + break; + case SIGNAL_TYPE_WIRELESS: + /*Do nothing for wireless display*/ + break; + default: + return AUDIO_RESULT_ERROR; + } + return AUDIO_RESULT_OK; +} + +/** +* initialize +* +* @brief +* Perform SW initialization - create audio hw context. Then do HW +* initialization. this function is called at dal_audio_power_up. +* +*/ +static enum audio_result initialize( + struct audio *audio) +{ + uint8_t audio_endpoint_enum_id = 0; + + audio_endpoint_enum_id = audio->id.enum_id; + + /* HW CTX already create*/ + if (audio->hw_ctx != NULL) + return AUDIO_RESULT_OK; + + audio->hw_ctx = dal_hw_ctx_audio_dce110_create( + audio->ctx, + audio_endpoint_enum_id); + + if (audio->hw_ctx == NULL) + return AUDIO_RESULT_ERROR; + + /* override HW default settings */ + audio->hw_ctx->funcs->hw_initialize(audio->hw_ctx); + + return AUDIO_RESULT_OK; +} + +/* enable multi channel split */ +static void enable_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable) +{ + audio->hw_ctx->funcs->setup_channel_splitting_mapping( + audio->hw_ctx, + engine_id, + signal, + audio_mapping, enable); +} + +/* get current multi channel split. */ +static enum audio_result get_channel_splitting_mapping( + struct audio *audio, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping) +{ + if (audio->hw_ctx->funcs->get_channel_splitting_mapping( + audio->hw_ctx, engine_id, audio_mapping)) { + return AUDIO_RESULT_OK; + } else { + return AUDIO_RESULT_ERROR; + } +} + +/** +* set_unsolicited_response_payload +* +* @brief +* Set payload value for the unsolicited response +*/ +static void set_unsolicited_response_payload( + struct audio *audio, + enum audio_payload payload) +{ + audio->hw_ctx->funcs->set_unsolicited_response_payload( + audio->hw_ctx, payload); +} + +/** +* setup_audio_wall_dto +* +* @brief +* Update audio source clock from hardware context. +* +*/ +static void setup_audio_wall_dto( + struct audio *audio, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + audio->hw_ctx->funcs->setup_audio_wall_dto( + audio->hw_ctx, signal, crtc_info, pll_info); +} + +/** +* get_supported_features +* +* @brief +* options and features supported by Audio +* returns supported engines, signals. +* features are reported for HW audio/Azalia block rather then Audio object +* itself the difference for DCE6.x is that MultiStream Audio is now supported +* +*/ +static struct audio_feature_support get_supported_features(struct audio *audio) +{ + struct audio_feature_support afs = {0}; + + afs.ENGINE_DIGA = 1; + afs.ENGINE_DIGB = 1; + afs.ENGINE_DIGC = 1; + afs.MULTISTREAM_AUDIO = 1; + + return afs; +} + +static const struct audio_funcs funcs = { + .destroy = destroy, + .setup = setup, + .enable_output = enable_output, + .disable_output = disable_output, + .unmute = unmute, + .mute = mute, + .initialize = initialize, + .enable_channel_splitting_mapping = + enable_channel_splitting_mapping, + .get_channel_splitting_mapping = + get_channel_splitting_mapping, + .set_unsolicited_response_payload = + set_unsolicited_response_payload, + .setup_audio_wall_dto = setup_audio_wall_dto, + .get_supported_features = get_supported_features, +}; + +static bool construct( + struct audio_dce110 *audio, + const struct audio_init_data *init_data) +{ + struct audio *base = &audio->base; + + /* base audio construct*/ + if (!dal_audio_construct_base(base, init_data)) + return false; + + /*vtable methods*/ + base->funcs = &funcs; + return true; +} + + +/* --- audio scope functions --- */ + +struct audio *dal_audio_create_dce110( + const struct audio_init_data *init_data) +{ + /*allocate memory for audio_dce110 */ + struct audio_dce110 *audio = dm_alloc(init_data->ctx, sizeof(*audio)); + + if (audio == NULL) { + ASSERT_CRITICAL(audio); + return NULL; + } + /*pointer to base_audio_block of audio_dce110 ==> audio base object */ + if (construct(audio, init_data)) + return &audio->base; + + dal_logger_write( + init_data->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_AUDIO, + "Failed to create audio object for DCE11\n"); + + /*release memory allocated if fail */ + dm_free(init_data->ctx, audio); + return NULL; +} + +/* Do not need expose construct_dce110 and destruct_dce110 becuase there is + *derived object after dce110 + */ + diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h new file mode 100644 index 000000000000..e5ff823368b3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/audio_dce110.h @@ -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 + * + */ +#ifndef __DAL_AUDIO_DCE_110_H__ +#define __DAL_AUDIO_DCE_110_H__ + +#include "audio/audio.h" +#include "audio/hw_ctx_audio.h" +#include "audio/dce110/hw_ctx_audio_dce110.h" + + + +struct audio_dce110 { + struct audio base; + /* dce-specific members are following */ + /* none */ +}; + +struct audio *dal_audio_create_dce110(const struct audio_init_data *init_data); + +#endif /*__DAL_AUDIO_DCE_110_H__*/ diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c new file mode 100644 index 000000000000..f24b964bdd8d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.c @@ -0,0 +1,1930 @@ +/* + * 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 "dm_services.h" + +#include "include/logger_interface.h" +#include "../hw_ctx_audio.h" +#include "hw_ctx_audio_dce110.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define FROM_BASE(ptr) \ + container_of((ptr), struct hw_ctx_audio_dce110, base) + +#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000 +#define DP_AUDIO_DTO_MODULE_WITHOUT_SS 360 +#define DP_AUDIO_DTO_PHASE_WITHOUT_SS 24 + +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUDIO_FRONT_END 0 +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1 +#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__REGISTER_PROGRAMMABLE 2 + +#define FIRST_AUDIO_STREAM_ID 1 + +#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_AUDIO, \ + "Audio:%s()\n", __func__) + +static const uint32_t engine_offset[] = { + 0, + mmDIG1_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, + mmDIG2_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL, + mmDIG3_DIG_FE_CNTL - mmDIG0_DIG_FE_CNTL +}; + +static void destruct( + struct hw_ctx_audio_dce110 *hw_ctx_dce110) +{ + dal_audio_destruct_hw_ctx_audio(&hw_ctx_dce110->base); +} + +static void destroy( + struct hw_ctx_audio **ptr) +{ + struct hw_ctx_audio_dce110 *hw_ctx_dce110; + + hw_ctx_dce110 = container_of( + *ptr, struct hw_ctx_audio_dce110, base); + + destruct(hw_ctx_dce110); + /* release memory allocated for struct hw_ctx_audio_dce110 */ + dm_free((*ptr)->ctx, hw_ctx_dce110); + + *ptr = NULL; +} + +/* --- helpers --- */ +static void write_indirect_azalia_reg( + const struct hw_ctx_audio *hw_ctx, + uint32_t reg_index, + uint32_t reg_data) +{ + uint32_t addr = 0; + uint32_t value = 0; + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + { + addr = + FROM_BASE(hw_ctx)->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index; + + set_reg_field_value(value, reg_index, + AZALIA_F0_CODEC_ENDPOINT_INDEX, + AZALIA_ENDPOINT_REG_INDEX); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + { + addr = + FROM_BASE(hw_ctx)->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data; + + value = 0; + set_reg_field_value(value, reg_data, + AZALIA_F0_CODEC_ENDPOINT_DATA, + AZALIA_ENDPOINT_REG_DATA); + dm_write_reg(hw_ctx->ctx, addr, value); + } + + dal_logger_write( + hw_ctx->ctx->logger, + LOG_MAJOR_HW_TRACE, + LOG_MINOR_HW_TRACE_AUDIO, + "AUDIO:write_indirect_azalia_reg: index: %u data: %u\n", + reg_index, reg_data); +} + +static uint32_t read_indirect_azalia_reg( + const struct hw_ctx_audio *hw_ctx, + uint32_t reg_index) +{ + uint32_t ret_val = 0; + uint32_t addr = 0; + uint32_t value = 0; + + + /* AZALIA_F0_CODEC_ENDPOINT_INDEX endpoint index */ + { + addr = + FROM_BASE(hw_ctx)->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index; + + set_reg_field_value(value, reg_index, + AZALIA_F0_CODEC_ENDPOINT_INDEX, + AZALIA_ENDPOINT_REG_INDEX); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AZALIA_F0_CODEC_ENDPOINT_DATA endpoint data */ + { + addr = + FROM_BASE(hw_ctx)->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data; + + value = dm_read_reg(hw_ctx->ctx, addr); + ret_val = value; + } + + dal_logger_write( + hw_ctx->ctx->logger, + LOG_MAJOR_HW_TRACE, + LOG_MINOR_HW_TRACE_AUDIO, + "AUDIO:read_indirect_azalia_reg: index: %u data: %u\n", + reg_index, ret_val); + + return ret_val; +} + +/* expose/not expose HBR capability to Audio driver */ +static void set_high_bit_rate_capable( + const struct hw_ctx_audio *hw_ctx, + bool capable) +{ + uint32_t value = 0; + + /* set high bit rate audio capable*/ + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR); + + set_reg_field_value(value, capable, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, + HBR_CAPABLE); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_HBR, + value); +} + +/* set HBR channnel count * +static void set_hbr_channel_count( + const struct hw_ctx_audio *hw_ctx, + uint32_t hbr_channel_count) +{ + uint32_t value = 0; + + if (hbr_channel_count > 7) + return; + + value = dal_read_reg(hw_ctx->ctx, + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); + + set_reg_field_value(value, hbr_channel_count, + AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, + HBR_CHANNEL_COUNT); + + dal_write_reg(hw_ctx->ctx, + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, value); + +} + +*set compressed audio channel count * +static void set_compressed_audio_channel_count( + const struct hw_ctx_audio *hw_ctx, + uint32_t compressed_audio_ch_count) +{ + uint32_t value = 0; + if (compressed_audio_ch_count > 7) + return; + + value = dal_read_reg(hw_ctx->ctx, + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL); + + set_reg_field_value(value, compressed_audio_ch_count, + AZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, + COMPRESSED_CHANNEL_COUNT); + + dal_write_reg(hw_ctx->ctx, + mmAZALIA_F0_CODEC_CHANNEL_COUNT_CONTROL, + value); + +} +*/ +/* set video latency in in ms/2+1 */ +static void set_video_latency( + const struct hw_ctx_audio *hw_ctx, + int latency_in_ms) +{ + uint32_t value = 0; + + if ((latency_in_ms < 0) || (latency_in_ms > 255)) + return; + + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + VIDEO_LIPSYNC); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); + +} + +/* set audio latency in in ms/2+1 */ +static void set_audio_latency( + const struct hw_ctx_audio *hw_ctx, + int latency_in_ms) +{ + uint32_t value = 0; + + if (latency_in_ms < 0) + latency_in_ms = 0; + + if (latency_in_ms > 255) + latency_in_ms = 255; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC); + + set_reg_field_value(value, latency_in_ms, + AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + AUDIO_LIPSYNC); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, + value); + +} + +/* enable HW/SW Sync */ +/*static void enable_hw_sw_sync( + const struct hw_ctx_audio *hw_ctx) +{ + union AZALIA_CYCLIC_BUFFER_SYNC value; + + value = dal_read_reg(mmAZALIA_CYCLIC_BUFFER_SYNC); + value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 1; + dal_write_reg(mmAZALIA_CYCLIC_BUFFER_SYNC, value); +}*/ + +/* disable HW/SW Sync */ +/*static void disable_hw_sw_sync( + const struct hw_ctx_audio *hw_ctx) +{ + union AZALIA_CYCLIC_BUFFER_SYNC value; + + value = dal_read_reg( + mmAZALIA_CYCLIC_BUFFER_SYNC); + value.bits.CYCLIC_BUFFER_SYNC_ENABLE = 0; + dal_write_reg( + mmAZALIA_CYCLIC_BUFFER_SYNC, value); +}*/ + +/* update hardware with software's current position in cyclic buffer */ +/*static void update_sw_write_ptr( + const struct hw_ctx_audio *hw_ctx, + uint32_t offset) +{ + union AZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER value; + + value = dal_read_reg( + mmAZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER); + value.bits.APPLICATION_POSITION_IN_CYCLIC_BUFFER = offset; + dal_write_reg( + mmAZALIA_APPLICATION_POSITION_IN_CYCLIC_BUFFER, + value); +}*/ + +/* update Audio/Video association */ +/*static void update_av_association( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + uint32_t displayId) +{ + +}*/ + +/* --- hook functions --- */ +static bool get_azalia_clock_info_hdmi( + const struct hw_ctx_audio *hw_ctx, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info); + +static bool get_azalia_clock_info_dp( + const struct hw_ctx_audio *hw_ctx, + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info); + +static void setup_audio_wall_dto( + const struct hw_ctx_audio *hw_ctx, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + struct azalia_clock_info clock_info = { 0 }; + + uint32_t value = dm_read_reg(hw_ctx->ctx, mmDCCG_AUDIO_DTO_SOURCE); + + /* TODO: GraphicsObject\inc\GraphicsObjectDefs.hpp(131): + *inline bool isHdmiSignal(SignalType signal) + *if (Signals::isHdmiSignal(signal)) + */ + if (dc_is_hdmi_signal(signal)) { + /*DTO0 Programming goal: + -generate 24MHz, 128*Fs from 24MHz + -use DTO0 when an active HDMI port is connected + (optionally a DP is connected) */ + + /* calculate DTO settings */ + get_azalia_clock_info_hdmi( + hw_ctx, + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &clock_info); + + /* On TN/SI, Program DTO source select and DTO select before + programming DTO modulo and DTO phase. These bits must be + programmed first, otherwise there will be no HDMI audio at boot + up. This is a HW sequence change (different from old ASICs). + Caution when changing this programming sequence. + + HDMI enabled, using DTO0 + program master CRTC for DTO0 */ + { + set_reg_field_value(value, + pll_info->dto_source - DTO_SOURCE_ID0, + DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO0_SOURCE_SEL); + + set_reg_field_value(value, + 0, + DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL); + + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO_SOURCE, value); + } + + /* module */ + { + value = dm_read_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO0_MODULE); + set_reg_field_value(value, + clock_info.audio_dto_module, + DCCG_AUDIO_DTO0_MODULE, + DCCG_AUDIO_DTO0_MODULE); + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO0_MODULE, value); + } + + /* phase */ + { + value = 0; + + value = dm_read_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO0_PHASE); + set_reg_field_value(value, + clock_info.audio_dto_phase, + DCCG_AUDIO_DTO0_PHASE, + DCCG_AUDIO_DTO0_PHASE); + + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO0_PHASE, value); + } + + } else { + /*DTO1 Programming goal: + -generate 24MHz, 512*Fs, 128*Fs from 24MHz + -default is to used DTO1, and switch to DTO0 when an audio + master HDMI port is connected + -use as default for DP + + calculate DTO settings */ + get_azalia_clock_info_dp( + hw_ctx, + crtc_info->requested_pixel_clock, + pll_info, + &clock_info); + + /* Program DTO select before programming DTO modulo and DTO + phase. default to use DTO1 */ + + { + set_reg_field_value(value, 1, + DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO_SEL); + /*dal_write_reg(mmDCCG_AUDIO_DTO_SOURCE, value)*/ + + /* Select 512fs for DP TODO: web register definition + does not match register header file + set_reg_field_value(value, 1, + DCCG_AUDIO_DTO_SOURCE, + DCCG_AUDIO_DTO2_USE_512FBR_DTO); + */ + + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO_SOURCE, value); + } + + /* module */ + { + value = 0; + + value = dm_read_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO1_MODULE); + + set_reg_field_value(value, + clock_info.audio_dto_module, + DCCG_AUDIO_DTO1_MODULE, + DCCG_AUDIO_DTO1_MODULE); + + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO1_MODULE, value); + } + + /* phase */ + { + value = 0; + + value = dm_read_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO1_PHASE); + + set_reg_field_value(value, + clock_info.audio_dto_phase, + DCCG_AUDIO_DTO1_PHASE, + DCCG_AUDIO_DTO1_PHASE); + + dm_write_reg(hw_ctx->ctx, + mmDCCG_AUDIO_DTO1_PHASE, value); + } + + /* DAL2 code separate DCCG_AUDIO_DTO_SEL and + DCCG_AUDIO_DTO2_USE_512FBR_DTO programming into two different + location. merge together should not hurt */ + /*value.bits.DCCG_AUDIO_DTO2_USE_512FBR_DTO = 1; + dal_write_reg(mmDCCG_AUDIO_DTO_SOURCE, value);*/ + } +} + +/* setup HDMI audio */ +static void setup_hdmi_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + const struct audio_crtc_info *crtc_info) +{ + struct audio_clock_info audio_clock_info = {0}; + uint32_t max_packets_per_line; + uint32_t addr = 0; + uint32_t value = 0; + + /* For now still do calculation, although this field is ignored when + above HDMI_PACKET_GEN_VERSION set to 1 */ + max_packets_per_line = + dal_audio_hw_ctx_calc_max_audio_packets_per_line( + hw_ctx, + crtc_info); + + /* HDMI_AUDIO_PACKET_CONTROL */ + { + addr = + mmHDMI_AUDIO_PACKET_CONTROL + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, max_packets_per_line, + HDMI_AUDIO_PACKET_CONTROL, + HDMI_AUDIO_PACKETS_PER_LINE); + /* still apply RS600's default setting which is 1. */ + set_reg_field_value(value, 1, + HDMI_AUDIO_PACKET_CONTROL, + HDMI_AUDIO_DELAY_EN); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_AUDIO_PACKET_CONTROL */ + { + addr = mmAFMT_AUDIO_PACKET_CONTROL + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 1, + AFMT_AUDIO_PACKET_CONTROL, + AFMT_60958_CS_UPDATE); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + { + addr = mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 0, + AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD); + + /*Register field changed.*/ + set_reg_field_value(value, 0, + AFMT_AUDIO_PACKET_CONTROL2, + AFMT_60958_OSF_OVRD); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_PACKET_CONTROL */ + { + addr = mmHDMI_ACR_PACKET_CONTROL + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, 1, + HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND); + + /* Set HDMI_ACR_SOURCE to 0, to use hardwre + * computed CTS values.*/ + set_reg_field_value(value, 0, + HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_SOURCE); + + /* For now clear HDMI_ACR_AUDIO_PRIORITY =>ACR packet has + higher priority over Audio Sample */ + set_reg_field_value(value, 0, + HDMI_ACR_PACKET_CONTROL, + HDMI_ACR_AUDIO_PRIORITY); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* Program audio clock sample/regeneration parameters */ + if (dal_audio_hw_ctx_get_audio_clock_info( + hw_ctx, + crtc_info->color_depth, + crtc_info->requested_pixel_clock, + crtc_info->calculated_pixel_clock, + &audio_clock_info)) { + + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + { + addr = mmHDMI_ACR_32_0 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, audio_clock_info.cts_32khz, + HDMI_ACR_32_0, + HDMI_ACR_CTS_32); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + { + addr = mmHDMI_ACR_32_1 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, audio_clock_info.n_32khz, + HDMI_ACR_32_1, + HDMI_ACR_N_32); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + { + addr = mmHDMI_ACR_44_0 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, audio_clock_info.cts_44khz, + HDMI_ACR_44_0, + HDMI_ACR_CTS_44); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + { + addr = mmHDMI_ACR_44_1 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, audio_clock_info.n_44khz, + HDMI_ACR_44_1, + HDMI_ACR_N_44); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + { + addr = mmHDMI_ACR_48_0 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, audio_clock_info.cts_48khz, + HDMI_ACR_48_0, + HDMI_ACR_CTS_48); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + { + addr = mmHDMI_ACR_48_1 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, audio_clock_info.n_48khz, + HDMI_ACR_48_1, + HDMI_ACR_N_48); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* Video driver cannot know in advance which sample rate will + be used by HD Audio driver + HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + programmed below in interruppt callback */ + } /* if */ + + /* AFMT_60958_0__AFMT_60958_CS_CHANNEL_NUMBER_L_MASK & + AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + { + addr = mmAFMT_60958_0 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, 1, + AFMT_60958_0, + AFMT_60958_CS_CHANNEL_NUMBER_L); + + /*HW default */ + set_reg_field_value(value, 0, + AFMT_60958_0, + AFMT_60958_CS_CLOCK_ACCURACY); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_60958_1 AFMT_60958_CS_CHALNNEL_NUMBER_R */ + { + addr = mmAFMT_60958_1 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, 2, + AFMT_60958_1, + AFMT_60958_CS_CHANNEL_NUMBER_R); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /*AFMT_60958_2 now keep this settings until + * Programming guide comes out*/ + { + addr = mmAFMT_60958_2 + engine_offset[engine_id]; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, 3, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_2); + + set_reg_field_value(value, 4, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_3); + + set_reg_field_value(value, 5, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_4); + + set_reg_field_value(value, 6, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_5); + + set_reg_field_value(value, 7, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_6); + + set_reg_field_value(value, 8, + AFMT_60958_2, + AFMT_60958_CS_CHANNEL_NUMBER_7); + + dm_write_reg(hw_ctx->ctx, addr, value); + } +} + + /* setup DP audio */ +static void setup_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /* --- DP Audio packet configurations --- */ + uint32_t addr = 0; + uint32_t value = 0; + + /* ATP Configuration */ + { + addr = mmDP_SEC_AUD_N + engine_offset[engine_id]; + + set_reg_field_value(value, + DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT, + DP_SEC_AUD_N, + DP_SEC_AUD_N); + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* Async/auto-calc timestamp mode */ + { + addr = mmDP_SEC_TIMESTAMP + + engine_offset[engine_id]; + + value = 0; + + set_reg_field_value(value, + DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC, + DP_SEC_TIMESTAMP, + DP_SEC_TIMESTAMP_MODE); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* --- The following are the registers + * copied from the SetupHDMI --- */ + + + /* AFMT_AUDIO_PACKET_CONTROL */ + { + addr = mmAFMT_AUDIO_PACKET_CONTROL + + engine_offset[engine_id]; + + value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, + 1, + AFMT_AUDIO_PACKET_CONTROL, + AFMT_60958_CS_UPDATE); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_AUDIO_PACKET_CONTROL2 */ + { + addr = + mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; + + value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, + 0, + AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_LAYOUT_OVRD); + + set_reg_field_value(value, + 0, + AFMT_AUDIO_PACKET_CONTROL2, + AFMT_60958_OSF_OVRD); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_INFOFRAME_CONTROL0 */ + { + addr = + mmAFMT_INFOFRAME_CONTROL0 + engine_offset[engine_id]; + + value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, + 1, + AFMT_INFOFRAME_CONTROL0, + AFMT_AUDIO_INFO_UPDATE); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* AFMT_60958_0__AFMT_60958_CS_CLOCK_ACCURACY_MASK */ + { + addr = mmAFMT_60958_0 + engine_offset[engine_id]; + + value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, + 0, + AFMT_60958_0, + AFMT_60958_CS_CLOCK_ACCURACY); + + dm_write_reg(hw_ctx->ctx, addr, value); + } +} + + /* setup VCE audio */ +static void setup_vce_audio( + const struct hw_ctx_audio *hw_ctx) +{ + struct dc_context *ctx = hw_ctx->ctx; + + NOT_IMPLEMENTED(); + + /*TODO: + const uint32_t addr = mmDOUT_DCE_VCE_CONTROL; + uint32_t value = 0; + + value = dal_read_reg(hw_ctx->ctx, + addr); + + set_reg_field_value(value, + FROM_BASE(hw_ctx)->azalia_stream_id - 1, + DOUT_DCE_VCE_CONTROL, + DC_VCE_AUDIO_STREAM_SELECT); + + dal_write_reg(hw_ctx->ctx, + addr, value);*/ +} + +static void enable_afmt_clock( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + bool enable_flag) +{ + uint32_t engine_offs = engine_offset[engine_id]; + uint32_t value; + uint32_t count = 0; + uint32_t enable = enable_flag ? 1:0; + + /* Enable Audio packets*/ + value = dm_read_reg(hw_ctx->ctx, mmAFMT_CNTL + engine_offs); + + /*enable AFMT clock*/ + set_reg_field_value(value, enable, + AFMT_CNTL, AFMT_AUDIO_CLOCK_EN); + dm_write_reg(hw_ctx->ctx, mmAFMT_CNTL + engine_offs, value); + + /*wait for AFMT clock to turn on, + * the expectation is that this + * should complete in 1-2 reads) + */ + do { + /* Wait for 1us between subsequent register reads.*/ + dm_delay_in_microseconds(hw_ctx->ctx, 1); + value = dm_read_reg(hw_ctx->ctx, + mmAFMT_CNTL + engine_offs); + } while (get_reg_field_value(value, + AFMT_CNTL, AFMT_AUDIO_CLOCK_ON) != + enable && count++ < 10); +} + +/* enable Azalia audio */ +static void enable_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + uint32_t value; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + if (get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED) != 1) + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + value); +} + +/* disable Azalia audio */ +static void disable_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + uint32_t value; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + AUDIO_ENABLED); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + value); +} + +/* enable DP audio */ +static void enable_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + const uint32_t addr = mmDP_SEC_CNTL + engine_offset[engine_id]; + + uint32_t value; + + /* Enable Audio packets */ + value = dm_read_reg(hw_ctx->ctx, addr); + set_reg_field_value(value, 1, + DP_SEC_CNTL, + DP_SEC_ASP_ENABLE); + + dm_write_reg(hw_ctx->ctx, addr, value); + + /* Program the ATP and AIP next */ + set_reg_field_value(value, 1, + DP_SEC_CNTL, + DP_SEC_ATP_ENABLE); + + set_reg_field_value(value, 1, + DP_SEC_CNTL, + DP_SEC_AIP_ENABLE); + + dm_write_reg(hw_ctx->ctx, addr, value); + + /* Program STREAM_ENABLE after all the other enables. */ + set_reg_field_value(value, 1, + DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE); + + dm_write_reg(hw_ctx->ctx, addr, value); +} + +/* disable DP audio */ +static void disable_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + const uint32_t addr = mmDP_SEC_CNTL + engine_offset[engine_id]; + + uint32_t value; + + /* Disable Audio packets */ + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 0, + DP_SEC_CNTL, + DP_SEC_ASP_ENABLE); + + set_reg_field_value(value, 0, + DP_SEC_CNTL, + DP_SEC_ATP_ENABLE); + + set_reg_field_value(value, 0, + DP_SEC_CNTL, + DP_SEC_AIP_ENABLE); + + set_reg_field_value(value, 0, + DP_SEC_CNTL, + DP_SEC_ACM_ENABLE); + + set_reg_field_value(value, 0, + DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE); + + /* This register shared with encoder info frame. Therefore we need to + keep master enabled if at least on of the fields is not 0 */ + if (value != 0) + set_reg_field_value(value, 1, + DP_SEC_CNTL, + DP_SEC_STREAM_ENABLE); + + dm_write_reg(hw_ctx->ctx, addr, value); +} + +static void configure_azalia( + const struct hw_ctx_audio *hw_ctx, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_info *audio_info) +{ + uint32_t speakers = audio_info->flags.info.ALLSPEAKERS; + uint32_t value; + uint32_t field = 0; + enum audio_format_code audio_format_code; + uint32_t format_index; + uint32_t index; + bool is_ac3_supported = false; + bool is_audio_format_supported = false; + union audio_sample_rates sample_rate; + uint32_t strlen = 0; + + /* Speaker Allocation */ + /* + uint32_t value; + uint32_t field = 0;*/ + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER); + + set_reg_field_value(value, + speakers, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + SPEAKER_ALLOCATION); + + /* LFE_PLAYBACK_LEVEL = LFEPBL + * LFEPBL = 0 : Unknown or refer to other information + * LFEPBL = 1 : 0dB playback + * LFEPBL = 2 : +10dB playback + * LFE_BL = 3 : Reserved + */ + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + LFE_PLAYBACK_LEVEL); + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + set_reg_field_value(value, + 0, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + + field = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + field &= ~0x1; + + set_reg_field_value(value, + field, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + /* set audio for output signal */ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + break; + case SIGNAL_TYPE_WIRELESS: { + /*LSB used for "is wireless" flag */ + field = 0; + field = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + field |= 0x1; + set_reg_field_value(value, + field, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + EXTRA_CONNECTION_INFO); + + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + HDMI_CONNECTION); + + } + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + set_reg_field_value(value, + 1, + AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + DP_CONNECTION); + + break; + default: + break; + } + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER, + value); + + /* Wireless Display identification */ + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION); + + set_reg_field_value(value, + signal == SIGNAL_TYPE_WIRELESS ? 1 : 0, + AZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION, + WIRELESS_DISPLAY_IDENTIFICATION); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_WIRELESS_DISPLAY_IDENTIFICATION, + value); + + /* Audio Descriptors */ + /* pass through all formats */ + for (format_index = 0; format_index < AUDIO_FORMAT_CODE_COUNT; + format_index++) { + audio_format_code = + (AUDIO_FORMAT_CODE_FIRST + format_index); + + /* those are unsupported, skip programming */ + if (audio_format_code == AUDIO_FORMAT_CODE_1BITAUDIO || + audio_format_code == AUDIO_FORMAT_CODE_DST) + continue; + + value = 0; + + /* check if supported */ + is_audio_format_supported = + dal_audio_hw_ctx_is_audio_format_supported( + hw_ctx, + audio_info, + audio_format_code, &index); + + if (is_audio_format_supported) { + const struct audio_mode *audio_mode = + &audio_info->modes[index]; + union audio_sample_rates sample_rates = + audio_mode->sample_rates; + uint8_t byte2 = audio_mode->max_bit_rate; + + /* adjust specific properties */ + switch (audio_format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: { + dal_hw_ctx_audio_check_audio_bandwidth( + hw_ctx, + crtc_info, + audio_mode->channel_count, + signal, + &sample_rates); + + byte2 = audio_mode->sample_size; + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES_STEREO); + + } + break; + case AUDIO_FORMAT_CODE_AC3: + is_ac3_supported = true; + break; + case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS: + case AUDIO_FORMAT_CODE_DTS_HD: + case AUDIO_FORMAT_CODE_MAT_MLP: + case AUDIO_FORMAT_CODE_DST: + case AUDIO_FORMAT_CODE_WMAPRO: + byte2 = audio_mode->vendor_specific; + break; + default: + break; + } + + /* fill audio format data */ + set_reg_field_value(value, + audio_mode->channel_count - 1, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + MAX_CHANNELS); + + set_reg_field_value(value, + sample_rates.all, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + SUPPORTED_FREQUENCIES); + + set_reg_field_value(value, + byte2, + AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0, + DESCRIPTOR_BYTE_2); + + } /* if */ + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 + + format_index, + value); + } /* for */ + + if (is_ac3_supported) + dm_write_reg(hw_ctx->ctx, + mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_STREAM_FORMATS, + 0x05); + + /* check for 192khz/8-Ch support for HBR requirements */ + sample_rate.all = 0; + sample_rate.rate.RATE_192 = 1; + dal_hw_ctx_audio_check_audio_bandwidth( + hw_ctx, + crtc_info, + 8, + signal, + &sample_rate); + + set_high_bit_rate_capable(hw_ctx, sample_rate.rate.RATE_192); + + /* Audio and Video Lipsync */ + set_video_latency(hw_ctx, audio_info->video_latency); + set_audio_latency(hw_ctx, audio_info->audio_latency); + + value = 0; + set_reg_field_value(value, audio_info->manufacture_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + MANUFACTURER_ID); + + set_reg_field_value(value, audio_info->product_id, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + PRODUCT_ID); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO0, + value); + + + value = 0; + + /*get display name string length */ + while (audio_info->display_name[strlen++] != '\0') { + if (strlen >= + MAX_HW_AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS) + break; + } + set_reg_field_value(value, strlen, + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + SINK_DESCRIPTION_LEN); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO1, + value); + + + /* + *write the port ID: + *PORT_ID0 = display index + *PORT_ID1 = 16bit BDF + *(format MSB->LSB: 8bit Bus, 5bit Device, 3bit Function) + */ + + value = 0; + + set_reg_field_value(value, audio_info->port_id[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, + PORT_ID0); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO2, + value); + + value = 0; + set_reg_field_value(value, audio_info->port_id[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, + PORT_ID1); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO3, + value); + + /*write the 18 char monitor string */ + + value = 0; + set_reg_field_value(value, audio_info->display_name[0], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION0); + + set_reg_field_value(value, audio_info->display_name[1], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION1); + + set_reg_field_value(value, audio_info->display_name[2], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION2); + + set_reg_field_value(value, audio_info->display_name[3], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + DESCRIPTION3); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO4, + value); + + + value = 0; + set_reg_field_value(value, audio_info->display_name[4], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION4); + + set_reg_field_value(value, audio_info->display_name[5], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION5); + + set_reg_field_value(value, audio_info->display_name[6], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION6); + + set_reg_field_value(value, audio_info->display_name[7], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + DESCRIPTION7); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO5, + value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[8], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION8); + + set_reg_field_value(value, audio_info->display_name[9], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION9); + + set_reg_field_value(value, audio_info->display_name[10], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION10); + + set_reg_field_value(value, audio_info->display_name[11], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + DESCRIPTION11); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO6, + value); + + value = 0; + set_reg_field_value(value, audio_info->display_name[12], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION12); + + set_reg_field_value(value, audio_info->display_name[13], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION13); + + set_reg_field_value(value, audio_info->display_name[14], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION14); + + set_reg_field_value(value, audio_info->display_name[15], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + DESCRIPTION15); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO7, + value); + + + value = 0; + set_reg_field_value(value, audio_info->display_name[16], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION16); + + set_reg_field_value(value, audio_info->display_name[17], + AZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + DESCRIPTION17); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_SINK_INFO8, + value); + +} + +/* setup Azalia HW block */ +static void setup_azalia( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info, + const struct audio_info *audio_info) +{ + uint32_t speakers = 0; + uint32_t channels = 0; + + if (audio_info == NULL) + /* This should not happen.it does so we don't get BSOD*/ + return; + + speakers = audio_info->flags.info.ALLSPEAKERS; + channels = dal_audio_hw_ctx_speakers_to_channels( + hw_ctx, + audio_info->flags.speaker_flags).all; + + /* setup the audio stream source select (audio -> dig mapping) */ + { + const uint32_t addr = + mmAFMT_AUDIO_SRC_CONTROL + engine_offset[engine_id]; + + uint32_t value = 0; + /*convert one-based index to zero-based */ + set_reg_field_value(value, + FROM_BASE(hw_ctx)->azalia_stream_id - 1, + AFMT_AUDIO_SRC_CONTROL, + AFMT_AUDIO_SRC_SELECT); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /* Channel allocation */ + { + const uint32_t addr = + mmAFMT_AUDIO_PACKET_CONTROL2 + engine_offset[engine_id]; + uint32_t value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, + channels, + AFMT_AUDIO_PACKET_CONTROL2, + AFMT_AUDIO_CHANNEL_ENABLE); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + configure_azalia(hw_ctx, signal, crtc_info, audio_info); +} + +/* unmute audio */ +static void unmute_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + const uint32_t addr = mmAFMT_AUDIO_PACKET_CONTROL + + engine_offset[engine_id]; + + uint32_t value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 1, + AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); + + dm_write_reg(hw_ctx->ctx, addr, value); +} + +/* mute audio */ +static void mute_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + const uint32_t addr = mmAFMT_AUDIO_PACKET_CONTROL + + engine_offset[engine_id]; + + uint32_t value = 0; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 0, + AFMT_AUDIO_PACKET_CONTROL, AFMT_AUDIO_SAMPLE_SEND); + + dm_write_reg(hw_ctx->ctx, addr, value); +} + +/* enable channel splitting mapping */ +static void setup_channel_splitting_mapping( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable) +{ + uint32_t value = 0; + + if ((audio_mapping == NULL || audio_mapping->u32all == 0) && enable) + return; + + + value = audio_mapping->u32all; + + if (enable == false) + /*0xFFFFFFFF;*/ + value = MULTI_CHANNEL_SPLIT_NO_ASSO_INFO; + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_ASSOCIATION_INFO, + value); +} + +/* get current channel spliting */ +static bool get_channel_splitting_mapping( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping) +{ + uint32_t value = 0; + + if (audio_mapping == NULL) + return false; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_ASSOCIATION_INFO); + + /*0xFFFFFFFF*/ + if (get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_ASSOCIATION_INFO, + ASSOCIATION_INFO) != + MULTI_CHANNEL_SPLIT_NO_ASSO_INFO) { + uint32_t multi_channel01_enable = 0; + uint32_t multi_channel23_enable = 0; + uint32_t multi_channel45_enable = 0; + uint32_t multi_channel67_enable = 0; + /* get the one we set.*/ + audio_mapping->u32all = value; + + /* check each enable status*/ + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE); + + multi_channel01_enable = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, + MULTICHANNEL01_ENABLE); + + multi_channel23_enable = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, + MULTICHANNEL23_ENABLE); + + multi_channel45_enable = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, + MULTICHANNEL45_ENABLE); + + multi_channel67_enable = get_reg_field_value(value, + AZALIA_F0_CODEC_PIN_CONTROL_MULTICHANNEL_ENABLE, + MULTICHANNEL67_ENABLE); + + if (multi_channel01_enable == 0 && + multi_channel23_enable == 0 && + multi_channel45_enable == 0 && + multi_channel67_enable == 0) + dal_logger_write(hw_ctx->ctx->logger, + LOG_MAJOR_HW_TRACE, + LOG_MINOR_COMPONENT_AUDIO, + "Audio driver did not enable multi-channel\n"); + + return true; + } + + return false; +} + +/* set the payload value for the unsolicited response */ +static void set_unsolicited_response_payload( + const struct hw_ctx_audio *hw_ctx, + enum audio_payload payload) +{ + /* set the payload value for the unsolicited response + Jack presence is not required to be enabled */ + uint32_t value = 0; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE); + + set_reg_field_value(value, payload, + AZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, + UNSOLICITED_RESPONSE_PAYLOAD); + + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, + UNSOLICITED_RESPONSE_FORCE); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_PIN_CONTROL_UNSOLICITED_RESPONSE_FORCE, + value); +} + +/* initialize HW state */ +static void hw_initialize( + const struct hw_ctx_audio *hw_ctx) +{ + uint32_t stream_id = FROM_BASE(hw_ctx)->azalia_stream_id; + uint32_t addr; + + /* we only need to program the following registers once, so we only do + it for the first audio stream.*/ + if (stream_id != FIRST_AUDIO_STREAM_ID) + return; + + /* Suport R5 - 32khz + * Suport R6 - 44.1khz + * Suport R7 - 48khz + */ + addr = mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES; + { + uint32_t value; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 0x70, + AZALIA_F0_CODEC_FUNCTION_PARAMETER_SUPPORTED_SIZE_RATES, + AUDIO_RATE_CAPABILITIES); + + dm_write_reg(hw_ctx->ctx, addr, value); + } + + /*Keep alive bit to verify HW block in BU. */ + addr = mmAZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES; + { + uint32_t value; + + value = dm_read_reg(hw_ctx->ctx, addr); + + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, + CLKSTOP); + + set_reg_field_value(value, 1, + AZALIA_F0_CODEC_FUNCTION_PARAMETER_POWER_STATES, + EPSS); + dm_write_reg(hw_ctx->ctx, addr, value); + } +} + +/* Assign GTC group and enable GTC value embedding */ +static void enable_gtc_embedding_with_group( + const struct hw_ctx_audio *hw_ctx, + uint32_t group_num, + uint32_t audio_latency) +{ + /*need to replace the static number with variable */ + if (group_num <= 6) { + uint32_t value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING); + + set_reg_field_value( + value, + group_num, + AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + PRESENTATION_TIME_EMBEDDING_GROUP); + + set_reg_field_value( + value, + 1, + AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + PRESENTATION_TIME_EMBEDDING_ENABLE); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + value); + + /*update audio latency to LIPSYNC*/ + set_audio_latency(hw_ctx, audio_latency); + } else { + dal_logger_write( + hw_ctx->ctx->logger, + LOG_MAJOR_HW_TRACE, + LOG_MINOR_COMPONENT_AUDIO, + "GTC group number %d is too big", + group_num); + } +} + + /* Disable GTC value embedding */ +static void disable_gtc_embedding( + const struct hw_ctx_audio *hw_ctx) +{ + uint32_t value = 0; + + value = read_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + PRESENTATION_TIME_EMBEDDING_ENABLE); + + set_reg_field_value(value, 0, + AZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + PRESENTATION_TIME_EMBEDDING_GROUP); + + write_indirect_azalia_reg( + hw_ctx, + ixAZALIA_F0_CODEC_CONVERTER_CONTROL_GTC_EMBEDDING, + value); +} + +/* search pixel clock value for Azalia HDMI Audio */ +static bool get_azalia_clock_info_hdmi( + const struct hw_ctx_audio *hw_ctx, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info) +{ + if (azalia_clock_info == NULL) + return false; + + /* audio_dto_phase= 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = + 24 * 10000; + + /* audio_dto_module = PCLKFrequency * 10,000; + * [khz] -> [100Hz] */ + azalia_clock_info->audio_dto_module = + actual_pixel_clock_in_khz * 10; + + return true; +} + +/* search pixel clock value for Azalia DP Audio */ +static bool get_azalia_clock_info_dp( + const struct hw_ctx_audio *hw_ctx, + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info) +{ + if (pll_info == NULL || azalia_clock_info == NULL) + return false; + + /* Reported dpDtoSourceClockInkhz value for + * DCE8 already adjusted for SS, do not need any + * adjustment here anymore + */ + + /*audio_dto_phase = 24 * 10,000; + * 24MHz in [100Hz] units */ + azalia_clock_info->audio_dto_phase = 24 * 10000; + + /*audio_dto_module = dpDtoSourceClockInkhz * 10,000; + * [khz] ->[100Hz] */ + azalia_clock_info->audio_dto_module = + pll_info->dp_dto_source_clock_in_khz * 10; + + return true; +} + +static const struct hw_ctx_audio_funcs funcs = { + .destroy = destroy, + .setup_audio_wall_dto = + setup_audio_wall_dto, + .setup_hdmi_audio = + setup_hdmi_audio, + .setup_dp_audio = setup_dp_audio, + .setup_vce_audio = setup_vce_audio, + .enable_azalia_audio = + enable_azalia_audio, + .disable_azalia_audio = + disable_azalia_audio, + .enable_dp_audio = + enable_dp_audio, + .disable_dp_audio = + disable_dp_audio, + .setup_azalia = + setup_azalia, + .disable_az_clock_gating = NULL, + .unmute_azalia_audio = + unmute_azalia_audio, + .mute_azalia_audio = + mute_azalia_audio, + .setup_channel_splitting_mapping = + setup_channel_splitting_mapping, + .get_channel_splitting_mapping = + get_channel_splitting_mapping, + .set_unsolicited_response_payload = + set_unsolicited_response_payload, + .hw_initialize = + hw_initialize, + .enable_gtc_embedding_with_group = + enable_gtc_embedding_with_group, + .disable_gtc_embedding = + disable_gtc_embedding, + .get_azalia_clock_info_hdmi = + get_azalia_clock_info_hdmi, + .get_azalia_clock_info_dp = + get_azalia_clock_info_dp, + .enable_afmt_clock = enable_afmt_clock +}; + +static bool construct( + struct hw_ctx_audio_dce110 *hw_ctx, + uint8_t azalia_stream_id, + struct dc_context *ctx) +{ + struct hw_ctx_audio *base = &hw_ctx->base; + + if (!dal_audio_construct_hw_ctx_audio(base)) + return false; + + base->funcs = &funcs; + + /* save audio endpoint or dig front for current dce110 audio object */ + hw_ctx->azalia_stream_id = azalia_stream_id; + hw_ctx->base.ctx = ctx; + + /* azalia audio endpoints register offsets. azalia is associated with + DIG front. save AUDIO register offset */ + switch (azalia_stream_id) { + case 1: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + case 2: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT1_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + case 3: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT2_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + case 4: { + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_index = + mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_INDEX; + hw_ctx->az_mm_reg_offsets. + azf0endpointx_azalia_f0_codec_endpoint_data = + mmAZF0ENDPOINT3_AZALIA_F0_CODEC_ENDPOINT_DATA; + } + break; + default: + dal_logger_write( + hw_ctx->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_AUDIO, + "Invalid Azalia stream ID!"); + break; + } + + return true; +} + +/* audio_dce110 is derived from audio directly, not via dce80 */ +struct hw_ctx_audio *dal_hw_ctx_audio_dce110_create( + struct dc_context *ctx, + uint32_t azalia_stream_id) +{ + /* allocate memory for struc hw_ctx_audio_dce110 */ + struct hw_ctx_audio_dce110 *hw_ctx_dce110 = + dm_alloc(ctx, sizeof(struct hw_ctx_audio_dce110)); + + if (!hw_ctx_dce110) { + ASSERT_CRITICAL(hw_ctx_dce110); + return NULL; + } + + /*return pointer to hw_ctx_audio back to caller -- audio object */ + if (construct( + hw_ctx_dce110, azalia_stream_id, ctx)) + return &hw_ctx_dce110->base; + + dal_logger_write( + ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_AUDIO, + "Failed to create hw_ctx_audio for DCE11\n"); + + + dm_free(ctx, hw_ctx_dce110); + + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h new file mode 100644 index 000000000000..1ad3826aeb9f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/dce110/hw_ctx_audio_dce110.h @@ -0,0 +1,47 @@ +/* + * 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_HW_CTX_AUDIO_DCE110_H__ +#define __DAL_HW_CTX_AUDIO_DCE110_H__ + +#include "audio/hw_ctx_audio.h" + +struct hw_ctx_audio_dce110 { + struct hw_ctx_audio base; + + /* azalia stream id 1 based indexing, corresponding to audio GO enumId*/ + uint32_t azalia_stream_id; + + /* azalia stream endpoint register offsets */ + struct azalia_reg_offsets az_mm_reg_offsets; + + /* audio encoder block MM register offset -- associate with DIG FRONT */ +}; + +struct hw_ctx_audio *dal_hw_ctx_audio_dce110_create( + struct dc_context *ctx, + uint32_t azalia_stream_id); + +#endif /* __DAL_HW_CTX_AUDIO_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c new file mode 100644 index 000000000000..58207f53bff1 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.c @@ -0,0 +1,771 @@ +/* + * 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 "dm_services.h" + +#include "hw_ctx_audio.h" + +/* 25.2MHz/1.001*/ +/* 25.2MHz/1.001*/ +/* 25.2MHz*/ +/* 27MHz */ +/* 27MHz*1.001*/ +/* 27MHz*1.001*/ +/* 54MHz*/ +/* 54MHz*1.001*/ +/* 74.25MHz/1.001*/ +/* 74.25MHz*/ +/* 148.5MHz/1.001*/ +/* 148.5MHz*/ + +static const struct audio_clock_info audio_clock_info_table[12] = { + {2517, 4576, 28125, 7007, 31250, 6864, 28125}, + {2518, 4576, 28125, 7007, 31250, 6864, 28125}, + {2520, 4096, 25200, 6272, 28000, 6144, 25200}, + {2700, 4096, 27000, 6272, 30000, 6144, 27000}, + {2702, 4096, 27027, 6272, 30030, 6144, 27027}, + {2703, 4096, 27027, 6272, 30030, 6144, 27027}, + {5400, 4096, 54000, 6272, 60000, 6144, 54000}, + {5405, 4096, 54054, 6272, 60060, 6144, 54054}, + {7417, 11648, 210937, 17836, 234375, 11648, 140625}, + {7425, 4096, 74250, 6272, 82500, 6144, 74250}, + {14835, 11648, 421875, 8918, 234375, 5824, 140625}, + {14850, 4096, 148500, 6272, 165000, 6144, 148500} +}; + +static const struct audio_clock_info audio_clock_info_table_36bpc[12] = { + {2517, 9152, 84375, 7007, 48875, 9152, 56250}, + {2518, 9152, 84375, 7007, 48875, 9152, 56250}, + {2520, 4096, 37800, 6272, 42000, 6144, 37800}, + {2700, 4096, 40500, 6272, 45000, 6144, 40500}, + {2702, 8192, 81081, 6272, 45045, 8192, 54054}, + {2703, 8192, 81081, 6272, 45045, 8192, 54054}, + {5400, 4096, 81000, 6272, 90000, 6144, 81000}, + {5405, 4096, 81081, 6272, 90090, 6144, 81081}, + {7417, 11648, 316406, 17836, 351562, 11648, 210937}, + {7425, 4096, 111375, 6272, 123750, 6144, 111375}, + {14835, 11648, 632812, 17836, 703125, 11648, 421875}, + {14850, 4096, 222750, 6272, 247500, 6144, 222750} +}; + +static const struct audio_clock_info audio_clock_info_table_48bpc[12] = { + {2517, 4576, 56250, 7007, 62500, 6864, 56250}, + {2518, 4576, 56250, 7007, 62500, 6864, 56250}, + {2520, 4096, 50400, 6272, 56000, 6144, 50400}, + {2700, 4096, 54000, 6272, 60000, 6144, 54000}, + {2702, 4096, 54054, 6267, 60060, 8192, 54054}, + {2703, 4096, 54054, 6272, 60060, 8192, 54054}, + {5400, 4096, 108000, 6272, 120000, 6144, 108000}, + {5405, 4096, 108108, 6272, 120120, 6144, 108108}, + {7417, 11648, 421875, 17836, 468750, 11648, 281250}, + {7425, 4096, 148500, 6272, 165000, 6144, 148500}, + {14835, 11648, 843750, 8918, 468750, 11648, 281250}, + {14850, 4096, 297000, 6272, 330000, 6144, 297000} +}; + + +/***** static function *****/ + +/* + * except of HW context create function, caller will access other functions of + * hw ctx via handle hw_ctx. Memory allocation for struct hw_ctx_audio_dce8x + * will happen in hw_ctx_audio_dce8x. Memory allocation is done with + * dal_audio_create_hw_ctx_audio_dce8x. Memory release is done by caller + * via hw_ctx->functions.destroy(). It will finally use destroy() of + * hw_ctx_audio_dce8x. Therefore, no memory allocate and release happen + * physically at hw ctx base object. + */ +static void destroy( + struct hw_ctx_audio **ptr) +{ + /* Attention! + * You must override this method in derived class */ +} + +static void setup_audio_wall_dto( + const struct hw_ctx_audio *hw_ctx, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* setup HDMI audio */ +static void setup_hdmi_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + const struct audio_crtc_info *crtc_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + + /* setup DP audio */ +static void setup_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + + /* setup VCE audio */ +static void setup_vce_audio( + const struct hw_ctx_audio *hw_ctx) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* enable Azalia audio */ +static void enable_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* disable Azalia audio */ +static void disable_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* enable DP audio */ +static void enable_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* disable DP audio */ +static void disable_dp_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* setup Azalia HW block */ +static void setup_azalia( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info, + const struct audio_info *audio_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* unmute audio */ +static void unmute_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* mute audio */ +static void mute_azalia_audio( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* enable channel splitting mapping */ +static void setup_channel_splitting_mapping( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* get current channel spliting */ +static bool get_channel_splitting_mapping( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return false; +} + +/* set the payload value for the unsolicited response */ +static void set_unsolicited_response_payload( + const struct hw_ctx_audio *hw_ctx, + enum audio_payload payload) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* initialize HW state */ +static void hw_initialize( + const struct hw_ctx_audio *hw_ctx) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* Assign GTC group and enable GTC value embedding */ +static void enable_gtc_embedding_with_group( + const struct hw_ctx_audio *hw_ctx, + uint32_t groupNum, + uint32_t audioLatency) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* Disable GTC value embedding */ +static void disable_gtc_embedding( + const struct hw_ctx_audio *hw_ctx) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* Disable Azalia Clock Gating Feature */ +static void disable_az_clock_gating( + const struct hw_ctx_audio *hw_ctx) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); +} + +/* search pixel clock value for Azalia HDMI Audio */ +static bool get_azalia_clock_info_hdmi( + const struct hw_ctx_audio *hw_ctx, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return false; +} + +/* search pixel clock value for Azalia DP Audio */ +static bool get_azalia_clock_info_dp( + const struct hw_ctx_audio *hw_ctx, + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info) +{ + /*DCE specific, must be implemented in derived*/ + BREAK_TO_DEBUGGER(); + return false; +} + + + + + + + + + +/*****SCOPE : within audio hw context dal-audio-hw-ctx *****/ + + +/* check whether specified sample rates can fit into a given timing */ +void dal_hw_ctx_audio_check_audio_bandwidth( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + enum signal_type signal, + union audio_sample_rates *sample_rates) +{ + switch (signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + dal_audio_hw_ctx_check_audio_bandwidth_hdmi( + hw_ctx, crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + dal_audio_hw_ctx_check_audio_bandwidth_dpsst( + hw_ctx, crtc_info, channel_count, sample_rates); + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + dal_audio_hw_ctx_check_audio_bandwidth_dpmst( + hw_ctx, crtc_info, channel_count, sample_rates); + break; + default: + break; + } +} + +/*For HDMI, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_hdmi( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + uint32_t samples; + uint32_t h_blank; + bool limit_freq_to_48_khz = false; + bool limit_freq_to_88_2_khz = false; + bool limit_freq_to_96_khz = false; + bool limit_freq_to_174_4_khz = false; + + /* For two channels supported return whatever sink support,unmodified*/ + if (channel_count > 2) { + + /* Based on HDMI spec 1.3 Table 7.5 */ + if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced) && + !(crtc_info->pixel_repetition == 2 || + crtc_info->pixel_repetition == 4)) { + limit_freq_to_48_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 27000) && + (crtc_info->v_active <= 576) && + (crtc_info->interlaced) && + (crtc_info->pixel_repetition == 2)) { + limit_freq_to_88_2_khz = true; + + } else if ((crtc_info->requested_pixel_clock <= 54000) && + (crtc_info->v_active <= 576) && + !(crtc_info->interlaced)) { + limit_freq_to_174_4_khz = true; + } + } + + /* Also do some calculation for the available Audio Bandwidth for the + * 8 ch (i.e. for the Layout 1 => ch > 2) + */ + h_blank = crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + h_blank *= crtc_info->pixel_repetition; + + /*based on HDMI spec 1.3 Table 7.5 */ + h_blank -= 58; + /*for Control Period */ + h_blank -= 16; + + samples = h_blank * 10; + /* Number of Audio Packets (multiplied by 10) per Line (for 8 ch number + * of Audio samples per line multiplied by 10 - Layout 1) + */ + samples /= 32; + samples *= crtc_info->v_active; + /*Number of samples multiplied by 10, per second */ + samples *= crtc_info->refresh_rate; + /*Number of Audio samples per second */ + samples /= 10; + + /* @todo do it after deep color is implemented + * 8xx - deep color bandwidth scaling + * Extra bandwidth is avaliable in deep color b/c link runs faster than + * pixel rate. This has the effect of allowing more tmds characters to + * be transmitted during blank + */ + + switch (crtc_info->color_depth) { + case COLOR_DEPTH_888: + samples *= 4; + break; + case COLOR_DEPTH_101010: + samples *= 5; + break; + case COLOR_DEPTH_121212: + samples *= 6; + break; + default: + samples *= 4; + break; + } + + samples /= 4; + + /*check limitation*/ + if (samples < 88200) + limit_freq_to_48_khz = true; + else if (samples < 96000) + limit_freq_to_88_2_khz = true; + else if (samples < 176400) + limit_freq_to_96_khz = true; + else if (samples < 192000) + limit_freq_to_174_4_khz = true; + + if (sample_rates != NULL) { + /* limit frequencies */ + if (limit_freq_to_174_4_khz) + sample_rates->rate.RATE_192 = 0; + + if (limit_freq_to_96_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + } + if (limit_freq_to_88_2_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + } + if (limit_freq_to_48_khz) { + sample_rates->rate.RATE_192 = 0; + sample_rates->rate.RATE_176_4 = 0; + sample_rates->rate.RATE_96 = 0; + sample_rates->rate.RATE_88_2 = 0; + } + } +} + +/*For DP SST, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_dpsst( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +/*For DP MST, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_dpmst( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates) +{ + /* do nothing */ +} + +/* calculate max number of Audio packets per line */ +uint32_t dal_audio_hw_ctx_calc_max_audio_packets_per_line( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info) +{ + uint32_t max_packets_per_line; + + max_packets_per_line = + crtc_info->h_total - crtc_info->h_active; + + if (crtc_info->pixel_repetition) + max_packets_per_line *= crtc_info->pixel_repetition; + + /* for other hdmi features */ + max_packets_per_line -= 58; + /* for Control Period */ + max_packets_per_line -= 16; + /* Number of Audio Packets per Line */ + max_packets_per_line /= 32; + + return max_packets_per_line; +} + +/** +* speakersToChannels +* +* @brief +* translate speakers to channels +* +* FL - Front Left +* FR - Front Right +* RL - Rear Left +* RR - Rear Right +* RC - Rear Center +* FC - Front Center +* FLC - Front Left Center +* FRC - Front Right Center +* RLC - Rear Left Center +* RRC - Rear Right Center +* LFE - Low Freq Effect +* +* FC +* FLC FRC +* FL FR +* +* LFE +* () +* +* +* RL RR +* RLC RRC +* RC +* +* ch 8 7 6 5 4 3 2 1 +* 0b00000011 - - - - - - FR FL +* 0b00000111 - - - - - LFE FR FL +* 0b00001011 - - - - FC - FR FL +* 0b00001111 - - - - FC LFE FR FL +* 0b00010011 - - - RC - - FR FL +* 0b00010111 - - - RC - LFE FR FL +* 0b00011011 - - - RC FC - FR FL +* 0b00011111 - - - RC FC LFE FR FL +* 0b00110011 - - RR RL - - FR FL +* 0b00110111 - - RR RL - LFE FR FL +* 0b00111011 - - RR RL FC - FR FL +* 0b00111111 - - RR RL FC LFE FR FL +* 0b01110011 - RC RR RL - - FR FL +* 0b01110111 - RC RR RL - LFE FR FL +* 0b01111011 - RC RR RL FC - FR FL +* 0b01111111 - RC RR RL FC LFE FR FL +* 0b11110011 RRC RLC RR RL - - FR FL +* 0b11110111 RRC RLC RR RL - LFE FR FL +* 0b11111011 RRC RLC RR RL FC - FR FL +* 0b11111111 RRC RLC RR RL FC LFE FR FL +* 0b11000011 FRC FLC - - - - FR FL +* 0b11000111 FRC FLC - - - LFE FR FL +* 0b11001011 FRC FLC - - FC - FR FL +* 0b11001111 FRC FLC - - FC LFE FR FL +* 0b11010011 FRC FLC - RC - - FR FL +* 0b11010111 FRC FLC - RC - LFE FR FL +* 0b11011011 FRC FLC - RC FC - FR FL +* 0b11011111 FRC FLC - RC FC LFE FR FL +* 0b11110011 FRC FLC RR RL - - FR FL +* 0b11110111 FRC FLC RR RL - LFE FR FL +* 0b11111011 FRC FLC RR RL FC - FR FL +* 0b11111111 FRC FLC RR RL FC LFE FR FL +* +* @param +* speakers - speaker information as it comes from CEA audio block +*/ +/* translate speakers to channels */ +union audio_cea_channels dal_audio_hw_ctx_speakers_to_channels( + const struct hw_ctx_audio *hw_ctx, + struct audio_speaker_flags speaker_flags) +{ + union audio_cea_channels cea_channels = {0}; + + /* these are one to one */ + cea_channels.channels.FL = speaker_flags.FL_FR; + cea_channels.channels.FR = speaker_flags.FL_FR; + cea_channels.channels.LFE = speaker_flags.LFE; + cea_channels.channels.FC = speaker_flags.FC; + + /* if Rear Left and Right exist move RC speaker to channel 7 + * otherwise to channel 5 + */ + if (speaker_flags.RL_RR) { + cea_channels.channels.RL_RC = speaker_flags.RL_RR; + cea_channels.channels.RR = speaker_flags.RL_RR; + cea_channels.channels.RC_RLC_FLC = speaker_flags.RC; + } else { + cea_channels.channels.RL_RC = speaker_flags.RC; + } + + /* FRONT Left Right Center and REAR Left Right Center are exclusive */ + if (speaker_flags.FLC_FRC) { + cea_channels.channels.RC_RLC_FLC = speaker_flags.FLC_FRC; + cea_channels.channels.RRC_FRC = speaker_flags.FLC_FRC; + } else { + cea_channels.channels.RC_RLC_FLC = speaker_flags.RLC_RRC; + cea_channels.channels.RRC_FRC = speaker_flags.RLC_RRC; + } + + return cea_channels; +} + +/* check whether specified audio format supported */ +bool dal_audio_hw_ctx_is_audio_format_supported( + const struct hw_ctx_audio *hw_ctx, + const struct audio_info *audio_info, + enum audio_format_code audio_format_code, + uint32_t *format_index) +{ + uint32_t index; + uint32_t max_channe_index = 0; + bool found = false; + + if (audio_info == NULL) + return found; + + /* pass through whole array */ + for (index = 0; index < audio_info->mode_count; index++) { + if (audio_info->modes[index].format_code == audio_format_code) { + if (found) { + /* format has multiply entries, choose one with + * highst number of channels */ + if (audio_info->modes[index].channel_count > + audio_info->modes[max_channe_index].channel_count) { + max_channe_index = index; + } + } else { + /* format found, save it's index */ + found = true; + max_channe_index = index; + } + } + } + + /* return index */ + if (found && format_index != NULL) + *format_index = max_channe_index; + + return found; +} + +/* search pixel clock value for HDMI */ +bool dal_audio_hw_ctx_get_audio_clock_info( + const struct hw_ctx_audio *hw_ctx, + enum dc_color_depth color_depth, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct audio_clock_info *audio_clock_info) +{ + const struct audio_clock_info *clock_info; + uint32_t index; + uint32_t crtc_pixel_clock_in_10khz = crtc_pixel_clock_in_khz / 10; + uint32_t audio_array_size; + + if (audio_clock_info == NULL) + return false; /* should not happen */ + + switch (color_depth) { + case COLOR_DEPTH_161616: + clock_info = audio_clock_info_table_48bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_48bpc); + break; + case COLOR_DEPTH_121212: + clock_info = audio_clock_info_table_36bpc; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table_36bpc); + break; + default: + clock_info = audio_clock_info_table; + audio_array_size = ARRAY_SIZE( + audio_clock_info_table); + break; + } + + if (clock_info != NULL) { + /* search for exact pixel clock in table */ + for (index = 0; index < audio_array_size; index++) { + if (clock_info[index].pixel_clock_in_10khz > + crtc_pixel_clock_in_10khz) + break; /* not match */ + else if (clock_info[index].pixel_clock_in_10khz == + crtc_pixel_clock_in_10khz) { + /* match found */ + if (audio_clock_info != NULL) { + *audio_clock_info = clock_info[index]; + return true; + } + } + } + } + + + /* not found */ + if (actual_pixel_clock_in_khz == 0) + actual_pixel_clock_in_khz = crtc_pixel_clock_in_khz; + + /* See HDMI spec the table entry under + * pixel clock of "Other". */ + audio_clock_info->pixel_clock_in_10khz = + actual_pixel_clock_in_khz / 10; + audio_clock_info->cts_32khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_44khz = actual_pixel_clock_in_khz; + audio_clock_info->cts_48khz = actual_pixel_clock_in_khz; + + audio_clock_info->n_32khz = 4096; + audio_clock_info->n_44khz = 6272; + audio_clock_info->n_48khz = 6144; + + return true; +} + +static const struct hw_ctx_audio_funcs funcs = { + .destroy = destroy, + .setup_audio_wall_dto = + setup_audio_wall_dto, + .setup_hdmi_audio = + setup_hdmi_audio, + .setup_dp_audio = setup_dp_audio, + .setup_vce_audio = setup_vce_audio, + .enable_azalia_audio = + enable_azalia_audio, + .disable_azalia_audio = + disable_azalia_audio, + .enable_dp_audio = + enable_dp_audio, + .disable_dp_audio = + disable_dp_audio, + .setup_azalia = + setup_azalia, + .disable_az_clock_gating = + disable_az_clock_gating, + .unmute_azalia_audio = + unmute_azalia_audio, + .mute_azalia_audio = + mute_azalia_audio, + .setup_channel_splitting_mapping = + setup_channel_splitting_mapping, + .get_channel_splitting_mapping = + get_channel_splitting_mapping, + .set_unsolicited_response_payload = + set_unsolicited_response_payload, + .hw_initialize = + hw_initialize, + .enable_gtc_embedding_with_group = + enable_gtc_embedding_with_group, + .disable_gtc_embedding = + disable_gtc_embedding, + .get_azalia_clock_info_hdmi = + get_azalia_clock_info_hdmi, + .get_azalia_clock_info_dp = + get_azalia_clock_info_dp, +}; +/* --- object creator, destroy, construct, destruct --- */ + +bool dal_audio_construct_hw_ctx_audio( + struct hw_ctx_audio *ctx) +{ + ctx->funcs = &funcs; + + /* internal variables */ + + return true; +} + +void dal_audio_destruct_hw_ctx_audio( + struct hw_ctx_audio *ctx) +{ + /* nothing to do */ +} diff --git a/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h new file mode 100644 index 000000000000..8ab2e5851f91 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/audio/hw_ctx_audio.h @@ -0,0 +1,285 @@ +/* + * 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_HW_CTX_AUDIO_H__ +#define __DAL_HW_CTX_AUDIO_H__ + +#include "include/audio_interface.h" +#include "include/link_service_types.h" + +struct hw_ctx_audio; + + +struct azalia_reg_offsets { + uint32_t azf0endpointx_azalia_f0_codec_endpoint_index; + uint32_t azf0endpointx_azalia_f0_codec_endpoint_data; +}; + +/***** hook functions *****/ + +struct hw_ctx_audio_funcs { + + /* functions for hw_ctx creation */ + void (*destroy)( + struct hw_ctx_audio **ptr); + + /***** from dal2 hwcontextaudio.hpp *****/ + + void (*setup_audio_wall_dto)( + const struct hw_ctx_audio *hw_ctx, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info); + + /* MM register access read_register write_register */ + + /***** from dal2 hwcontextaudio_hal.hpp *****/ + + /* setup HDMI audio */ + void (*setup_hdmi_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + const struct audio_crtc_info *crtc_info); + + /* setup DP audio */ + void (*setup_dp_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* setup VCE audio */ + void (*setup_vce_audio)( + const struct hw_ctx_audio *hw_ctx); + + /* enable Azalia audio */ + void (*enable_azalia_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* disable Azalia audio */ + void (*disable_azalia_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* enable DP audio */ + void (*enable_dp_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* disable DP audio */ + void (*disable_dp_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* setup Azalia HW block */ + void (*setup_azalia)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_crtc_info *crtc_info, + const struct audio_pll_info *pll_info, + const struct audio_info *audio_info); + + /* unmute audio */ + void (*unmute_azalia_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* mute audio */ + void (*mute_azalia_audio)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id); + + /* enable channel splitting mapping */ + void (*setup_channel_splitting_mapping)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + enum signal_type signal, + const struct audio_channel_associate_info *audio_mapping, + bool enable); + + /* get current channel spliting */ + bool (*get_channel_splitting_mapping)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + struct audio_channel_associate_info *audio_mapping); + + /* set the payload value for the unsolicited response */ + void (*set_unsolicited_response_payload)( + const struct hw_ctx_audio *hw_ctx, + enum audio_payload payload); + + /* initialize HW state */ + void (*hw_initialize)( + const struct hw_ctx_audio *hw_ctx); + + /* check_audio_bandwidth */ + + /* Assign GTC group and enable GTC value embedding */ + void (*enable_gtc_embedding_with_group)( + const struct hw_ctx_audio *hw_ctx, + uint32_t groupNum, + uint32_t audioLatency); + + /* Disable GTC value embedding */ + void (*disable_gtc_embedding)( + const struct hw_ctx_audio *hw_ctx); + + /* Disable Azalia Clock Gating Feature */ + void (*disable_az_clock_gating)( + const struct hw_ctx_audio *hw_ctx); + + /* ~~~~ protected: ~~~~*/ + + /* calc_max_audio_packets_per_line */ + /* speakers_to_channels */ + /* is_audio_format_supported */ + /* get_audio_clock_info */ + + /* search pixel clock value for Azalia HDMI Audio */ + bool (*get_azalia_clock_info_hdmi)( + const struct hw_ctx_audio *hw_ctx, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct azalia_clock_info *azalia_clock_info); + + /* search pixel clock value for Azalia DP Audio */ + bool (*get_azalia_clock_info_dp)( + const struct hw_ctx_audio *hw_ctx, + uint32_t requested_pixel_clock_in_khz, + const struct audio_pll_info *pll_info, + struct azalia_clock_info *azalia_clock_info); + + void (*enable_afmt_clock)( + const struct hw_ctx_audio *hw_ctx, + enum engine_id engine_id, + bool enable); + + /* @@@@ private: @@@@ */ + + /* check_audio_bandwidth_hdmi */ + /* check_audio_bandwidth_dpsst */ + /* check_audio_bandwidth_dpmst */ + +}; + + +struct hw_ctx_audio { + const struct hw_ctx_audio_funcs *funcs; + struct dc_context *ctx; + + /*audio_clock_infoTable[12]; + *audio_clock_infoTable_36bpc[12]; + *audio_clock_infoTable_48bpc[12]; + *used by hw_ctx_audio.c file only. Will declare as static array + *azaliaclockinfoTable[12] -- not used + *BusNumberMask; BusNumberShift; DeviceNumberMask; + *not used by dce6 and after + */ +}; + + + +/* --- object construct, destruct --- */ + +/* + *called by derived audio object for specific ASIC. In case no derived object, + *these two functions do not need exposed. + */ +bool dal_audio_construct_hw_ctx_audio( + struct hw_ctx_audio *hw_ctx); + +void dal_audio_destruct_hw_ctx_audio( + struct hw_ctx_audio *hw_ctx); + +/* + *creator of audio HW context will be implemented by specific ASIC object only. + *Top base or interface object does not have implementation of creator. + */ + + +/* --- functions called by audio hw context itself --- */ + +/* MM register access */ +/*read_register - dal_read_reg */ +/*write_register - dal_write_reg*/ + + +/*check whether specified sample rates can fit into a given timing */ +void dal_hw_ctx_audio_check_audio_bandwidth( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + enum signal_type signal, + union audio_sample_rates *sample_rates); + +/*For HDMI, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_hdmi( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates); + +/*For DPSST, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_dpsst( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates); + +/*For DPMST, calculate if specified sample rates can fit into a given timing */ +void dal_audio_hw_ctx_check_audio_bandwidth_dpmst( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info, + uint32_t channel_count, + union audio_sample_rates *sample_rates); + +/* calculate max number of Audio packets per line */ +uint32_t dal_audio_hw_ctx_calc_max_audio_packets_per_line( + const struct hw_ctx_audio *hw_ctx, + const struct audio_crtc_info *crtc_info); + +/* translate speakers to channels */ +union audio_cea_channels dal_audio_hw_ctx_speakers_to_channels( + const struct hw_ctx_audio *hw_ctx, + struct audio_speaker_flags speaker_flags); + +/* check whether specified audio format supported */ +bool dal_audio_hw_ctx_is_audio_format_supported( + const struct hw_ctx_audio *hw_ctx, + const struct audio_info *audio_info, + enum audio_format_code audio_format_code, + uint32_t *format_index); + +/* search pixel clock value for HDMI */ +bool dal_audio_hw_ctx_get_audio_clock_info( + const struct hw_ctx_audio *hw_ctx, + enum dc_color_depth color_depth, + uint32_t crtc_pixel_clock_in_khz, + uint32_t actual_pixel_clock_in_khz, + struct audio_clock_info *audio_clock_info); + + +#endif /* __DAL_HW_CTX_AUDIO_H__ */ + -- 2.1.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel