1739 lines
42 KiB
C
1739 lines
42 KiB
C
/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
#include "mdss_hdmi_util.h"
|
|
|
|
#define RESOLUTION_NAME_STR_LEN 30
|
|
#define HDMI_SEC_TO_MS 1000
|
|
#define HDMI_MS_TO_US 1000
|
|
#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
|
|
#define HDMI_KHZ_TO_HZ 1000
|
|
#define HDMI_BUSY_WAIT_DELAY_US 100
|
|
|
|
#define HDMI_SCDC_UNKNOWN_REGISTER "Unknown register"
|
|
|
|
static char res_buf[RESOLUTION_NAME_STR_LEN];
|
|
|
|
enum trigger_mode {
|
|
TRIGGER_WRITE,
|
|
TRIGGER_READ
|
|
};
|
|
|
|
int hdmi_utils_get_timeout_in_hysnc(struct msm_hdmi_mode_timing_info *timing,
|
|
u32 timeout_ms)
|
|
{
|
|
u32 fps, v_total;
|
|
u32 time_taken_by_one_line_us, lines_needed_for_given_time;
|
|
|
|
if (!timing || !timeout_ms) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
fps = timing->refresh_rate / HDMI_KHZ_TO_HZ;
|
|
v_total = hdmi_tx_get_v_total(timing);
|
|
|
|
/*
|
|
* pixel clock = h_total * v_total * fps
|
|
* 1 sec = pixel clock number of pixels are transmitted.
|
|
* time taken by one line (h_total) = 1 / (v_total * fps).
|
|
*/
|
|
time_taken_by_one_line_us = HDMI_SEC_TO_US / (v_total * fps);
|
|
lines_needed_for_given_time = (timeout_ms * HDMI_MS_TO_US) /
|
|
time_taken_by_one_line_us;
|
|
|
|
return lines_needed_for_given_time;
|
|
}
|
|
|
|
static int hdmi_ddc_clear_irq(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
|
|
char *what)
|
|
{
|
|
u32 ddc_int_ctrl, ddc_status, in_use, timeout;
|
|
u32 sw_done_mask = BIT(2);
|
|
u32 sw_done_ack = BIT(1);
|
|
u32 hw_done_mask = BIT(6);
|
|
u32 hw_done_ack = BIT(5);
|
|
u32 in_use_by_sw = BIT(0);
|
|
u32 in_use_by_hw = BIT(1);
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* clear and enable interrutps */
|
|
ddc_int_ctrl = sw_done_mask | sw_done_ack | hw_done_mask | hw_done_ack;
|
|
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, ddc_int_ctrl);
|
|
|
|
/* wait until DDC HW is free */
|
|
timeout = 100;
|
|
do {
|
|
ddc_status = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_HW_STATUS);
|
|
in_use = ddc_status & (in_use_by_sw | in_use_by_hw);
|
|
if (in_use) {
|
|
pr_debug("ddc is in use by %s, timeout(%d)\n",
|
|
ddc_status & in_use_by_sw ? "sw" : "hw",
|
|
timeout);
|
|
udelay(100);
|
|
}
|
|
} while (in_use && --timeout);
|
|
|
|
if (!timeout) {
|
|
pr_err("%s: timedout\n", what);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hdmi_scrambler_ddc_reset(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("Invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
/* clear ack and disable interrupts */
|
|
reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
|
|
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL2, reg_val);
|
|
|
|
/* Reset DDC timers */
|
|
reg_val = BIT(0) | DSS_REG_R(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_CTRL);
|
|
DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
|
|
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_CTRL);
|
|
reg_val &= ~BIT(0);
|
|
DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
|
|
}
|
|
|
|
void hdmi_scrambler_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("Invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
hdmi_scrambler_ddc_reset(ctrl);
|
|
|
|
/* Disable HW DDC access to RxStatus register */
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
|
|
reg_val &= ~(BIT(8) | BIT(9));
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
|
|
}
|
|
|
|
static int hdmi_scrambler_ddc_check_status(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
int rc = 0;
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("invalid ddc ctrl\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check for errors and clear status */
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_STATUS);
|
|
|
|
if (reg_val & BIT(4)) {
|
|
pr_err("ddc aborted\n");
|
|
reg_val |= BIT(5);
|
|
rc = -ECONNABORTED;
|
|
}
|
|
|
|
if (reg_val & BIT(8)) {
|
|
pr_err("timed out\n");
|
|
reg_val |= BIT(9);
|
|
rc = -ETIMEDOUT;
|
|
}
|
|
|
|
if (reg_val & BIT(12)) {
|
|
pr_err("NACK0\n");
|
|
reg_val |= BIT(13);
|
|
rc = -EIO;
|
|
}
|
|
|
|
if (reg_val & BIT(14)) {
|
|
pr_err("NACK1\n");
|
|
reg_val |= BIT(15);
|
|
rc = -EIO;
|
|
}
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_STATUS, reg_val);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int hdmi_scrambler_status_timer_setup(struct hdmi_tx_ddc_ctrl *ctrl,
|
|
u32 timeout_hsync)
|
|
{
|
|
u32 reg_val;
|
|
int rc;
|
|
struct dss_io_data *io = NULL;
|
|
|
|
if (!ctrl || !ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
io = ctrl->io;
|
|
|
|
hdmi_ddc_clear_irq(ctrl, "scrambler");
|
|
|
|
DSS_REG_W(io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL, timeout_hsync);
|
|
DSS_REG_W(io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2, timeout_hsync);
|
|
|
|
reg_val = DSS_REG_R(io, HDMI_DDC_INT_CTRL5);
|
|
reg_val |= BIT(10);
|
|
DSS_REG_W(io, HDMI_DDC_INT_CTRL5, reg_val);
|
|
|
|
reg_val = DSS_REG_R(io, HDMI_DDC_INT_CTRL2);
|
|
/* Trigger interrupt if scrambler status is 0 or DDC failure */
|
|
reg_val |= BIT(10);
|
|
reg_val &= ~(BIT(15) | BIT(16));
|
|
reg_val |= BIT(16);
|
|
DSS_REG_W(io, HDMI_DDC_INT_CTRL2, reg_val);
|
|
|
|
/* Enable DDC access */
|
|
reg_val = DSS_REG_R(io, HDMI_HW_DDC_CTRL);
|
|
|
|
reg_val &= ~(BIT(8) | BIT(9));
|
|
reg_val |= BIT(8);
|
|
DSS_REG_W(io, HDMI_HW_DDC_CTRL, reg_val);
|
|
|
|
/* WAIT for 200ms as per HDMI 2.0 standard for sink to respond */
|
|
msleep(200);
|
|
|
|
/* clear the scrambler status */
|
|
rc = hdmi_scrambler_ddc_check_status(ctrl);
|
|
if (rc)
|
|
pr_err("scrambling ddc error %d\n", rc);
|
|
|
|
hdmi_scrambler_ddc_disable(ctrl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static inline char *hdmi_scdc_reg2string(u32 type)
|
|
{
|
|
switch (type) {
|
|
case HDMI_TX_SCDC_SCRAMBLING_STATUS:
|
|
return "HDMI_TX_SCDC_SCRAMBLING_STATUS";
|
|
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
|
return "HDMI_TX_SCDC_SCRAMBLING_ENABLE";
|
|
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
|
return "HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE";
|
|
case HDMI_TX_SCDC_CLOCK_DET_STATUS:
|
|
return "HDMI_TX_SCDC_CLOCK_DET_STATUS";
|
|
case HDMI_TX_SCDC_CH0_LOCK_STATUS:
|
|
return "HDMI_TX_SCDC_CH0_LOCK_STATUS";
|
|
case HDMI_TX_SCDC_CH1_LOCK_STATUS:
|
|
return "HDMI_TX_SCDC_CH1_LOCK_STATUS";
|
|
case HDMI_TX_SCDC_CH2_LOCK_STATUS:
|
|
return "HDMI_TX_SCDC_CH2_LOCK_STATUS";
|
|
case HDMI_TX_SCDC_CH0_ERROR_COUNT:
|
|
return "HDMI_TX_SCDC_CH0_ERROR_COUNT";
|
|
case HDMI_TX_SCDC_CH1_ERROR_COUNT:
|
|
return "HDMI_TX_SCDC_CH1_ERROR_COUNT";
|
|
case HDMI_TX_SCDC_CH2_ERROR_COUNT:
|
|
return "HDMI_TX_SCDC_CH2_ERROR_COUNT";
|
|
case HDMI_TX_SCDC_READ_ENABLE:
|
|
return"HDMI_TX_SCDC_READ_ENABLE";
|
|
default:
|
|
return HDMI_SCDC_UNKNOWN_REGISTER;
|
|
}
|
|
}
|
|
|
|
static struct msm_hdmi_mode_timing_info hdmi_resv_timings[
|
|
RESERVE_VFRMT_END - HDMI_VFRMT_RESERVE1 + 1];
|
|
|
|
static int hdmi_get_resv_timing_info(
|
|
struct msm_hdmi_mode_timing_info *mode, int id)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hdmi_resv_timings); i++) {
|
|
struct msm_hdmi_mode_timing_info *info = &hdmi_resv_timings[i];
|
|
if (info->video_format == id) {
|
|
*mode = *info;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int hdmi_set_resv_timing_info(struct msm_hdmi_mode_timing_info *mode)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hdmi_resv_timings); i++) {
|
|
struct msm_hdmi_mode_timing_info *info = &hdmi_resv_timings[i];
|
|
if (info->video_format == 0) {
|
|
*info = *mode;
|
|
info->video_format = HDMI_VFRMT_RESERVE1 + i;
|
|
return info->video_format;
|
|
}
|
|
}
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
bool hdmi_is_valid_resv_timing(int mode)
|
|
{
|
|
struct msm_hdmi_mode_timing_info *info;
|
|
|
|
if (mode < HDMI_VFRMT_RESERVE1 || mode > RESERVE_VFRMT_END) {
|
|
pr_err("invalid mode %d\n", mode);
|
|
return false;
|
|
}
|
|
|
|
info = &hdmi_resv_timings[mode - HDMI_VFRMT_RESERVE1];
|
|
|
|
return info->video_format >= HDMI_VFRMT_RESERVE1 &&
|
|
info->video_format <= RESERVE_VFRMT_END;
|
|
}
|
|
|
|
void hdmi_reset_resv_timing_info(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hdmi_resv_timings); i++) {
|
|
struct msm_hdmi_mode_timing_info *info = &hdmi_resv_timings[i];
|
|
info->video_format = 0;
|
|
}
|
|
}
|
|
|
|
int msm_hdmi_get_timing_info(
|
|
struct msm_hdmi_mode_timing_info *mode, int id)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (id) {
|
|
case HDMI_VFRMT_640x480p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_640x480p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_720x480p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_720x480p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_720x480p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_720x480p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1280x720p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1280x720p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080i60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080i60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1440x480i60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1440x480i60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1440x480i60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1440x480i60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_720x576p50_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_720x576p50_4_3);
|
|
break;
|
|
case HDMI_VFRMT_720x576p50_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_720x576p50_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1280x720p50_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1280x720p50_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1440x576i50_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1440x576i50_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1440x576i50_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1440x576i50_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080p50_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080p50_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080p24_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080p24_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080p25_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080p25_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1920x1080p30_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1080p30_16_9);
|
|
break;
|
|
case HDMI_EVFRMT_3840x2160p30_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_EVFRMT_3840x2160p30_16_9);
|
|
break;
|
|
case HDMI_EVFRMT_3840x2160p25_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_EVFRMT_3840x2160p25_16_9);
|
|
break;
|
|
case HDMI_EVFRMT_3840x2160p24_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_EVFRMT_3840x2160p24_16_9);
|
|
break;
|
|
case HDMI_EVFRMT_4096x2160p24_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_EVFRMT_4096x2160p24_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1024x768p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1024x768p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1280x1024p60_5_4:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1280x1024p60_5_4);
|
|
break;
|
|
case HDMI_VFRMT_2560x1600p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_2560x1600p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_800x600p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_800x600p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_848x480p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_848x480p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1280x960p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1280x960p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1360x768p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1360x768p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_1440x900p60_16_10:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1440x900p60_16_10);
|
|
break;
|
|
case HDMI_VFRMT_1400x1050p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1400x1050p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1680x1050p60_16_10:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1680x1050p60_16_10);
|
|
break;
|
|
case HDMI_VFRMT_1600x1200p60_4_3:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1600x1200p60_4_3);
|
|
break;
|
|
case HDMI_VFRMT_1920x1200p60_16_10:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1920x1200p60_16_10);
|
|
break;
|
|
case HDMI_VFRMT_1366x768p60_16_10:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1366x768p60_16_10);
|
|
break;
|
|
case HDMI_VFRMT_1280x800p60_16_10:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_1280x800p60_16_10);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p24_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p24_16_9);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p25_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p25_16_9);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p30_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p30_16_9);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p50_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p50_16_9);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p60_16_9:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p60_16_9);
|
|
break;
|
|
case HDMI_VFRMT_4096x2160p24_256_135:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode,
|
|
HDMI_VFRMT_4096x2160p24_256_135);
|
|
break;
|
|
case HDMI_VFRMT_4096x2160p25_256_135:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode,
|
|
HDMI_VFRMT_4096x2160p25_256_135);
|
|
break;
|
|
case HDMI_VFRMT_4096x2160p30_256_135:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode,
|
|
HDMI_VFRMT_4096x2160p30_256_135);
|
|
break;
|
|
case HDMI_VFRMT_4096x2160p50_256_135:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode,
|
|
HDMI_VFRMT_4096x2160p50_256_135);
|
|
break;
|
|
case HDMI_VFRMT_4096x2160p60_256_135:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode,
|
|
HDMI_VFRMT_4096x2160p60_256_135);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p24_64_27:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p24_64_27);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p25_64_27:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p25_64_27);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p30_64_27:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p30_64_27);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p50_64_27:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p50_64_27);
|
|
break;
|
|
case HDMI_VFRMT_3840x2160p60_64_27:
|
|
MSM_HDMI_MODES_GET_DETAILS(mode, HDMI_VFRMT_3840x2160p60_64_27);
|
|
break;
|
|
default:
|
|
ret = hdmi_get_resv_timing_info(mode, id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int hdmi_get_supported_mode(struct msm_hdmi_mode_timing_info *info,
|
|
struct hdmi_util_ds_data *ds_data, u32 mode)
|
|
{
|
|
int ret;
|
|
|
|
if (!info)
|
|
return -EINVAL;
|
|
|
|
if (mode >= HDMI_VFRMT_MAX)
|
|
return -EINVAL;
|
|
|
|
ret = msm_hdmi_get_timing_info(info, mode);
|
|
|
|
if (!ret && ds_data && ds_data->ds_registered && ds_data->ds_max_clk) {
|
|
if (info->pixel_freq > ds_data->ds_max_clk)
|
|
info->supported = false;
|
|
}
|
|
|
|
return ret;
|
|
} /* hdmi_get_supported_mode */
|
|
|
|
const char *msm_hdmi_mode_2string(u32 mode)
|
|
{
|
|
static struct msm_hdmi_mode_timing_info ri = {0};
|
|
char *aspect_ratio;
|
|
|
|
if (mode >= HDMI_VFRMT_MAX)
|
|
return "???";
|
|
|
|
if (hdmi_get_supported_mode(&ri, NULL, mode))
|
|
return "???";
|
|
|
|
memset(res_buf, 0, sizeof(res_buf));
|
|
|
|
if (!ri.supported) {
|
|
snprintf(res_buf, RESOLUTION_NAME_STR_LEN, "%d", mode);
|
|
return res_buf;
|
|
}
|
|
|
|
switch (ri.ar) {
|
|
case HDMI_RES_AR_4_3:
|
|
aspect_ratio = "4/3";
|
|
break;
|
|
case HDMI_RES_AR_5_4:
|
|
aspect_ratio = "5/4";
|
|
break;
|
|
case HDMI_RES_AR_16_9:
|
|
aspect_ratio = "16/9";
|
|
break;
|
|
case HDMI_RES_AR_16_10:
|
|
aspect_ratio = "16/10";
|
|
break;
|
|
default:
|
|
aspect_ratio = "???";
|
|
};
|
|
|
|
snprintf(res_buf, RESOLUTION_NAME_STR_LEN, "%dx%d %s%dHz %s",
|
|
ri.active_h, ri.active_v, ri.interlaced ? "i" : "p",
|
|
ri.refresh_rate / 1000, aspect_ratio);
|
|
|
|
return res_buf;
|
|
}
|
|
|
|
int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in,
|
|
struct hdmi_util_ds_data *ds_data)
|
|
{
|
|
int i, vic = -1;
|
|
struct msm_hdmi_mode_timing_info supported_timing = {0};
|
|
u32 ret;
|
|
|
|
if (!timing_in) {
|
|
pr_err("invalid input\n");
|
|
goto exit;
|
|
}
|
|
|
|
/* active_low_h, active_low_v and interlaced are not checked against */
|
|
for (i = 0; i < HDMI_VFRMT_MAX; i++) {
|
|
ret = hdmi_get_supported_mode(&supported_timing, ds_data, i);
|
|
|
|
if (ret || !supported_timing.supported)
|
|
continue;
|
|
if (timing_in->active_h != supported_timing.active_h)
|
|
continue;
|
|
if (timing_in->front_porch_h != supported_timing.front_porch_h)
|
|
continue;
|
|
if (timing_in->pulse_width_h != supported_timing.pulse_width_h)
|
|
continue;
|
|
if (timing_in->back_porch_h != supported_timing.back_porch_h)
|
|
continue;
|
|
if (timing_in->active_v != supported_timing.active_v)
|
|
continue;
|
|
if (timing_in->front_porch_v != supported_timing.front_porch_v)
|
|
continue;
|
|
if (timing_in->pulse_width_v != supported_timing.pulse_width_v)
|
|
continue;
|
|
if (timing_in->back_porch_v != supported_timing.back_porch_v)
|
|
continue;
|
|
if (timing_in->pixel_freq != supported_timing.pixel_freq)
|
|
continue;
|
|
if (timing_in->refresh_rate != supported_timing.refresh_rate)
|
|
continue;
|
|
|
|
vic = (int)supported_timing.video_format;
|
|
break;
|
|
}
|
|
|
|
if (vic < 0) {
|
|
for (i = 0; i < HDMI_VFRMT_MAX; i++) {
|
|
ret = hdmi_get_supported_mode(&supported_timing,
|
|
ds_data, i);
|
|
if (ret || !supported_timing.supported)
|
|
continue;
|
|
if (timing_in->active_h != supported_timing.active_h)
|
|
continue;
|
|
if (timing_in->active_v != supported_timing.active_v)
|
|
continue;
|
|
vic = (int)supported_timing.video_format;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vic < 0) {
|
|
pr_err("timing is not supported h=%d v=%d\n",
|
|
timing_in->active_h, timing_in->active_v);
|
|
}
|
|
|
|
exit:
|
|
pr_debug("vic = %d timing = %s\n", vic,
|
|
msm_hdmi_mode_2string((u32)vic));
|
|
|
|
return vic;
|
|
} /* hdmi_get_video_id_code */
|
|
|
|
static const char *hdmi_get_single_video_3d_fmt_2string(u32 format)
|
|
{
|
|
switch (format) {
|
|
case TOP_AND_BOTTOM: return "TAB";
|
|
case FRAME_PACKING: return "FP";
|
|
case SIDE_BY_SIDE_HALF: return "SSH";
|
|
}
|
|
return "";
|
|
} /* hdmi_get_single_video_3d_fmt_2string */
|
|
|
|
ssize_t hdmi_get_video_3d_fmt_2string(u32 format, char *buf, u32 size)
|
|
{
|
|
ssize_t ret, len = 0;
|
|
ret = scnprintf(buf, size, "%s",
|
|
hdmi_get_single_video_3d_fmt_2string(
|
|
format & FRAME_PACKING));
|
|
len += ret;
|
|
|
|
if (len && (format & TOP_AND_BOTTOM))
|
|
ret = scnprintf(buf + len, size - len, ":%s",
|
|
hdmi_get_single_video_3d_fmt_2string(
|
|
format & TOP_AND_BOTTOM));
|
|
else
|
|
ret = scnprintf(buf + len, size - len, "%s",
|
|
hdmi_get_single_video_3d_fmt_2string(
|
|
format & TOP_AND_BOTTOM));
|
|
len += ret;
|
|
|
|
if (len && (format & SIDE_BY_SIDE_HALF))
|
|
ret = scnprintf(buf + len, size - len, ":%s",
|
|
hdmi_get_single_video_3d_fmt_2string(
|
|
format & SIDE_BY_SIDE_HALF));
|
|
else
|
|
ret = scnprintf(buf + len, size - len, "%s",
|
|
hdmi_get_single_video_3d_fmt_2string(
|
|
format & SIDE_BY_SIDE_HALF));
|
|
len += ret;
|
|
|
|
return len;
|
|
} /* hdmi_get_video_3d_fmt_2string */
|
|
|
|
static void hdmi_ddc_trigger(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
|
|
enum trigger_mode mode, bool seg)
|
|
{
|
|
struct hdmi_tx_ddc_data *ddc_data = &ddc_ctrl->ddc_data;
|
|
struct dss_io_data *io = ddc_ctrl->io;
|
|
u32 const seg_addr = 0x60, seg_num = 0x01;
|
|
u32 ddc_ctrl_reg_val;
|
|
|
|
ddc_data->dev_addr &= 0xFE;
|
|
|
|
if (mode == TRIGGER_READ && seg) {
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA, BIT(31) | (seg_addr << 8));
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA, seg_num << 8);
|
|
}
|
|
|
|
/* handle portion #1 */
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA, BIT(31) | (ddc_data->dev_addr << 8));
|
|
|
|
/* handle portion #2 */
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA, ddc_data->offset << 8);
|
|
|
|
if (mode == TRIGGER_READ) {
|
|
/* handle portion #3 */
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA,
|
|
(ddc_data->dev_addr | BIT(0)) << 8);
|
|
|
|
/* HDMI_I2C_TRANSACTION0 */
|
|
DSS_REG_W_ND(io, HDMI_DDC_TRANS0, BIT(12) | BIT(16));
|
|
|
|
/* Write to HDMI_I2C_TRANSACTION1 */
|
|
if (seg) {
|
|
DSS_REG_W_ND(io, HDMI_DDC_TRANS1, BIT(12) | BIT(16));
|
|
DSS_REG_W_ND(io, HDMI_DDC_TRANS2,
|
|
BIT(0) | BIT(12) | BIT(13) |
|
|
(ddc_data->request_len << 16));
|
|
|
|
ddc_ctrl_reg_val = BIT(0) | BIT(21);
|
|
} else {
|
|
DSS_REG_W_ND(io, HDMI_DDC_TRANS1,
|
|
BIT(0) | BIT(12) | BIT(13) |
|
|
(ddc_data->request_len << 16));
|
|
|
|
ddc_ctrl_reg_val = BIT(0) | BIT(20);
|
|
}
|
|
} else {
|
|
int ndx;
|
|
|
|
/* write buffer */
|
|
for (ndx = 0; ndx < ddc_data->data_len; ++ndx)
|
|
DSS_REG_W_ND(io, HDMI_DDC_DATA,
|
|
((u32)ddc_data->data_buf[ndx]) << 8);
|
|
|
|
DSS_REG_W_ND(io, HDMI_DDC_TRANS0,
|
|
(ddc_data->data_len + 1) << 16 | BIT(12) | BIT(13));
|
|
|
|
ddc_ctrl_reg_val = BIT(0);
|
|
}
|
|
|
|
/* Trigger the I2C transfer */
|
|
DSS_REG_W_ND(io, HDMI_DDC_CTRL, ddc_ctrl_reg_val);
|
|
}
|
|
|
|
static int hdmi_ddc_check_status(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
u32 reg_val;
|
|
int rc = 0;
|
|
|
|
/* Read DDC status */
|
|
reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_SW_STATUS);
|
|
reg_val &= BIT(12) | BIT(13) | BIT(14) | BIT(15);
|
|
|
|
/* Check if any NACK occurred */
|
|
if (reg_val) {
|
|
pr_debug("%s: NACK: HDMI_DDC_SW_STATUS 0x%x\n",
|
|
ddc_ctrl->ddc_data.what, reg_val);
|
|
|
|
/* SW_STATUS_RESET, SOFT_RESET */
|
|
reg_val = BIT(3) | BIT(1);
|
|
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, reg_val);
|
|
|
|
rc = -ECOMM;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
u32 reg_val, ndx, time_out_count, wait_time;
|
|
struct hdmi_tx_ddc_data *ddc_data;
|
|
int status, rc;
|
|
int busy_wait_us;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_data = &ddc_ctrl->ddc_data;
|
|
|
|
if (!ddc_data->data_buf) {
|
|
status = -EINVAL;
|
|
pr_err("%s: invalid buf\n", ddc_data->what);
|
|
goto error;
|
|
}
|
|
|
|
if (ddc_data->retry < 0) {
|
|
pr_err("invalid no. of retries %d\n", ddc_data->retry);
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
do {
|
|
status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what);
|
|
if (status)
|
|
continue;
|
|
|
|
if (ddc_data->hard_timeout) {
|
|
pr_debug("using hard_timeout %dms\n",
|
|
ddc_data->hard_timeout);
|
|
|
|
busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
|
|
atomic_set(&ddc_ctrl->read_busy_wait_done, 0);
|
|
} else {
|
|
reinit_completion(&ddc_ctrl->ddc_sw_done);
|
|
wait_time = HZ / 2;
|
|
}
|
|
|
|
hdmi_ddc_trigger(ddc_ctrl, TRIGGER_READ, false);
|
|
|
|
if (ddc_data->hard_timeout) {
|
|
while (busy_wait_us > 0 &&
|
|
!atomic_read(&ddc_ctrl->read_busy_wait_done)) {
|
|
udelay(HDMI_BUSY_WAIT_DELAY_US);
|
|
busy_wait_us -= HDMI_BUSY_WAIT_DELAY_US;
|
|
};
|
|
|
|
if (busy_wait_us < 0)
|
|
busy_wait_us = 0;
|
|
|
|
time_out_count = busy_wait_us / HDMI_MS_TO_US;
|
|
|
|
ddc_data->timeout_left = time_out_count;
|
|
} else {
|
|
time_out_count = wait_for_completion_timeout(
|
|
&ddc_ctrl->ddc_sw_done, wait_time);
|
|
|
|
ddc_data->timeout_left =
|
|
jiffies_to_msecs(time_out_count);
|
|
}
|
|
|
|
pr_debug("ddc read done at %dms\n", jiffies_to_msecs(jiffies));
|
|
|
|
if (!time_out_count) {
|
|
pr_debug("%s: timedout\n", ddc_data->what);
|
|
|
|
status = -ETIMEDOUT;
|
|
}
|
|
|
|
rc = hdmi_ddc_check_status(ddc_ctrl);
|
|
|
|
if (!status)
|
|
status = rc;
|
|
} while (status && ddc_data->retry--);
|
|
|
|
if (status)
|
|
goto error;
|
|
|
|
/* Write data to DDC buffer */
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA,
|
|
BIT(0) | (3 << 16) | BIT(31));
|
|
|
|
/* Discard first byte */
|
|
DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_DATA);
|
|
for (ndx = 0; ndx < ddc_data->data_len; ++ndx) {
|
|
reg_val = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_DATA);
|
|
ddc_data->data_buf[ndx] = (u8)((reg_val & 0x0000FF00) >> 8);
|
|
}
|
|
|
|
pr_debug("%s: success\n", ddc_data->what);
|
|
|
|
error:
|
|
return status;
|
|
} /* hdmi_ddc_read_retry */
|
|
|
|
void hdmi_ddc_config(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return;
|
|
}
|
|
|
|
/* Configure Pre-Scale multiplier & Threshold */
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_SPEED, (10 << 16) | (2 << 0));
|
|
|
|
/*
|
|
* Setting 31:24 bits : Time units to wait before timeout
|
|
* when clock is being stalled by external sink device
|
|
*/
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_SETUP, 0xFF000000);
|
|
|
|
/* Enable reference timer to 19 micro-seconds */
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_REF, (1 << 16) | (19 << 0));
|
|
} /* hdmi_ddc_config */
|
|
|
|
int hdmi_hdcp2p2_ddc_check_status(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
int rc = 0;
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("invalid ddc ctrl\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check for errors and clear status */
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_STATUS);
|
|
|
|
if (reg_val & BIT(4)) {
|
|
pr_err("ddc aborted\n");
|
|
reg_val |= BIT(5);
|
|
rc = -ECONNABORTED;
|
|
}
|
|
|
|
if (reg_val & BIT(8)) {
|
|
pr_err("timed out\n");
|
|
reg_val |= BIT(9);
|
|
rc = -ETIMEDOUT;
|
|
}
|
|
|
|
if (reg_val & BIT(12)) {
|
|
pr_err("NACK0\n");
|
|
reg_val |= BIT(13);
|
|
rc = -EIO;
|
|
}
|
|
|
|
if (reg_val & BIT(14)) {
|
|
pr_err("NACK1\n");
|
|
reg_val |= BIT(15);
|
|
rc = -EIO;
|
|
}
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_STATUS, reg_val);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
struct dss_io_data *io = NULL;
|
|
struct hdmi_tx_hdcp2p2_ddc_data *data;
|
|
u32 intr0, intr2, intr5;
|
|
u32 msg_size;
|
|
int rc = 0;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
io = ddc_ctrl->io;
|
|
|
|
data = &ddc_ctrl->hdcp2p2_ddc_data;
|
|
|
|
intr0 = DSS_REG_R(io, HDMI_DDC_INT_CTRL0);
|
|
intr2 = DSS_REG_R(io, HDMI_HDCP_INT_CTRL2);
|
|
intr5 = DSS_REG_R_ND(io, HDMI_DDC_INT_CTRL5);
|
|
|
|
pr_debug("intr0: 0x%x, intr2: 0x%x, intr5: 0x%x\n",
|
|
intr0, intr2, intr5);
|
|
|
|
/* check if encryption is enabled */
|
|
if (intr2 & BIT(0)) {
|
|
/*
|
|
* ack encryption ready interrupt.
|
|
* disable encryption ready interrupt.
|
|
* enable encryption not ready interrupt.
|
|
*/
|
|
intr2 &= ~BIT(2);
|
|
intr2 |= BIT(1) | BIT(6);
|
|
|
|
pr_debug("HDCP 2.2 Encryption enabled\n");
|
|
data->encryption_ready = true;
|
|
}
|
|
|
|
/* check if encryption is disabled */
|
|
if (intr2 & BIT(4)) {
|
|
/*
|
|
* ack encryption not ready interrupt.
|
|
* disable encryption not ready interrupt.
|
|
* enable encryption ready interrupt.
|
|
*/
|
|
intr2 &= ~BIT(6);
|
|
intr2 |= BIT(5) | BIT(2);
|
|
|
|
pr_debug("HDCP 2.2 Encryption disabled\n");
|
|
data->encryption_ready = false;
|
|
}
|
|
|
|
DSS_REG_W_ND(io, HDMI_HDCP_INT_CTRL2, intr2);
|
|
|
|
/* get the message size bits 29:20 */
|
|
msg_size = (intr0 & (0x3FF << 20)) >> 20;
|
|
|
|
if (msg_size) {
|
|
/* ack and disable message size interrupt */
|
|
intr0 |= BIT(30);
|
|
intr0 &= ~BIT(31);
|
|
|
|
data->message_size = msg_size;
|
|
}
|
|
|
|
/* check and disable ready interrupt */
|
|
if (intr0 & BIT(16)) {
|
|
/* ack ready/not ready interrupt */
|
|
intr0 |= BIT(17);
|
|
|
|
intr0 &= ~BIT(18);
|
|
data->ready = true;
|
|
}
|
|
|
|
/* check and disable not ready interrupt */
|
|
if (intr0 & BIT(15)) {
|
|
/* ack ready/not ready interrupt */
|
|
intr0 |= BIT(17);
|
|
|
|
intr0 &= ~BIT(19);
|
|
data->ready = false;
|
|
}
|
|
|
|
/* check for reauth req interrupt */
|
|
if (intr0 & BIT(12)) {
|
|
/* ack and disable reauth req interrupt */
|
|
intr0 |= BIT(13);
|
|
intr0 &= ~BIT(14);
|
|
|
|
data->reauth_req = true;
|
|
}
|
|
|
|
/* check for ddc fail interrupt */
|
|
if (intr0 & BIT(8)) {
|
|
/* ack ddc fail interrupt */
|
|
intr0 |= BIT(9);
|
|
|
|
data->ddc_max_retries_fail = true;
|
|
}
|
|
|
|
/* check for ddc done interrupt */
|
|
if (intr0 & BIT(4)) {
|
|
/* ack ddc done interrupt */
|
|
intr0 |= BIT(5);
|
|
|
|
data->ddc_done = true;
|
|
}
|
|
|
|
/* check for ddc read req interrupt */
|
|
if (intr0 & BIT(0)) {
|
|
/* ack read req interrupt */
|
|
intr0 |= BIT(1);
|
|
|
|
data->ddc_read_req = true;
|
|
}
|
|
|
|
DSS_REG_W_ND(io, HDMI_DDC_INT_CTRL0, intr0);
|
|
|
|
if (intr5 & BIT(0)) {
|
|
pr_err("RXSTATUS_DDC_REQ_TIMEOUT\n");
|
|
|
|
/* ack and disable timeout interrupt */
|
|
intr5 |= BIT(1);
|
|
intr5 &= ~BIT(2);
|
|
|
|
data->ddc_timeout = true;
|
|
}
|
|
DSS_REG_W_ND(io, HDMI_DDC_INT_CTRL5, intr5);
|
|
|
|
if (data->message_size || data->ready || data->reauth_req) {
|
|
if (data->wait) {
|
|
atomic_set(&ddc_ctrl->rxstatus_busy_wait_done, 1);
|
|
} else if (data->link_cb && data->link_data) {
|
|
data->link_cb(data->link_data);
|
|
} else {
|
|
pr_err("new msg/reauth not handled\n");
|
|
rc = -EINVAL;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int hdmi_ddc_scrambling_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
struct dss_io_data *io;
|
|
bool scrambler_timer_off = false;
|
|
u32 intr2, intr5;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
io = ddc_ctrl->io;
|
|
|
|
intr2 = DSS_REG_R_ND(io, HDMI_DDC_INT_CTRL2);
|
|
intr5 = DSS_REG_R_ND(io, HDMI_DDC_INT_CTRL5);
|
|
|
|
pr_debug("intr2: 0x%x, intr5: 0x%x\n", intr2, intr5);
|
|
|
|
if (intr2 & BIT(12)) {
|
|
pr_err("SCRAMBLER_STATUS_NOT\n");
|
|
|
|
intr2 |= BIT(14);
|
|
|
|
scrambler_timer_off = true;
|
|
}
|
|
|
|
if (intr2 & BIT(8)) {
|
|
pr_err("SCRAMBLER_STATUS_DDC_FAILED\n");
|
|
|
|
intr2 |= BIT(9);
|
|
|
|
scrambler_timer_off = true;
|
|
}
|
|
DSS_REG_W_ND(io, HDMI_DDC_INT_CTRL2, intr2);
|
|
|
|
if (intr5 & BIT(8)) {
|
|
pr_err("SCRAMBLER_STATUS_DDC_REQ_TIMEOUT\n");
|
|
|
|
intr5 |= BIT(9);
|
|
intr5 &= ~BIT(10);
|
|
|
|
scrambler_timer_off = true;
|
|
}
|
|
DSS_REG_W_ND(io, HDMI_DDC_INT_CTRL5, intr5);
|
|
|
|
if (scrambler_timer_off)
|
|
hdmi_scrambler_ddc_disable(ddc_ctrl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u32 version)
|
|
{
|
|
u32 ddc_int_ctrl, ret = 0;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_int_ctrl = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL);
|
|
pr_debug("intr: 0x%x\n", ddc_int_ctrl);
|
|
|
|
if (ddc_int_ctrl & BIT(0)) {
|
|
pr_debug("sw done\n");
|
|
|
|
ddc_int_ctrl |= BIT(1);
|
|
if (ddc_ctrl->ddc_data.hard_timeout) {
|
|
atomic_set(&ddc_ctrl->read_busy_wait_done, 1);
|
|
atomic_set(&ddc_ctrl->write_busy_wait_done, 1);
|
|
} else {
|
|
complete(&ddc_ctrl->ddc_sw_done);
|
|
}
|
|
}
|
|
|
|
if (ddc_int_ctrl & BIT(4)) {
|
|
pr_debug("hw done\n");
|
|
ddc_int_ctrl |= BIT(5);
|
|
}
|
|
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, ddc_int_ctrl);
|
|
|
|
if (version >= HDMI_TX_SCRAMBLER_MIN_TX_VERSION) {
|
|
ret = hdmi_ddc_scrambling_isr(ddc_ctrl);
|
|
if (ret)
|
|
pr_err("err in scrambling isr\n");
|
|
}
|
|
|
|
ret = hdmi_ddc_hdcp2p2_isr(ddc_ctrl);
|
|
if (ret)
|
|
pr_err("err in hdcp2p2 isr\n");
|
|
|
|
return ret;
|
|
} /* hdmi_ddc_isr */
|
|
|
|
int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
int rc = 0;
|
|
int retry;
|
|
struct hdmi_tx_ddc_data *ddc_data;
|
|
|
|
if (!ddc_ctrl) {
|
|
pr_err("invalid ddc ctrl\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_data = &ddc_ctrl->ddc_data;
|
|
retry = ddc_data->retry;
|
|
|
|
rc = hdmi_ddc_read_retry(ddc_ctrl);
|
|
if (!rc)
|
|
return rc;
|
|
|
|
if (ddc_data->retry_align) {
|
|
ddc_data->retry = retry;
|
|
|
|
ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32);
|
|
rc = hdmi_ddc_read_retry(ddc_ctrl);
|
|
}
|
|
|
|
return rc;
|
|
} /* hdmi_ddc_read */
|
|
|
|
int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
int status, rc;
|
|
u32 reg_val, ndx, time_out_count;
|
|
struct hdmi_tx_ddc_data *ddc_data;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_data = &ddc_ctrl->ddc_data;
|
|
|
|
if (!ddc_data->data_buf) {
|
|
status = -EINVAL;
|
|
pr_err("%s: invalid buf\n", ddc_data->what);
|
|
goto error;
|
|
}
|
|
|
|
if (ddc_data->retry < 0) {
|
|
pr_err("invalid no. of retries %d\n", ddc_data->retry);
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
do {
|
|
status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what);
|
|
if (status)
|
|
continue;
|
|
|
|
reinit_completion(&ddc_ctrl->ddc_sw_done);
|
|
|
|
hdmi_ddc_trigger(ddc_ctrl, TRIGGER_READ, true);
|
|
|
|
time_out_count = wait_for_completion_timeout(
|
|
&ddc_ctrl->ddc_sw_done, HZ / 2);
|
|
|
|
if (!time_out_count) {
|
|
pr_debug("%s: timedout\n", ddc_data->what);
|
|
|
|
status = -ETIMEDOUT;
|
|
}
|
|
|
|
rc = hdmi_ddc_check_status(ddc_ctrl);
|
|
|
|
if (!status)
|
|
status = rc;
|
|
} while (status && ddc_data->retry--);
|
|
|
|
if (status)
|
|
goto error;
|
|
|
|
/* Write data to DDC buffer */
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA,
|
|
BIT(0) | (5 << 16) | BIT(31));
|
|
|
|
/* Discard first byte */
|
|
DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_DATA);
|
|
|
|
for (ndx = 0; ndx < ddc_data->data_len; ++ndx) {
|
|
reg_val = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_DATA);
|
|
ddc_data->data_buf[ndx] = (u8) ((reg_val & 0x0000FF00) >> 8);
|
|
}
|
|
|
|
pr_debug("%s: success\n", ddc_data->what);
|
|
|
|
error:
|
|
return status;
|
|
} /* hdmi_ddc_read_seg */
|
|
|
|
int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
int status, rc;
|
|
u32 time_out_count;
|
|
struct hdmi_tx_ddc_data *ddc_data;
|
|
u32 wait_time;
|
|
int busy_wait_us;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_data = &ddc_ctrl->ddc_data;
|
|
|
|
if (!ddc_data->data_buf) {
|
|
status = -EINVAL;
|
|
pr_err("%s: invalid buf\n", ddc_data->what);
|
|
goto error;
|
|
}
|
|
|
|
if (ddc_data->retry < 0) {
|
|
pr_err("invalid no. of retries %d\n", ddc_data->retry);
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
do {
|
|
status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what);
|
|
if (status)
|
|
continue;
|
|
|
|
if (ddc_data->hard_timeout) {
|
|
pr_debug("using hard_timeout %dms\n",
|
|
ddc_data->hard_timeout);
|
|
|
|
busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US;
|
|
atomic_set(&ddc_ctrl->write_busy_wait_done, 0);
|
|
} else {
|
|
reinit_completion(&ddc_ctrl->ddc_sw_done);
|
|
wait_time = HZ / 2;
|
|
}
|
|
|
|
hdmi_ddc_trigger(ddc_ctrl, TRIGGER_WRITE, false);
|
|
|
|
if (ddc_data->hard_timeout) {
|
|
while (busy_wait_us > 0 &&
|
|
!atomic_read(&ddc_ctrl->write_busy_wait_done)) {
|
|
udelay(HDMI_BUSY_WAIT_DELAY_US);
|
|
busy_wait_us -= HDMI_BUSY_WAIT_DELAY_US;
|
|
};
|
|
|
|
if (busy_wait_us < 0)
|
|
busy_wait_us = 0;
|
|
|
|
time_out_count = busy_wait_us / HDMI_MS_TO_US;
|
|
|
|
ddc_data->timeout_left = time_out_count;
|
|
} else {
|
|
time_out_count = wait_for_completion_timeout(
|
|
&ddc_ctrl->ddc_sw_done, wait_time);
|
|
|
|
ddc_data->timeout_left =
|
|
jiffies_to_msecs(time_out_count);
|
|
}
|
|
|
|
pr_debug("DDC write done at %dms\n", jiffies_to_msecs(jiffies));
|
|
|
|
if (!time_out_count) {
|
|
pr_debug("%s timout\n", ddc_data->what);
|
|
|
|
status = -ETIMEDOUT;
|
|
}
|
|
|
|
rc = hdmi_ddc_check_status(ddc_ctrl);
|
|
|
|
if (!status)
|
|
status = rc;
|
|
} while (status && ddc_data->retry--);
|
|
|
|
if (status)
|
|
goto error;
|
|
|
|
pr_debug("%s: success\n", ddc_data->what);
|
|
error:
|
|
return status;
|
|
} /* hdmi_ddc_write */
|
|
|
|
|
|
int hdmi_ddc_abort_transaction(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
|
|
{
|
|
int status;
|
|
struct hdmi_tx_ddc_data *ddc_data;
|
|
|
|
if (!ddc_ctrl || !ddc_ctrl->io) {
|
|
pr_err("invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ddc_data = &ddc_ctrl->ddc_data;
|
|
|
|
status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what);
|
|
if (status)
|
|
goto error;
|
|
|
|
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_ARBITRATION, BIT(12)|BIT(8));
|
|
|
|
error:
|
|
return status;
|
|
|
|
}
|
|
|
|
int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val)
|
|
{
|
|
struct hdmi_tx_ddc_data data = {0};
|
|
int rc = 0;
|
|
u8 data_buf[2] = {0};
|
|
|
|
if (!ctrl || !ctrl->io || !val) {
|
|
pr_err("Bad Parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data_type >= HDMI_TX_SCDC_MAX) {
|
|
pr_err("Unsupported data type\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data.what = hdmi_scdc_reg2string(data_type);
|
|
data.dev_addr = 0xA8;
|
|
data.retry = 1;
|
|
data.data_buf = data_buf;
|
|
|
|
switch (data_type) {
|
|
case HDMI_TX_SCDC_SCRAMBLING_STATUS:
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_SCRAMBLER_STATUS;
|
|
break;
|
|
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
|
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_TMDS_CONFIG;
|
|
break;
|
|
case HDMI_TX_SCDC_CLOCK_DET_STATUS:
|
|
case HDMI_TX_SCDC_CH0_LOCK_STATUS:
|
|
case HDMI_TX_SCDC_CH1_LOCK_STATUS:
|
|
case HDMI_TX_SCDC_CH2_LOCK_STATUS:
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_STATUS_FLAGS_0;
|
|
break;
|
|
case HDMI_TX_SCDC_CH0_ERROR_COUNT:
|
|
data.data_len = 2;
|
|
data.request_len = 2;
|
|
data.offset = HDMI_SCDC_ERR_DET_0_L;
|
|
break;
|
|
case HDMI_TX_SCDC_CH1_ERROR_COUNT:
|
|
data.data_len = 2;
|
|
data.request_len = 2;
|
|
data.offset = HDMI_SCDC_ERR_DET_1_L;
|
|
break;
|
|
case HDMI_TX_SCDC_CH2_ERROR_COUNT:
|
|
data.data_len = 2;
|
|
data.request_len = 2;
|
|
data.offset = HDMI_SCDC_ERR_DET_2_L;
|
|
break;
|
|
case HDMI_TX_SCDC_READ_ENABLE:
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_CONFIG_0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ctrl->ddc_data = data;
|
|
|
|
rc = hdmi_ddc_read(ctrl);
|
|
if (rc) {
|
|
pr_err("DDC Read failed for %s\n", data.what);
|
|
return rc;
|
|
}
|
|
|
|
switch (data_type) {
|
|
case HDMI_TX_SCDC_SCRAMBLING_STATUS:
|
|
*val = (data_buf[0] & BIT(0)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
|
*val = (data_buf[0] & BIT(0)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
|
*val = (data_buf[0] & BIT(1)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_CLOCK_DET_STATUS:
|
|
*val = (data_buf[0] & BIT(0)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_CH0_LOCK_STATUS:
|
|
*val = (data_buf[0] & BIT(1)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_CH1_LOCK_STATUS:
|
|
*val = (data_buf[0] & BIT(2)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_CH2_LOCK_STATUS:
|
|
*val = (data_buf[0] & BIT(3)) ? 1 : 0;
|
|
break;
|
|
case HDMI_TX_SCDC_CH0_ERROR_COUNT:
|
|
case HDMI_TX_SCDC_CH1_ERROR_COUNT:
|
|
case HDMI_TX_SCDC_CH2_ERROR_COUNT:
|
|
if (data_buf[1] & BIT(7))
|
|
*val = (data_buf[0] | ((data_buf[1] & 0x7F) << 8));
|
|
else
|
|
*val = 0;
|
|
break;
|
|
case HDMI_TX_SCDC_READ_ENABLE:
|
|
*val = (data_buf[0] & BIT(0)) ? 1 : 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val)
|
|
{
|
|
struct hdmi_tx_ddc_data data = {0};
|
|
struct hdmi_tx_ddc_data rdata = {0};
|
|
int rc = 0;
|
|
u8 data_buf[2] = {0};
|
|
u8 read_val = 0;
|
|
|
|
if (!ctrl || !ctrl->io) {
|
|
pr_err("Bad Parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data_type >= HDMI_TX_SCDC_MAX) {
|
|
pr_err("Unsupported data type\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data.what = hdmi_scdc_reg2string(data_type);
|
|
data.dev_addr = 0xA8;
|
|
data.retry = 1;
|
|
data.data_buf = data_buf;
|
|
|
|
switch (data_type) {
|
|
case HDMI_TX_SCDC_SCRAMBLING_ENABLE:
|
|
case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE:
|
|
rdata.what = "TMDS CONFIG";
|
|
rdata.dev_addr = 0xA8;
|
|
rdata.retry = 2;
|
|
rdata.data_buf = &read_val;
|
|
rdata.data_len = 1;
|
|
rdata.offset = HDMI_SCDC_TMDS_CONFIG;
|
|
rdata.request_len = 1;
|
|
ctrl->ddc_data = rdata;
|
|
rc = hdmi_ddc_read(ctrl);
|
|
if (rc) {
|
|
pr_err("scdc read failed\n");
|
|
return rc;
|
|
}
|
|
if (data_type == HDMI_TX_SCDC_SCRAMBLING_ENABLE) {
|
|
data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(0))) |
|
|
((u8)(val & BIT(0))));
|
|
} else {
|
|
data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(1))) |
|
|
(((u8)(val & BIT(0))) << 1));
|
|
}
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_TMDS_CONFIG;
|
|
break;
|
|
case HDMI_TX_SCDC_READ_ENABLE:
|
|
data.data_len = 1;
|
|
data.request_len = 1;
|
|
data.offset = HDMI_SCDC_CONFIG_0;
|
|
data_buf[0] = (u8)(val & 0x1);
|
|
break;
|
|
default:
|
|
pr_err("Cannot write to read only reg (%d)\n",
|
|
data_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctrl->ddc_data = data;
|
|
|
|
rc = hdmi_ddc_write(ctrl);
|
|
if (rc) {
|
|
pr_err("DDC Read failed for %s\n", data.what);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
|
|
u32 type, u32 to_in_num_lines)
|
|
{
|
|
if (!ctrl) {
|
|
pr_err("Invalid parameters\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (type >= HDMI_TX_DDC_TIMER_MAX) {
|
|
pr_err("Invalid timer type %d\n", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (type) {
|
|
case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS:
|
|
hdmi_scrambler_status_timer_setup(ctrl, to_in_num_lines);
|
|
break;
|
|
default:
|
|
pr_err("%d type not supported\n", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hdmi_hdcp2p2_ddc_reset(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("Invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear acks for DDC_REQ, DDC_DONE, DDC_FAILED, RXSTATUS_READY,
|
|
* RXSTATUS_MSG_SIZE
|
|
*/
|
|
reg_val = BIT(30) | BIT(17) | BIT(13) | BIT(9) | BIT(5) | BIT(1);
|
|
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, reg_val);
|
|
|
|
/* Reset DDC timers */
|
|
reg_val = BIT(0) | DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
|
|
reg_val &= ~BIT(0);
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
|
|
}
|
|
|
|
void hdmi_hdcp2p2_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
u32 reg_val;
|
|
|
|
if (!ctrl) {
|
|
pr_err("Invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
hdmi_hdcp2p2_ddc_reset(ctrl);
|
|
|
|
/* Disable HW DDC access to RxStatus register */
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
|
|
reg_val &= ~(BIT(1) | BIT(0));
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
|
|
}
|
|
|
|
int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl)
|
|
{
|
|
u32 reg_val;
|
|
u32 intr_en_mask;
|
|
u32 timeout;
|
|
u32 timer;
|
|
int rc = 0;
|
|
struct hdmi_tx_hdcp2p2_ddc_data *data;
|
|
int busy_wait_us;
|
|
|
|
if (!ctrl) {
|
|
pr_err("Invalid ctrl data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data = &ctrl->hdcp2p2_ddc_data;
|
|
if (!data) {
|
|
pr_err("Invalid ddc data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = hdmi_ddc_clear_irq(ctrl, "rxstatus");
|
|
if (rc)
|
|
return rc;
|
|
|
|
intr_en_mask = data->intr_mask;
|
|
intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
|
|
intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_DONE);
|
|
|
|
/* Disable short read for now, sinks don't support it */
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
|
|
reg_val |= BIT(4);
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
|
|
|
|
/*
|
|
* Setup the DDC timers for HDMI_HDCP2P2_DDC_TIMER_CTRL1 and
|
|
* HDMI_HDCP2P2_DDC_TIMER_CTRL2.
|
|
* Following are the timers:
|
|
* 1. DDC_REQUEST_TIMER: Timeout in hsyncs in which to wait for the
|
|
* HDCP 2.2 sink to respond to an RxStatus request
|
|
* 2. DDC_URGENT_TIMER: Time period in hsyncs to issue an urgent flag
|
|
* when an RxStatus DDC request is made but not accepted by I2C
|
|
* engine
|
|
* 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when
|
|
* a request is made and stops when it is accepted by DDC arbiter
|
|
*/
|
|
timeout = data->timeout_hsync;
|
|
timer = data->periodic_timer_hsync;
|
|
pr_debug("timeout: %d hsyncs, timer %d hsync\n", timeout, timer);
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer);
|
|
|
|
/* Set both urgent and hw-timeout fields to the same value */
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL2,
|
|
(timeout << 16 | timeout));
|
|
|
|
/* enable interrupts */
|
|
reg_val = intr_en_mask;
|
|
/* Clear interrupt status bits */
|
|
reg_val |= intr_en_mask >> 1;
|
|
|
|
pr_debug("writng HDMI_DDC_INT_CTRL0 0x%x\n", reg_val);
|
|
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, reg_val);
|
|
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL5);
|
|
/* clear and enable RxStatus read timeout */
|
|
reg_val |= BIT(2) | BIT(1);
|
|
|
|
DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL5, reg_val);
|
|
|
|
/*
|
|
* Enable hardware DDC access to RxStatus register
|
|
*
|
|
* HDMI_HW_DDC_CTRL:Bits 1:0 (RXSTATUS_DDC_ENABLE) read like this:
|
|
*
|
|
* 0 = disable HW controlled DDC access to RxStatus
|
|
* 1 = automatic on when HDCP 2.2 is authenticated and loop based on
|
|
* request timer (i.e. the hardware will loop automatically)
|
|
* 2 = force on and loop based on request timer (hardware will loop)
|
|
* 3 = enable by sw trigger and loop until interrupt is generated for
|
|
* RxStatus.reauth_req, RxStatus.ready or RxStatus.message_Size.
|
|
*
|
|
* Depending on the value of ddc_data::poll_sink, we make the decision
|
|
* to use either SW_TRIGGER(3) (poll_sink = false) which means that the
|
|
* hardware will poll sink and generate interrupt when sink responds,
|
|
* or use AUTOMATIC_LOOP(1) (poll_sink = true) which will poll the sink
|
|
* based on request timer
|
|
*/
|
|
reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
|
|
reg_val &= ~(BIT(1) | BIT(0));
|
|
|
|
busy_wait_us = data->timeout_ms * HDMI_MS_TO_US;
|
|
atomic_set(&ctrl->rxstatus_busy_wait_done, 0);
|
|
|
|
/* read method: HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER */
|
|
reg_val |= BIT(1) | BIT(0);
|
|
DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
|
|
DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);
|
|
|
|
if (data->wait) {
|
|
while (busy_wait_us > 0 &&
|
|
!atomic_read(&ctrl->rxstatus_busy_wait_done)) {
|
|
udelay(HDMI_BUSY_WAIT_DELAY_US);
|
|
busy_wait_us -= HDMI_BUSY_WAIT_DELAY_US;
|
|
};
|
|
|
|
if (busy_wait_us < 0)
|
|
busy_wait_us = 0;
|
|
|
|
data->timeout_left = busy_wait_us / HDMI_MS_TO_US;
|
|
|
|
if (!data->timeout_left) {
|
|
pr_err("sw ddc rxstatus timeout\n");
|
|
rc = -ETIMEDOUT;
|
|
}
|
|
|
|
rc = hdmi_hdcp2p2_ddc_check_status(ctrl);
|
|
hdmi_hdcp2p2_ddc_disable(ctrl);
|
|
}
|
|
|
|
return rc;
|
|
}
|