/* Copyright (c) 2010-2013, 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. * */ #include #include #include "mdss_hdmi_util.h" static struct msm_hdmi_mode_timing_info hdmi_supported_video_mode_lut[HDMI_VFRMT_MAX]; void hdmi_del_supported_mode(u32 mode) { struct msm_hdmi_mode_timing_info *ret = NULL; DEV_DBG("%s: removing %s\n", __func__, msm_hdmi_mode_2string(mode)); ret = &hdmi_supported_video_mode_lut[mode]; if (ret != NULL && ret->supported) ret->supported = false; } const struct msm_hdmi_mode_timing_info *hdmi_get_supported_mode(u32 mode) { const struct msm_hdmi_mode_timing_info *ret = NULL; if (mode >= HDMI_VFRMT_MAX) return NULL; ret = &hdmi_supported_video_mode_lut[mode]; if (ret == NULL || !ret->supported) return NULL; return ret; } /* hdmi_get_supported_mode */ int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in) { int i, vic = -1; if (!timing_in) { DEV_ERR("%s: invalid input\n", __func__); goto exit; } /* active_low_h, active_low_v and interlaced are not checked against */ for (i = 0; i < HDMI_VFRMT_MAX; i++) { struct msm_hdmi_mode_timing_info *supported_timing = &hdmi_supported_video_mode_lut[i]; if (!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) DEV_ERR("%s: timing asked is not yet supported\n", __func__); exit: DEV_DBG("%s: vic = %d timing = %s\n", __func__, vic, msm_hdmi_mode_2string((u32)vic)); return vic; } /* hdmi_get_video_id_code */ /* Table indicating the video format supported by the HDMI TX Core */ /* Valid pclk rates (Mhz): 25.2, 27, 27.03, 74.25, 148.5, 268.5, 297 */ void hdmi_setup_video_mode_lut(void) { MSM_HDMI_MODES_INIT_TIMINGS(hdmi_supported_video_mode_lut); /* Add all supported CEA modes to the lut */ MSM_HDMI_MODES_SET_SUPP_TIMINGS( hdmi_supported_video_mode_lut, MSM_HDMI_MODES_CEA); /* Add all supported extended hdmi modes to the lut */ MSM_HDMI_MODES_SET_SUPP_TIMINGS( hdmi_supported_video_mode_lut, MSM_HDMI_MODES_XTND); /* Add any other specific DVI timings (DVI modes, etc.) */ MSM_HDMI_MODES_SET_SUPP_TIMINGS( hdmi_supported_video_mode_lut, MSM_HDMI_MODES_DVI); } /* hdmi_setup_video_mode_lut */ 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) { ssize_t ret, len = 0; ret = snprintf(buf, PAGE_SIZE, "%s", hdmi_get_single_video_3d_fmt_2string( format & FRAME_PACKING)); len += ret; if (len && (format & TOP_AND_BOTTOM)) ret = snprintf(buf + len, PAGE_SIZE, ":%s", hdmi_get_single_video_3d_fmt_2string( format & TOP_AND_BOTTOM)); else ret = snprintf(buf + len, PAGE_SIZE, "%s", hdmi_get_single_video_3d_fmt_2string( format & TOP_AND_BOTTOM)); len += ret; if (len && (format & SIDE_BY_SIDE_HALF)) ret = snprintf(buf + len, PAGE_SIZE, ":%s", hdmi_get_single_video_3d_fmt_2string( format & SIDE_BY_SIDE_HALF)); else ret = snprintf(buf + len, PAGE_SIZE, "%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_print_data(struct hdmi_tx_ddc_data *ddc_data, const char *caller) { if (!ddc_data) { DEV_ERR("%s: invalid input\n", __func__); return; } DEV_DBG("%s: buf=%p, d_len=0x%x, d_addr=0x%x, no_align=%d\n", caller, ddc_data->data_buf, ddc_data->data_len, ddc_data->dev_addr, ddc_data->no_align); DEV_DBG("%s: offset=0x%x, req_len=0x%x, retry=%d, what=%s\n", caller, ddc_data->offset, ddc_data->request_len, ddc_data->retry, ddc_data->what); } /* hdmi_ddc_print_data */ static int hdmi_ddc_clear_irq(struct hdmi_tx_ddc_ctrl *ddc_ctrl, char *what) { u32 reg_val, time_out_count; if (!ddc_ctrl || !ddc_ctrl->io) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } /* clear pending and enable interrupt */ time_out_count = 0xFFFF; do { --time_out_count; /* Clear and Enable DDC interrupt */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, BIT(2) | BIT(1)); reg_val = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL); } while ((reg_val & BIT(0)) && time_out_count); if (!time_out_count) { DEV_ERR("%s[%s]: timedout\n", __func__, what); return -ETIMEDOUT; } return 0; } /*hdmi_ddc_clear_irq */ static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl, struct hdmi_tx_ddc_data *ddc_data) { u32 reg_val, ndx, time_out_count; int status = 0; int log_retry_fail; if (!ddc_ctrl || !ddc_ctrl->io || !ddc_data) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } if (!ddc_data->data_buf) { status = -EINVAL; DEV_ERR("%s[%s]: invalid buf\n", __func__, ddc_data->what); goto error; } hdmi_ddc_print_data(ddc_data, __func__); log_retry_fail = ddc_data->retry != 1; again: status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what); if (status) goto error; /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ ddc_data->dev_addr &= 0xFE; /* * 1. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #1 * DATA_RW = 0x0 (write) * DATA = linkAddress (primary link address and writing) * INDEX = 0x0 (initial offset into buffer) * INDEX_WRITE = 0x1 (setting initial offset) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, BIT(31) | (ddc_data->dev_addr << 8)); /* * 2. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #2 * DATA_RW = 0x0 (write) * DATA = offsetAddress * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, ddc_data->offset << 8); /* * 3. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #3 * DATA_RW = 0x0 (write) * DATA = linkAddress + 1 (primary link address 0x74 and reading) * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, (ddc_data->dev_addr | BIT(0)) << 8); /* Data setup is complete, now setup the transaction characteristics */ /* * 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in * order to handle characteristics of portion #1 and portion #2 * RW0 = 0x0 (write) * START0 = 0x1 (insert START bit) * 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 (it's 128 (0x80) for a blk read) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS1, BIT(0) | BIT(12) | BIT(13) | (ddc_data->request_len << 16)); /* Trigger the I2C transfer */ /* * 6. Write to HDMI_I2C_CONTROL to kick off the hardware. * Note that NOTHING has been transmitted on the DDC lines up to this * point. * TRANSACTION_CNT = 0x1 (execute transaction0 followed by * transaction1) * SEND_RESET = Set to 1 to send reset sequence * GO = 0x1 (kicks off hardware) */ INIT_COMPLETION(ddc_ctrl->ddc_sw_done); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0) | BIT(20)); time_out_count = wait_for_completion_interruptible_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, BIT(1)); if (!time_out_count) { if (ddc_data->retry-- > 0) { DEV_INFO("%s: failed timout, retry=%d\n", __func__, ddc_data->retry); goto again; } status = -ETIMEDOUT; DEV_ERR("%s: timedout(7), Int Ctrl=%08x\n", __func__, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL)); DEV_ERR("%s: DDC SW Status=%08x, HW Status=%08x\n", __func__, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_SW_STATUS), DSS_REG_R(ddc_ctrl->io, HDMI_DDC_HW_STATUS)); goto error; } /* 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) { /* SW_STATUS_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(3)); if (ddc_data->retry == 1) /* SOFT_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(1)); if (ddc_data->retry-- > 0) { DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d\n", __func__, ddc_data->what, reg_val, ddc_data->retry); DEV_DBG("%s: daddr=0x%02x,off=0x%02x,len=%d\n", __func__, ddc_data->dev_addr, ddc_data->offset, ddc_data->data_len); goto again; } status = -EIO; if (log_retry_fail) { DEV_ERR("%s(%s): failed NACK=0x%08x\n", __func__, ddc_data->what, reg_val); DEV_ERR("%s: daddr=0x%02x,off=0x%02x,len=%d\n", __func__, ddc_data->dev_addr, ddc_data->offset, ddc_data->data_len); } goto error; } /* * 8. ALL data is now available and waiting in the DDC buffer. * Read HDMI_I2C_DATA with the following fields set * RW = 0x1 (read) * DATA = BCAPS (this is field where data is pulled from) * INDEX = 0x3 (where the data has been placed in buffer by hardware) * INDEX_WRITE = 0x1 (explicitly define offset) */ /* Write this 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); } DEV_DBG("%s[%s] success\n", __func__, 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) { DEV_ERR("%s: invalid input\n", __func__); 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_ddc_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl) { u32 ddc_int_ctrl; if (!ddc_ctrl || !ddc_ctrl->io) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } ddc_int_ctrl = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL); if ((ddc_int_ctrl & BIT(2)) && (ddc_int_ctrl & BIT(0))) { /* SW_DONE INT occured, clr it */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, ddc_int_ctrl | BIT(1)); complete(&ddc_ctrl->ddc_sw_done); } DEV_DBG("%s: ddc_int_ctrl=%04x\n", __func__, ddc_int_ctrl); return 0; } /* hdmi_ddc_isr */ int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *ddc_ctrl, struct hdmi_tx_ddc_data *ddc_data) { int rc = 0; if (!ddc_ctrl || !ddc_data) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } rc = hdmi_ddc_read_retry(ddc_ctrl, ddc_data); if (!rc) return rc; if (ddc_data->no_align) { rc = hdmi_ddc_read_retry(ddc_ctrl, ddc_data); } else { ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32); rc = hdmi_ddc_read_retry(ddc_ctrl, ddc_data); } return rc; } /* hdmi_ddc_read */ int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *ddc_ctrl, struct hdmi_tx_ddc_data *ddc_data) { int status = 0; u32 reg_val, ndx, time_out_count; int log_retry_fail; int seg_addr = 0x60, seg_num = 0x01; if (!ddc_ctrl || !ddc_ctrl->io || !ddc_data) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } if (!ddc_data->data_buf) { status = -EINVAL; DEV_ERR("%s[%s]: invalid buf\n", __func__, ddc_data->what); goto error; } log_retry_fail = ddc_data->retry != 1; again: status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what); if (status) goto error; /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ ddc_data->dev_addr &= 0xFE; /* * 1. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #1 * DATA_RW = 0x0 (write) * DATA = linkAddress (primary link address and writing) * INDEX = 0x0 (initial offset into buffer) * INDEX_WRITE = 0x1 (setting initial offset) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, BIT(31) | (seg_addr << 8)); /* * 2. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #2 * DATA_RW = 0x0 (write) * DATA = offsetAddress * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, seg_num << 8); /* * 3. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #3 * DATA_RW = 0x0 (write) * DATA = linkAddress + 1 (primary link address 0x74 and reading) * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, ddc_data->dev_addr << 8); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, ddc_data->offset << 8); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, (ddc_data->dev_addr | BIT(0)) << 8); /* Data setup is complete, now setup the transaction characteristics */ /* * 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in * order to handle characteristics of portion #1 and portion #2 * RW0 = 0x0 (write) * START0 = 0x1 (insert START bit) * 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 (it's 128 (0x80) for a blk read) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS1, 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 (it's 128 (0x80) for a blk read) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_TRANS2, BIT(0) | BIT(12) | BIT(13) | (ddc_data->request_len << 16)); /* Trigger the I2C transfer */ /* * 6. Write to HDMI_I2C_CONTROL to kick off the hardware. * Note that NOTHING has been transmitted on the DDC lines up to this * point. * TRANSACTION_CNT = 0x2 (execute transaction0 followed by * transaction1) * GO = 0x1 (kicks off hardware) */ INIT_COMPLETION(ddc_ctrl->ddc_sw_done); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0) | BIT(21)); time_out_count = wait_for_completion_interruptible_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, reg_val & (~BIT(2))); if (!time_out_count) { if (ddc_data->retry-- > 0) { DEV_INFO("%s: failed timout, retry=%d\n", __func__, ddc_data->retry); goto again; } status = -ETIMEDOUT; DEV_ERR("%s: timedout(7), Int Ctrl=%08x\n", __func__, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL)); DEV_ERR("%s: DDC SW Status=%08x, HW Status=%08x\n", __func__, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_SW_STATUS), DSS_REG_R(ddc_ctrl->io, HDMI_DDC_HW_STATUS)); goto error; } /* 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) { /* SW_STATUS_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(3)); if (ddc_data->retry == 1) /* SOFT_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(1)); if (ddc_data->retry-- > 0) { DEV_DBG("%s(%s): failed NACK=0x%08x, retry=%d\n", __func__, ddc_data->what, reg_val, ddc_data->retry); DEV_DBG("%s: daddr=0x%02x,off=0x%02x,len=%d\n", __func__, ddc_data->dev_addr, ddc_data->offset, ddc_data->data_len); goto again; } status = -EIO; if (log_retry_fail) { DEV_ERR("%s(%s): failed NACK=0x%08x\n", __func__, ddc_data->what, reg_val); DEV_ERR("%s: daddr=0x%02x,off=0x%02x,len=%d\n", __func__, ddc_data->dev_addr, ddc_data->offset, ddc_data->data_len); } goto error; } /* * 8. ALL data is now available and waiting in the DDC buffer. * Read HDMI_I2C_DATA with the following fields set * RW = 0x1 (read) * DATA = BCAPS (this is field where data is pulled from) * INDEX = 0x5 (where the data has been placed in buffer by hardware) * INDEX_WRITE = 0x1 (explicitly define offset) */ /* Write this 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); } DEV_DBG("%s[%s] success\n", __func__, ddc_data->what); error: return status; } /* hdmi_ddc_read_seg */ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl, struct hdmi_tx_ddc_data *ddc_data) { u32 reg_val, ndx; int status = 0, retry = 10; u32 time_out_count; if (!ddc_ctrl || !ddc_ctrl->io || !ddc_data) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } if (!ddc_data->data_buf) { status = -EINVAL; DEV_ERR("%s[%s]: invalid buf\n", __func__, ddc_data->what); goto error; } again: status = hdmi_ddc_clear_irq(ddc_ctrl, ddc_data->what); if (status) goto error; /* Ensure Device Address has LSB set to 0 to indicate Slave addr read */ ddc_data->dev_addr &= 0xFE; /* * 1. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #1 * DATA_RW = 0x1 (write) * DATA = linkAddress (primary link address and writing) * INDEX = 0x0 (initial offset into buffer) * INDEX_WRITE = 0x1 (setting initial offset) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, BIT(31) | (ddc_data->dev_addr << 8)); /* * 2. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #2 * DATA_RW = 0x0 (write) * DATA = offsetAddress * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, ddc_data->offset << 8); /* * 3. Write to HDMI_I2C_DATA with the following fields set in order to * handle portion #3 * DATA_RW = 0x0 (write) * DATA = data_buf[ndx] * INDEX = 0x0 * INDEX_WRITE = 0x0 (auto-increment by hardware) */ for (ndx = 0; ndx < ddc_data->data_len; ++ndx) DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_DATA, ((u32)ddc_data->data_buf[ndx]) << 8); /* Data setup is complete, now setup the transaction characteristics */ /* * 4. Write to HDMI_I2C_TRANSACTION0 with the following fields set in * order to handle characteristics of portion #1 and portion #2 * RW0 = 0x0 (write) * START0 = 0x1 (insert START bit) * 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)); /* Trigger the I2C transfer */ /* * 6. Write to HDMI_I2C_CONTROL to kick off the hardware. * Note that NOTHING has been transmitted on the DDC lines up to this * point. * TRANSACTION_CNT = 0x1 (execute transaction0 followed by * transaction1) * GO = 0x1 (kicks off hardware) */ INIT_COMPLETION(ddc_ctrl->ddc_sw_done); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(0) | BIT(20)); time_out_count = wait_for_completion_interruptible_timeout( &ddc_ctrl->ddc_sw_done, HZ/2); reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL); DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_INT_CTRL, reg_val & (~BIT(2))); if (!time_out_count) { if (retry-- > 0) { DEV_INFO("%s[%s]: failed timout, retry=%d\n", __func__, ddc_data->what, retry); goto again; } status = -ETIMEDOUT; DEV_ERR("%s[%s]: timedout, Int Ctrl=%08x\n", __func__, ddc_data->what, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_INT_CTRL)); DEV_ERR("%s: DDC SW Status=%08x, HW Status=%08x\n", __func__, DSS_REG_R(ddc_ctrl->io, HDMI_DDC_SW_STATUS), DSS_REG_R(ddc_ctrl->io, HDMI_DDC_HW_STATUS)); goto error; } /* Read DDC status */ reg_val = DSS_REG_R_ND(ddc_ctrl->io, HDMI_DDC_SW_STATUS); reg_val &= 0x00001000 | 0x00002000 | 0x00004000 | 0x00008000; /* Check if any NACK occurred */ if (reg_val) { if (retry > 1) /* SW_STATUS_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(3)); else /* SOFT_RESET */ DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, BIT(1)); if (retry-- > 0) { DEV_DBG("%s[%s]: failed NACK=%08x, retry=%d\n", __func__, ddc_data->what, reg_val, retry); msleep(100); goto again; } status = -EIO; DEV_ERR("%s[%s]: failed NACK: %08x\n", __func__, ddc_data->what, reg_val); goto error; } DEV_DBG("%s[%s] success\n", __func__, ddc_data->what); error: return status; } /* hdmi_ddc_write */