diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 9279042c1b5a..70cdb45106de 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -61,6 +61,20 @@ ((d & 0xff) + ((d >> 8) & 0xff) + \ ((d >> 16) & 0xff) + ((d >> 24) & 0xff)) + +/* + * Pixel Clock to TMDS Character Rate Ratios. + */ +#define HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO 2 +#define HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO 1 +#define HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO 1 + +#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000 +#define HDMI_TX_SCRAMBLER_TIMEOUT_USEC 200000 + +#define HDMI_TX_KHZ_TO_HZ 1000 +#define HDMI_TX_MHZ_TO_HZ 1000000 + /* Enable HDCP by default */ static bool hdcp_feature_on = true; @@ -236,6 +250,39 @@ static const struct hdmi_tx_audio_acr_arry hdmi_tx_audio_acr_lut[] = { {20480, 247500} } }, }; +static int hdmi_tx_get_version(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + int rc; + int reg_val; + struct dss_io_data *io; + + rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, true); + if (rc) { + DEV_ERR("%s: Failed to read HDMI version\n", __func__); + goto fail; + } + + io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; + if (!io->base) { + DEV_ERR("%s: core io not inititalized\n", __func__); + rc = -EINVAL; + goto fail; + } + + reg_val = DSS_REG_R(io, HDMI_VERSION); + reg_val = (reg_val & 0xF0000000) >> 28; + hdmi_ctrl->hdmi_tx_ver = reg_val; + + rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, false); + if (rc) { + DEV_ERR("%s: FAILED to disable power\n", __func__); + goto fail; + } + +fail: + return rc; +} + int register_hdmi_cable_notification(struct hdmi_cable_notify *handler) { struct hdmi_tx_ctrl *hdmi_ctrl = NULL; @@ -1461,11 +1508,9 @@ static int hdmi_tx_video_setup(struct hdmi_tx_ctrl *hdmi_ctrl) if (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format == MDP_Y_CBCR_H2V2) div = 1; - total_h = (timing->active_h >> div) + (timing->front_porch_h >> div) + - (timing->back_porch_h >> div) + (timing->pulse_width_h >> div) - - 1; - total_v = timing->active_v + timing->front_porch_v + - timing->back_porch_v + timing->pulse_width_v - 1; + total_h = (hdmi_tx_get_h_total(timing) >> div) - 1; + total_v = hdmi_tx_get_v_total(timing) - 1; + if (((total_v << 16) & 0xE0000000) || (total_h & 0xFFFFE000)) { DEV_ERR("%s: total v=%d or h=%d is larger than supported\n", __func__, total_v, total_h); @@ -2740,6 +2785,107 @@ static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl) DEV_INFO("HDMI Audio: Disabled\n"); } /* hdmi_tx_audio_off */ +static int hdmi_tx_setup_scrambler(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + int rc = 0; + u32 rate = 0; + u32 reg_val = 0; + bool scrambler_on = false; + struct dss_io_data *io = NULL; + const struct msm_hdmi_mode_timing_info *timing = NULL; + + if (!hdmi_ctrl) { + DEV_ERR("%s: Bad input parameters\n", __func__); + return -EINVAL; + } + + io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; + if (!io->base) { + DEV_ERR("%s: core io is not initialized\n", __func__); + return -EINVAL; + } + + timing = hdmi_ctrl->vid_cfg.timing; + if (!timing) { + DEV_ERR("%s: Invalid timing info\n", __func__); + return -EINVAL; + } + + /* Scrambling is supported from HDMI TX 4.0 */ + if (hdmi_ctrl->hdmi_tx_ver < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) { + DEV_DBG("%s: HDMI TX does not support scrambling\n", __func__); + return 0; + } + + switch (hdmi_ctrl->vid_cfg.avi_iframe.pixel_format) { + case MDP_Y_CBCR_H2V2: + rate = timing->pixel_freq / + HDMI_TX_YUV420_24BPP_PCLK_TMDS_CH_RATE_RATIO; + break; + case MDP_Y_CBCR_H2V1: + rate = timing->pixel_freq / + HDMI_TX_YUV422_24BPP_PCLK_TMDS_CH_RATE_RATIO; + break; + default: + rate = timing->pixel_freq / + HDMI_TX_RGB_24BPP_PCLK_TMDS_CH_RATE_RATIO; + break; + } + + if (rate > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) { + scrambler_on = true; + } else { + if (hdmi_edid_get_sink_scrambler_support( + hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])) + scrambler_on = true; + } + + if (scrambler_on) { + reg_val = DSS_REG_R(io, HDMI_CTRL); + reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */ + reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */ + + DSS_REG_W(io, HDMI_CTRL, reg_val); + + rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, + 0x1); + } else { + rc = hdmi_scdc_write(&hdmi_ctrl->ddc_ctrl, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, + 0x0); + } + + if (rc) { + DEV_ERR("%s: SCDC write failed\n", __func__); + return rc; + } + + /* + * Setup hardware to periodically check for scrambler status bit on the + * sink. Sink should set this bit with in 200ms after scrambler is + * enabled. + */ + if (scrambler_on) { + u64 mult; + u64 div; + /* calculate number of lines sent in 200ms */ + mult = HDMI_TX_SCRAMBLER_TIMEOUT_USEC * + ((u64)timing->pixel_freq * HDMI_TX_KHZ_TO_HZ); + div = hdmi_tx_get_v_total(timing) * HDMI_TX_MHZ_TO_HZ; + if (div) + do_div(mult, div); + else + mult = 0; + + rc = hdmi_setup_ddc_timers(&hdmi_ctrl->ddc_ctrl, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, + (u32)mult); + } + + return rc; +} + static int hdmi_tx_start(struct hdmi_tx_ctrl *hdmi_ctrl) { int rc = 0; @@ -2782,6 +2928,12 @@ static int hdmi_tx_start(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_tx_set_spd_infoframe(hdmi_ctrl); } + rc = hdmi_tx_setup_scrambler(hdmi_ctrl); + if (rc) { + DEV_ERR("%s: Scrambler setup failed\n", __func__); + hdmi_tx_set_mode(hdmi_ctrl, false); + return rc; + } /* todo: CEC */ DEV_INFO("%s: HDMI Core: Initialized\n", __func__); @@ -3185,7 +3337,8 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data) queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work); } - if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl)) + if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl, + hdmi_ctrl->hdmi_tx_ver)) DEV_ERR("%s: hdmi_ddc_isr failed\n", __func__); if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]) @@ -4172,7 +4325,6 @@ static int hdmi_tx_probe(struct platform_device *pdev) struct device_node *of_node = pdev->dev.of_node; struct hdmi_tx_ctrl *hdmi_ctrl = NULL; struct mdss_panel_cfg *pan_cfg = NULL; - if (!of_node) { DEV_ERR("%s: FAILED: of_node not found\n", __func__); rc = -ENODEV; @@ -4236,6 +4388,13 @@ static int hdmi_tx_probe(struct platform_device *pdev) goto failed_dev_init; } + rc = hdmi_tx_get_version(hdmi_ctrl); + if (rc) { + DEV_ERR("%s: FAILED: hdmi_tx_get_version. rc=%d\n", + __func__, rc); + goto failed_reg_panel; + } + rc = hdmi_tx_register_panel(hdmi_ctrl); if (rc) { DEV_ERR("%s: FAILED: register_panel. rc=%d\n", __func__, rc); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 810116ecf70e..edfae7c29a72 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. +/* 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 @@ -125,6 +125,7 @@ struct hdmi_video_config { struct hdmi_tx_ctrl { struct platform_device *pdev; + u32 hdmi_tx_ver; struct hdmi_tx_platform_data pdata; struct mdss_panel_data panel_data; struct mdss_util_intf *mdss_util; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 4d8105cbd147..88c26d901ca9 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. +/* 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 @@ -17,11 +17,71 @@ #define RESOLUTION_NAME_STR_LEN 30 +#define HDMI_SCDC_UNKNOWN_REGISTER "Unknown register" + static struct msm_hdmi_mode_timing_info hdmi_supported_video_mode_lut[HDMI_VFRMT_MAX]; static char res_buf[RESOLUTION_NAME_STR_LEN]; +static void hdmi_scrambler_status_timer_setup(struct hdmi_tx_ddc_ctrl *ctrl, + u32 to_in_num_lines) +{ + u32 reg_val; + + DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL, + to_in_num_lines); + DSS_REG_W(ctrl->io, HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2, + 0xFFFF); + reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL5); + reg_val |= BIT(10); + DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL5, reg_val); + + reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL2); + /* Trigger interrupt if scrambler status is 0 or DDC failure */ + reg_val |= BIT(10); + reg_val &= 0x18000; + reg_val |= (0x2 << 15); + DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL2, reg_val); + + /* Enable DDC access */ + reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL); + + reg_val &= ~0x300; + reg_val |= (0x1 << 8); + DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val); +} + +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; + } +} + const char *msm_hdmi_mode_2string(u32 mode) { static struct msm_hdmi_mode_timing_info *ri; @@ -454,9 +514,11 @@ void hdmi_ddc_config(struct hdmi_tx_ddc_ctrl *ddc_ctrl) DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_REF, (1 << 16) | (19 << 0)); } /* hdmi_ddc_config */ -int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl) +int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl, u32 version) { u32 ddc_int_ctrl; + u32 ddc_timer_int; + bool scrambler_timer_off = false; if (!ddc_ctrl || !ddc_ctrl->io) { DEV_ERR("%s: invalid input\n", __func__); @@ -472,7 +534,44 @@ int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl) } DEV_DBG("%s: ddc_int_ctrl=%04x\n", __func__, ddc_int_ctrl); + if (version < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) + goto end; + ddc_timer_int = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL2); + if (ddc_timer_int & BIT(12)) { + /* DDC_INT_CTRL2.SCRAMBLER_STATUS_NOT is set */ + DEV_ERR("%s: Sink cannot descramble the signal\n", + __func__); + /* Clear interrupt */ + ddc_timer_int |= BIT(14); + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL2, + ddc_timer_int); + scrambler_timer_off = true; + } + + ddc_timer_int = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL5); + if (ddc_timer_int & BIT(8)) { + /* + * DDC_INT_CTRL5.SCRAMBLER_STATUS_DDC_REQ_TIMEOUT + * is set + */ + DEV_ERR( + "%s: DDC timeout while reading SCRAMBLER STATUS\n", + __func__); + ddc_timer_int |= BIT(13); + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL5, + ddc_timer_int); + scrambler_timer_off = true; + } + + /* Disable scrambler status timer if it has been acknowledged */ + if (scrambler_timer_off) { + u32 regval = DSS_REG_R_ND(ddc_ctrl->io, + HDMI_HW_DDC_CTRL); + regval &= 0x300; + DSS_REG_W_ND(ddc_ctrl->io, HDMI_HW_DDC_CTRL, regval); + } +end: return 0; } /* hdmi_ddc_isr */ @@ -755,20 +854,10 @@ again: * STOP0 = 0x0 (do NOT insert STOP bit) * CNT0 = 0x1 (single byte transaction excluding address) */ - DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS0, BIT(12) | BIT(16)); - - /* - * 5. Write to HDMI_I2C_TRANSACTION1 with the following fields set in - * order to handle characteristics of portion #3 - * RW1 = 0x1 (read) - * START1 = 0x1 (insert START bit) - * STOP1 = 0x1 (insert STOP bit) - * CNT1 = data_len (0xN (write N bytes of data)) - * Byte count for second transition (excluding the first - * Byte which is usually the address) - */ - DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS1, - BIT(13) | ((ddc_data->data_len-1) << 16)); + reg_val = (ddc_data->data_len + 1) << 16; + reg_val |= BIT(12); + reg_val |= BIT(13); + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS0, reg_val); /* Trigger the I2C transfer */ /* @@ -780,7 +869,7 @@ again: * GO = 0x1 (kicks off hardware) */ reinit_completion(&ddc_ctrl->ddc_sw_done); - DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0) | BIT(20)); + DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0)); time_out_count = wait_for_completion_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); @@ -834,3 +923,213 @@ again: error: return status; } /* hdmi_ddc_write */ + + +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) { + DEV_ERR("%s: Bad Parameters\n", __func__); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + DEV_ERR("%s: Unsupported data type\n", __func__); + return -EINVAL; + } + + data.what = hdmi_scdc_reg2string(data_type); + data.dev_addr = 0xA8; + data.no_align = true; + 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; + } + + rc = hdmi_ddc_read(ctrl, &data); + if (rc) { + DEV_ERR("%s: DDC Read failed for %s\n", __func__, 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) { + DEV_ERR("%s: Bad Parameters\n", __func__); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + DEV_ERR("%s: Unsupported data type\n", __func__); + return -EINVAL; + } + + data.what = hdmi_scdc_reg2string(data_type); + data.dev_addr = 0xA8; + data.no_align = true; + 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.no_align = true; + rdata.retry = 2; + rdata.data_buf = &read_val; + rdata.data_len = 1; + rdata.offset = HDMI_SCDC_TMDS_CONFIG; + rdata.request_len = 1; + rc = hdmi_ddc_read(ctrl, &rdata); + if (rc) { + DEV_ERR("%s: scdc read failed\n", __func__); + 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: + DEV_ERR("%s Cannot write to read only reg (%d)\n", __func__, + data_type); + return -EINVAL; + } + + rc = hdmi_ddc_write(ctrl, &data); + if (rc) { + DEV_ERR("%s: DDC Read failed for %s\n", __func__, 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) { + DEV_ERR("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + if (type >= HDMI_TX_DDC_TIMER_MAX) { + DEV_ERR("%s: Invalid timer type %d\n", __func__, type); + return -EINVAL; + } + + switch (type) { + case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS: + hdmi_scrambler_status_timer_setup(ctrl, to_in_num_lines); + break; + default: + DEV_ERR("%s: %d type not supported\n", __func__, type); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h index cb9fb02998ad..f3f510c1d694 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. +/* 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 @@ -207,6 +207,19 @@ #define HDMI_CEC_RD_TOTAL_RANGE (0x00000368) #define HDMI_CEC_RD_ERR_RESP_LO (0x0000036C) #define HDMI_CEC_WR_CHECK_CONFIG (0x00000370) +#define HDMI_DDC_INT_CTRL0 (0x00000430) +#define HDMI_DDC_INT_CTRL1 (0x00000434) +#define HDMI_DDC_INT_CTRL2 (0x00000438) +#define HDMI_DDC_INT_CTRL3 (0x0000043C) +#define HDMI_DDC_INT_CTRL4 (0x00000440) +#define HDMI_DDC_INT_CTRL5 (0x00000444) +#define HDMI_SCRAMBLER_STATUS_DDC_CTRL (0x00000464) +#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL (0x00000468) +#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 (0x0000046C) +#define HDMI_SCRAMBLER_STATUS_DDC_STATUS (0x00000470) +#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS (0x00000474) +#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 (0x00000478) +#define HDMI_HW_DDC_CTRL (0x000004CC) /* HDMI PHY Registers */ #define HDMI_PHY_ANA_CFG0 (0x00000000) @@ -232,6 +245,25 @@ #define LPASS_LPAIF_RDDMA_CTL0 (0xFE152000) #define LPASS_LPAIF_RDDMA_PER_CNT0 (0x00000014) +/* TX major version that supports scrambling */ +#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04 + +/* HDMI SCDC register offsets */ +#define HDMI_SCDC_UPDATE_0 0x10 +#define HDMI_SCDC_UPDATE_1 0x11 +#define HDMI_SCDC_TMDS_CONFIG 0x20 +#define HDMI_SCDC_SCRAMBLER_STATUS 0x21 +#define HDMI_SCDC_CONFIG_0 0x30 +#define HDMI_SCDC_STATUS_FLAGS_0 0x40 +#define HDMI_SCDC_STATUS_FLAGS_1 0x41 +#define HDMI_SCDC_ERR_DET_0_L 0x50 +#define HDMI_SCDC_ERR_DET_0_H 0x51 +#define HDMI_SCDC_ERR_DET_1_L 0x52 +#define HDMI_SCDC_ERR_DET_1_H 0x53 +#define HDMI_SCDC_ERR_DET_2_L 0x54 +#define HDMI_SCDC_ERR_DET_2_H 0x55 +#define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 + enum hdmi_tx_feature_type { HDMI_TX_FEAT_EDID, HDMI_TX_FEAT_HDCP, @@ -239,6 +271,30 @@ enum hdmi_tx_feature_type { HDMI_TX_FEAT_MAX, }; +enum hdmi_tx_scdc_access_type { + HDMI_TX_SCDC_SCRAMBLING_STATUS, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + HDMI_TX_SCDC_CLOCK_DET_STATUS, + HDMI_TX_SCDC_CH0_LOCK_STATUS, + HDMI_TX_SCDC_CH1_LOCK_STATUS, + HDMI_TX_SCDC_CH2_LOCK_STATUS, + HDMI_TX_SCDC_CH0_ERROR_COUNT, + HDMI_TX_SCDC_CH1_ERROR_COUNT, + HDMI_TX_SCDC_CH2_ERROR_COUNT, + HDMI_TX_SCDC_READ_ENABLE, + HDMI_TX_SCDC_MAX, +}; + +enum hdmi_tx_ddc_timer_type { + HDMI_TX_DDC_TIMER_HDCP2P2_RD_MSG, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, + HDMI_TX_DDC_TIMER_UPDATE_FLAGS, + HDMI_TX_DDC_TIMER_STATUS_FLAGS, + HDMI_TX_DDC_TIMER_CED, + HDMI_TX_DDC_TIMER_MAX, +}; + struct hdmi_tx_ddc_ctrl { struct dss_io_data *io; struct completion ddc_sw_done; @@ -268,9 +324,14 @@ void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, u32 type); /* DDC */ void hdmi_ddc_config(struct hdmi_tx_ddc_ctrl *); -int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *); +int hdmi_ddc_isr(struct hdmi_tx_ddc_ctrl *, u32 version); int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *); int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *); int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *, struct hdmi_tx_ddc_data *); +int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val); +int hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val); +int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl, + u32 type, u32 to_in_num_lines); + #endif /* __HDMI_UTIL_H__ */ diff --git a/include/uapi/video/msm_hdmi_modes.h b/include/uapi/video/msm_hdmi_modes.h index 0fa0820a4e85..28276ea02416 100644 --- a/include/uapi/video/msm_hdmi_modes.h +++ b/include/uapi/video/msm_hdmi_modes.h @@ -259,4 +259,24 @@ static inline void MSM_HDMI_MODES_SET_SUPP_TIMINGS( } } +static inline int hdmi_tx_get_v_total(const struct msm_hdmi_mode_timing_info *t) +{ + if (t) { + return t->active_v + t->front_porch_v + t->pulse_width_v + + t->back_porch_v; + } + + return 0; +} + +static inline int hdmi_tx_get_h_total(const struct msm_hdmi_mode_timing_info *t) +{ + if (t) { + return t->active_h + t->front_porch_h + t->pulse_width_h + + t->back_porch_h; + } + + return 0; +} + #endif /* _UAPI_MSM_HDMI_MODES_H__ */