| /****************************************************************************** |
| * |
| * Copyright (C) 2016-2017 Cadence Design Systems, Inc. |
| * All rights reserved worldwide. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * 3. Neither the name of the copyright holder nor the names of its contributors |
| * may be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * 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 AUTHORS OR COPYRIGHT HOLDERS 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. |
| * |
| * Copyright 2017-2018 NXP |
| * |
| ****************************************************************************** |
| * |
| * API_HDMITX.c |
| * |
| ****************************************************************************** |
| */ |
| |
| #include "API_HDMITX.h" |
| #include "util.h" |
| #include "opcodes.h" |
| #include "mhl_hdtx_top.h" |
| #include "source_phy.h" |
| #include "address.h" |
| #include "source_car.h" |
| #include "source_vif.h" |
| #include "general_handler.h" |
| #include <soc/imx8/soc.h> |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_READ(state_struct *state, |
| HDMITX_TRANS_DATA *data_in, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_macro_command_txrx(state, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ, |
| CDN_BUS_TYPE_APB, 3, 1, data_in->slave, 1, |
| data_in->offset, 2, data_in->len); |
| internal_readmsg(state, 5, 1, &data_out->status, 1, &data_out->slave, 1, |
| &data_out->offset, 2, &data_out->len, 0, |
| &data_out->buff); |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_READ_blocking(state_struct *state, |
| HDMITX_TRANS_DATA *data_in, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_DDC_READ |
| (state, data_in, data_out)); |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_WRITE(state_struct *state, |
| HDMITX_TRANS_DATA *data_in, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_macro_command_txrx(state, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE, |
| CDN_BUS_TYPE_APB, 4, 1, data_in->slave, 1, |
| data_in->offset, 2, data_in->len, |
| -data_in->len, data_in->buff); |
| internal_readmsg(state, 4, 1, &data_out->status, 1, &data_out->slave, 1, |
| &data_out->offset, 2, &data_out->len); |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_WRITE_blocking(state_struct *state, |
| HDMITX_TRANS_DATA *data_in, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_DDC_WRITE |
| (state, data_in, data_out)); |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_UPDATE_READ(state_struct *state, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_macro_command_txrx(state, MB_MODULE_ID_HDMI_TX, |
| HDMI_TX_UPDATE_READ, CDN_BUS_TYPE_APB, 0); |
| internal_readmsg(state, 2, 1, &data_out->status, 0, &data_out->buff); |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_DDC_UPDATE_READ_blocking(state_struct *state, |
| HDMITX_TRANS_DATA * |
| data_out) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_DDC_UPDATE_READ |
| (state, data_out)); |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_READ_EDID(state_struct *state, u8 block, |
| u8 segment, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_macro_command_txrx(state, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID, |
| CDN_BUS_TYPE_APB, 2, 1, block, 1, segment); |
| internal_readmsg(state, 5, 1, &data_out->status, 1, &data_out->slave, 1, |
| &data_out->offset, 2, &data_out->len, 0, |
| &data_out->buff); |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_READ_EDID_blocking(state_struct *state, u8 block, |
| u8 segment, |
| HDMITX_TRANS_DATA *data_out) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_READ_EDID |
| (state, block, segment, data_out)); |
| } |
| |
| CDN_API_STATUS |
| CDN_API_HDMITX_Set_Mode_blocking(state_struct *state, |
| HDMI_TX_MAIL_HANDLER_PROTOCOL_TYPE protocol, |
| u32 character_rate) |
| { |
| CDN_API_STATUS ret; |
| GENERAL_Read_Register_response resp; |
| u32 clk_reg_0, clk_reg_1; |
| |
| ret = CDN_API_General_Read_Register_blocking( |
| state, ADDR_SOURCE_MHL_HD + (HDTX_CONTROLLER << 2), &resp); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* remove data enable */ |
| resp.val = resp.val & (~(F_DATA_EN(1))); |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| if (ret != CDN_OK) |
| return ret; |
| clk_reg_0 = 0x7c1f; |
| clk_reg_1 = 0x7c1f; |
| if (protocol == HDMI_TX_MODE_HDMI_2_0) { |
| if (character_rate >= 340000) { |
| clk_reg_0 = 0; |
| clk_reg_1 = 0xFFFFF; |
| } |
| } |
| ret = CDN_API_General_Write_Register_blocking( |
| state, ADDR_SOURCE_MHL_HD + (HDTX_CLOCK_REG_0 << 2), |
| F_DATA_REGISTER_VAL_0(clk_reg_0)); |
| if (ret != CDN_OK) |
| return ret; |
| ret = CDN_API_General_Write_Register_blocking( |
| state, ADDR_SOURCE_MHL_HD + (HDTX_CLOCK_REG_1 << 2), |
| F_DATA_REGISTER_VAL_1(clk_reg_1)); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* set hdmi mode and preemble mode */ |
| resp.val = resp.val & (~(F_HDMI_MODE(3))); |
| resp.val = resp.val & (~(F_HDMI2_PREAMBLE_EN(1))); |
| |
| resp.val = |
| (resp.val) | (F_HDMI_MODE(protocol)) | (F_HDMI2_PREAMBLE_EN(1)); |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* data enable */ |
| resp.val |= F_DATA_EN(1); |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| return ret; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_Init_blocking(state_struct *state) |
| { |
| CDN_API_STATUS ret; |
| |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCD_PHY + |
| (PHY_DATA_SEL << 2), |
| F_SOURCE_PHY_MHDP_SEL(1)); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_HPD << 2), |
| F_HPD_VALID_WIDTH(4) | |
| F_HPD_GLITCH_WIDTH(0)); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| F_HDMI_MODE(1) | |
| F_AUTO_MODE(0) | F_GCP_EN(1) |
| | F_DATA_EN(1) | |
| F_CLEAR_AVMUTE(1) | |
| F_HDMI2_PREAMBLE_EN(1) | |
| F_HDMI2_CTRL_IL_MODE(1) | |
| F_PIC_3D(0XF) | |
| F_BCH_EN(1)); |
| if (ret != CDN_OK) |
| return ret; |
| /* open CARS */ |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_PHY_CAR << 2), 0xF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_HDTX_CAR << 2), |
| 0xFF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_PKT_CAR << 2), 0xF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_AIF_CAR << 2), 0xF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_CIPHER_CAR << 2), |
| 0xF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_CRYPTO_CAR << 2), |
| 0xF); |
| if (ret != CDN_OK) |
| return ret; |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_CAR + |
| (SOURCE_CEC_CAR << 2), 3); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* init vif */ |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_VIF + |
| (HSYNC2VSYNC_POL_CTRL << 2), |
| F_HPOL(0) | F_VPOL(0)); |
| return ret; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_SetVic_blocking(state_struct *state, |
| struct drm_display_mode *mode, int bpp, |
| VIC_PXL_ENCODING_FORMAT format) |
| { |
| CDN_API_STATUS ret; |
| GENERAL_Read_Register_response resp; |
| u32 vsync_lines = mode->vsync_end - mode->vsync_start; |
| u32 eof_lines = mode->vsync_start - mode->vdisplay; |
| u32 sof_lines = mode->vtotal - mode->vsync_end; |
| u32 hblank = mode->htotal - mode->hdisplay; |
| u32 hactive = mode->hdisplay; |
| u32 vblank = mode->vtotal - mode->vdisplay; |
| u32 vactive = mode->vdisplay; |
| u32 hfront = mode->hsync_start - mode->hdisplay; |
| u32 hback = mode->htotal - mode->hsync_end; |
| u32 vfront = eof_lines; |
| u32 hsync = hblank - hfront - hback; |
| u32 vsync = vsync_lines; |
| u32 vback = sof_lines; |
| u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) + |
| ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2); |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (SCHEDULER_H_SIZE << 2), |
| (hactive << 16) + hblank); |
| if (ret != CDN_OK) |
| return ret; |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (SCHEDULER_V_SIZE << 2), |
| (vactive << 16) + vblank); |
| if (ret != CDN_OK) |
| return ret; |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_SIGNAL_FRONT_WIDTH << |
| 2), |
| (vfront << 16) + hfront); |
| if (ret != CDN_OK) |
| return ret; |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_SIGNAL_SYNC_WIDTH << |
| 2), (vsync << 16) + hsync); |
| if (ret != CDN_OK) |
| return ret; |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_SIGNAL_BACK_WIDTH << |
| 2), (vback << 16) + hback); |
| if (ret != CDN_OK) |
| return ret; |
| |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_VIF + |
| (HSYNC2VSYNC_POL_CTRL << 2), |
| v_h_polarity); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* Reset Data Enable */ |
| ret = CDN_API_General_Read_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), &resp); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* reset data enable */ |
| resp.val = resp.val & (~(F_DATA_EN(1))); |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* set bpp */ |
| resp.val = resp.val & (~(F_VIF_DATA_WIDTH(3))); |
| switch (bpp) { |
| case 8: |
| resp.val = resp.val | (F_VIF_DATA_WIDTH(0)); |
| break; |
| |
| case 10: |
| resp.val = resp.val | (F_VIF_DATA_WIDTH(1)); |
| break; |
| |
| case 12: |
| resp.val = resp.val | (F_VIF_DATA_WIDTH(2)); |
| break; |
| |
| case 16: |
| resp.val = resp.val | (F_VIF_DATA_WIDTH(3)); |
| break; |
| } |
| |
| /* select color encoding */ |
| resp.val = resp.val & (~(F_HDMI_ENCODING(3))); |
| switch (format) { |
| case PXL_RGB: |
| |
| resp.val = resp.val | (F_HDMI_ENCODING(0)); |
| break; |
| |
| case YCBCR_4_4_4: |
| resp.val = resp.val | (F_HDMI_ENCODING(2)); |
| break; |
| |
| case YCBCR_4_2_2: |
| resp.val = resp.val | (F_HDMI_ENCODING(1)); |
| break; |
| |
| case YCBCR_4_2_0: |
| resp.val = resp.val | (F_HDMI_ENCODING(3)); |
| break; |
| case Y_ONLY: |
| /* not exist in hdmi */ |
| break; |
| } |
| |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| if (ret != CDN_OK) |
| return ret; |
| |
| /* set data enable */ |
| resp.val = resp.val | (F_DATA_EN(1)); |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCE_MHL_HD + |
| (HDTX_CONTROLLER << 2), |
| resp.val); |
| if (ret != CDN_OK) |
| return ret; |
| return ret; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_Disable_GCP(state_struct *state) |
| { |
| GENERAL_Read_Register_response resp; |
| |
| CDN_API_General_Read_Register_blocking(state, ADDR_SOURCE_MHL_HD +(HDTX_CONTROLLER<<2), &resp); |
| resp.val = resp.val & (~F_GCP_EN(1)); |
| return CDN_API_General_Write_Register_blocking(state, |
| ADDR_SOURCE_MHL_HD +(HDTX_CONTROLLER<<2), resp.val); |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_ForceColorDepth_blocking(state_struct *state, |
| u8 force, u8 val) |
| { |
| u32 valToWrite = F_COLOR_DEPTH_VAL(val) | F_COLOR_DEPTH_FORCE(force); |
| return CDN_API_General_Write_Register_blocking(state, |
| ADDR_SOURCE_MHL_HD + |
| (GCP_FORCE_COLOR_DEPTH_CODING |
| << 2), valToWrite); |
| |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_ReadEvents(state_struct *state, |
| uint32_t *events) |
| { |
| CDN_API_STATUS ret; |
| |
| if (!state->running) { |
| if (!internal_apb_available(state)) |
| return CDN_BSY; |
| |
| internal_tx_mkfullmsg(state, MB_MODULE_ID_HDMI_TX, |
| HDMI_TX_EVENTS, 0); |
| state->rxEnable = 1; |
| state->bus_type = CDN_BUS_TYPE_APB; |
| |
| return CDN_STARTED; |
| } |
| |
| internal_process_messages(state); |
| |
| ret = |
| internal_test_rx_head(state, MB_MODULE_ID_HDMI_TX, HDMI_TX_EVENTS); |
| |
| if (ret != CDN_OK) |
| return ret; |
| |
| internal_readmsg(state, 1, 4, events); |
| |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_ReadEvents_blocking(state_struct *state, |
| uint32_t *events) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_ReadEvents(state, events)); |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_GetHpdStatus(state_struct *state, u8 *hpd_sts) |
| { |
| CDN_API_STATUS ret; |
| if (!state->running) { |
| if (!internal_apb_available(state)) |
| return CDN_BSY; |
| internal_tx_mkfullmsg(state, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE, 0); |
| state->rxEnable = 1; |
| state->bus_type = CDN_BUS_TYPE_APB; |
| return CDN_STARTED; |
| } |
| internal_process_messages(state); |
| ret = |
| internal_test_rx_head(state, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE); |
| if (ret != CDN_OK) |
| return ret; |
| internal_readmsg(state, 1, 1, hpd_sts); |
| return CDN_OK; |
| } |
| |
| CDN_API_STATUS CDN_API_HDMITX_GetHpdStatus_blocking(state_struct *state, |
| u8 *hpd_sts) |
| { |
| internal_block_function(&state->mutex, CDN_API_HDMITX_GetHpdStatus(state, hpd_sts)); |
| } |