| /* |
| * Copyright 2017-2018 NXP |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/clk.h> |
| #ifdef DEBUG_FW_LOAD |
| #include "hdmitx_firmware.h" |
| #endif |
| #include "imx-hdp.h" |
| #include "imx-hdmi.h" |
| #include "API_AFE_ss28fdsoi_kiran_hdmitx.h" |
| #include "API_AFE_t28hpc_hdmitx.h" |
| |
| static int character_freq_khz; |
| #ifdef DEBUG_FW_LOAD |
| void hdmi_fw_load(state_struct *state) |
| { |
| DRM_INFO("loading hdmi firmware\n"); |
| CDN_API_LoadFirmware(state, |
| (u8 *)hdmitx_iram0_get_ptr(), |
| hdmitx_iram0_get_size(), |
| (u8 *)hdmitx_dram0_get_ptr(), |
| hdmitx_dram0_get_size()); |
| } |
| #endif |
| int hdmi_fw_init(state_struct *state) |
| { |
| u8 echo_msg[] = "echo test"; |
| u8 echo_resp[sizeof(echo_msg) + 1]; |
| struct imx_hdp *hdp = state_to_imx_hdp(state); |
| u32 core_rate; |
| int ret; |
| u8 sts; |
| |
| core_rate = clk_get_rate(hdp->clks.clk_core); |
| |
| /* configure the clock */ |
| CDN_API_SetClock(state, core_rate/1000000); |
| pr_info("CDN_API_SetClock completed\n"); |
| |
| /* moved from CDN_API_LoadFirmware */ |
| cdn_apb_write(state, APB_CTRL << 2, 0); |
| DRM_INFO("Started firmware!\n"); |
| |
| ret = CDN_API_CheckAlive_blocking(state); |
| if (ret != 0) { |
| DRM_ERROR("CDN_API_CheckAlive failed - check firmware!\n"); |
| return -ENXIO; |
| } else |
| DRM_INFO("CDN_API_CheckAlive returned ret = %d\n", ret); |
| |
| /* turn on IP activity */ |
| ret = CDN_API_MainControl_blocking(state, 1, &sts); |
| DRM_INFO("CDN_API_MainControl_blocking ret = %d sts = %u\n", ret, sts); |
| |
| ret = CDN_API_General_Test_Echo_Ext_blocking(state, echo_msg, echo_resp, |
| sizeof(echo_msg), CDN_BUS_TYPE_APB); |
| |
| if (0 != strncmp(echo_msg, echo_resp, sizeof(echo_msg))) { |
| DRM_ERROR("CDN_API_General_Test_Echo_Ext_blocking - echo test failed, check firmware!"); |
| return -ENXIO; |
| } |
| DRM_INFO("CDN_API_General_Test_Echo_Ext_blocking - APB(ret = %d echo_resp = %s)\n", |
| ret, echo_resp); |
| |
| return 0; |
| } |
| |
| int hdmi_phy_init(state_struct *state, struct drm_display_mode *mode, int format, int color_depth) |
| { |
| struct imx_hdp *hdp = state_to_imx_hdp(state); |
| int ret; |
| |
| /* reset phy */ |
| imx_hdp_call(hdp, phy_reset, hdp->ipcHndl, 0); |
| |
| /* Configure PHY */ |
| character_freq_khz = phy_cfg_hdp_ss28fdsoi(state, 4, mode, color_depth, format); |
| if (character_freq_khz == 0) { |
| DRM_ERROR("failed to set phy pclock\n"); |
| return -EINVAL; |
| } |
| |
| imx_hdp_call(hdp, phy_reset, hdp->ipcHndl, 1); |
| |
| hdmi_tx_kiran_power_configuration_seq(state, 4); |
| |
| /* Set the lane swapping */ |
| ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCD_PHY + (LANES_CONFIG << 2), |
| F_SOURCE_PHY_LANE0_SWAP(3) | F_SOURCE_PHY_LANE1_SWAP(0) | |
| F_SOURCE_PHY_LANE2_SWAP(1) | F_SOURCE_PHY_LANE3_SWAP(2) | |
| F_SOURCE_PHY_COMB_BYPASS(0) | F_SOURCE_PHY_20_10(1)); |
| DRM_INFO("CDN_API_General_Write_Register_blocking LANES_CONFIG ret = %d\n", ret); |
| |
| return true; |
| } |
| |
| void hdmi_mode_set(state_struct *state, struct drm_display_mode *mode, int format, int color_depth, int temp) |
| { |
| int ret; |
| |
| /* Mode = 0 - DVI, 1 - HDMI1.4, 2 HDMI 2.0 */ |
| HDMI_TX_MAIL_HANDLER_PROTOCOL_TYPE ptype = 1; |
| |
| if (drm_match_cea_mode(mode) == VIC_MODE_97_60Hz) |
| ptype = 2; |
| |
| ret = CDN_API_HDMITX_Init_blocking(state); |
| if (ret != CDN_OK) { |
| DRM_INFO("CDN_API_STATUS CDN_API_HDMITX_Init_blocking ret = %d\n", ret); |
| return; |
| } |
| |
| /* Set HDMI TX Mode */ |
| ret = CDN_API_HDMITX_Set_Mode_blocking(state, ptype, character_freq_khz); |
| if (ret != CDN_OK) { |
| DRM_INFO("CDN_API_HDMITX_Set_Mode_blocking ret = %d\n", ret); |
| return; |
| } |
| |
| ret = CDN_API_Set_AVI(state, mode, format, 0, 0); |
| if (ret != CDN_OK) { |
| DRM_INFO("CDN_API_Set_AVI ret = %d\n", ret); |
| return; |
| } |
| |
| ret = CDN_API_HDMITX_SetVic_blocking(state, mode, color_depth, format); |
| if (ret != CDN_OK) { |
| DRM_INFO("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); |
| return; |
| } |
| |
| msleep(50); |
| } |
| |
| int hdmi_phy_init_t28hpc(state_struct *state, struct drm_display_mode *mode, int format, int color_depth) |
| { |
| int ret; |
| /* 0- pixel clock from phy */ |
| u32 pixel_clk_from_phy = 1; |
| char echo_msg[] = "echo test"; |
| char echo_resp[sizeof(echo_msg) + 1]; |
| |
| /* Parameterization done */ |
| |
| ret = CDN_API_CheckAlive_blocking(state); |
| if (ret != 0) { |
| DRM_ERROR("NO HDMI FW running\n"); |
| return -ENXIO; |
| } |
| |
| ret = CDN_API_General_Test_Echo_Ext_blocking(state, echo_msg, echo_resp, |
| sizeof(echo_msg), |
| CDN_BUS_TYPE_APB); |
| if (ret != 0) { |
| DRM_ERROR("HDMI mailbox access failed\n"); |
| return -ENXIO; |
| } |
| |
| /* Configure PHY */ |
| character_freq_khz = |
| phy_cfg_t28hpc(state, 4, mode, color_depth, format, pixel_clk_from_phy); |
| if (character_freq_khz == 0) { |
| DRM_ERROR("failed to set phy pclock\n"); |
| return -EINVAL; |
| } |
| |
| hdmi_tx_t28hpc_power_config_seq(state, 4); |
| |
| /* Set the lane swapping */ |
| ret = |
| CDN_API_General_Write_Register_blocking(state, ADDR_SOURCD_PHY + |
| (LANES_CONFIG << 2), |
| F_SOURCE_PHY_LANE0_SWAP(0) | |
| F_SOURCE_PHY_LANE1_SWAP(1) | |
| F_SOURCE_PHY_LANE2_SWAP(2) | |
| F_SOURCE_PHY_LANE3_SWAP(3) | |
| F_SOURCE_PHY_COMB_BYPASS(0) |
| | F_SOURCE_PHY_20_10(1)); |
| DRM_INFO |
| ("CDN_API_General_Write_Register_blocking LANES_CONFIG ret = %d\n", |
| ret); |
| |
| return true; |
| } |
| |
| #define RGB_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB)) |
| #define YCC_ALLOWED_COLORIMETRY (BIT(HDMI_EXTENDED_COLORIMETRY_BT2020) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709) |\ |
| BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601)) |
| |
| static void hdmi_compute_colorimetry(struct drm_display_info *di, |
| int format, |
| enum hdmi_colorimetry *colorimetry, |
| enum hdmi_extended_colorimetry *ext_colorimetry) |
| { |
| u32 sink_colorimetry; |
| u32 allowed_colorimetry; |
| |
| allowed_colorimetry = format == PXL_RGB ? RGB_ALLOWED_COLORIMETRY : |
| YCC_ALLOWED_COLORIMETRY; |
| |
| sink_colorimetry = di->hdmi.colorimetry & allowed_colorimetry; |
| |
| if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_709)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_S_YCC_601)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_S_YCC_601; |
| else if (sink_colorimetry & BIT(HDMI_EXTENDED_COLORIMETRY_XV_YCC_601)) |
| *ext_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; |
| else |
| *ext_colorimetry = 0; |
| |
| *colorimetry = sink_colorimetry ? HDMI_COLORIMETRY_EXTENDED : |
| HDMI_COLORIMETRY_NONE; |
| } |
| |
| void hdmi_mode_set_t28hpc(state_struct *state, struct drm_display_mode *mode, int format, int color_depth, int temp) |
| { |
| struct imx_hdp *hdp = container_of(state, struct imx_hdp, state); |
| int ret; |
| enum hdmi_colorimetry colorimetry; |
| enum hdmi_extended_colorimetry ext_colorimetry; |
| |
| /* Set HDMI TX Mode */ |
| /* Mode = 0 - DVI, 1 - HDMI1.4, 2 HDMI 2.0 */ |
| HDMI_TX_MAIL_HANDLER_PROTOCOL_TYPE ptype = 1; |
| |
| if (drm_match_cea_mode(mode) == VIC_MODE_97_60Hz) |
| ptype = 2; |
| |
| ret = CDN_API_HDMITX_Init_blocking(state); |
| if (ret != CDN_OK) { |
| DRM_ERROR("CDN_API_STATUS CDN_API_HDMITX_Init_blocking ret = %d\n", ret); |
| return; |
| } |
| |
| /* Set HDMI TX Mode */ |
| ret = CDN_API_HDMITX_Set_Mode_blocking(state, ptype, character_freq_khz); |
| if (ret != CDN_OK) { |
| DRM_ERROR("CDN_API_HDMITX_Set_Mode_blocking ret = %d\n", ret); |
| return; |
| } |
| |
| hdmi_compute_colorimetry(&hdp->connector.display_info, format, |
| &colorimetry, &ext_colorimetry); |
| |
| ret = CDN_API_Set_AVI(state, mode, format, colorimetry, ext_colorimetry); |
| if (ret != CDN_OK) { |
| DRM_ERROR("CDN_API_Set_AVI ret = %d\n", ret); |
| return; |
| } |
| |
| ret = CDN_API_HDMITX_SetVic_blocking(state, mode, color_depth, format); |
| if (ret != CDN_OK) { |
| DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); |
| return; |
| } |
| } |
| |
| int hdmi_get_edid_block(void *data, u8 *buf, u32 block, size_t len) |
| { |
| HDMITX_TRANS_DATA edidResp; |
| state_struct *state = data; |
| CDN_API_STATUS ret = 0; |
| |
| memset(&edidResp, 0, sizeof(edidResp)); |
| switch (block) { |
| case 0: |
| ret = CDN_API_HDMITX_READ_EDID_blocking(state, 0, 0, &edidResp); |
| break; |
| case 1: |
| ret = CDN_API_HDMITX_READ_EDID_blocking(state, 0, 1, &edidResp); |
| break; |
| case 2: |
| ret = CDN_API_HDMITX_READ_EDID_blocking(state, 1, 0, &edidResp); |
| break; |
| case 3: |
| ret = CDN_API_HDMITX_READ_EDID_blocking(state, 1, 1, &edidResp); |
| break; |
| default: |
| pr_warn("EDID block %x read not support\n", block); |
| } |
| |
| if (ret == CDN_OK) |
| memcpy(buf, edidResp.buff, 128); |
| |
| return ret; |
| } |
| |
| int hdmi_get_hpd_state(state_struct *state, u8 *hpd) |
| { |
| int ret; |
| |
| ret = CDN_API_HDMITX_GetHpdStatus_blocking(state, hpd); |
| return ret; |
| } |
| |
| int hdmi_write_hdr_metadata(state_struct *state, |
| union hdmi_infoframe *hdr_infoframe) |
| { |
| struct imx_hdp *hdp = container_of(state, struct imx_hdp, state); |
| u8 buffer[40]; |
| int infoframe_size; |
| |
| infoframe_size = hdmi_infoframe_pack(hdr_infoframe, |
| buffer + 1, sizeof(buffer) - 1); |
| if (infoframe_size < 0) { |
| dev_err(hdp->dev, "Wrong metadata infoframe: %d\n", |
| infoframe_size); |
| return infoframe_size; |
| } |
| |
| buffer[0] = 0; |
| infoframe_size++; |
| |
| return CDN_API_InfoframeSet(state, 1, infoframe_size, |
| (u32 *)buffer, |
| HDMI_INFOFRAME_TYPE_DRM); |
| } |