/* * SiI8620 Linux Driver * * Copyright (C) 2013-2014 Silicon Image, Inc. * * 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 version 2. * This program is distributed AS-IS WITHOUT ANY WARRANTY of any * kind, whether express or implied; INCLUDING without the implied warranty * of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE or NON-INFRINGEMENT. * See the GNU General Public License for more details at * http://www.gnu.org/licenses/gpl-2.0.html. */ #include #include #include #include #include #include #include #include "si_fw_macros.h" #include "si_infoframe.h" #include "si_edid.h" #include "si_mhl_defs.h" #include "si_mhl2_edid_3d_api.h" #include "si_8620_internal_api.h" #include "si_mhl_tx_hw_drv_api.h" #ifdef MEDIA_DATA_TUNNEL_SUPPORT #include "si_mdt_inputdev.h" #endif #include "mhl_rcp_inputdev.h" #if (INCLUDE_RBP == 1) #include "mhl_rbp_inputdev.h" #endif #include "mhl_linux_tx.h" #include "mhl_supp.h" #include "si_app_devcap.h" #include "platform.h" #include "si_mhl_callback_api.h" #include "si_8620_drv.h" /* We choose 150 ms as the sample period due to hardware taking 140 ms to distinguish between an error and a disconnection. We will keep the last three samples and discard most recent two. */ #define LOCAL_eCBUS_ERR_SAMPLE_PERIOD 150 static void si_mhl_tx_refresh_peer_devcap_entries_impl( struct mhl_dev_context *dev_context, const char *function, int line); #define si_mhl_tx_refresh_peer_devcap_entries(context) \ si_mhl_tx_refresh_peer_devcap_entries_impl(context, __func__, __LINE__) static void cbus_abort_timer_callback(void *callback_param); static void bist_timer_callback(void *callback_param); static void cbus_dcap_rdy_timeout_callback(void *callback_param); static void cbus_dcap_chg_timeout_callback(void *callback_param); static void cbus_mode_up_timeout_callback(void *callback_param); uint16_t plim_table[] = { 500, 900, 1500, 100, 2000, 0, 0, 0 }; #ifdef DEBUG static char *get_cbus_command_string(int command) { #define CBUS_COMMAND_CASE(command) case command: return #command; switch (command) { CBUS_COMMAND_CASE(MHL_ACK) CBUS_COMMAND_CASE(MHL_NACK) CBUS_COMMAND_CASE(MHL_ABORT) CBUS_COMMAND_CASE(MHL_WRITE_STAT) CBUS_COMMAND_CASE(MHL_SET_INT) CBUS_COMMAND_CASE(MHL_READ_DEVCAP_REG) CBUS_COMMAND_CASE(MHL_READ_XDEVCAP_REG) CBUS_COMMAND_CASE(MHL_GET_STATE) CBUS_COMMAND_CASE(MHL_GET_VENDOR_ID) CBUS_COMMAND_CASE(MHL_SET_HPD) CBUS_COMMAND_CASE(MHL_CLR_HPD) CBUS_COMMAND_CASE(MHL_SET_CAP_ID) CBUS_COMMAND_CASE(MHL_GET_CAP_ID) CBUS_COMMAND_CASE(MHL_MSC_MSG) CBUS_COMMAND_CASE(MHL_GET_SC1_ERRORCODE) CBUS_COMMAND_CASE(MHL_GET_DDC_ERRORCODE) CBUS_COMMAND_CASE(MHL_GET_MSC_ERRORCODE) CBUS_COMMAND_CASE(MHL_WRITE_BURST) CBUS_COMMAND_CASE(MHL_GET_SC3_ERRORCODE) CBUS_COMMAND_CASE(MHL_WRITE_XSTAT) CBUS_COMMAND_CASE(MHL_READ_DEVCAP) CBUS_COMMAND_CASE(MHL_READ_XDEVCAP) CBUS_COMMAND_CASE(MHL_READ_EDID_BLOCK) CBUS_COMMAND_CASE(MHL_SEND_3D_REQ_OR_FEAT_REQ) } return "unknown"; } #else #define get_cbus_command_string(command) "" #endif static char *rapk_error_code_string[] = { "NO_ERROR", "UNRECOGNIZED_ACTION_CODE", "UNSUPPORTED_ACTION_CODE", "RESPONDER_BUSY" }; struct mhl_dev_context *get_mhl_device_context(void *context) { struct mhl_dev_context *dev_context = context; if (dev_context->signature != MHL_DEV_CONTEXT_SIGNATURE) dev_context = container_of(context, struct mhl_dev_context, drv_context); return dev_context; } void init_cbus_queue(struct mhl_dev_context *dev_context) { struct cbus_req *entry; int idx; INIT_LIST_HEAD(&dev_context->cbus_queue); INIT_LIST_HEAD(&dev_context->cbus_free_list); dev_context->current_cbus_req = NULL; /* Place pre-allocated CBUS queue entries on the free list */ for (idx = 0; idx < NUM_CBUS_EVENT_QUEUE_EVENTS; idx++) { entry = &dev_context->cbus_req_entries[idx]; memset(entry, 0, sizeof(struct cbus_req)); list_add(&entry->link, &dev_context->cbus_free_list); } } static struct cbus_req *get_free_cbus_queue_entry_impl( struct mhl_dev_context *dev_context, const char *function, int line) { struct cbus_req *req; struct list_head *entry; if (list_empty(&dev_context->cbus_free_list)) { int i; MHL_TX_GENERIC_DBG_PRINT(-1, "No free cbus queue entries available %s:%d\n", function, line); list_for_each(entry, &dev_context->cbus_queue) { req = list_entry(entry, struct cbus_req, link); MHL_TX_GENERIC_DBG_PRINT(-1, "cbus_queue entry %d called from %s:%d\n\t%s " "0x%02x 0x%02x\n", req->sequence, req->function, req->line, get_cbus_command_string(req->command), req->reg, req->reg_data); } for (i = 0; i < ARRAY_SIZE(dev_context->cbus_req_entries); ++i) { req = &dev_context->cbus_req_entries[i]; MHL_TX_GENERIC_DBG_PRINT(-1, "%d cbus_req_entries[%d] called from %s:%d\n", req->sequence, i, req->function, req->line); } return NULL; } entry = dev_context->cbus_free_list.next; list_del(entry); req = list_entry(entry, struct cbus_req, link); /* Start clean */ req->status.flags.cancel = 0; req->completion = NULL; req->function = function; req->line = line; req->sequence = dev_context->sequence++; /*MHL_TX_DBG_ERR(,"q %d get:0x%p %s:%d\n", req->sequence,req,function,line); */ return req; } #define get_free_cbus_queue_entry(context) \ get_free_cbus_queue_entry_impl(context, __func__, __LINE__) static void return_cbus_queue_entry_impl(struct mhl_dev_context *dev_context, struct cbus_req *pReq, const char *function, int line) { /* MHL_TX_DBG_ERR(,"q ret:0x%p %s:%d\n",pReq,function,line); */ list_add(&pReq->link, &dev_context->cbus_free_list); } #define return_cbus_queue_entry(context, req) \ return_cbus_queue_entry_impl(context, req, __func__, __LINE__) void queue_cbus_transaction(struct mhl_dev_context *dev_context, struct cbus_req *pReq) { MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", pReq->command, (MHL_MSC_MSG == pReq->command) ? pReq->msg_data[0] : pReq->reg, (MHL_MSC_MSG == pReq->command) ? pReq->msg_data[1] : pReq->reg_data); list_add_tail(&pReq->link, &dev_context->cbus_queue); /* try to send immediately, if possible */ si_mhl_tx_drive_states(dev_context); } void queue_priority_cbus_transaction(struct mhl_dev_context *dev_context, struct cbus_req *req) { MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", req->command, (MHL_MSC_MSG == req->command) ? req->msg_data[0] : req->reg, (MHL_MSC_MSG == req->command) ? req->msg_data[1] : req->reg_data); list_add(&req->link, &dev_context->cbus_queue); } struct cbus_req *peek_next_cbus_transaction(struct mhl_dev_context *dev_context) { struct list_head *entry; struct cbus_req *req; if (list_empty(&dev_context->cbus_queue)) { MHL_TX_DBG_INFO("Queue empty\n"); return NULL; } entry = dev_context->cbus_queue.next; req = list_entry(entry, struct cbus_req, link); return req; } struct cbus_req *get_next_cbus_transaction(struct mhl_dev_context *dev_context) { struct cbus_req *req; struct list_head *entry; enum cbus_mode_e cbus_mode; if (list_empty(&dev_context->cbus_queue)) { MHL_TX_DBG_INFO("Queue empty\n"); return NULL; } if (dev_context->misc_flags.flags.cbus_abort_delay_active) { MHL_TX_DBG_INFO("CBUS abort delay in progress " "can't send any messages\n"); return NULL; } cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); switch (cbus_mode) { case CM_NO_CONNECTION: case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_S: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: case CM_TRANSITIONAL_TO_eCBUS_D_BIST: case CM_TRANSITIONAL_TO_eCBUS_D: case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: MHL_TX_DBG_INFO("CBUS not available\n"); return NULL; default: break; } entry = dev_context->cbus_queue.next; req = list_entry(entry, struct cbus_req, link); list_del(entry); MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", req->command, (MHL_MSC_MSG == req->command) ? req->msg_data[0] : req->reg, (MHL_MSC_MSG == req->command) ? req->msg_data[1] : req->reg_data); return req; } #ifdef DEBUG static char *get_block_id_string(int id) { #define BURST_ID_CASE(id) case id: return #id; switch (id) { BURST_ID_CASE(MHL_TEST_ADOPTER_ID) BURST_ID_CASE(burst_id_3D_VIC) BURST_ID_CASE(burst_id_3D_DTD) BURST_ID_CASE(burst_id_HEV_VIC) BURST_ID_CASE(burst_id_HEV_DTDA) BURST_ID_CASE(burst_id_HEV_DTDB) BURST_ID_CASE(burst_id_VC_ASSIGN) BURST_ID_CASE(burst_id_VC_CONFIRM) BURST_ID_CASE(burst_id_AUD_DELAY) BURST_ID_CASE(burst_id_ADT_BURSTID) BURST_ID_CASE(burst_id_BIST_SETUP) BURST_ID_CASE(burst_id_BIST_RETURN_STAT) BURST_ID_CASE(burst_id_EMSC_SUPPORT) BURST_ID_CASE(burst_id_HID_PAYLOAD) BURST_ID_CASE(burst_id_BLK_RCV_BUFFER_INFO) BURST_ID_CASE(burst_id_BITS_PER_PIXEL_FMT) BURST_ID_CASE(LOCAL_ADOPTER_ID) } return "unknown"; } #else #define get_block_id_string(command) "" #endif static struct block_req *start_new_block_marshalling_req_impl( struct mhl_dev_context *dev_context, const char *function, int line) { struct block_req *req; struct list_head *entry; union SI_PACK_THIS_STRUCT emsc_payload_t *payload; if (list_empty(&dev_context->block_protocol.free_list)) { int i; MHL_TX_DBG_ERR("No free block queue entries available %s:%d\n", function, line); list_for_each(entry, &dev_context->block_protocol.queue) { req = list_entry(entry, struct block_req, link); MHL_TX_DBG_ERR("block_protocol.queue entry %d called " "from %s:%d\n\t%s 0x%04x\n", req->sequence, req->function, req->line, get_block_id_string(BURST_ID(req->payload-> hdr_and_burst_id.burst_id)), BURST_ID(req->payload-> hdr_and_burst_id.burst_id)); } for (i = 0; i < ARRAY_SIZE(dev_context->block_protocol.req_entries); ++i) { req = &dev_context->block_protocol.req_entries[i]; MHL_TX_DBG_ERR("%d block_protocol.req_entries[%d] " "called from %s:%d\n", req->sequence, i, req->function, req->line); } return NULL; } entry = dev_context->block_protocol.free_list.next; list_del(entry); req = list_entry(entry, struct block_req, link); payload = req->payload; req->function = function; req->line = line; req->sequence = dev_context->block_protocol.sequence++; req->sub_payload_size = 0; req->space_remaining = sizeof(payload->as_bytes) - sizeof(struct SI_PACK_THIS_STRUCT standard_transport_header_t); dev_context->block_protocol.marshalling_req = req; MHL_TX_DBG_WARN("q %d get:0x%p %s:%d\n", req->sequence, req, function, line); return req; } #define start_new_block_marshalling_req(context) \ start_new_block_marshalling_req_impl(context, __func__, __LINE__) static void return_block_queue_entry_impl(struct mhl_dev_context *dev_context, struct block_req *pReq, const char *function, int line) { /* MHL_TX_DBG_ERR(,"q ret:0x%p %s:%d\n",pReq,function,line); */ list_add(&pReq->link, &dev_context->block_protocol.free_list); } #define return_block_queue_entry(context, req) \ return_block_queue_entry_impl(context, req, __func__, __LINE__) struct block_req *get_next_block_transaction(struct mhl_dev_context *dev_context) { struct block_req *req; struct list_head *entry; if (list_empty(&dev_context->block_protocol.queue)) { MHL_TX_DBG_INFO("Queue empty\n"); return NULL; } entry = dev_context->block_protocol.queue.next; list_del(entry); req = list_entry(entry, struct block_req, link); MHL_TX_DBG_INFO("0x%04x\n", req->payload->hdr_and_burst_id.burst_id); return req; } void si_mhl_tx_push_block_transactions(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); struct block_req *req; struct list_head *entry; uint16_t ack_byte_count; /* Send the requests out, starting with those in the queue */ ack_byte_count = hw_context->block_protocol.received_byte_count; req = dev_context->block_protocol.marshalling_req; if (NULL == req) { MHL_TX_DBG_ERR("%s wayward pointer%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); return; } /* Need to send the unload count even if no other payload. */ /* If there is no payload and 2 or less unload bytes, don't bother -- * they will be unloaded with the next payload write. * This could violate the MHL3 eMSC block transfer protocol requirement * to ACK within 50ms, but it can also cause an CK feedback loop * between the two peer devices. If the unload count is larger, * go ahead and send it even if it does cause an extra response * from the other side. */ if ((ack_byte_count > EMSC_BLK_STD_HDR_LEN) || req->sub_payload_size) { /* don't use queue_block_transaction here */ list_add_tail(&req->link, &dev_context->block_protocol.queue); dev_context->block_protocol.marshalling_req = NULL; } while (!list_empty(&dev_context->block_protocol.queue)) { uint16_t payload_size; entry = dev_context->block_protocol.queue.next; req = list_entry(entry, struct block_req, link); payload_size = sizeof(req->payload->hdr_and_burst_id.tport_hdr) + req->sub_payload_size; MHL_TX_DBG_INFO( "=== sub_payload_size: %d, ack count: %d\n", req->sub_payload_size, hw_context->block_protocol.received_byte_count); if (hw_context->block_protocol.peer_blk_rx_buf_avail < payload_size) { /* not enough space in peer's receive buffer, so wait to send until later */ MHL_TX_DBG_ERR("==== not enough space in peer's " "receive buffer, send later payload_size:0x%x," "blk_rx_buffer_avail:0x%x sub-payload size:" "0x%x tport_hdr size:0x%x\n", payload_size, hw_context->block_protocol. peer_blk_rx_buf_avail, req->sub_payload_size, sizeof(req->payload-> hdr_and_burst_id.tport_hdr)); break; } list_del(entry); hw_context->block_protocol.peer_blk_rx_buf_avail -= payload_size; MHL_TX_DBG_WARN("PEER Buffer Available After Write: %d\n", hw_context->block_protocol. peer_blk_rx_buf_avail); req->payload->hdr_and_burst_id.tport_hdr.length_remaining = req->sub_payload_size; req->count = payload_size; if (req->count < EMSC_PAYLOAD_LEN) { /* The driver layer will fill */ /* in the rx_unload_ack field */ mhl_tx_drv_send_block((struct drv_hw_context *) (&dev_context->drv_context), req); /* return request to free list */ return_block_queue_entry(dev_context, req); } } if (NULL == dev_context->block_protocol.marshalling_req) { /* now start a new marshalling request */ req = start_new_block_marshalling_req(dev_context); if (NULL == req) { MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } } void *si_mhl_tx_get_sub_payload_buffer(struct mhl_dev_context *dev_context, uint8_t size) { void *buffer; struct block_req *req; union emsc_payload_t *payload; req = dev_context->block_protocol.marshalling_req; if (NULL == req) { /* this can only happen if we run out of free requests */ /* TODO: Lee - can't call this here, because the first thing * it does is check to see if * dev_context->block_protocol.marshalling_req == NULL, * which we know it is, and if it finds NULL, it prints an * error and returns. So why bother? */ si_mhl_tx_push_block_transactions(dev_context); req = dev_context->block_protocol.marshalling_req; if (NULL == req) { MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); return NULL; } } if (size > req->space_remaining) { MHL_TX_DBG_INFO("0x%04x\n", req->payload->hdr_and_burst_id.burst_id); list_add_tail(&req->link, &dev_context->block_protocol.queue); si_mhl_tx_push_block_transactions(dev_context); req = start_new_block_marshalling_req(dev_context); if (NULL == req) { MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); return NULL; } } if (size > EMSC_BLK_CMD_MAX_LEN) return NULL; payload = req->payload; buffer = &payload->as_bytes[sizeof(payload->as_bytes) - req->space_remaining]; req->space_remaining -= size; req->sub_payload_size += size; return buffer; } /* * Send the BLK_RCV_BUFFER_INFO BLOCK message. This must be the first BLOCK * message sent, but we will wait until the XDEVCAPs have been read to allow * time for each side to initialize their eMSC message handling. */ void si_mhl_tx_send_blk_rcv_buf_info(struct mhl_dev_context *context) { uint16_t rcv_buffer_size; struct SI_PACK_THIS_STRUCT block_rcv_buffer_info_t *buf_info; struct SI_PACK_THIS_STRUCT MHL3_emsc_support_data_t *emsc_supp; size_t total_size; total_size = sizeof(*buf_info) + sizeof(*emsc_supp) - sizeof(emsc_supp->payload.burst_ids) + sizeof(emsc_supp->payload.burst_ids[0]); buf_info = si_mhl_tx_get_sub_payload_buffer(context, total_size); if (NULL == buf_info) { MHL_TX_DBG_ERR("%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } else { /* next byte after blk_rcv_buf_info */ emsc_supp = (struct SI_PACK_THIS_STRUCT MHL3_emsc_support_data_t *) (buf_info + 1); buf_info->burst_id.low = (uint8_t)(burst_id_BLK_RCV_BUFFER_INFO & 0xFF); buf_info->burst_id.high = (uint8_t)(burst_id_BLK_RCV_BUFFER_INFO >> 8); rcv_buffer_size = si_mhl_tx_drv_get_blk_rcv_buf_size(); buf_info->blk_rcv_buffer_size_low = (uint8_t)(rcv_buffer_size & 0xFF); buf_info->blk_rcv_buffer_size_high = (uint8_t)(rcv_buffer_size >> 8); emsc_supp->header.burst_id.high = (uint8_t) (burst_id_EMSC_SUPPORT >> 8); emsc_supp->header.burst_id.low = (uint8_t) (burst_id_EMSC_SUPPORT & 0xFF); emsc_supp->header.checksum = 0; emsc_supp->header.total_entries = 1; emsc_supp->header.sequence_index = 1; emsc_supp->num_entries_this_burst = 1; emsc_supp->payload.burst_ids[0].high = (uint8_t) (SILICON_IMAGE_ADOPTER_ID >> 8); emsc_supp->payload.burst_ids[0].low = (uint8_t) (SILICON_IMAGE_ADOPTER_ID & 0xFF); emsc_supp->header.checksum = calculate_generic_checksum(emsc_supp, 0, total_size - sizeof(*buf_info)); MHL_TX_DBG_INFO( "blk_rcv_buffer_info: id:0x%02X%02X\n" " sz: 0x%02X%02X\n" " emsc: 0x%02X%02X\n" " emsc- cksum: 0x%02X\n" " emsc- tot_ent: 0x%02X\n" " emsc- seq_idx: 0x%02X\n" " emsc-this_bst: 0x%02X\n" " emsc- high: 0x%02X\n" " emsc- low: 0x%02X\n", buf_info->burst_id.high, buf_info->burst_id.low, buf_info->blk_rcv_buffer_size_high, buf_info->blk_rcv_buffer_size_low, emsc_supp->header.burst_id.high, emsc_supp->header.burst_id.low, emsc_supp->header.checksum, emsc_supp->header.total_entries, emsc_supp->header.sequence_index, emsc_supp->num_entries_this_burst, emsc_supp->payload.burst_ids[0].high, emsc_supp->payload.burst_ids[0].low); } } void si_mhl_tx_initialize_block_transport(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); struct block_req *req; uint8_t buffer_size; int idx; struct block_buffer_info_t block_buffer_info; si_mhl_tx_platform_get_block_buffer_info(&block_buffer_info); hw_context->block_protocol.peer_blk_rx_buf_avail = EMSC_RCV_BUFFER_DEFAULT; hw_context->block_protocol.peer_blk_rx_buf_max = EMSC_RCV_BUFFER_DEFAULT; INIT_LIST_HEAD(&dev_context->block_protocol.queue); INIT_LIST_HEAD(&dev_context->block_protocol.free_list); /* Place pre-allocated BLOCK queue entries on the free list */ for (idx = 0; idx < NUM_BLOCK_QUEUE_REQUESTS; idx++) { req = &dev_context->block_protocol.req_entries[idx]; memset(req, 0, sizeof(*req)); req->platform_header = block_buffer_info.buffer + block_buffer_info.req_size * idx; req->payload = (union SI_PACK_THIS_STRUCT emsc_payload_t *) (req->platform_header + block_buffer_info.payload_offset); list_add(&req->link, &dev_context->block_protocol.free_list); } buffer_size = sizeof(struct SI_PACK_THIS_STRUCT standard_transport_header_t) + sizeof(struct SI_PACK_THIS_STRUCT block_rcv_buffer_info_t); if (buffer_size != 6) { MHL_TX_DBG_ERR("%scheck structure packing%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } /* we just initialized the free list, this call cannot fail */ start_new_block_marshalling_req(dev_context); } int si_mhl_tx_get_num_block_reqs(void) { return NUM_BLOCK_QUEUE_REQUESTS; } uint8_t si_get_peer_mhl_version(struct mhl_dev_context *dev_context) { uint8_t ret_val = dev_context->dev_cap_cache.mdc.mhl_version; if (0 == dev_context->dev_cap_cache.mdc.mhl_version) { /* If we come here it means we have not read devcap and * VERSION_STAT must have placed the version asynchronously * in peer_mhl3_version */ ret_val = dev_context->peer_mhl3_version; } return ret_val; } uint8_t calculate_generic_checksum(void *info_frame_data_parm, uint8_t checksum, uint8_t length) { uint8_t i; uint8_t *info_frame_data = (uint8_t *) info_frame_data_parm; for (i = 0; i < length; i++) checksum += info_frame_data[i]; checksum = 0x100 - checksum; return checksum; } static struct cbus_req *write_stat_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); /* * si_mhl_tx_set_status * * Set MHL defined STATUS bits in peer's register set. * * xstat true for XSTATUS bits * register MHL register to write * value data to write to the register */ bool si_mhl_tx_set_status(struct mhl_dev_context *dev_context, bool xstat, uint8_t reg_to_write, uint8_t value) { struct cbus_req *req; MHL_TX_DBG_INFO("called\n"); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; if (xstat) req->command = MHL_WRITE_XSTAT; else { req->command = MHL_WRITE_STAT; req->completion = write_stat_done; } req->reg = reg_to_write; req->reg_data = value; queue_cbus_transaction(dev_context, req); return true; } /* * si_mhl_tx_send_3d_req_hawb * Send SET_INT(3D_REQ) as an atomic command. * completion is defined as finishing the 3D_DTD/3D_REQ * This function returns true if operation was successfully performed. * */ bool si_mhl_tx_send_3d_req_or_feat_req(struct mhl_dev_context *dev_context) { struct cbus_req *req; MHL_TX_DBG_INFO ("%sQueue 3D_REQ(MHL2.x) or FEAT_REQ(MHL 3.x) %s. MHL %02x\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, si_get_peer_mhl_version(dev_context)); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_SEND_3D_REQ_OR_FEAT_REQ; req->reg = MHL_RCHANGE_INT; if (si_get_peer_mhl_version(dev_context) >= 0x30) { req->reg_data = MHL3_INT_FEAT_REQ; } else if (si_get_peer_mhl_version(dev_context) >= 0x20) { req->reg_data = MHL2_INT_3D_REQ; } else { /* Code must not come here. This is just a trap so look * for the following message in log */ MHL_TX_DBG_ERR("%sMHL 1 does not support 3D\n"); return false; } queue_cbus_transaction(dev_context, req); return true; } static struct cbus_req *set_int_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); /* * si_mhl_tx_set_int * Set MHL defined INTERRUPT bits in peer's register set. * This function returns true if operation was successfully performed. * * regToWrite Remote interrupt register to write * mask the bits to write to that register * * priority 0: add to head of CBusQueue * 1: add to tail of CBusQueue */ bool si_mhl_tx_set_int(struct mhl_dev_context *dev_context, uint8_t reg_to_write, uint8_t mask, uint8_t priority_level) { struct cbus_req *req; req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_SET_INT; req->reg = reg_to_write; req->reg_data = mask; req->completion = set_int_done; if (priority_level) queue_cbus_transaction(dev_context, req); else queue_priority_cbus_transaction(dev_context, req); return true; } static struct cbus_req *write_burst_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); bool si_mhl_tx_send_write_burst(struct mhl_dev_context *dev_context, void *buffer) { struct cbus_req *req; MHL_TX_DBG_INFO("%sQueue WRITE_BURST%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 1; req->command = MHL_WRITE_BURST; req->length = MHL_SCRATCHPAD_SIZE; req->burst_offset = 0; req->completion = write_burst_done; memcpy(req->msg_data, buffer, MHL_SCRATCHPAD_SIZE); queue_cbus_transaction(dev_context, req); return true; } static void si_mhl_tx_reset_states(struct mhl_dev_context *dev_context) { /* * Make sure that these timers do not start prematurely */ MHL_TX_DBG_INFO("stopping timers for DCAP_RDY and DCAP_CHG\n"); mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); mhl_tx_stop_timer(dev_context, dev_context->t_rap_max_timer); /* * Make sure that this timer does not start prematurely */ MHL_TX_DBG_INFO("stopping timer for CBUS_MODE_UP\n"); mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); init_cbus_queue(dev_context); dev_context->mhl_connection_event = false; dev_context->edid_valid = false; dev_context->mhl_connected = MHL_TX_EVENT_DISCONNECTION; dev_context->msc_msg_arrived = false; dev_context->status_0 = 0; dev_context->status_1 = 0; dev_context->link_mode = MHL_STATUS_CLK_MODE_NORMAL; /* dev_context->preferred_clk_mode can be overridden by the application * calling si_mhl_tx_set_preferred_pixel_format() */ dev_context->preferred_clk_mode = MHL_STATUS_CLK_MODE_NORMAL; { /* preserve BIST role as DUT or TE over disconnection */ bool temp = dev_context->misc_flags.flags.bist_role_TE; dev_context->misc_flags.as_uint32 = 0; dev_context->misc_flags.flags.bist_role_TE = temp ? 1 : 0; } dev_context->bist_timeout_total = 0; #ifdef MEDIA_DATA_TUNNEL_SUPPORT memset(dev_context->mdt_devs.is_dev_registered, INPUT_WAITING_FOR_REGISTRATION, MDT_TYPE_COUNT); dev_context->mdt_devs.x_max = X_MAX; dev_context->mdt_devs.x_screen = SCALE_X_SCREEN; dev_context->mdt_devs.x_raw = SCALE_X_RAW; dev_context->mdt_devs.x_shift = X_SHIFT; dev_context->mdt_devs.y_max = Y_MAX; dev_context->mdt_devs.y_screen = SCALE_Y_SCREEN; dev_context->mdt_devs.y_raw = SCALE_Y_RAW; dev_context->mdt_devs.y_shift = Y_SHIFT; dev_context->mdt_devs.swap_xy = SWAP_XY; dev_context->mdt_devs.swap_updown = SWAP_UPDOWN; dev_context->mdt_devs.swap_leftright = SWAP_LEFTRIGHT; #endif dev_context->peer_mhl3_version = 0; memset(&dev_context->dev_cap_cache, 0, sizeof(dev_context->dev_cap_cache)); memset(&dev_context->xdev_cap_cache, 0, sizeof(dev_context->xdev_cap_cache)); #if (INCLUDE_HID == 1) mhl3_hid_remove_all(dev_context); #endif mhl_tx_stop_timer(dev_context, dev_context->bist_timer); } static void t_rap_max_timer_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; mhl_event_notify(dev_context, MHL_TX_EVENT_T_RAP_MAX_EXPIRED, 0x00, NULL); } int si_mhl_tx_reserve_resources(struct mhl_dev_context *dev_context) { int ret; MHL_TX_DBG_INFO("called\n"); ret = mhl_tx_create_timer(dev_context, cbus_abort_timer_callback, dev_context, &dev_context->cbus_abort_timer); if (ret != 0) { MHL_TX_DBG_ERR("Failed to allocate CBUS abort timer!\n"); return ret; } ret = mhl_tx_create_timer(dev_context, bist_timer_callback, dev_context, &dev_context->bist_timer); if (ret != 0) { MHL_TX_DBG_ERR("Failed to allocate BIST timer!\n"); return ret; } ret = mhl_tx_create_timer(dev_context, cbus_dcap_rdy_timeout_callback, dev_context, &dev_context->dcap_rdy_timer); if (ret != 0) { MHL_TX_DBG_ERR("Failed to allocate dcap_rdy timeout timer!\n"); return ret; } ret = mhl_tx_create_timer(dev_context, cbus_dcap_chg_timeout_callback, dev_context, &dev_context->dcap_chg_timer); if (ret != 0) { MHL_TX_DBG_ERR("Failed to allocate dcap_chg timeout timer!\n"); return ret; } ret = mhl_tx_create_timer(dev_context, cbus_mode_up_timeout_callback, dev_context, &dev_context->cbus_mode_up_timer); if (ret != 0) { MHL_TX_DBG_ERR ("Failed to allocate cbus_mode_up timeout timer!\n"); return ret; } ret = mhl_tx_create_timer(dev_context, t_rap_max_timer_callback, dev_context, &dev_context->t_rap_max_timer); if (ret != 0) { MHL_TX_DBG_ERR ("Failed to allocate cbus_mode_up timeout timer!\n"); return ret; } return ret; } int si_mhl_tx_initialize(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("called\n"); si_mhl_tx_reset_states(dev_context); dev_context->bist_setup.t_bist_mode_down = T_BIST_MODE_DOWN_MAX; return dev_context->drv_info->mhl_device_initialize( (struct drv_hw_context *) (&dev_context->drv_context)); } static void cbus_abort_timer_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; MHL_TX_DBG_INFO("CBUS abort timer expired, enable CBUS messaging\n"); dev_context->misc_flags.flags.cbus_abort_delay_active = false; si_mhl_tx_drive_states(dev_context); } void si_mhl_tx_bist_cleanup(struct mhl_dev_context *dev_context) { mhl_tx_stop_timer(dev_context, dev_context->bist_timer); MHL_TX_DBG_ERR("BIST duration elapsed\n"); msleep(dev_context->bist_setup.t_bist_mode_down); if (BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info) { si_mhl_tx_drv_stop_ecbus_bist((struct drv_hw_context *) &dev_context->drv_context, &dev_context->bist_setup); } if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & dev_context->bist_trigger_info) { si_mhl_tx_drv_stop_avlink_bist( (struct drv_hw_context *)&dev_context->drv_context); dev_context->bist_stat.avlink_stat = 0; } if (BIST_TRIGGER_IMPEDANCE_TEST & dev_context->bist_trigger_info) { si_mhl_tx_drv_stop_impedance_bist((struct drv_hw_context *) &dev_context->drv_context, &dev_context->bist_setup); } else { enum cbus_mode_e cbus_mode; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (cbus_mode > CM_oCBUS_PEER_IS_MHL3) { /* allow the other end some time to inspect their error count registers */ MHL_TX_DBG_ERR("T_bist_mode_down\n") si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_STAT); } } mhl_event_notify(dev_context, MHL_TX_EVENT_BIST_TEST_DONE, 0x00, NULL); si_mhl_tx_drive_states(dev_context); } static void bist_timer_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; uint8_t test_sel; uint32_t bist_timeout_value = dev_context->bist_timeout_value; uint8_t ecbus_rx_run_done = false; uint8_t ecbus_tx_run_done = false; enum cbus_mode_e cbus_mode; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); test_sel = dev_context->bist_trigger_info; dev_context->bist_timeout_total += LOCAL_eCBUS_ERR_SAMPLE_PERIOD; MHL_TX_DBG_INFO("%s\n", si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) if (CM_BIST_DONE_PENDING_DISCONNECT == cbus_mode) { MHL_TX_DBG_ERR("%s Peer disconnected before" "T_BIST_MODE_DOWN expired%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) return; } else if (dev_context->bist_timeout_total < bist_timeout_value) { int32_t temp; int32_t err_cnt = -1; temp = si_mhl_tx_drv_get_ecbus_bist_status(dev_context, &ecbus_rx_run_done, &ecbus_tx_run_done); /* sample the error counter as appropriate */ if (dev_context->misc_flags.flags.bist_role_TE) { if (BIST_TRIGGER_E_CBUS_TX & test_sel) { err_cnt = temp; MHL_TX_DBG_WARN( "local eCBUS error count: %d\n", err_cnt) } } else { if (BIST_TRIGGER_E_CBUS_RX & test_sel) { err_cnt = temp; MHL_TX_DBG_WARN( "local eCBUS error count: %d\n", err_cnt) } } if (CM_NO_CONNECTION_BIST_STAT == cbus_mode) { MHL_TX_DBG_ERR("%s Peer disconnected before" " bist timeout expired%s: %d\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, dev_context->bist_timeout_total); ; } else if (si_mhl_tx_drv_ecbus_connected(dev_context)) { /* accept the error count only if we're still connected Since we can not distinguish between "real" errors and errors that happen as part of a disconnection, we keep track of the last three results and discard the two most recent. */ mhl_tx_start_timer(dev_context, dev_context->bist_timer, LOCAL_eCBUS_ERR_SAMPLE_PERIOD); dev_context->bist_stat.e_cbus_prev_local_stat = dev_context->bist_stat.e_cbus_local_stat; dev_context->bist_stat.e_cbus_local_stat = dev_context->bist_stat.e_cbus_next_local_stat; dev_context->bist_stat.e_cbus_next_local_stat = err_cnt; return; } } else if (0 == dev_context->bist_timeout_value) { MHL_TX_DBG_INFO("infinite duration AV BIST\n") mhl_tx_start_timer(dev_context, dev_context->bist_timer, LOCAL_eCBUS_ERR_SAMPLE_PERIOD); return; } si_mhl_tx_bist_cleanup(dev_context); } static void cbus_dcap_rdy_timeout_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; enum cbus_mode_e cbus_mode; MHL_TX_DBG_ERR("%sCBUS DCAP_RDY timer expired%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (CM_oCBUS_PEER_VERSION_PENDING == cbus_mode) { MHL_TX_DBG_ERR("%s%signoring lack of DCAP_RDY%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_YELLOW_BG, ANSI_ESC_RESET_TEXT); /* Initialize registers to operate in oCBUS mode */ si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_oCBUS_PEER_IS_MHL1_2); si_mhl_tx_refresh_peer_devcap_entries(dev_context); si_mhl_tx_drive_states(dev_context); } } static void cbus_dcap_chg_timeout_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; enum cbus_mode_e cbus_mode; MHL_TX_DBG_ERR("%sCBUS DCAP_CHG timer expired%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (CM_oCBUS_PEER_IS_MHL1_2 == cbus_mode) { MHL_TX_DBG_ERR("%s%signoring lack of DCAP_CHG%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_YELLOW_BG, ANSI_ESC_RESET_TEXT); si_mhl_tx_refresh_peer_devcap_entries(dev_context); si_mhl_tx_drive_states(dev_context); } } static void cbus_mode_up_timeout_callback(void *callback_param) { struct mhl_dev_context *dev_context = callback_param; bool status; MHL_TX_DBG_INFO("CBUS_MODE_UP timer expired\n"); status = si_mhl_tx_rap_send(dev_context, MHL_RAP_CBUS_MODE_UP); if (status) si_mhl_tx_drive_states(dev_context); } void process_cbus_abort(struct mhl_dev_context *dev_context) { struct cbus_req *req; /* * Place the CBUS message that errored back on * transmit queue if it has any retries left. */ if (dev_context->current_cbus_req != NULL) { req = dev_context->current_cbus_req; dev_context->current_cbus_req = NULL; if (req->retry_count) { req->retry_count -= 1; queue_priority_cbus_transaction(dev_context, req); } else { return_cbus_queue_entry(dev_context, req); } } /* Delay the sending of any new CBUS messages for 2 seconds */ dev_context->misc_flags.flags.cbus_abort_delay_active = true; mhl_tx_start_timer(dev_context, dev_context->cbus_abort_timer, 2000); } /* * si_mhl_tx_drive_states * * This function is called by the interrupt handler in the driver layer. * to move the MSC engine to do the next thing before allowing the application * to run RCP APIs. */ void si_mhl_tx_drive_states(struct mhl_dev_context *dev_context) { struct cbus_req *req; struct cbus_req *peek; MHL_TX_DBG_INFO("\n"); peek = peek_next_cbus_transaction(dev_context); if (NULL == peek) { /* nothing to send */ return; } switch (si_mhl_tx_drv_get_cbus_mode(dev_context)) { case CM_NO_CONNECTION: case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_S: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: case CM_TRANSITIONAL_TO_eCBUS_D_BIST: case CM_TRANSITIONAL_TO_eCBUS_D: case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: MHL_TX_DBG_ERR ("CBUS transactions forbidden in transitional state" " command:0x%x\n", peek->command); return; default: break; } req = dev_context->current_cbus_req; if (req != NULL) { char *command_string = get_cbus_command_string(req->command); switch (req->command) { case MHL_WRITE_BURST: if (MHL_WRITE_BURST == peek->command) { /* pending and next transactions * are both WRITE_BURST */ if (si_mhl_tx_drv_hawb_xfifo_avail (dev_context)) { /* it's OK to send WRITE_BURSTs */ break; } } MHL_TX_DBG_INFO("WRITE_BURST in progress\n"); return; default: MHL_TX_DBG_INFO("%s[0x%02x]=0x%02x in progress\n", command_string, dev_context->current_cbus_req->reg, dev_context->current_cbus_req-> reg_data); return; } } if (MHL_WRITE_BURST != peek->command) { if (si_mhl_tx_drv_get_pending_hawb_write_status(dev_context)) { /* hawb still pending */ return; } } /* process queued CBus transactions */ req = get_next_cbus_transaction(dev_context); if (req == NULL) return; MHL_TX_DBG_INFO("req: %p\n", req); /* coordinate write burst requests and grants. */ if (MHL_MSC_MSG == req->command) { dev_context->msc_msg_last_data = req->msg_data[1]; } else if (MHL_SET_INT == req->command) { if (MHL_RCHANGE_INT == req->reg) { if (MHL_INT_GRT_WRT == req->reg_data) { dev_context->misc_flags.flags. rcv_scratchpad_busy = true; } } } MHL_TX_DBG_INFO("req: %p\n", req); if (req) { uint8_t ret_val; dev_context->current_cbus_req = req; switch (req->command) { case MHL_WRITE_BURST: do { struct cbus_req *next_req; ret_val = si_mhl_tx_drv_send_cbus_command( (struct drv_hw_context *) (&dev_context->drv_context), req); if (0 == ret_val) { /* WB XMIT level not available */ MHL_TX_DBG_INFO("\n"); break; } next_req = peek_next_cbus_transaction(dev_context); /* process queued CBus transactions */ if (next_req == NULL) { MHL_TX_DBG_INFO("\n"); break; /* out of the do-while loop */ } if (MHL_WRITE_BURST != next_req->command) { MHL_TX_DBG_INFO("\n"); break; } next_req = get_next_cbus_transaction(dev_context); if (ret_val) { return_cbus_queue_entry(dev_context, req); req = next_req; dev_context->current_cbus_req = next_req; } } while (ret_val && req); break; case MHL_MSC_MSG: if (MHL_MSC_MSG_RAP == req->msg_data[0]) { MHL_TX_DBG_INFO("sending RAP\n"); mhl_tx_start_timer(dev_context, dev_context->t_rap_max_timer, 1000); } goto case_default; default: case_default: ret_val = si_mhl_tx_drv_send_cbus_command( (struct drv_hw_context *) (&dev_context->drv_context), req); if (ret_val) { MHL_TX_DBG_INFO("current command: %s0x%02x%s\n", ANSI_ESC_YELLOW_TEXT, ret_val, ANSI_ESC_RESET_TEXT); req->command = ret_val; } else { return_cbus_queue_entry(dev_context, req); dev_context->current_cbus_req = NULL; if (MHL_READ_EDID_BLOCK == req->command) { dev_context->misc_flags.flags. edid_loop_active = 0; MHL_TX_DBG_INFO ("tag: EDID active: %d\n", dev_context->misc_flags.flags. edid_loop_active); } } break; } } } enum scratch_pad_status si_mhl_tx_request_write_burst(struct mhl_dev_context *dev_context, uint8_t burst_offset, uint8_t length, uint8_t *data) { enum scratch_pad_status status = SCRATCHPAD_BUSY; if ((si_get_peer_mhl_version(dev_context) < 0x20) && !(dev_context->dev_cap_cache.mdc.featureFlag & MHL_FEATURE_SP_SUPPORT)) { MHL_TX_DBG_ERR("failed SCRATCHPAD_NOT_SUPPORTED\n"); status = SCRATCHPAD_NOT_SUPPORTED; } else if ((burst_offset + length) > SCRATCHPAD_SIZE) { MHL_TX_DBG_ERR("invalid offset + length\n"); status = SCRATCHPAD_BAD_PARAM; } else { MHL_TX_DBG_ERR("Sending WB\n"); si_mhl_tx_send_write_burst(dev_context, data); status = SCRATCHPAD_SUCCESS; } return status; } /* * si_mhl_tx_send_msc_msg * * This function sends a MSC_MSG command to the peer. * It returns true if successful in doing so. */ bool si_mhl_tx_send_msc_msg(struct mhl_dev_context *dev_context, uint8_t command, uint8_t cmdData, struct cbus_req *(*completion)(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) ) { struct cbus_req *req; MHL_TX_DBG_INFO("called\n"); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_MSC_MSG; req->msg_data[0] = command; req->msg_data[1] = cmdData; req->completion = completion; queue_cbus_transaction(dev_context, req); return true; } struct cbus_req *rapk_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { if (MHL_RAP_CBUS_MODE_DOWN == dev_context->rap_in_sub_command) { MHL_TX_DBG_ERR("%sRAPK complete%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context-> drv_context, CM_oCBUS_PEER_IS_MHL3); } else if (MHL_RAP_CBUS_MODE_UP == dev_context->rap_in_sub_command) { MHL_TX_DBG_ERR("%sRAPK complete%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_eCBUS_S); } return req; } /* * si_mhl_rapk_send * This function sends RAPK to the peer device. */ static bool si_mhl_rapk_send(struct mhl_dev_context *dev_context, uint8_t status) { return si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAPK, status, rapk_done); } struct cbus_req *rcpe_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { /* * RCPE is always followed by an RCPK with * original key code received. */ si_mhl_tx_rcpk_send(dev_context, dev_context->msc_save_rcp_key_code); return req; } /* * si_mhl_tx_rcpe_send * * The function will return a value of true if it could successfully send the * RCPE subcommand. Otherwise false. * * When successful, mhl_tx internally sends RCPK with original (last known) * keycode. */ bool si_mhl_tx_rcpe_send(struct mhl_dev_context *dev_context, uint8_t rcpe_error_code) { bool status; status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCPE, rcpe_error_code, rcpe_done); if (status) si_mhl_tx_drive_states(dev_context); return status; } /* * si_mhl_tx_bist_setup * * This function sends a BIST_SETUP WRITE_BURST to the MHL3 peer. * See Section 15 of the MHL3 specification * * This function returns the status of the command sent. * * This function is called only when Titan is the BIST initiator, * i.e. test equipment. This function can only be called when the CBUS is * in oCBUS mode. If the CBUS is in any other mode, the WRITE_BURST will not * be sent to the MHL3 peer and this function will return failure. */ enum bist_cmd_status si_mhl_tx_bist_setup(struct mhl_dev_context *dev_context, struct bist_setup_info *setup) { enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; /* Validate current cbus mode and bist_setup_info */ if (si_mhl_tx_drv_get_cbus_mode(dev_context) != CM_oCBUS_PEER_IS_MHL3_BIST_SETUP) { ret_status = BIST_STATUS_NOT_IN_OCBUS; } else if (setup->e_cbus_duration == 0x00) { ret_status = BIST_STATUS_INVALID_SETUP; } else if ((setup->e_cbus_pattern == BIST_ECBUS_PATTERN_UNSPECIFIED) || (setup->e_cbus_pattern > BIST_ECBUS_PATTERN_MAX)) { ret_status = BIST_STATUS_INVALID_SETUP; /*} if setup->e_cbus_pattern is Fixed10 * with no support for eCBUS-D { */ /* ret_status = BIST_STATUS_INVALID_SETUP; */ } else if ((setup->avlink_data_rate == BIST_AVLINK_DATA_RATE_UNSPECIFIED) || (setup->avlink_pattern > BIST_AVLINK_DATA_RATE_MAX)) { ret_status = BIST_STATUS_INVALID_SETUP; } else if ((setup->avlink_pattern == BIST_AVLINK_PATTERN_UNSPECIFIED) || (setup->avlink_pattern > BIST_AVLINK_PATTERN_MAX)) { ret_status = BIST_STATUS_INVALID_SETUP; /*} validate video mode { */ /* ret_status = BIST_STATUS_INVALID_SETUP; */ } else if ((setup->impedance_mode == BIST_IMPEDANCE_MODE_RESERVED_1) || (setup->impedance_mode == BIST_IMPEDANCE_MODE_RESERVED_2) || (setup->impedance_mode > BIST_IMPEDANCE_MODE_MAX)) { ret_status = BIST_STATUS_INVALID_SETUP; } else { /* Build BIST_SETUP WRITE_BURST */ struct bist_setup_burst burst; burst.burst_id_h = HIGH_BYTE_16(burst_id_BIST_SETUP); burst.burst_id_l = LOW_BYTE_16(burst_id_BIST_SETUP); burst.checksum = 0x00; burst.e_cbus_duration = setup->e_cbus_duration; burst.e_cbus_pattern = setup->e_cbus_pattern; burst.e_cbus_fixed_h = HIGH_BYTE_16(setup->e_cbus_fixed_pat); burst.e_cbus_fixed_l = LOW_BYTE_16(setup->e_cbus_fixed_pat); burst.avlink_data_rate = setup->avlink_data_rate; burst.avlink_pattern = setup->avlink_pattern; burst.avlink_video_mode = setup->avlink_video_mode; burst.avlink_duration = setup->avlink_duration; burst.avlink_fixed_h = HIGH_BYTE_16(setup->avlink_fixed_pat); burst.avlink_fixed_l = LOW_BYTE_16(setup->avlink_fixed_pat); burst.avlink_randomizer = setup->avlink_randomizer; burst.impedance_mode = setup->impedance_mode; /* calculate checksum */ burst.checksum = calculate_generic_checksum((uint8_t *) &burst, 0, sizeof(burst)); /* Send WRITE_BURST */ si_mhl_tx_request_write_burst(dev_context, 0, sizeof(burst), (uint8_t *) &burst); si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT); } return ret_status; } void si_mhl_tx_set_bist_timer_impl(struct mhl_dev_context *dev_context, const char *caller, int line_num) { MHL_TX_DBG_ERR("BIST timeout (%d,%d) from %s:%d\n", dev_context->bist_timeout_value, LOCAL_eCBUS_ERR_SAMPLE_PERIOD, caller, line_num) dev_context->bist_timeout_total = 0; mhl_tx_start_timer(dev_context, dev_context->bist_timer, LOCAL_eCBUS_ERR_SAMPLE_PERIOD); } static bool determine_bist_timeout_value(struct mhl_dev_context *dev_context) { uint32_t bist_timeout = 0; uint32_t av_link_timeout = 0; uint8_t test_sel; MHL_TX_DBG_INFO("\n"); test_sel = dev_context->bist_trigger_info; dev_context->bist_timeout_value = 0; if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { MHL_TX_DBG_INFO("\n") bist_timeout = dev_context->bist_setup.e_cbus_duration * 1000; dev_context->bist_timeout_value = bist_timeout; si_mhl_tx_set_bist_timer(dev_context); } else { if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { MHL_TX_DBG_INFO("Initiate eCBUS BIST\n"); bist_timeout = dev_context->bist_setup.e_cbus_duration * 1000; dev_context->bist_timeout_value = bist_timeout; } if (test_sel & BIST_TRIGGER_ECBUS_AV_LINK_MASK) { MHL_TX_DBG_INFO("\n") av_link_timeout = dev_context->bist_setup.avlink_duration; if (dev_context->bist_setup.avlink_pattern <= BIST_AVLINK_PATTERN_FIXED_8) { MHL_TX_DBG_INFO("\n") /* ~17ms. per frame */ av_link_timeout *= 32 * 17; } else { MHL_TX_DBG_INFO("\n") av_link_timeout *= 1000; } /* * Run the test for the longer of either the * eCBUS test time or the AV_LINK test time. */ if (av_link_timeout > bist_timeout) { MHL_TX_DBG_INFO("\n") dev_context->bist_timeout_value = av_link_timeout; } if (!(test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK)) { if (0 == av_link_timeout) { MHL_TX_DBG_ERR("indefinite\n") dev_context->bist_timeout_value = 0; } } } dev_context->bist_setup.t_bist_mode_down = (dev_context->bist_timeout_value * 10)/100 + T_BIST_MODE_DOWN_MIN; MHL_TX_DBG_ERR("%d\n", dev_context->bist_timeout_value) } if (!dev_context->bist_timeout_value) MHL_TX_DBG_ERR("No BIST timeout - wait for BIST_STOP\n"); return true; } struct cbus_req *bist_trigger_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { /* We just requested a BIST test * so now set up for it. */ MHL_TX_DBG_ERR("\n") if (BIST_TRIGGER_IMPEDANCE_TEST == dev_context->bist_trigger_info) { MHL_TX_DBG_ERR("\n") start_bist_initiator_test(dev_context); } else if (dev_context->bist_trigger_info) { enum cbus_mode_e mode = dev_context->msc_msg_data & BIST_TRIGGER_TEST_E_CBUS_D ? CM_eCBUS_D_BIST : CM_eCBUS_S_BIST; MHL_TX_DBG_ERR("\n") determine_bist_timeout_value(dev_context); if (1 == si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, mode)) MHL_TX_DBG_ERR("wait TDM\n"); } return req; } /* * si_mhl_tx_bist_trigger * * This function sends a BIST_TRIGGER MSC_MSG to the MHL3 peer. * See Section 15 of the MHL3 specification * * This function returns the status of the command sent. * * This function is called only when Titan is the BIST initiator, * i.e. test equipment. This function can only be called when the CBUS is * in oCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not * be sent to the MHL3 peer and this function will return failure. */ enum bist_cmd_status si_mhl_tx_bist_trigger(struct mhl_dev_context *dev_context, uint8_t trigger_operand) { enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; trigger_operand &= BIST_TRIGGER_OPERAND_VALID_MASK; /* Validate current cbus mode and trigger_operand */ if (si_mhl_tx_drv_get_cbus_mode(dev_context) != CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY) { MHL_TX_DBG_ERR("%sBIST_STATUS_NOT_IN_OCBUS%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) ret_status = BIST_STATUS_DUT_NOT_READY; /*} else if (trigger_operand & BIST_TRIGGER_AVLINK_TX) { MHL_TX_DBG_ERR("%sBIST_STATUS_INVALID_TRIGGER%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) ret_status = BIST_STATUS_INVALID_TRIGGER; } else if ((trigger_operand & BIST_TRIGGER_OPERAND_SELECT_eCBUS_D) && (eCBUS_D not supported)) { */ } else if ((trigger_operand & BIST_TRIGGER_IMPEDANCE_TEST) && ((trigger_operand & ~BIST_TRIGGER_IMPEDANCE_TEST) != 0x00)) { MHL_TX_DBG_ERR("%sBIST_STATUS_INVALID_TRIGGER%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) ret_status = BIST_STATUS_INVALID_TRIGGER; } else { dev_context->bist_trigger_info = trigger_operand; /* Send BIST_TRIGGER */ si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_TRIGGER, dev_context->bist_trigger_info, bist_trigger_done); si_mhl_tx_drive_states(dev_context); } return ret_status; } struct cbus_req *bist_stop_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") /* bist timer always runs, even for bist_timeout_value == 0 (infinite) */ si_mhl_tx_bist_cleanup(dev_context); return req; } /* * si_mhl_tx_bist_stop * * This function sends a BIST_STOP MSC_MSG to the MHL3 peer. * See Section 15 of the MHL3 specification * * This function returns the status of the command sent. * * This function is called only when Titan is the BIST initiator, * i.e. test equipment. This function can only be called when the CBUS is * in eCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not * be sent to the MHL3 peer and this function will return failure. */ enum bist_cmd_status si_mhl_tx_bist_stop(struct mhl_dev_context *dev_context) { enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; /* Validate current cbus mode */ if (si_mhl_tx_drv_get_cbus_mode(dev_context) < CM_eCBUS_S) ret_status = BIST_STATUS_NOT_IN_ECBUS; /* Send BIST_STOP */ si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_STOP, 0x00, bist_stop_done); si_mhl_tx_drive_states(dev_context); return ret_status; } struct cbus_req *bist_request_stat_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_bist_request_stat * * This function sends a BIST_REQUEST_STAT MSC_MSG to the MHL3 peer. * See Section 15 of the MHL3 specification * * This function returns the status of the command sent. * * This function is called only when Titan is the BIST initiator, * i.e. test equipment. This function can only be called when the CBUS is * in oCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not * be sent to the MHL3 peer and this function will return failure. */ enum bist_cmd_status si_mhl_tx_bist_request_stat(struct mhl_dev_context *dev_context, uint8_t request_operand) { enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; /* Validate current cbus mode */ if (si_mhl_tx_drv_get_cbus_mode(dev_context) != CM_oCBUS_PEER_IS_MHL3_BIST_STAT) ret_status = BIST_STATUS_NOT_IN_OCBUS; /* verify operand 0x00 or 0x01 */ /* Send BIST_REQUEST_STAT */ si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_REQUEST_STAT, request_operand, bist_request_stat_done); si_mhl_tx_drive_states(dev_context); return ret_status; } void send_bist_status(struct mhl_dev_context *dev_context) { struct bist_return_stat_burst bist_status; uint16_t ecbus_local_stat; memset(&bist_status, 0, sizeof(bist_status)); bist_status.burst_id_h = burst_id_BIST_RETURN_STAT >> 8; bist_status.burst_id_l = burst_id_BIST_RETURN_STAT; bist_status.avlink_stat_h = dev_context->bist_stat.avlink_stat >> 8; bist_status.avlink_stat_l = dev_context->bist_stat.avlink_stat; if (dev_context->bist_stat.e_cbus_prev_local_stat < 0) ecbus_local_stat = 0xFFFF; else if (dev_context->bist_stat.e_cbus_prev_local_stat > 0xFFFF) ecbus_local_stat = 0xFFFE; else ecbus_local_stat = (uint16_t)dev_context->bist_stat.e_cbus_prev_local_stat; bist_status.e_cbus_stat_h = ecbus_local_stat >> 8; bist_status.e_cbus_stat_l = ecbus_local_stat; bist_status.checksum = calculate_generic_checksum((uint8_t *) (&bist_status), 0, sizeof(bist_status)); si_mhl_tx_request_write_burst(dev_context, 0, sizeof(bist_status), (uint8_t *) (&bist_status)); } bool invalid_bist_parms(struct mhl_dev_context *dev_context, struct bist_setup_info *setup_info) { uint8_t test_sel; bool e_cbus_d_sel = false; MHL_TX_DBG_ERR("\n") test_sel = setup_info->bist_trigger_parm; e_cbus_d_sel = test_sel & BIST_TRIGGER_TEST_E_CBUS_D ? true : false; test_sel &= ~BIST_TRIGGER_E_CBUS_TYPE_MASK; if (test_sel > BIST_TRIGGER_IMPEDANCE_TEST) { MHL_TX_DBG_ERR("Impedance test cannot be run " "concurrently with other tests!\n"); return true; } if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & test_sel) { switch (setup_info->avlink_video_mode) { case 4: /* 1280 X 720 (720P) */ break; case 3: /* 720 X 480 (480P) */ break; default: MHL_TX_DBG_ERR("Unsupported VIC received!\n"); return true; } switch (setup_info->avlink_data_rate) { case 1: /* 1.5 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); break; case 2: /* 3.0 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); break; case 3: /* 6.0 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); break; default: MHL_TX_DBG_ERR("%sUnsupported " "AVLINK_DATA_RATE %02X%s\n", ANSI_ESC_RED_TEXT, setup_info->avlink_data_rate, ANSI_ESC_RESET_TEXT); return true; } switch (setup_info->avlink_pattern) { case BIST_AVLINK_PATTERN_UNSPECIFIED: case BIST_AVLINK_PATTERN_PRBS: MHL_TX_DBG_ERR("AV LINK_PATTERN %sPRBS%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) break; case BIST_AVLINK_PATTERN_FIXED_8: MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed8%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); break; case BIST_AVLINK_PATTERN_FIXED_10: MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed10%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); break; default: MHL_TX_DBG_ERR("%sUnrecognized test " "pattern detected!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); return true; } } if (BIST_TRIGGER_ECBUS_TX_RX_MASK & test_sel) { MHL_TX_DBG_ERR("\n") if (e_cbus_d_sel) { MHL_TX_DBG_ERR("Testing of eCBUS-D not supported yet\n") dev_context->bist_stat.e_cbus_prev_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_next_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_remote_stat = 0xFFFF; test_sel &= ~BIST_TRIGGER_ECBUS_TX_RX_MASK; return true; } } return false; } bool invalid_bist_te_parms(struct mhl_dev_context *dev_context, struct bist_setup_info *setup_info) { uint8_t test_sel; test_sel = setup_info->bist_trigger_parm; if (invalid_bist_parms(dev_context, setup_info)) { MHL_TX_DBG_ERR("\n") return true; } else if (test_sel & BIST_TRIGGER_AVLINK_TX) { MHL_TX_DBG_ERR("%sinvalid test_sel%s:0x%02x\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, test_sel) /* Invalid test for MHL transmitter TE */ dev_context->bist_stat.avlink_stat = 0xFFFF; return true; } return false; } bool invalid_bist_dut_parms(struct mhl_dev_context *dev_context, struct bist_setup_info *setup_info) { uint8_t test_sel; test_sel = setup_info->bist_trigger_parm; if (invalid_bist_parms(dev_context, setup_info)) { MHL_TX_DBG_ERR("\n") return true; } else if (test_sel & BIST_TRIGGER_AVLINK_RX) { /* Invalid test for MHL transmitter DUT */ MHL_TX_DBG_ERR("%sInvalid test:0x%02X for MHL Tx DUT%s\n", ANSI_ESC_RED_TEXT, test_sel, ANSI_ESC_RESET_TEXT) dev_context->bist_stat.avlink_stat = 0xFFFF; return true; } return false; } void initiate_bist_test(struct mhl_dev_context *dev_context) { uint8_t test_sel; enum cbus_mode_e cbus_mode; bool e_cbus_d_sel = false; MHL_TX_DBG_ERR("\n") if (invalid_bist_dut_parms(dev_context, &dev_context->bist_setup)) { MHL_TX_DBG_ERR("\n") si_mhl_tx_bist_cleanup(dev_context); return; } test_sel = dev_context->bist_trigger_info; e_cbus_d_sel = test_sel & BIST_TRIGGER_TEST_E_CBUS_D ? true : false; test_sel &= ~BIST_TRIGGER_E_CBUS_TYPE_MASK; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (test_sel == 0) { MHL_TX_DBG_ERR("%sNo test selected%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) si_mhl_tx_bist_cleanup(dev_context); return; } if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { MHL_TX_DBG_ERR("another BIST_TRIGGER\n") si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); dev_context->bist_stat.avlink_stat = 0; dev_context->bist_stat.e_cbus_prev_local_stat = 0; dev_context->bist_stat.e_cbus_local_stat = 0; dev_context->bist_stat.e_cbus_next_local_stat = 0; dev_context->bist_stat.e_cbus_remote_stat = 0; } else if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY != cbus_mode) { dev_context->bist_stat.avlink_stat = 0; dev_context->bist_stat.e_cbus_prev_local_stat = 0; dev_context->bist_stat.e_cbus_local_stat = 0; dev_context->bist_stat.e_cbus_next_local_stat = 0; dev_context->bist_stat.e_cbus_remote_stat = 0; } else { MHL_TX_DBG_ERR("BIST test requested without prior " "valid BIST setup command\n"); dev_context->bist_stat.avlink_stat = 0xFFFF; dev_context->bist_stat.e_cbus_prev_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_next_local_stat = 0xFFFF; dev_context->bist_stat.e_cbus_remote_stat = 0xFFFF; si_mhl_tx_bist_cleanup(dev_context); return; } if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { if (cbus_mode != CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY) { si_mhl_tx_bist_cleanup(dev_context); return; } if (0 == si_mhl_tx_drv_start_impedance_bist( (struct drv_hw_context *) &dev_context->drv_context, &dev_context->bist_setup)) { MHL_TX_DBG_ERR("\n") } } else { if (cbus_mode < CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST) { MHL_TX_DBG_ERR ("%sCannot initiate eCBUS-S BIST when " "CBUS mode is%s %s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); si_mhl_tx_bist_cleanup(dev_context); return; } if (e_cbus_d_sel && cbus_mode < CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST) { MHL_TX_DBG_ERR ("%sCannot initiate eCBUS-S BIST when " "CBUS mode is%s %s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); si_mhl_tx_bist_cleanup(dev_context); return; } if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { MHL_TX_DBG_INFO("Initiate eCBUS BIST\n"); si_mhl_tx_drv_start_ecbus_bist( (struct drv_hw_context *) &dev_context->drv_context, &dev_context->bist_setup); } if (test_sel & BIST_TRIGGER_AVLINK_TX) { MHL_TX_DBG_ERR("total: %d value:%d\n", dev_context->bist_timeout_total, dev_context->bist_timeout_value) si_mhl_tx_drv_start_avlink_bist(dev_context, &dev_context->bist_setup); } } } void start_bist_initiator_test(struct mhl_dev_context *dev_context) { uint8_t test_sel; enum cbus_mode_e cbus_mode; bool e_cbus_d_sel = false; MHL_TX_DBG_ERR("\n"); if (invalid_bist_te_parms(dev_context, &dev_context->bist_setup)) { MHL_TX_DBG_ERR("\n") si_mhl_tx_bist_cleanup(dev_context); return; } test_sel = dev_context->bist_trigger_info; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (test_sel == 0) { MHL_TX_DBG_ERR("%sNo test selected%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) si_mhl_tx_bist_cleanup(dev_context); return; } if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { MHL_TX_DBG_ERR("\n") if (0 == si_mhl_tx_drv_start_impedance_bist((struct drv_hw_context *) &dev_context-> drv_context, &dev_context-> bist_setup)) { MHL_TX_DBG_ERR("\n") } } else { MHL_TX_DBG_ERR("\n") if (cbus_mode < CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST) { MHL_TX_DBG_ERR ("Cannot initiate eCBUS-S BIST when CBUS mode is" " %s\n", si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) si_mhl_tx_bist_cleanup(dev_context); return; } if (e_cbus_d_sel && cbus_mode < CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST) { MHL_TX_DBG_ERR ("Cannot initiate eCBUS-D BIST when CBUS mode is" " %s\n", si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) si_mhl_tx_bist_cleanup(dev_context); return; } if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { MHL_TX_DBG_ERR("active\n") si_mhl_tx_drv_start_ecbus_bist( (struct drv_hw_context *) &dev_context->drv_context, &dev_context->bist_setup); } if (test_sel & BIST_TRIGGER_AVLINK_RX) { MHL_TX_DBG_ERR("active\n") si_mhl_tx_drv_start_avlink_bist(dev_context, &dev_context->bist_setup); } } } /* API for CTS tester. si_mhl_tx_execute_bist Parameters: dev_context: pointer to device context. setup_info: pointer to a structure containing a complete abstraction of a BIST_SETUP BURST_ID packet along with parameters for BIST_TRIGGER and BIST_REQUEST_STAT and a run-time adjustable value for T_BIST_MODE_DOWN whose default is the maximum value of 5 seconds. Parameters for BIST_SETUP are converted from the abstraction to the BIST_SETUP WRITE_BURST format just before sending the WRITE_BURST. */ void si_mhl_tx_execute_bist(struct mhl_dev_context *dev_context, struct bist_setup_info *setup_info) { dev_context->misc_flags.flags.bist_role_TE = 1; if (invalid_bist_te_parms(dev_context, setup_info)) { MHL_TX_DBG_ERR("\n"); } else { dev_context->bist_setup = *setup_info; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP); } } /* * si_mhl_tx_process_events * This internal function is called at the end of interrupt processing. It's * purpose is to process events detected during the interrupt. Some events * are internally handled here but most are handled by a notification to * interested applications. */ void si_mhl_tx_process_events(struct mhl_dev_context *dev_context) { uint8_t rapk_status; enum cbus_mode_e cbus_mode; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); /* Make sure any events detected during the interrupt are processed. */ si_mhl_tx_drive_states(dev_context); if (dev_context->mhl_connection_event) { MHL_TX_DBG_INFO("mhl_connection_event\n"); /* Consume the message */ dev_context->mhl_connection_event = false; /* * Let interested apps know about the connection state change */ mhl_event_notify(dev_context, dev_context->mhl_connected, dev_context->dev_cap_cache.mdc.featureFlag, NULL); /* If connection has been lost, reset all state flags. */ if (MHL_TX_EVENT_DISCONNECTION == dev_context->mhl_connected) { MHL_TX_DBG_WARN("MHL Disconnect Event. Reset states\n"); si_mhl_tx_reset_states(dev_context); } else if (MHL_TX_EVENT_CONNECTION == dev_context->mhl_connected) { /* queue up all three in this order to indicate MHL 3.0 version according to spec. */ #ifdef FORCE_OCBUS_FOR_ECTS /* This compile option is always enabled. * It is intended to help identify code deletion by * adopters who do not need this feauture. The control * for forcing oCBUS works by using module parameter * below. Peer version is forced to 2.0 allowing 8620 * to treat the sink as if it is MHL 2.0 device and as * a result never switch cbus to MHL3 eCBUS. */ if (force_ocbus_for_ects) { MHL_TX_DBG_ERR("%sQueue DCAP_RDY, DCAP_CHG%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_CONNECTED_RDY, MHL_STATUS_DCAP_RDY); si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL_INT_DCAP_CHG, 1); } else { MHL_TX_DBG_WARN("%sQueue VERSION_STAT,DCAP_RDY" ", DCAP_CHG%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_VERSION_STAT, MHL_VERSION); si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_CONNECTED_RDY, MHL_STATUS_DCAP_RDY | MHL_STATUS_XDEVCAPP_SUPP | ((DEVCAP_VAL_DEV_CAT & (MHL_DEV_CATEGORY_PLIM2_0 | MHL_DEV_CATEGORY_POW_BIT)) >> 2) ); si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL_INT_DCAP_CHG, 1); } #else MHL_TX_DBG_ERR ("%sQueue VERSION_STAT, DCAP_RDY, DCAP_CHG%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_VERSION_STAT, MHL_VERSION); si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_CONNECTED_RDY, MHL_STATUS_DCAP_RDY | MHL_STATUS_XDEVCAPP_SUPP | ((DEVCAP_VAL_DEV_CAT & (MHL_DEV_CATEGORY_PLIM2_0 | MHL_DEV_CATEGORY_POW_BIT) ) >> 2) ); si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL_INT_DCAP_CHG, 1); #endif /* * Start timer here to circumvent issue of not getting * DCAP_RDY. Use timeout durations of 7 seconds or more * to distinguish between non-compliant dongles and the * CBUS CTS tester. */ switch (cbus_mode) { case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: /* stop rather than start the timer for these cases */ mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); break; default: mhl_tx_start_timer(dev_context, dev_context->dcap_rdy_timer, 7000); } } } else if (dev_context->msc_msg_arrived) { MHL_TX_DBG_INFO("MSC MSG <%02X, %02X>\n", dev_context->msc_msg_sub_command, dev_context->msc_msg_data); /* Consume the message */ dev_context->msc_msg_arrived = false; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); /* * Map MSG sub-command to an event ID */ switch (dev_context->msc_msg_sub_command) { case MHL_MSC_MSG_RAP: /* * RAP messages are fully handled here. */ if (dev_context-> mhl_flags & MHL_STATE_APPLICATION_RAP_BUSY) { rapk_status = MHL_RAPK_BUSY; } else { rapk_status = MHL_RAPK_NO_ERR; } dev_context->rap_in_sub_command = dev_context->msc_msg_data; if (MHL_RAP_POLL == dev_context->msc_msg_data) { /* just do the ack */ } else if (MHL_RAP_CONTENT_ON == dev_context->msc_msg_data) { MHL_TX_DBG_ERR("Got RAP{CONTENT_ON}\n"); dev_context->misc_flags.flags.rap_content_on = true; si_mhl_tx_drv_content_on( (struct drv_hw_context *) &dev_context->drv_context); } else if (MHL_RAP_CONTENT_OFF == dev_context->msc_msg_data) { MHL_TX_DBG_ERR("Got RAP{CONTENT_OFF}\n"); if (dev_context->misc_flags.flags. rap_content_on) { dev_context->misc_flags.flags. rap_content_on = false; si_mhl_tx_drv_content_off( (struct drv_hw_context *) &dev_context->drv_context); } } else if (MHL_RAP_CBUS_MODE_DOWN == dev_context->msc_msg_data) { MHL_TX_DBG_ERR("Got RAP{CBUS_MODE_DOWN}\n"); } else if (MHL_RAP_CBUS_MODE_UP == dev_context->msc_msg_data) { MHL_TX_DBG_ERR("Got RAP{CBUS_MODE_UP}\n"); mhl_tx_stop_timer(dev_context, dev_context-> cbus_mode_up_timer); } else { MHL_TX_DBG_ERR("Unrecognized RAP code: 0x%02x " "received\n", dev_context->msc_msg_data); rapk_status = MHL_RAPK_UNRECOGNIZED; } /* Always RAPK to the peer */ si_mhl_rapk_send(dev_context, rapk_status); if (rapk_status == MHL_RAPK_NO_ERR) mhl_event_notify(dev_context, MHL_TX_EVENT_RAP_RECEIVED, dev_context->msc_msg_data, NULL); break; case MHL_MSC_MSG_RCP: /* * If we get a RCP key that we do NOT support, * send back RCPE. Do not notify app layer. */ if (rcpSupportTable [dev_context->msc_msg_data & MHL_RCP_KEY_ID_MASK]. rcp_support & MHL_LOGICAL_DEVICE_MAP) { mhl_event_notify(dev_context, MHL_TX_EVENT_RCP_RECEIVED, dev_context->msc_msg_data, NULL); } else { /* Save keycode to send a RCPK after RCPE. */ dev_context->msc_save_rcp_key_code = dev_context->msc_msg_data; si_mhl_tx_rcpe_send(dev_context, RCPE_INEFFECTIVE_KEY_CODE); } break; case MHL_MSC_MSG_RCPK: mhl_event_notify(dev_context, MHL_TX_EVENT_RCPK_RECEIVED, dev_context->msc_msg_data, NULL); break; case MHL_MSC_MSG_RCPE: mhl_event_notify(dev_context, MHL_TX_EVENT_RCPE_RECEIVED, dev_context->msc_msg_data, NULL); break; case MHL_MSC_MSG_UCP: /* * Save key code so that we can send an UCPE message in * case the UCP key code is rejected by the host * application. */ dev_context->msc_save_ucp_key_code = dev_context->msc_msg_data; mhl_event_notify(dev_context, MHL_TX_EVENT_UCP_RECEIVED, dev_context->msc_save_ucp_key_code, NULL); break; case MHL_MSC_MSG_UCPK: mhl_event_notify(dev_context, MHL_TX_EVENT_UCPK_RECEIVED, dev_context->msc_msg_data, NULL); break; case MHL_MSC_MSG_UCPE: mhl_event_notify(dev_context, MHL_TX_EVENT_UCPE_RECEIVED, dev_context->msc_msg_data, NULL); break; #if (INCLUDE_RBP == 1) case MHL_MSC_MSG_RBP: /* * Save button code so that we can send an RBPE message * in case the RBP button code is rejected by the host * application. */ dev_context->msc_save_rbp_button_code = dev_context->msc_msg_data; mhl_event_notify(dev_context, MHL_TX_EVENT_RBP_RECEIVED, dev_context->msc_save_rbp_button_code, NULL); break; case MHL_MSC_MSG_RBPK: mhl_event_notify(dev_context, MHL_TX_EVENT_RBPK_RECEIVED, dev_context->msc_msg_data, NULL); break; case MHL_MSC_MSG_RBPE: mhl_event_notify(dev_context, MHL_TX_EVENT_RBPE_RECEIVED, dev_context->msc_msg_data, NULL); break; #endif case MHL_MSC_MSG_RAPK: mhl_tx_stop_timer(dev_context, dev_context->t_rap_max_timer); /* the only RAP commands that we send are * CBUS_MODE_UP and CBUS_MODE_DOWN. */ if (MHL_RAP_CBUS_MODE_DOWN == dev_context->msc_msg_last_data) { MHL_TX_DBG_ERR ("Got RAPK{%s} for RAP{CBUS_MODE_DOWN}\n", rapk_error_code_string[dev_context-> msc_msg_data & 0x03]); si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } else if (MHL_RAP_CBUS_MODE_UP == dev_context->msc_msg_last_data) { enum cbus_mode_e cbus_mode; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); MHL_TX_DBG_ERR ("Got RAPK{%s}\n", rapk_error_code_string[dev_context-> msc_msg_data & 0x03]); if (MHL_RAPK_NO_ERR == dev_context->msc_msg_data) { /* CBUS Mode switch to eCBUS */ si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_eCBUS_S); } else if (MHL_RAPK_BUSY == dev_context->msc_msg_data) { if (CM_oCBUS_PEER_IS_MHL3 == cbus_mode) { MHL_TX_DBG_ERR( "starting timer for " "CBUS_MODE_UP\n") mhl_tx_start_timer(dev_context, dev_context-> cbus_mode_up_timer, 100); } } else { /* * Nothing to do for * MHL_RAPK_UNRECOGNIZED, * MHL_RAPK_UNSUPPORTED */ } } else { MHL_TX_DBG_ERR("Got RAPK for RAP " "cmd: %02x,err_code: %02x\n", dev_context->msc_msg_last_data, dev_context->msc_msg_data); /* post status */ dev_context->rap_out_status = dev_context->msc_msg_data; } break; case MHL_MSC_MSG_RHID: case MHL_MSC_MSG_RHIDK: #if (INCLUDE_HID == 1) MHL_TX_DBG_WARN("Received MSC_MSG_RHID/K from sink\n"); mhl_tx_hid_host_negotiation(dev_context); #endif break; case MHL_MSC_MSG_BIST_READY: MHL_TX_DBG_INFO("Got BIST_READY\n"); if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT == cbus_mode) { bool status; si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY ); mhl_event_notify(dev_context, MHL_TX_EVENT_BIST_READY_RECEIVED, dev_context->msc_msg_data, NULL); status = si_mhl_tx_bist_trigger( dev_context, dev_context-> bist_setup.bist_trigger_parm); if (BIST_STATUS_NO_ERROR != status) { si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_STAT); } } break; case MHL_MSC_MSG_BIST_TRIGGER: if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { MHL_TX_DBG_ERR("another BIST_TRIGGER\n"); si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); } else if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY != cbus_mode) { MHL_TX_DBG_ERR ("Got BIST_TRIGGER when CBUS mode is %s\n", si_mhl_tx_drv_get_cbus_mode_str (cbus_mode)); break; } MHL_TX_DBG_ERR("Got BIST_TRIGGER\n"); dev_context->bist_setup.bist_trigger_parm = dev_context->bist_trigger_info = dev_context->msc_msg_data; if (dev_context->msc_msg_data == BIST_TRIGGER_IMPEDANCE_TEST) { initiate_bist_test(dev_context); } else if (dev_context->msc_msg_data != 0) { cbus_mode = dev_context->msc_msg_data & BIST_TRIGGER_TEST_E_CBUS_D ? CM_eCBUS_D_BIST : CM_eCBUS_S_BIST; MHL_TX_DBG_ERR("\n") determine_bist_timeout_value(dev_context); if (1 == si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, cbus_mode)) MHL_TX_DBG_ERR("Wait CoC Cal\n") } break; case MHL_MSC_MSG_BIST_STOP: if (cbus_mode < CM_eCBUS_S_AV_BIST) { MHL_TX_DBG_ERR ("Got BIST_STOP when CBUS mode is %s\n", si_mhl_tx_drv_get_cbus_mode_str (cbus_mode)); break; } MHL_TX_DBG_INFO("Got BIST_STOP\n"); mhl_tx_stop_timer(dev_context, dev_context->bist_timer); dev_context->bist_stat.avlink_stat = 0; /* bist timer always runs, even for bist_timeout_value == 0 (infinite) */ si_mhl_tx_bist_cleanup(dev_context); break; case MHL_MSC_MSG_BIST_REQUEST_STAT: if (cbus_mode != CM_oCBUS_PEER_IS_MHL3_BIST_STAT) { MHL_TX_DBG_ERR("Got BIST_REQUEST_STAT when " "CBUS mode is %s\n", si_mhl_tx_drv_get_cbus_mode_str (cbus_mode)); break; } MHL_TX_DBG_ERR("Got BIST_REQUEST_STAT\n"); if (dev_context->msc_msg_data) { MHL_TX_DBG_ERR("Send BIST status\n"); send_bist_status(dev_context); } break; default: MHL_TX_DBG_WARN("Unexpected MSC message " "sub-command code: 0x%02x received!\n", dev_context->msc_msg_sub_command); break; } } } static struct cbus_req *read_devcap_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); bool si_mhl_tx_read_devcap(struct mhl_dev_context *dev_context) { struct cbus_req *req; MHL_TX_DBG_INFO("called\n"); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_READ_DEVCAP; req->reg = 0; req->reg_data = 0; /* do this to avoid confusion */ req->completion = read_devcap_done; queue_cbus_transaction(dev_context, req); return true; } bool si_mhl_tx_read_devcap_reg(struct mhl_dev_context *dev_context, uint8_t offset) { struct cbus_req *req; MHL_TX_DBG_INFO("called\n"); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_READ_DEVCAP_REG; req->reg = offset; req->reg_data = 0; /* do this to avoid confusion */ queue_cbus_transaction(dev_context, req); return true; } static struct cbus_req *read_xdevcap_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); bool si_mhl_tx_read_xdevcap_impl(struct mhl_dev_context *dev_context, const char *func_name, int line_num) { struct cbus_req *req; MHL_TX_DBG_INFO("called from %s:%d\n", func_name, line_num); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_READ_XDEVCAP; req->reg = 0; req->reg_data = 0; /* do this to avoid confusion */ req->completion = read_xdevcap_done; queue_cbus_transaction(dev_context, req); return true; } static struct cbus_req *read_xdevcap_reg_done( struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); #define si_mhl_tx_read_xdevcap(dev_context) \ si_mhl_tx_read_xdevcap_impl(dev_context, __func__, __LINE__) bool si_mhl_tx_read_xdevcap_reg_impl(struct mhl_dev_context *dev_context, uint8_t reg_addr, const char *func_name, int line_num) { struct cbus_req *req; MHL_TX_DBG_WARN("called from %s:%d\n", func_name, line_num); req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") return false; } req->retry_count = 2; req->command = MHL_READ_XDEVCAP_REG; req->reg = reg_addr; req->reg_data = 0; /* avoid confusion */ req->completion = read_xdevcap_reg_done; queue_cbus_transaction(dev_context, req); return true; } #define si_mhl_tx_read_xdevcap_reg(dev_context, offset) \ si_mhl_tx_read_xdevcap_reg_impl(dev_context, offset, __func__, \ __LINE__) struct cbus_req *rcpk_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } bool si_mhl_tx_rcpk_send(struct mhl_dev_context *dev_context, uint8_t rcp_key_code) { bool status; status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCPK, rcp_key_code, rcpk_done); if (status) si_mhl_tx_drive_states(dev_context); return status; } static struct cbus_req *read_edid_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1); /* * si_mhl_tx_request_first_edid_block * * This function initiates a CBUS command to read the specified EDID block. * Returns true if the command was queued successfully. */ void si_mhl_tx_request_first_edid_block(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("tag: EDID active: %d\n", dev_context->misc_flags.flags.edid_loop_active); if (!dev_context->misc_flags.flags.edid_loop_active) { struct cbus_req *req; req = get_free_cbus_queue_entry(dev_context); if (req == NULL) { MHL_TX_DBG_INFO("couldn't get free cbus req\n"); } else { dev_context->misc_flags.flags.edid_loop_active = 1; MHL_TX_DBG_INFO("tag: EDID active: %d\n", dev_context->misc_flags.flags. edid_loop_active); /* Send MHL_READ_EDID_BLOCK command */ req->retry_count = 2; req->command = MHL_READ_EDID_BLOCK; req->burst_offset = 0; /* block number */ req->msg_data[0] = 0; /* avoid confusion */ req->completion = read_edid_done; queue_cbus_transaction(dev_context, req); si_mhl_tx_drive_states(dev_context); } } } /* si_mhl_tx_ecbus_speeds_done */ static void si_mhl_tx_ecbus_speeds_done(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; enum cbus_mode_e cbus_mode; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); /* * Set default eCBUS virtual channel slot assignments * based on the capabilities of both the transmitter * and receiver. */ if ((dev_context->xdev_cap_cache.mxdc.ecbus_speeds & MHL_XDC_ECBUS_D_150) && si_mhl_tx_drv_support_e_cbus_d( (struct drv_hw_context *) &dev_context->drv_context)) { dev_context->virt_chan_slot_counts[TDM_VC_CBUS1] = 1; dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = 39; dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = 160; } else { dev_context->virt_chan_slot_counts[TDM_VC_CBUS1] = 1; dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = 4; dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = 20; } memcpy(hw_context->tdm_virt_chan_slot_counts, dev_context->virt_chan_slot_counts, sizeof(hw_context->tdm_virt_chan_slot_counts)); if (cbus_mode <= CM_oCBUS_PEER_IS_MHL3) { bool status; enum bist_cmd_status bcs; bool send_bist_setup = true; status = si_mhl_tx_set_status( dev_context, true, MHL_XSTATUS_REG_CBUS_MODE, MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); switch (cbus_mode) { case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: MHL_TX_DBG_ERR("\n") if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & dev_context->bist_setup.bist_trigger_parm) { MHL_TX_DBG_ERR("\n") if (dev_context-> misc_flags.flags.bist_role_TE) { MHL_TX_DBG_ERR("\n") send_bist_setup = false; setup_sans_cbus1(dev_context); } } if (send_bist_setup) { MHL_TX_DBG_ERR( "%sissuing BIST_SETUP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) bcs = si_mhl_tx_bist_setup(dev_context, &dev_context->bist_setup); } MHL_TX_DBG_ERR("status: %d\n", status); break; case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: if (dev_context->misc_flags.flags.bist_role_TE) { if ((BIST_TRIGGER_ECBUS_AV_LINK_MASK | BIST_TRIGGER_E_CBUS_RX | BIST_TRIGGER_IMPEDANCE_TEST) & dev_context->bist_trigger_info) { MHL_TX_DBG_ERR( "%sissuing BIST_REQUEST_STAT%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) status = si_mhl_tx_bist_request_stat( dev_context, dev_context-> bist_setup.bist_stat_parm); MHL_TX_DBG_ERR("status: %d\n", status); break; } MHL_TX_DBG_ERR ("%sLocal BIST results%s" " eCBUS TX:%s0x%06x%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, ANSI_ESC_GREEN_TEXT, dev_context->bist_stat. e_cbus_prev_local_stat, ANSI_ESC_RESET_TEXT) } else if (BIST_TRIGGER_E_CBUS_RX & dev_context->bist_trigger_info) { MHL_TX_DBG_ERR("%swaiting for " "BIST_REQUEST_STAT " "from sink TE%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) break; } dev_context->bist_trigger_info = 0x00; /* fall through here to do cbus_mode_up */ default: /* issue RAP(CBUS_MODE_UP) * RAP support is required for MHL3 and * later devices */ MHL_TX_DBG_ERR( "%sissuing CBUS_MODE_UP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) #ifdef BIST_DONE_DEBUG si_dump_important_regs( (struct drv_hw_context *) &dev_context->drv_context); #endif status = si_mhl_tx_rap_send( dev_context, MHL_RAP_CBUS_MODE_UP); if (status) si_mhl_tx_drive_states(dev_context); } } } static struct cbus_req *read_xdevcap_reg_done( struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_INFO("mhl: peer XDEVCAP[0x%02x] data1:0x%02x\n", req->reg, data1); dev_context->xdev_cap_cache. xdevcap_cache[XDEVCAP_OFFSET(req->reg)] = data1; if (XDEVCAP_ADDR_ECBUS_SPEEDS == req->reg) si_mhl_tx_ecbus_speeds_done(dev_context); return req; } static struct cbus_req *read_xdevcap_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { int i; union MHLXDevCap_u old_xdevcap, xdevcap_changes; uint8_t roles; old_xdevcap = dev_context->xdev_cap_cache; MHL_TX_DBG_INFO("mhl: peer XDEVCAP data1:0x%02x\n", data1); si_mhl_tx_read_xdevcap_fifo((struct drv_hw_context *) &dev_context->drv_context, &dev_context->xdev_cap_cache); /* * Generate a change mask between the old and new devcaps */ for (i = 0; i < XDEVCAP_OFFSET(XDEVCAP_LIMIT); ++i) { xdevcap_changes.xdevcap_cache[i] = dev_context->xdev_cap_cache.xdevcap_cache[i] ^ old_xdevcap.xdevcap_cache[i]; if (xdevcap_changes.xdevcap_cache[i]) { MHL_TX_DBG_INFO("XDEVCAP[%d] changed from " "0x%02x to 0x%02x\n", i, old_xdevcap.xdevcap_cache[i], dev_context->xdev_cap_cache. xdevcap_cache[i]); } } roles = XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_DEV_ROLES); MHL_TX_DBG_INFO ("mhl: xdevcap_changes.xdevcap_cache[%d]= %02X\n", roles, xdevcap_changes.xdevcap_cache[roles]); if (xdevcap_changes.xdevcap_cache[roles]) { roles = dev_context->xdev_cap_cache.xdevcap_cache[roles]; MHL_TX_DBG_INFO ("mhl: XDEVCAP_ADDR_ECBUS_DEV_ROLES= %02X\n", roles); /* * If sink supports HID_DEVICE, * tell it we want to be the host */ #if (INCLUDE_HID) if (roles & MHL_XDC_HID_DEVICE) { MHL_TX_DBG_INFO("mhl: calling " "mhl_tx_hid_host_role_request()\n"); mhl_tx_hid_host_role_request(dev_context, MHL_RHID_REQUEST_HOST); } #endif } si_mhl_tx_read_devcap(dev_context); return req; } static struct cbus_req *read_devcap_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { uint8_t temp; int i; union MHLDevCap_u old_devcap, devcap_changes; old_devcap = dev_context->dev_cap_cache; si_mhl_tx_read_devcap_fifo((struct drv_hw_context *) &dev_context->drv_context, &dev_context->dev_cap_cache); if (dev_context->peer_mhl3_version < 0x30) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; dev_context->peer_mhl3_version = dev_context->dev_cap_cache.mdc.mhl_version; hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; si_set_cbus_mode_leds(CM_oCBUS_PEER_IS_MHL1_2); #ifdef FORCE_OCBUS_FOR_ECTS /* This compile option is always enabled. * It is intended to help identify code deletion by * adopters who do not need this feauture. The control * for forcing oCBUS works by using module parameter * below. Peer version is forced to 2.0 allowing 8620 * to treat the sink as if it is MHL 2.0 device and as * a result never switch cbus to MHL3 eCBUS. */ { if (force_ocbus_for_ects) { /* todo: what if the peer is MHL 1.x? */ dev_context->peer_mhl3_version = 0x20; } } #endif } MHL_TX_DBG_WARN("DEVCAP->MHL_VER = %02x\n", dev_context->peer_mhl3_version); /* * Generate a change mask between the old and new devcaps */ for (i = 0; i < sizeof(old_devcap); ++i) { devcap_changes.devcap_cache[i] = dev_context->dev_cap_cache.devcap_cache[i] ^ old_devcap.devcap_cache[i]; } /* look for a change in the pow bit */ if (MHL_DEV_CATEGORY_POW_BIT & devcap_changes.mdc. deviceCategory) { uint8_t param; uint8_t index; index = dev_context->dev_cap_cache.mdc.deviceCategory & MHL_DEV_CATEGORY_PLIM2_0; index >>= 5; param = dev_context->dev_cap_cache.mdc.deviceCategory & MHL_DEV_CATEGORY_POW_BIT; if (param) { MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* limit incoming current */ mhl_tx_vbus_control(VBUS_OFF); mhl_tx_vbus_current_ctl(plim_table[index]); } else { MHL_TX_DBG_WARN("%ssource drives VBUS%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_vbus_control(VBUS_ON); } /* Inform interested Apps of the MHL power change */ mhl_event_notify(dev_context, MHL_TX_EVENT_POW_BIT_CHG, param, NULL); } /* indicate that the DEVCAP cache is up to date. */ dev_context->misc_flags.flags.have_complete_devcap = true; /* * Check to see if any other bits besides POW_BIT have changed */ devcap_changes.mdc.deviceCategory &= ~(MHL_DEV_CATEGORY_POW_BIT | MHL_DEV_CATEGORY_PLIM2_0); temp = 0; for (i = 0; i < sizeof(devcap_changes); ++i) temp |= devcap_changes.devcap_cache[i]; if (temp) { if (dev_context->misc_flags.flags.mhl_hpd) { MHL_TX_DBG_INFO("Have HPD\n"); si_mhl_tx_initiate_edid_sequence( dev_context->edid_parser_context); } else { MHL_TX_DBG_INFO("No HPD\n"); } } return req; } static struct cbus_req *read_edid_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { si_mhl_tx_drive_states(dev_context); MHL_TX_GENERIC_DBG_PRINT(data1 ? -1 : 1, "data1: %d\n", data1) if (0 == data1) { dev_context->edid_valid = true; si_mhl_tx_handle_atomic_hw_edid_read_complete (dev_context->edid_parser_context); } dev_context->misc_flags.flags.edid_loop_active = 0; MHL_TX_DBG_INFO("tag: EDID active: %d\n", dev_context->misc_flags.flags.edid_loop_active); return req; } static struct cbus_req *write_stat_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_INFO("Sent WRITE_STAT[0x%02x]->0x%02x" " miscFlags: %08X\n", req->reg, req->reg_data, dev_context->misc_flags.as_uint32); if (MHL_STATUS_REG_CONNECTED_RDY == req->reg) { if (MHL_STATUS_DCAP_RDY & req->reg_data) { dev_context->misc_flags.flags.sent_dcap_rdy = true; MHL_TX_DBG_INFO("%sSent DCAP_RDY%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } } else if (MHL_STATUS_REG_LINK_MODE == req->reg) { if (MHL_STATUS_PATH_ENABLED & req->reg_data) { dev_context->misc_flags.flags.sent_path_en = true; MHL_TX_DBG_INFO("FLAGS_SENT_PATH_EN\n"); } } return req; } static struct cbus_req *write_burst_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { struct tdm_alloc_burst *tdm_burst; tdm_burst = (struct tdm_alloc_burst *) req->msg_data; MHL_TX_DBG_INFO("MHL_WRITE_BURST\n"); if (burst_id_VC_CONFIRM == BURST_ID(tdm_burst->header.burst_id)) { int i; int can_reassign = 1; MHL_TX_DBG_ERR("VC_CONFIRM done\n"); for (i = 0; i < VC_MAX; ++i) { if (VC_RESPONSE_ACCEPT != tdm_burst->vc_info[i]. req_resp.channel_size) { can_reassign = 0; } } /* changing slot allocations may result in a loss of data; however, * the link will self-synchronize */ if ((can_reassign) && (0 == si_mhl_tx_drv_set_tdm_slot_allocation( (struct drv_hw_context *)&dev_context->drv_context, dev_context->virt_chan_slot_counts, true))) { MHL_TX_DBG_ERR("Slots reassigned.\n"); } } else if (burst_id_BIST_RETURN_STAT == BURST_ID(tdm_burst->header.burst_id)) { bool status; MHL_TX_DBG_ERR("%sBIST DONE%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT) /* we already know that were in MHL_BIST */ si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); /* issue RAP(CBUS_MODE_UP) * RAP support is required for MHL3 and * later devices */ MHL_TX_DBG_ERR( "%sissuing CBUS_MODE_UP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) #ifdef BIST_DONE_DEBUG si_dump_important_regs( (struct drv_hw_context *) &dev_context->drv_context); #endif status = si_mhl_tx_rap_send( dev_context, MHL_RAP_CBUS_MODE_UP); if (status) { si_mhl_tx_drive_states( dev_context); } } return req; } static struct cbus_req *set_int_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_INFO("MHL_SET_INT\n"); if (MHL_RCHANGE_INT == req->reg) { MHL_TX_DBG_INFO("%sSent MHL_RCHANGE_INT%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); if (MHL3_INT_FEAT_COMPLETE == req->reg_data) { MHL_TX_DBG_WARN("%sSent FEAT_COMPLETE%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } } return req; } /* * si_mhl_tx_msc_command_done * * This function is called by the driver to notify completion of last command. * * It is called in interrupt context to meet some MHL specified timings. * Therefore, it should not call app layer and do negligible processing, no * printfs. */ void si_mhl_tx_msc_command_done(struct mhl_dev_context *dev_context, uint8_t data1) { struct cbus_req *req; MHL_TX_DBG_INFO("data1 = %02X\n", data1); req = dev_context->current_cbus_req; if (req == NULL) { MHL_TX_DBG_ERR("No message to associate with " "completion notification\n"); return; } MHL_TX_DBG_INFO("current command:0x%02x\n", req->command); dev_context->current_cbus_req = NULL; if (req->status.flags.cancel == true) { MHL_TX_DBG_INFO("Canceling request with command 0x%02x\n", req->command); } else if (MHL_MSC_MSG == req->command) { if (dev_context->intr_info.flags & DRV_INTR_MSC_NAK) { msleep(1000); MHL_TX_DBG_INFO("MSC_NAK, re-trying...\n"); /* * Request must be retried, so place it back * on the front of the queue. */ req->status.as_uint8 = 0; queue_priority_cbus_transaction(dev_context, req); req = NULL; } else if (req->completion) { MHL_TX_DBG_INFO ("MHL_MSC_MSG sub cmd: 0x%02x data: 0x%02x\n", req->msg_data[0], req->msg_data[1]); req = req->completion(dev_context, req, data1); } else { MHL_TX_DBG_INFO("default\n" "\tcommand: 0x%02X\n" "\tmsg_data: 0x%02X " "msc_msg_last_data: 0x%02X\n", req->command, req->msg_data[0], dev_context->msc_msg_last_data); } } else if (req->completion) { req = req->completion(dev_context, req, data1); #if 0 } else if (MHL_READ_XDEVCAP == req->command) { req = read_xdevcap_done(dev_context, req, data1); } else if (MHL_READ_XDEVCAP_REG == req->command) { req = read_xdevcap_reg_done(dev_context, req, data1); } else if (MHL_READ_DEVCAP == req->command) { req = read_devcap_done(dev_context, req, data1); } else if (MHL_READ_EDID_BLOCK == req->command) { req = read_edid_done(dev_context, req, data1); } else if (MHL_WRITE_STAT == req->command) { req = write_stat_done(dev_context, req, data1); #endif #if 0 } else if (MHL_WRITE_BURST == req->command) { req = write_burst_done(dev_context, req, data1); } else if (MHL_SET_INT == req->command) { req = set_int_done(dev_context, req, data1); #endif } else if (MHL_SEND_3D_REQ_OR_FEAT_REQ == req->command) { MHL_TX_DBG_ERR("3D_REQ or FEAT_REQ completed\n"); } else { MHL_TX_DBG_INFO("default\n" "\tcommand: 0x%02X reg: 0x%02x reg_data: " "0x%02x burst_offset: 0x%02x msg_data[0]: " "0x%02x msg_data[1]: 0x%02x\n", req->command, req->reg, req->reg_data, req->burst_offset, req->msg_data[0], req->msg_data[1]); } if (req != NULL) return_cbus_queue_entry(dev_context, req); } void si_mhl_tx_process_vc_assign_burst(struct mhl_dev_context *dev_context, uint8_t *write_burst_data) { struct tdm_alloc_burst *tdm_burst; uint8_t idx = 0; int8_t save_burst_TDM_VC_E_MSC_idx = -1; int8_t save_burst_TDM_VC_T_CBUS_idx = -1; tdm_burst = (struct tdm_alloc_burst *)write_burst_data; if (0 != calculate_generic_checksum(write_burst_data, 0, 16)) { uint8_t i; for (i = 0; i < 16; i++) MHL_TX_DBG_ERR("0x%02X\n", write_burst_data[i]); MHL_TX_DBG_ERR("Bad checksum in virtual channel assign\n"); return; } /* The virtual channel assignment in the WRITE_BURST may contain one, * two or three channel allocations */ if (tdm_burst->num_entries_this_burst > 3) { MHL_TX_DBG_ERR("Bad number of assignment requests in " "virtual channel assign\n"); return; } for (idx = 0; idx < VC_MAX; idx++) { dev_context->prev_virt_chan_slot_counts[idx] = dev_context->virt_chan_slot_counts[idx]; } for (idx = 0; idx < tdm_burst->num_entries_this_burst; idx++) { switch (tdm_burst->vc_info[idx].feature_id) { case FEATURE_ID_E_MSC: dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = tdm_burst->vc_info[idx].req_resp.channel_size; save_burst_TDM_VC_E_MSC_idx = idx; break; case FEATURE_ID_USB: dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = tdm_burst->vc_info[idx].req_resp.channel_size; save_burst_TDM_VC_T_CBUS_idx = idx; break; default: tdm_burst->vc_info[idx].req_resp.channel_size = VC_RESPONSE_BAD_FEATURE_ID; break; } } if (si_mhl_tx_drv_set_tdm_slot_allocation ((struct drv_hw_context *)&dev_context->drv_context, dev_context->virt_chan_slot_counts, false)) { MHL_TX_DBG_INFO("Source will reject request to assign " "CBUS virtual channels\n"); for (idx = 0; idx < VC_MAX; idx++) dev_context->virt_chan_slot_counts[idx] = dev_context->prev_virt_chan_slot_counts[idx]; if (save_burst_TDM_VC_E_MSC_idx >= 0) tdm_burst->vc_info[save_burst_TDM_VC_E_MSC_idx]. req_resp.channel_size = VC_RESPONSE_BAD_CHANNEL_SIZE; if (save_burst_TDM_VC_T_CBUS_idx >= 0) tdm_burst->vc_info[save_burst_TDM_VC_T_CBUS_idx]. req_resp.channel_size = VC_RESPONSE_BAD_CHANNEL_SIZE; } else { if (save_burst_TDM_VC_E_MSC_idx >= 0) tdm_burst->vc_info[save_burst_TDM_VC_E_MSC_idx]. req_resp.channel_size = VC_RESPONSE_ACCEPT; if (save_burst_TDM_VC_T_CBUS_idx >= 0) tdm_burst->vc_info[save_burst_TDM_VC_T_CBUS_idx]. req_resp.channel_size = VC_RESPONSE_ACCEPT; } /* Respond back to requester to indicate acceptance or rejection */ tdm_burst->header.burst_id.high = burst_id_VC_CONFIRM >> 8; tdm_burst->header.burst_id.low = (uint8_t) burst_id_VC_CONFIRM; tdm_burst->header.checksum = 0; tdm_burst->header.checksum = calculate_generic_checksum((uint8_t *) (tdm_burst), 0, sizeof(*tdm_burst)); si_mhl_tx_request_write_burst(dev_context, 0, sizeof(*tdm_burst), (uint8_t *) (tdm_burst)); /* the actual assignment will occur when the write_burst is completed*/ } void si_mhl_tx_process_vc_confirm_burst(struct mhl_dev_context *dev_context, uint8_t *write_burst_data) { struct tdm_alloc_burst *tdm_burst; bool assign_ok = true; tdm_burst = (struct tdm_alloc_burst *)write_burst_data; if (0 != calculate_generic_checksum(write_burst_data, 0, 16)) { uint8_t i; for (i = 0; i < 16; i++) MHL_TX_DBG_ERR("0x%02X\n", write_burst_data[i]); MHL_TX_DBG_ERR("Bad checksum in virtual channel confirm\n"); return; } if (tdm_burst->vc_info[TDM_VC_E_MSC - 1].req_resp.response != VC_RESPONSE_ACCEPT) { MHL_TX_DBG_WARN("Sink rejected request to assign" "%d slots to E_MSC virtual channel with status: 0x%02x\n", dev_context-> virt_chan_slot_counts[TDM_VC_E_MSC], tdm_burst->vc_info[TDM_VC_E_MSC - 1].req_resp.response); assign_ok = false; } if (tdm_burst->vc_info[TDM_VC_T_CBUS - 1].req_resp.response != VC_RESPONSE_ACCEPT) { MHL_TX_DBG_WARN("Sink rejected request to assign" "%d slots to T_CBUS virtual channel with status: 0x%02x\n", dev_context-> virt_chan_slot_counts[TDM_VC_T_CBUS], tdm_burst->vc_info[TDM_VC_T_CBUS - 1].req_resp.response); assign_ok = false; } if (assign_ok) { si_mhl_tx_drv_set_tdm_slot_allocation((struct drv_hw_context *) &dev_context->drv_context, dev_context-> virt_chan_slot_counts, true); MHL_TX_DBG_ERR ("Sink accepted requested virtual channel assignments\n"); } else { dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = dev_context->prev_virt_chan_slot_counts[TDM_VC_E_MSC]; dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = dev_context->prev_virt_chan_slot_counts[TDM_VC_T_CBUS]; } } struct cbus_req *bist_ready_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } void send_bist_ready(struct mhl_dev_context *dev_context) { si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_READY, dev_context->bist_ready_status, bist_ready_done); } void si_mhl_tx_process_bist_setup_burst(struct mhl_dev_context *dev_context, uint8_t *write_burst_data) { struct bist_setup_burst *bist_setup; enum cbus_mode_e cbus_mode; uint8_t tmp; uint8_t setup_status = 0; MHL_TX_DBG_INFO("\n"); cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); switch (cbus_mode) { case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *)&dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); break; case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: case CM_oCBUS_PEER_IS_MHL3: break; default: MHL_TX_DBG_ERR("BIST_SETUP received while CBUS mode is %s\n", si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); return; } bist_setup = (struct bist_setup_burst *)write_burst_data; dev_context->misc_flags.flags.bist_role_TE = 0; /* * Validate the received BIST setup info and if it checks out * save it for use later when a BIST test is requested. */ tmp = calculate_generic_checksum(write_burst_data, 0, sizeof(*bist_setup)); if (tmp != 0) { MHL_TX_DBG_ERR("Bad checksum (0x%02x) in BIST setup burst\n", tmp); return; } mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); dev_context->bist_setup.avlink_data_rate = bist_setup->avlink_data_rate; dev_context->bist_setup.avlink_duration = bist_setup->avlink_duration; dev_context->bist_setup.avlink_fixed_pat = (bist_setup->avlink_fixed_h << 8) | bist_setup->avlink_fixed_l; dev_context->bist_setup.avlink_pattern = bist_setup->avlink_pattern; dev_context->bist_setup.avlink_randomizer = bist_setup->avlink_randomizer; dev_context->bist_setup.avlink_video_mode = bist_setup->avlink_video_mode; dev_context->bist_setup.e_cbus_duration = bist_setup->e_cbus_duration; dev_context->bist_setup.e_cbus_fixed_pat = (bist_setup->e_cbus_fixed_h << 8) | bist_setup->e_cbus_fixed_l; dev_context->bist_setup.e_cbus_pattern = bist_setup->e_cbus_pattern; dev_context->bist_setup.impedance_mode = bist_setup->impedance_mode; si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); switch (bist_setup->impedance_mode) { case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: break; case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: if (si_mhl_tx_drv_support_e_cbus_d( (struct drv_hw_context *)&dev_context->drv_context)) { break; } default: MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "IMPEDANCE_MODE field\n", bist_setup->impedance_mode); setup_status |= BIST_READY_TERM_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); break; } if (bist_setup->e_cbus_duration == 0) { MHL_TX_DBG_ERR("Invalid value 0x00 specified in " "eCBUS_DURATION field\n"); setup_status |= BIST_READY_E_CBUS_ERROR | BIST_READY_TERM_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } if (bist_setup->e_cbus_pattern > BIST_ECBUS_PATTERN_MAX) { MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "eCBUS_PATTERN field\n", bist_setup->e_cbus_pattern); setup_status |= BIST_READY_E_CBUS_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } if (bist_setup->avlink_pattern > BIST_AVLINK_PATTERN_MAX) { MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "AVLINK_PATTERN field\n", bist_setup->avlink_pattern); setup_status |= BIST_READY_AVLINK_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } if (!(bist_setup->avlink_video_mode == 3 || bist_setup->avlink_video_mode == 4)) { MHL_TX_DBG_ERR("Invalid value specified in " "AVLINK_VIDEO_MODE field\n"); setup_status |= BIST_READY_AVLINK_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } if (bist_setup->avlink_randomizer > 1) { MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "AVLINK_RANDOMIZER field\n", bist_setup->avlink_randomizer); setup_status |= BIST_READY_AVLINK_ERROR; si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } /* * Make sure the specified AV data rate is valid and supported by the * transmitter. */ if (bist_setup->avlink_data_rate == 0 || bist_setup->avlink_data_rate > 3) { MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "AVLINK_PATTERN field\n", bist_setup->avlink_data_rate); setup_status |= BIST_READY_AVLINK_ERROR; } if (!(setup_status & BIST_READY_E_CBUS_ERROR)) setup_status |= BIST_READY_E_CBUS_READY; if (!(setup_status & BIST_READY_AVLINK_ERROR)) setup_status |= BIST_READY_AVLINK_READY; if (!(setup_status & BIST_READY_TERM_ERROR)) setup_status |= BIST_READY_TERM_READY; cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY == cbus_mode) { mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); MHL_TX_DBG_ERR("\n") dev_context->bist_ready_status = setup_status; setup_sans_cbus1(dev_context); } } void si_mhl_tx_process_write_burst_data(struct mhl_dev_context *dev_context) { int ret_val = 0; enum BurstId_e burst_id; enum cbus_mode_e cbus_mode; MHL_TX_DBG_INFO("\n"); cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); /* continue else statement to support 3D along with MDT */ ret_val = si_mhl_tx_drv_get_scratch_pad( (struct drv_hw_context *)(&dev_context->drv_context), 0, dev_context->incoming_scratch_pad.asBytes, sizeof(dev_context->incoming_scratch_pad)); if (ret_val < 0) { MHL_TX_DBG_ERR("scratch pad failure 0x%x\n", ret_val); } else { burst_id = BURST_ID(dev_context->incoming_scratch_pad.videoFormatData. header.burst_id); MHL_TX_DBG_WARN("Got WRITE_BURST {0x%04x}\n", burst_id); switch (burst_id) { case burst_id_3D_VIC: si_mhl_tx_process_3d_vic_burst( dev_context->edid_parser_context, &dev_context->incoming_scratch_pad. videoFormatData); break; case burst_id_3D_DTD: si_mhl_tx_process_3d_dtd_burst( dev_context->edid_parser_context, &dev_context->incoming_scratch_pad. videoFormatData); break; case burst_id_HEV_VIC: si_mhl_tx_process_hev_vic_burst( dev_context->edid_parser_context, &dev_context->incoming_scratch_pad. hev_vic_data); break; case burst_id_HEV_DTDA: si_mhl_tx_process_hev_dtd_a_burst( dev_context->edid_parser_context, &dev_context->incoming_scratch_pad. hev_dtd_a_data); break; case burst_id_HEV_DTDB: si_mhl_tx_process_hev_dtd_b_burst( dev_context->edid_parser_context, &dev_context->incoming_scratch_pad. hev_dtd_b_data); break; case burst_id_VC_ASSIGN: si_mhl_tx_process_vc_assign_burst(dev_context, (uint8_t *)(&dev_context-> incoming_scratch_pad)); break; case burst_id_VC_CONFIRM: si_mhl_tx_process_vc_confirm_burst(dev_context, (uint8_t *)(&dev_context-> incoming_scratch_pad)); break; case burst_id_AUD_DELAY: mhl_event_notify(dev_context, MHL_EVENT_AUD_DELAY_RCVD, 0x00, &dev_context->incoming_scratch_pad); break; case burst_id_ADT_BURSTID: break; case burst_id_BIST_SETUP: si_mhl_tx_process_bist_setup_burst(dev_context, (uint8_t *)(&dev_context-> incoming_scratch_pad)); break; case burst_id_BIST_RETURN_STAT: { struct bist_return_stat_burst *bist_status; bist_status = (struct bist_return_stat_burst *) dev_context->incoming_scratch_pad.asBytes; if (0 != calculate_generic_checksum( (uint8_t *)bist_status, 0, sizeof(*bist_status))) { MHL_TX_DBG_ERR ("BIST_RETURN_STAT received " "with bad checksum\n"); } else { uint16_t e_cbus_remote_stat; uint16_t av_link_stat; bool status; e_cbus_remote_stat = bist_status->e_cbus_stat_h << 8; e_cbus_remote_stat |= bist_status->e_cbus_stat_l; av_link_stat = bist_status->avlink_stat_h << 8; av_link_stat |= bist_status->avlink_stat_l; dev_context->bist_stat.e_cbus_remote_stat = e_cbus_remote_stat; dev_context->bist_stat.avlink_stat = av_link_stat; MHL_TX_DBG_ERR ("BIST_RETURN_STAT received eCBUS_STAT" " remote:%s0x%04x%s" " local:%s0x%06x%s" " AV_LINK_STAT %s0x%04x%s\n", ANSI_ESC_GREEN_TEXT, e_cbus_remote_stat, ANSI_ESC_RESET_TEXT, ANSI_ESC_GREEN_TEXT, dev_context->bist_stat. e_cbus_prev_local_stat, ANSI_ESC_RESET_TEXT, ANSI_ESC_GREEN_TEXT, av_link_stat, ANSI_ESC_RESET_TEXT); mhl_event_notify(dev_context, MHL_TX_EVENT_BIST_STATUS_RECEIVED, 0x00, &(dev_context->bist_stat)); /* * BIST run completed */ si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); MHL_TX_DBG_ERR( "%sissuing CBUS_MODE_UP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) #ifdef BIST_DONE_DEBUG si_dump_important_regs( (struct drv_hw_context *) &dev_context->drv_context); #endif status = si_mhl_tx_rap_send( dev_context, MHL_RAP_CBUS_MODE_UP); if (status) { si_mhl_tx_drive_states( dev_context); } } } break; case burst_id_BIST_DISCARD: { struct bist_discard_burst *bist_discard; bist_discard = (struct bist_discard_burst *) dev_context->incoming_scratch_pad.asBytes; MHL_TX_DBG_ERR("BIST_DISCARD:0x%x\n", bist_discard->hdr.remaining_length) } break; case burst_id_BIST_ECHO_REQUEST: { struct bist_echo_request_burst *echo_request; struct bist_echo_response_burst echo_response; int i; echo_request = (struct bist_echo_request_burst *) dev_context->incoming_scratch_pad.asBytes; echo_response.hdr.burst_id.high = HIGH_BYTE_16(burst_id_BIST_ECHO_RESPONSE); echo_response.hdr.burst_id.low = LOW_BYTE_16(burst_id_BIST_ECHO_RESPONSE); echo_response.hdr.remaining_length = echo_request->hdr.remaining_length; for (i = 0; (i < echo_request->hdr.remaining_length) && (i < sizeof(echo_response.payload)); ++i) { echo_response.payload[i] = echo_request->payload[i]; } si_mhl_tx_request_write_burst( dev_context, 0, sizeof(echo_response), (uint8_t *)&echo_response); } break; case burst_id_BIST_ECHO_RESPONSE: break; case burst_id_EMSC_SUPPORT: break; case burst_id_HID_PAYLOAD: break; case burst_id_BLK_RCV_BUFFER_INFO: break; case burst_id_BITS_PER_PIXEL_FMT: break; case LOCAL_ADOPTER_ID: #ifdef MEDIA_DATA_TUNNEL_SUPPORT case MHL_TEST_ADOPTER_ID: if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { MHL_TX_DBG_ERR("MHL_TEST_ID 0x%04x\n", burst_id); si_mhl_tx_drv_switch_cbus_mode( (struct drv_hw_context *) &dev_context->drv_context, CM_oCBUS_PEER_IS_MHL3); } else { si_mhl_tx_mdt_process_packet(dev_context, (void *)&dev_context->incoming_scratch_pad. asBytes); } #else /* * Cause a notification event to be raised to allow * interested applications a chance to process the * received write burst data. */ mhl_event_notify(dev_context, MHL_TX_EVENT_SPAD_RECEIVED, sizeof(dev_context->incoming_scratch_pad), dev_context->incoming_scratch_pad.asBytes); #endif break; default: MHL_TX_DBG_ERR("Dropping write burst with " "invalid adopter id: 0x%04x\n", burst_id); break; } } } static bool si_mhl_tx_set_path_en(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("called\n"); si_mhl_tx_drv_enable_video_path((struct drv_hw_context *) (&dev_context->drv_context)); dev_context->link_mode |= MHL_STATUS_PATH_ENABLED; return si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_LINK_MODE, dev_context->link_mode); } static bool si_mhl_tx_clr_path_en(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("called\n"); si_mhl_tx_drv_disable_video_path((struct drv_hw_context *) (&dev_context->drv_context)); dev_context->link_mode &= ~MHL_STATUS_PATH_ENABLED; return si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_LINK_MODE, dev_context->link_mode); } static void si_mhl_tx_refresh_peer_devcap_entries_impl(struct mhl_dev_context *dev_context, const char *function, int line) { MHL_TX_DBG_INFO("from %s:%d\n", function, line); MHL_TX_DBG_INFO("DCAP_RDY DEVCAP: %s\n", dev_context->misc_flags.flags. have_complete_devcap ? "current" : "stale"); /* * If there is a DEV CAP read operation in progress * cancel it and issue a new DEV CAP read to make sure * we pick up all the DEV CAP register changes. */ if (dev_context->current_cbus_req != NULL) { if (dev_context->current_cbus_req->command == MHL_READ_DEVCAP) { dev_context->current_cbus_req->status.flags.cancel = true; MHL_TX_DBG_ERR("%s cancelling MHL_READ_DEVCAP%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } si_mhl_tx_read_devcap(dev_context); MHL_TX_DBG_INFO("mhl_version:0x%x\n", dev_context->dev_cap_cache.mdc.mhl_version); } /* * si_mhl_tx_got_mhl_intr * * This function is called to inform of the arrival * of an MHL INTERRUPT message. */ void si_mhl_tx_got_mhl_intr(struct mhl_dev_context *dev_context, uint8_t intr_0, uint8_t intr_1) { MHL_TX_DBG_INFO("INTERRUPT Arrived. %02X, %02X\n", intr_0, intr_1); /* Handle DCAP_CHG INTR here */ if (MHL_INT_DCAP_CHG & intr_0) { MHL_TX_DBG_WARN("got DCAP_CHG stopping timer...\n"); mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); if (MHL_STATUS_DCAP_RDY & dev_context->status_0) { MHL_TX_DBG_WARN("got DCAP_CHG & DCAP_RDY\n"); if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { if (si_mhl_tx_drv_get_cbus_mode(dev_context) < CM_eCBUS_S) { si_mhl_tx_read_xdevcap_reg(dev_context, XDEVCAP_ADDR_ECBUS_SPEEDS); } else { si_mhl_tx_read_xdevcap(dev_context); } } else { si_mhl_tx_refresh_peer_devcap_entries (dev_context); } } } if (MHL_INT_DSCR_CHG & intr_0) { /* remote WRITE_BURST is complete */ dev_context->misc_flags.flags.rcv_scratchpad_busy = false; si_mhl_tx_process_write_burst_data(dev_context); } if (MHL_INT_REQ_WRT & intr_0) { /* Scratch pad write request from the sink device. */ if (dev_context->misc_flags.flags.rcv_scratchpad_busy) { /* * Use priority 1 to defer sending grant until * local traffic is done */ si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL_INT_GRT_WRT, 1); } else { /* use priority 0 to respond immediately */ si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL_INT_GRT_WRT, 0); } } if (MHL3_INT_FEAT_REQ & intr_0) { /* Send write bursts for all features that we support */ struct MHL3_emsc_support_data_t emsc_support = { {ENCODE_BURST_ID(burst_id_EMSC_SUPPORT), 0, /* checksum starts at 0 */ 1, /* total entries */ 1 /* sequence id */ }, 1, /* num entries this burst */ { { ENCODE_BURST_ID(burst_id_HID_PAYLOAD), ENCODE_BURST_ID(0), ENCODE_BURST_ID(0), ENCODE_BURST_ID(0), ENCODE_BURST_ID(0) } } }; struct MHL3_adt_data_t adt_burst = { { ENCODE_BURST_ID(burst_id_ADT_BURSTID), 0, /* checksum starts at 0 */ 1, /* total entries */ 1 /* sequence id */ }, { 0, /* format flags */ .descriptors = { .short_descs = {0, 0, 0, 0, 0, 0, 0, 0, 0} #if 0 .spkr_alloc_db = { .cea861f_spkr_alloc = {0, 0, 0}, .cea861f_spkr_alloc = {0, 0, 0}, .cea861f_spkr_alloc = {0, 0, 0} } #endif } } }; MHL_TX_DBG_WARN("%sGot FEAT_REQ%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); emsc_support.header.checksum = calculate_generic_checksum((uint8_t *) &emsc_support, 0, sizeof(emsc_support)); adt_burst.header.checksum = calculate_generic_checksum((uint8_t *) &adt_burst, 0, sizeof(adt_burst)); /* Step 6: Store HEV_VIC in Burst Out-Queue. */ if (false == si_mhl_tx_send_write_burst(dev_context, &emsc_support)) { MHL_TX_DBG_ERR("%scbus queue flooded%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } else if (false == si_mhl_tx_send_write_burst(dev_context, &adt_burst)) { MHL_TX_DBG_ERR("%scbus queue flooded%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } /* send with normal priority */ si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, MHL3_INT_FEAT_COMPLETE, 1); } if (MHL3_INT_FEAT_COMPLETE & intr_0) { /* sink is finished sending write bursts in response to * MHL3_INT_FEAT_REQ */ si_mhl_tx_display_timing_enumeration_end(dev_context-> edid_parser_context); } if (MHL_INT_EDID_CHG & intr_1) { dev_context->edid_valid = false; MHL_TX_DBG_INFO("MHL_INT_EDID_CHG\n"); si_edid_reset(dev_context->edid_parser_context); if (dev_context->misc_flags.flags.have_complete_devcap) { if (dev_context->misc_flags.flags.mhl_hpd) { MHL_TX_DBG_INFO("tag: EDID_CHG\n"); si_mhl_tx_initiate_edid_sequence( dev_context->edid_parser_context); } } } } /* * si_si_mhl_tx_check_av_link_status */ static void si_mhl_tx_check_av_link_status(struct mhl_dev_context *dev_context) { /* is TMDS normal */ if (MHL_XDS_LINK_STATUS_TMDS_NORMAL & dev_context->xstatus_1) { MHL_TX_DBG_WARN("AV LINK_MODE_STATUS is NORMAL.\n"); if (dev_context->misc_flags.flags.edid_loop_active) { MHL_TX_DBG_ERR("EDID busy\n"); } else if (!dev_context->misc_flags.flags.mhl_hpd) { MHL_TX_DBG_ERR( "No HPD when receiving AV LINK_MODE_STATUS\n"); } else { si_mhl_tx_drv_start_cp(dev_context); } } else { MHL_TX_DBG_WARN("AV LINK_MODE_STATUS not NORMAL\n"); si_mhl_tx_drv_shut_down_HDCP2((struct drv_hw_context *) (&dev_context->drv_context)); } } /* * si_mhl_tx_got_mhl_status * * This function is called by the driver to inform of arrival of a MHL STATUS. */ void si_mhl_tx_got_mhl_status(struct mhl_dev_context *dev_context, struct mhl_device_status *dev_status) { uint8_t status_change_bit_mask_0; uint8_t status_change_bit_mask_1; uint8_t xstatus_change_bit_mask_1; uint8_t xstatus_change_bit_mask_3; MHL_TX_DBG_WARN ("STATUS Arrived. %02X, %02X %02X : %02X %02X %02X %02X\n", dev_status->write_stat[0], dev_status->write_stat[1], dev_status->write_stat[2], dev_status->write_xstat[0], dev_status->write_xstat[1], dev_status->write_xstat[2], dev_status->write_xstat[3]); /* * Handle DCAP_RDY STATUS here itself */ status_change_bit_mask_0 = dev_status->write_stat[0] ^ dev_context->status_0; status_change_bit_mask_1 = dev_status->write_stat[1] ^ dev_context->status_1; xstatus_change_bit_mask_1 = dev_status->write_xstat[1] ^ dev_context->xstatus_1; xstatus_change_bit_mask_3 = dev_status->write_xstat[3] ^ dev_context->xstatus_3; /* * Remember the event. (other code checks the saved values, * so save the values early, but not before the XOR operations above) */ dev_context->status_0 = dev_status->write_stat[0]; dev_context->status_1 = dev_status->write_stat[1]; dev_context->xstatus_1 = dev_status->write_xstat[1]; dev_context->xstatus_3 = dev_status->write_xstat[3]; if (0xFF & dev_status->write_stat[2]) { if (0 == dev_context->peer_mhl3_version) { dev_context->peer_mhl3_version = dev_status->write_stat[2]; } MHL_TX_DBG_WARN("Got VERSION_STAT->MHL_VER = %02x\n", dev_context->peer_mhl3_version); } if (MHL_STATUS_DCAP_RDY & status_change_bit_mask_0) { MHL_TX_DBG_WARN("DCAP_RDY changed\n"); if (MHL_STATUS_DCAP_RDY & dev_status->write_stat[0]) { if (dev_context->peer_mhl3_version >= 0x30) { MHL_TX_DBG_INFO("Assuming minimum MHL3.x" " feature support\n"); dev_context->dev_cap_cache.mdc.featureFlag |= MHL_FEATURE_RCP_SUPPORT | MHL_FEATURE_RAP_SUPPORT | MHL_FEATURE_SP_SUPPORT; } mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); /* some dongles send DCAP_RDY, but not DCAP_CHG */ mhl_tx_start_timer(dev_context, dev_context->dcap_chg_timer, 2000); if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) MHL_TX_DBG_WARN("waiting for DCAP_CHG\n"); } } /* look for a change in the pow bit */ if (MHL_STATUS_POW_STAT & status_change_bit_mask_0) { uint8_t sink_drives_vbus = (MHL_STATUS_POW_STAT & dev_status->write_stat[0]); uint8_t param = 0; if (sink_drives_vbus) { uint8_t index; index = dev_status->write_stat[0]; index &= MHL_STATUS_PLIM_STAT_MASK; index >>= 3; param = MHL_DEV_CATEGORY_POW_BIT; /* * Since downstream device is supplying VBUS power, * turn off our VBUS power here. If the platform * application can control VBUS power it should turn * off it's VBUS power now. */ MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* limit incoming current */ mhl_tx_vbus_control(VBUS_OFF); mhl_tx_vbus_current_ctl(plim_table[index]); } else { MHL_TX_DBG_WARN("%ssource drives VBUS" " according to PLIM_STAT%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); /* limit outgoing current */ mhl_tx_vbus_control(VBUS_ON); } /* we only get POW_STAT if the sink is MHL3 or newer, * so update the POW bit */ dev_context->dev_cap_cache.mdc.deviceCategory &= ~MHL_DEV_CATEGORY_POW_BIT; dev_context->dev_cap_cache.mdc.deviceCategory |= param; /* Inform interested Apps of the MHL power change */ mhl_event_notify(dev_context, MHL_TX_EVENT_POW_BIT_CHG, param, NULL); } /* did PATH_EN change? */ if (MHL_STATUS_PATH_ENABLED & status_change_bit_mask_1) { MHL_TX_DBG_INFO("PATH_EN changed\n"); if (MHL_STATUS_PATH_ENABLED & dev_status->write_stat[1]) si_mhl_tx_set_path_en(dev_context); else si_mhl_tx_clr_path_en(dev_context); } if (xstatus_change_bit_mask_1) { MHL_TX_DBG_WARN("link mode status changed %02X\n", xstatus_change_bit_mask_1); si_mhl_tx_check_av_link_status(dev_context); } } struct cbus_req *rcp_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_rcp_send * * This function checks if the peer device supports RCP and sends rcpKeyCode. * The function will return a value of true if it could successfully send the * RCP subcommand and the key code. Otherwise false. * */ bool si_mhl_tx_rcp_send(struct mhl_dev_context *dev_context, uint8_t rcpKeyCode) { bool status; MHL_TX_DBG_INFO("called\n"); /* * Make sure peer supports RCP */ if (dev_context->dev_cap_cache.mdc.featureFlag & MHL_FEATURE_RCP_SUPPORT) { status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCP, rcpKeyCode, rcp_done); if (status) si_mhl_tx_drive_states(dev_context); } else { MHL_TX_DBG_ERR("failed, peer feature flag = 0x%X\n", dev_context->dev_cap_cache.mdc.featureFlag); status = false; } return status; } struct cbus_req *ucp_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_ucp_send * * This function is (indirectly) called by a host application to send * a UCP key code to the downstream device. * * Returns true if the key code can be sent, false otherwise. */ bool si_mhl_tx_ucp_send(struct mhl_dev_context *dev_context, uint8_t ucp_key_code) { bool status; MHL_TX_DBG_INFO("called key code: 0x%02x\n", ucp_key_code); /* * Make sure peer supports UCP and that the connection is * in a state where a UCP message can be sent. */ if (dev_context->dev_cap_cache.mdc.featureFlag & MHL_FEATURE_UCP_RECV_SUPPORT) { status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_UCP, ucp_key_code, ucp_done); if (status) si_mhl_tx_drive_states(dev_context); } else { MHL_TX_DBG_ERR("failed\n"); status = false; } return status; } struct cbus_req *ucpk_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_ucp_send * * This function is (indirectly) called by a host application to send * a UCP acknowledge message for a received UCP key code message. * * Returns true if the message can be sent, false otherwise. */ bool si_mhl_tx_ucpk_send(struct mhl_dev_context *dev_context, uint8_t ucp_key_code) { bool status; MHL_TX_DBG_INFO("called key code: 0x%02x\n", ucp_key_code); status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_UCPK, ucp_key_code, ucpk_done); return status; } struct cbus_req *ucpe_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") /* * UCPE is always followed by an UCPK with * original key code received. */ si_mhl_tx_ucpk_send(dev_context, dev_context->msc_save_ucp_key_code); return req; } /* * si_mhl_tx_ucpe_send * * This function is (indirectly) called by a host application to send a * UCP negative acknowledgment message for a received UCP key code message. * * Returns true if the message can be sent, false otherwise. * * When successful, mhl_tx internally sends UCPK with original (last known) * UCP keycode. */ bool si_mhl_tx_ucpe_send(struct mhl_dev_context *dev_context, uint8_t ucpe_error_code) { MHL_TX_DBG_INFO("called\n"); return si_mhl_tx_send_msc_msg (dev_context, MHL_MSC_MSG_UCPE, ucpe_error_code, ucpe_done); } #if (INCLUDE_RBP == 1) struct cbus_req *rbp_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_rbp_send * * This function checks if the peer device supports RBP and sends rbpButtonCode. * The function will return a value of true if it could successfully send the * RBP subcommand and the button code. Otherwise false. * */ bool si_mhl_tx_rbp_send(struct mhl_dev_context *dev_context, uint8_t rbpButtonCode) { bool status; MHL_TX_DBG_INFO("called\n"); /* * Make sure peer supports RBP */ if (dev_context->dev_cap_cache.mdc.featureFlag & MHL_FEATURE_RBP_SUPPORT) { status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBP, rbpButtonCode, rbp_done); if (status) si_mhl_tx_drive_states(dev_context); } else { MHL_TX_DBG_ERR("failed, peer feature flag = 0x%X\n", dev_context->dev_cap_cache.mdc.featureFlag); status = false; } return status; } struct cbus_req *rbpk_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") return req; } /* * si_mhl_tx_rbpk_send * * This function is (indirectly) called by a host application to send * a RBP acknowledge message for a received RBP button code message. * * Returns true if the message can be sent, false otherwise. */ bool si_mhl_tx_rbpk_send(struct mhl_dev_context *dev_context, uint8_t rbp_button_code) { bool status; status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBPK, rbp_button_code, rbpk_done); if (status) si_mhl_tx_drive_states(dev_context); return status; } struct cbus_req *rbpe_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { /* * RBPE is always followed by an RBPK with * original button code received. */ MHL_TX_DBG_ERR("\n") si_mhl_tx_rbpk_send(dev_context, dev_context->msc_save_rbp_button_code); return req; } /* * si_mhl_tx_rbpe_send * * The function will return a value of true if it could successfully send the * RBPE subcommand. Otherwise false. * * When successful, mhl_tx internally sends RBPK with original (last known) * button code. */ bool si_mhl_tx_rbpe_send(struct mhl_dev_context *dev_context, uint8_t rbpe_error_code) { bool status; status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBPE, rbpe_error_code, rbpe_done); if (status) si_mhl_tx_drive_states(dev_context); return status; } #endif char *rap_strings_high[4] = { "POLL", "CONTENT_", "CBUS_MODE_", "out of range" }; char *rap_strings_low[4][2] = { {"", ""}, {"ON", "OFF"}, {"DOWN", "UP"}, {"OOR", "OOR"}, }; struct cbus_req *rap_done(struct mhl_dev_context *dev_context, struct cbus_req *req, uint8_t data1) { MHL_TX_DBG_ERR("\n") /* the only RAP commands that we send are * CBUS_MODE_UP AND CBUS_MODE_DOWN. */ /* if (MHL_RAP_CBUS_MODE_DOWN == dev_context->msc_msg_last_data) { } else if (MHL_RAP_CBUS_MODE_UP == dev_context->msc_msg_last_data) { } */ return req; } /* * si_mhl_tx_rap_send * * This function sends the requested RAP action code message if RAP * is supported by the downstream device. * * The function returns true if the message can be sent, false otherwise. */ bool si_mhl_tx_rap_send(struct mhl_dev_context *dev_context, uint8_t rap_action_code) { bool status; MHL_TX_DBG_ERR("%sSend RAP{%s%s}%s\n", ANSI_ESC_GREEN_TEXT, rap_strings_high[(rap_action_code>>4)&3], rap_strings_low[(rap_action_code>>4)&3][rap_action_code & 1], ANSI_ESC_RESET_TEXT); /* * Make sure peer supports RAP and that the connection is * in a state where a RAP message can be sent. */ if (si_get_peer_mhl_version(dev_context) >= 0x30) { /* * MHL3.0 Requires RAP support, * no need to check if sink is MHL 3.0 */ status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAP, rap_action_code, rap_done); } else if (dev_context->dev_cap_cache.mdc. featureFlag & MHL_FEATURE_RAP_SUPPORT) { status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAP, rap_action_code, rap_done); } else { MHL_TX_DBG_ERR("%sERROR in sending RAP{CBUS_MODE_%s}%s\n", ANSI_ESC_RED_TEXT, (rap_action_code == 0x21) ? "UP" : "DOWN", ANSI_ESC_RESET_TEXT); status = false; } return status; } /* * si_mhl_tx_notify_downstream_hpd_change * * Handle the arrival of SET_HPD or CLEAR_HPD messages. * * Turn the content off or on based on what we got. */ void si_mhl_tx_notify_downstream_hpd_change(struct mhl_dev_context *dev_context, uint8_t downstream_hpd) { MHL_TX_DBG_INFO("HPD = %s\n", downstream_hpd ? "HIGH" : "LOW"); if (0 == downstream_hpd) { struct cbus_req *req = dev_context->current_cbus_req; dev_context->misc_flags.flags.mhl_hpd = false; dev_context->edid_valid = false; if (req) { if (MHL_READ_EDID_BLOCK == req->command) { return_cbus_queue_entry(dev_context, req); dev_context->current_cbus_req = NULL; dev_context->misc_flags.flags.edid_loop_active = 0; MHL_TX_DBG_INFO("tag: EDID active: %d\n", dev_context->misc_flags.flags. edid_loop_active); } } si_edid_reset(dev_context->edid_parser_context); } else { dev_context->misc_flags.flags.mhl_hpd = true; /* * possible EDID read is complete here * see MHL spec section 5.9.1 */ if (dev_context->misc_flags.flags.have_complete_devcap) { /* Devcap refresh is complete */ MHL_TX_DBG_INFO("tag:\n"); si_mhl_tx_initiate_edid_sequence( dev_context->edid_parser_context); } } } /* * si_mhl_tx_get_peer_dev_cap_entry * * index -- the devcap index to get * *data pointer to location to write data * * returns * 0 -- success * 1 -- busy. */ uint8_t si_mhl_tx_get_peer_dev_cap_entry(struct mhl_dev_context *dev_context, uint8_t index, uint8_t *data) { if (!dev_context->misc_flags.flags.have_complete_devcap) { /* update is in progress */ return 1; } else { *data = dev_context->dev_cap_cache.devcap_cache[index]; return 0; } } /* * si_get_scratch_pad_vector * * offset * The beginning offset into the scratch pad from which to fetch entries. * length * The number of entries to fetch * *data * A pointer to an array of bytes where the data should be placed. * * returns: * scratch_pad_status see si_mhl_tx_api.h for details */ enum scratch_pad_status si_get_scratch_pad_vector(struct mhl_dev_context *dev_context, uint8_t offset, uint8_t length, uint8_t *data) { if (!(dev_context->dev_cap_cache.mdc.featureFlag & MHL_FEATURE_SP_SUPPORT)) { MHL_TX_DBG_INFO("failed SCRATCHPAD_NOT_SUPPORTED\n"); return SCRATCHPAD_NOT_SUPPORTED; } else if (dev_context->misc_flags.flags.rcv_scratchpad_busy) { return SCRATCHPAD_BUSY; } else if ((offset >= sizeof(dev_context->incoming_scratch_pad)) || (length > (sizeof(dev_context->incoming_scratch_pad) - offset))) { return SCRATCHPAD_BAD_PARAM; } else { uint8_t *scratch_pad = dev_context->incoming_scratch_pad.asBytes; scratch_pad += offset; memcpy(data, scratch_pad, length); } return SCRATCHPAD_SUCCESS; } #ifdef ENABLE_DUMP_INFOFRAME #define AT_ROW_END(i, length) ((i & (length-1)) == (length-1)) void DumpIncomingInfoFrameImpl(char *pszId, char *pszFile, int iLine, info_frame_t *pInfoFrame, uint8_t length) { uint8_t j; uint8_t *pData = (uint8_t *) pInfoFrame; printk(KERN_DEFAULT "mhl_tx: %s: length:0x%02x -- ", pszId, length); for (j = 0; j < length; j++) { printk(KERN_DEFAULT "%02X ", pData[j]); if (AT_ROW_END(j, 32)) printk("\n"); } printk(KERN_DEFAULT "\n"); } #endif void *si_mhl_tx_get_drv_context(void *context) { struct mhl_dev_context *dev_context = context; if (dev_context->signature == MHL_DEV_CONTEXT_SIGNATURE) return &dev_context->drv_context; else return context; } int si_peer_supports_packed_pixel(void *dev_context) { struct mhl_dev_context *dev_context_ptr = (struct mhl_dev_context *)dev_context; return PACKED_PIXEL_AVAILABLE(dev_context_ptr); } int si_mhl_tx_shutdown(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("SiI8620 Driver Shutdown.\n"); mhl_tx_stop_all_timers(dev_context); si_mhl_tx_drv_shutdown((struct drv_hw_context *)&dev_context-> drv_context); return 0; } int si_mhl_tx_ecbus_started(struct mhl_dev_context *dev_context) { MHL_TX_DBG_INFO("eCBUS started\n"); /* queue up both of these */ si_mhl_tx_read_xdevcap(dev_context); return 0; }