//----------------------------------------------------------------------------- // get_copy_sg_list_from_smd_contig_memory // // Utility method for forming the scatter-gather list for multipart copy // buffers from SMD memory which is contiguous in DRAM. //----------------------------------------------------------------------------- static sec_result_t get_copy_sg_list_from_smd_contig_memory(sec_multipart_buff_list_t *buff_list, int read_write, int copy_parts, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_SUCCESS; int i = 0; //index for a part in multipart list int j = 0; unsigned long* sg_args = NULL; sec_addr_t phy_addr = 0; sec_addr_t vir_addr = 0; *sg_count = 0; *sg_list = NULL; if (!buff_list) goto exit; *sg_count = copy_parts; *sg_list = (sfaf_mem_ptr_t*) OS_ALLOC(sizeof(sfaf_mem_ptr_t) * (*sg_count)); VERIFY( *sg_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); memset(*sg_list, 0, sizeof(sfaf_mem_ptr_t) * (*sg_count)); for(i =0, j = 0; i < buff_list->nParts; i++) { if ( buff_list->part[i].copyonly == 1) //deals with COPY parts only { vir_addr = (sec_address_t)(read_write == USER_BUF_RO ? buff_list->part[i].src_buffer : buff_list->part[i].dst_buffer); phy_addr = find_pa_of_smd_memory(vir_addr); VERIFY( phy_addr != 0, exit, rc, SEC_FAIL); (*sg_list)[j].address = (void*)(phy_addr); (*sg_list)[j].length = buff_list->part[i].size_bytes; (*sg_list)[j].external = 1; (*sg_list)[j].swap = 1; (*sg_list)[j].pmr_type = (read_write == USER_BUF_RO) ? 0 : PMR_TYPE_CMP_VIDEO; (*sg_list)[j].rsvd = 0; //swap the endianness for SEC sg_args = (unsigned long *)&((*sg_list)[j].external); sg_args[0] = bswap(sg_args[0]); j++; } } exit: return rc; }
os_lock_t os_create_lock(void) { os_lock_pvt_t *p_lock = NULL; if(NULL == (p_lock = OS_ALLOC(sizeof(os_lock_pvt_t)))) { OS_ERROR("OS_ALLOC failed\n"); goto error; } if(NULL == (p_lock->p_sema = OS_ALLOC(sizeof(os_sema_t)))) { OS_ERROR("OS_ALLOC failed\n"); goto error; } os_sema_init(p_lock->p_sema, 1); return ((os_lock_t)p_lock); error: if(p_lock != NULL) { OS_FREE(p_lock); } return (NULL); }
pci_dev_t *pci_find_device( unsigned int vid, unsigned int did, pci_dev_t *pci_dev) { pci_dev_t * device; FILE* fDevices; char buf[BUF_SIZE]; unsigned int nDevAddr, nVenDevId, irqnum; if(NULL == (fDevices = fopen("/proc/bus/pci/devices", "r"))) { return NULL; } while(NULL != fgets(buf, BUF_SIZE, fDevices)) { if(3 != sscanf(buf, "%x %x %x", &nDevAddr, &nVenDevId, &irqnum)) { fclose(fDevices); return NULL; } //if looking for the next device, go past current dev if((pci_dev != NULL) && ((nDevAddr << 8) <= pci_dev->slot_address)) { continue; } OS_DEBUG("OSAL_PCI cur id: 0x%X, seeking id: 0x%X\n", nVenDevId, (did | (vid<<16))); if(nVenDevId == (did | (vid<<16))) { OS_DEBUG("Device Found!\n"); device = (pci_dev_t *) OS_ALLOC(sizeof(pci_dev_t)); if(NULL == device) { fclose(fDevices); return NULL; } device->slot_address = nDevAddr << 8; //for Windows compatibility device->irq = irqnum; OS_DEBUG("slot address: 0x%08X\n", nDevAddr << 8); fclose(fDevices); return(device); } } OS_DEBUG("Device NOT found!\n"); fclose(fDevices); return(NULL); }
//----------------------------------------------------------------------------- // get_copy_sg_list_from_non_contig_memory // // Utility method for forming the scatter-gather list for multipart copy // buffers which are non-contiguous in DRAM. //----------------------------------------------------------------------------- static sec_result_t get_copy_sg_list_from_non_contig_memory(sec_multipart_buff_list_t *buff_list, int read_write, int copy_parts, user_buf_t **user_buf_list, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_SUCCESS; user_buf_t * user_buff = NULL; int i = 0; //index for a part in multipart list int j = 0; //index for a user_buf_t in user_buf_list sec_address_t vir_addr; *sg_count = 0; *sg_list = NULL; if (!copy_parts) goto exit; *user_buf_list = (user_buf_t*)OS_ALLOC (sizeof(user_buf_t) * copy_parts); VERIFY( *user_buf_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); memset(*user_buf_list, 0, (sizeof(user_buf_t) * copy_parts)); for(i =0; i < buff_list->nParts; i++) { if ( buff_list->part[i].copyonly == 1) //deals with ONLY copy parts { user_buff = ((user_buf_t*)(*user_buf_list) + j); j++; vir_addr = (sec_address_t)(read_write == USER_BUF_RO ? buff_list->part[i].src_buffer : buff_list->part[i].dst_buffer); rc = form_sfaf_sg_list( user_buff, vir_addr, buff_list->part[i].size_bytes, read_write, sg_list, sg_count ); VERIFY_QUICK( rc == SEC_SUCCESS, exit); } } exit: return rc; }
int pd_print(const char *funcname, const int error, const int error_exit, const char *format, ...) { #ifdef DEBUG_BUILD_TYPE va_list ap; unsigned int *blah; char *priority = error ? KERN_ERR : EMGD_DEBUG_MSG_PRIORITY; char *fmt = NULL; /* Can't directly use the EMGD_DEBUG_S macro (because "format" is a string * variable), so duplicate some of it here: */ if (!(emgd_debug && emgd_debug-> MODULE_NAME)) { return 0; } va_start(ap, format); blah = (unsigned int *)ap; if (error_exit) { EMGD_DEBUG("EXIT With Error..."); } /* Create a new format string, with all of the correct parts: */ fmt = OS_ALLOC(strlen(priority) + strlen(funcname) + strlen(format) + 2); if (fmt == NULL) { printk(format, blah[0], blah[1], blah[2], blah[3], blah[4], blah[5], blah[6], blah[7], blah[8], blah[9]); } else { sprintf(fmt, "%s%s %s", priority, funcname, format); printk(fmt, blah[0], blah[1], blah[2], blah[3], blah[4], blah[5], blah[6], blah[7], blah[8], blah[9]); OS_FREE(fmt); } printk("\n"); va_end(ap); return 0; #endif }
osal_result os_pci_device_from_slot(os_pci_dev_t *pci_dev, unsigned int slot) { pci_dev_t *device; FILE* fDev; char szDevAddr[64]; *pci_dev = NULL; if(21 != snprintf(szDevAddr, sizeof(szDevAddr), "/proc/bus/pci/%2.2x/%2.2x.%1.1x", (unsigned int)PCI_BUS(slot), (unsigned int)PCI_DEV(slot), (unsigned int)PCI_FUNC(slot))) { return OSAL_ERROR; } if(NULL == (fDev = fopen(szDevAddr, "r"))) { return OSAL_NOT_FOUND; } device = (pci_dev_t*) OS_ALLOC(sizeof(pci_dev_t)); if(device == NULL) { fclose(fDev); return OSAL_INSUFFICIENT_MEMORY; } device->slot_address = slot; *pci_dev = ((os_pci_dev_t*) device); fclose(fDev); OS_DEBUG("OSAL_PCI Found Dev: %s\n", szDevAddr); return OSAL_SUCCESS; }
/* This is user space implementation of osal_firmware_request() It copies fw image file to a malloced buffer and update the size and buffer pointer in fw_ctxt. */ osal_result os_firmware_request(const char *fw_image_fs_path, os_firmware_t *fw_ctxt) { FILE *fp = NULL; osal_result os_ret = OSAL_ERROR; if ( NULL != fw_ctxt) { fp = fopen(fw_image_fs_path, "rb"); if (NULL != fp) { fseek(fp, 0L, SEEK_END); fw_ctxt->fw_size = ftell(fp); /* get image size */ fseek(fp, 0L, SEEK_SET ); /*Allocate buffer for fw image */ fw_ctxt->fw_address = OS_ALLOC(fw_ctxt->fw_size); if (NULL != fw_ctxt->fw_address) { size_t data_read; data_read = fread(fw_ctxt->fw_address, 1, fw_ctxt->fw_size, fp); if (data_read == fw_ctxt->fw_size) { os_ret = OSAL_SUCCESS; } } fclose(fp); } else { OS_PRINT("ERR: %s file not found \n", fw_image_fs_path); } } else { os_ret = OSAL_INVALID_PARAM; OS_PRINT("ERR: fw_ctxt is NULL\n"); } return os_ret; }
/*! * This function is directly exported. * * @param found_device * @param pdev * * @return igd_driver_h * @return NULL on failure */ igd_driver_h igd_driver_init( igd_init_info_t *init_info ) { igd_context_t *context; os_pci_dev_t pdev = (os_pci_dev_t)NULL; os_pci_dev_t vga_disable_dev; iegd_pci_t *found_device; int ret; int i; EMGD_TRACE_ENTER; /* Allocate a context */ context = (void *) OS_ALLOC(sizeof(igd_context_t)); fixme_vbios_context = context; if(!context) { EMGD_ERROR_EXIT("igd_driver_init failed to create context"); return NULL; } OS_MEMSET(context, 0, sizeof(igd_context_t)); /* Search VGA devices for a supported one */ ret = detect_device(&found_device, &pdev); if(ret) { OS_FREE(context); return NULL; } /* * Some platforms (currently only Atom E6xx) use two PCI devices (the * second device being for SDVO) and this causes the VGA arbiter to get * involved. Legacy VGA decoding must be disabled for all PCI devices * except one, otherwise the VGA arbiter will prevent DRI usage in the * X server. */ for (i = 0; i < MAX_LEGACY_VGA_DISABLE; i++) { vga_disable_dev = os_pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDVO_TNC, 0xFFFF, 0, 0, NULL); if (vga_disable_dev) { printk(KERN_INFO "VGA arbiter detected; disabling legacy VGA" " decoding on SDVO device\n"); os_pci_disable_legacy_vga_decoding(vga_disable_dev); os_pci_free_device(vga_disable_dev); } } context->device_context.did = found_device->device_id; init_dispatch = (init_dispatch_t *)dispatch_acquire(context, init_dispatch_table); if(!init_dispatch) { EMGD_ERROR_EXIT("No dispatch found for listed device"); return NULL; } ret = init_dispatch->query(context, init_dispatch, pdev, &init_info->bus, &init_info->slot, &init_info->func); if(ret) { OS_FREE(context); EMGD_ERROR_EXIT("Device Dependent Query Failed"); return NULL; } /* init info */ init_info->vendor_id = found_device->vendor_id; init_info->device_id = found_device->device_id; init_info->name = init_dispatch->name; init_info->chipset = init_dispatch->chipset; init_info->default_pd_list = init_dispatch->default_pd_list; EMGD_TRACE_EXIT; return (igd_driver_h)context; }
/*! * Function to allocate memory * * @param handle * @param pd_driver * * @return void */ void *igd_pd_malloc(unsigned long size) { return OS_ALLOC(size); } /* end igd_pd_malloc */
//----------------------------------------------------------------------------- // form_sfaf_sg_list // // This method forms the scatter-gather list understood by SEC firmware. // This is the sequence of steps: // 1. Lock physical pages corresponding to virtual memory by using // get_user_pages Linux Kernel API. // 2. Alloc memory for a scatter-gather element // 3. Set physical start address, length and various parameters in the // scatter-gather element. // 4. get_user_pages returns a reference to every page found in // vir_buffer - vir+buffer+vir_buffer_size range. Whereas, SEC needs // a SG list with elements which are contiguous, irrespective of page // boundaries. Therefore, we need additional manipulation for this. // 5. Loop 2(by expanding memory for SG list) & 3 until all the physical // pages are covered. //----------------------------------------------------------------------------- static sec_result_t form_sfaf_sg_list(user_buf_t * user_buffer, sec_address_t vir_buffer, unsigned long vir_buffer_size, int write, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_SUCCESS; int xfer_size = 0; int num_pages_processed = 0; //to keep track of pages returned by get_user_pages API bool need_another_sg_entry = true; //to keep track of elements in SG list provided to SEC sfaf_mem_ptr_t * new_sg_list = NULL; int new_sg_count = 0; unsigned long* sg_args = NULL; VERIFY( sg_list != NULL, exit, rc, SEC_NULL_POINTER); VERIFY( sg_count != NULL, exit, rc, SEC_NULL_POINTER); //Step 1 : get_user_pages API call rc = sec_kernel_user_buf_lock( user_buffer, (sec_address_t) vir_buffer, vir_buffer_size, write); VERIFY_QUICK(rc == SEC_SUCCESS, exit); //We traverse the 'user_buffer' list returned by get_user_pages kernel API, while (need_another_sg_entry) { //Step 2: realloc memory for a additional sfaf_const_mem_ptr data structure and //update it's variables new_sg_count = (*sg_count) + 1; new_sg_list = (sfaf_mem_ptr_t*) OS_ALLOC(sizeof(sfaf_mem_ptr_t) * (new_sg_count)); VERIFY( new_sg_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); if (*sg_list) { memcpy(new_sg_list, *sg_list, sizeof(sfaf_mem_ptr_t) * (new_sg_count -1)); OS_FREE(*sg_list); } //Update the method output pointers *sg_count = new_sg_count; *sg_list = new_sg_list; //Step 3: Set SG elements xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); new_sg_list[new_sg_count - 1].address = (void*)(user_buffer->page_addr + user_buffer->offset); new_sg_list[new_sg_count - 1].length = xfer_size; new_sg_list[new_sg_count - 1].external = 1; new_sg_list[new_sg_count - 1].swap = 1; new_sg_list[new_sg_count - 1].pmr_type = 0; new_sg_list[new_sg_count - 1].rsvd = 0; //swap the endianness for SEC sg_args = (unsigned long *)&(new_sg_list[new_sg_count - 1].external); sg_args[0] = bswap(sg_args[0]); need_another_sg_entry = false; num_pages_processed++; if (num_pages_processed == user_buffer->num_pages) { //we are done processing the buffer...get out now goto exit; } //Step 4: get_user_pages returns a reference to every page found in //vir_buffer - vir+buffer+vir_buffer_size range. Whereas, SEC needs //a SG list with elements which are contiguous, irrespective of page //boundaries. Therefore, we need additional manipulation for this. rc = user_buf_advance( user_buffer, xfer_size); VERIFY_QUICK(rc == SEC_SUCCESS , exit); xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); while ((num_pages_processed < user_buffer->num_pages) && (!need_another_sg_entry)) { //navigate through for every page in user_buf_t if ((void*)(user_buffer->page_addr + user_buffer->offset) != (new_sg_list[new_sg_count - 1].address + new_sg_list[new_sg_count - 1].length)) { //until you finish the list or the page's start address is not next to previous page's end address //generate another sfaf_const_mem_ptr data structure and follow the process need_another_sg_entry = true; } else { new_sg_list[new_sg_count - 1].length += xfer_size; num_pages_processed++; if (num_pages_processed == user_buffer->num_pages) { //we are done processing the buffer...get out now goto exit; } rc = user_buf_advance( user_buffer, xfer_size); VERIFY_QUICK(rc == SEC_SUCCESS , exit); xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); } } } exit: return rc; }
sec_result_t pr2_multipart_op(sec_kernel_ipc_t * ipc_arg, ipl_t * ipl, opl_t * opl, ipc_shmem_t * ish_pl ) { sec_ipc_return_t ipc_ret = IPC_RET_COMMAND_COMPLETE; sec_result_t rc = SEC_SUCCESS; sec_address_t buf_addr; uint32_t buf_size = 0; uint32_t sg_count = 0; user_buf_t *user_buf_list = NULL; sfaf_mem_ptr_t *sg_list = NULL; buf_addr = (sec_address_t) ipc_arg->src; buf_size = ipc_arg->src_size; if(buf_addr && buf_size) { /* Enter here only if we have to do scatter gather */ user_buf_list = (user_buf_t*)OS_ALLOC (sizeof(user_buf_t)); VERIFY( user_buf_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); memset(user_buf_list, 0, (sizeof(user_buf_t))); rc = form_sfaf_sg_list( user_buf_list, buf_addr, buf_size, USER_BUF_RO, &sg_list, &sg_count ); VERIFY_QUICK( rc == SEC_SUCCESS, exit); #if 0 { int i; sfaf_mem_ptr_t *temp = sg_list; for(i=0; i<sg_count; i++) { pv(temp[i].address, temp[i].length); } } #endif // Put the scatter gather list in the ipl if(ipc_arg->sub_cmd.sc_pr2 == IPC_SC_PR2_CALCULATE_OMAC) { ipl->pr2_sg_op.pr2_calc_omac.pr2_sg_data_start = (uint32_t) OS_VIRT_TO_PHYS(sg_list); ipl->pr2_sg_op.pr2_calc_omac.pr2_sg_data_count = sg_count; } else if(ipc_arg->sub_cmd.sc_pr2 == IPC_SC_PR2_HASH_VALUE) { ipl->pr2_sg_op.pr2_hash_value.pr2_sg_data_start = (uint32_t) OS_VIRT_TO_PHYS(sg_list); ipl->pr2_sg_op.pr2_hash_value.pr2_sg_data_count = sg_count; } } ipc_ret = sec_kernel_ipc(ipc_arg->cmd, ipc_arg->sub_cmd, ipc_arg->io_sizes, ipl, opl, ish_pl, NULL); rc = ipc2sec(ipc_ret); exit: free_user_buffer_list( user_buf_list, 1); OS_FREE(sg_list); return rc; }
pci_dev_t *pci_find_device_by_class( unsigned char subclass, unsigned char baseclass, unsigned char pi, pci_dev_t *pci_dev) { pci_dev_t * device; pci_dev_t temp_dev; FILE* fDevices; char buf[BUF_SIZE]; unsigned int nDevAddr; unsigned int tempData; unsigned char curSubclass, curBaseclass, curPi; if(NULL == (fDevices = fopen("/proc/bus/pci/devices", "r"))) { return NULL; } while(NULL != fgets(buf, BUF_SIZE, fDevices)) { if(1 != sscanf(buf, "%x", &nDevAddr)) { fclose(fDevices); return NULL; } //if looking for the next device, go past current dev if((pci_dev != NULL) && ((nDevAddr << 8) <= pci_dev->slot_address)) { continue; } temp_dev.slot_address = nDevAddr << 8; //for Windows compatibility if(OSAL_SUCCESS != os_pci_read_config_32(&temp_dev, 0x8, &tempData)) { fclose(fDevices); return NULL; } curPi = (tempData & 0xFF00) >> 8; curSubclass = (tempData & 0xFF0000) >> 16; curBaseclass = (tempData & 0xFF000000) >> 24; OS_DEBUG("OSAL_PCI (BaseClass, SubClass, PI) Current: (0x%X, 0x%X, 0x%X), seeking: (0x%X, 0x%X, 0x%X)\n", curBaseclass, curSubclass, curPi, baseclass, subclass, pi); if( (curBaseclass == baseclass) && (curSubclass == subclass) && (curPi == pi)) { OS_DEBUG("Device Found!\n"); device = (pci_dev_t *) OS_ALLOC(sizeof(pci_dev_t)); if(NULL == device) { fclose(fDevices); return NULL; } device->slot_address = nDevAddr << 8; //for Windows compatibility OS_DEBUG("slot address: 0x%08X\n", nDevAddr << 8); fclose(fDevices); return(device); } } OS_DEBUG("Device NOT found!\n"); fclose(fDevices); return(NULL); }
/*! * Configure either the primary or secondary display. This means all the * timings, the framebuffer, the ports, the plane, and the pipe. * * The port range could be calculated based on primary or secondary * display but it seems easier at this point to pass the port range * in since it is used for all the for loops. * * @param driver_handle * @param display * @param pt_info * @param fb_info * @param dc * @param p0 * @param pn * @param flags * * @return 0 on success * @return -IGD_ERROR_INVAL on failure */ static int configure_display( igd_driver_h driver_handle, igd_display_context_t *display, igd_display_info_t *pt_info, igd_framebuffer_info_t *fb_info, unsigned long dc, int p0, int pn, unsigned long flags) { igd_context_t *context = (igd_context_t *)driver_handle; int p; igd_display_port_t *port; igd_timing_info_t *timing_info; unsigned long update_flags; unsigned short port_number = 0; int ret; int seamless = FALSE; EMGD_TRACE_ENTER; /* FIXME: Should this be an assert? */ if (display == NULL) { EMGD_DEBUG("Trying to configure a NULL display"); return 0; } EMGD_DEBUG("Configure timings"); for (p = pn; p > p0; p--) { EMGD_DEBUG("Configure port %d", DC_PORT_NUMBER(dc, p)); if ((port_number = DC_PORT_NUMBER(dc, p))) { port = context->mod_dispatch.dsp_port_list[port_number]; if (!port) { EMGD_DEBUG("Port %d not found", port_number); } else { /* Put a copy of the timings in the port's structure */ if (pt_info) { if (port->pt_info == NULL) { port->pt_info = OS_ALLOC(sizeof(igd_display_info_t)); if (!port->pt_info) { EMGD_ERROR_EXIT("unable to alloc a pt_info " "struct in port."); return -IGD_ERROR_INVAL; } } OS_MEMCPY(port->pt_info, pt_info, sizeof(igd_display_info_t)); } else { EMGD_ERROR("No primary timing info!"); } } } } if(!(pt_info->flags & IGD_DISPLAY_ENABLE)) { EMGD_ERROR_EXIT("Ptinfo has no IGD_DISPLAY_ENABLE!"); return 0; } display->port_number = DC_PORT_NUMBER(dc, (p0 + 1)); /* Set mode */ EMGD_DEBUG("Set mode, using port %ld", display->port_number); port = PORT(display, display->port_number); EMGD_DEBUG("Calling matchmode on display"); ret = match_mode(display, port->timing_table, fb_info, pt_info, &timing_info); if(ret) { EMGD_DEBUG("Match Mode for display failed"); EMGD_TRACE_EXIT; return -IGD_ERROR_INVAL; } /* Now b4, we program the timing_info, let's first see if seamless * option is requested, if it is then * We need to make sure the incoming dc, timing, framebuffer * info and etc match. We must respect the FORCE_ALTER flag. * * Seamless option buys you a one-time ticket for the seamless * experience from the firmware to the driver. After the first mode set * in driver, you don't get it the next time when you alter display. * */ #ifndef CONFIG_MICRO if(dc && !(flags & IGD_FORCE_ALTER) && (mode_context->seamless == TRUE) ) { /* User wants seamless */ if(mode_context->fw_info != NULL) { OPT_MICRO_CALL_RET(seamless, query_seamless(dc, /*(p0/4),*/ PIPE(display)->pipe_num, timing_info, fb_info, 0)); EMGD_DEBUG(":Seamless = %s", seamless ?"ON" : "OFF"); mode_context->seamless = FALSE; /* FIXME: For clone you get called twice. Need to * Fix that corner case */ } } #endif /* In case the seamless is FALSE, we do reset_plane_pipe_ports * which is supposed to be called in alter_displays anyway. * But we have to delay it since the user asked for seamless. * And we don't want to switch-off the display during * seamless. * Now we know that even though the user asked for it, we cannot * support seamless, so we call reset_plane_pipe_ports now. */ if(seamless == FALSE) { /* Reset planes/pipes/ports before doing first alter display */ if (mode_context->first_alter) { mode_context->dispatch->reset_plane_pipe_ports( mode_context->context); mode_context->first_alter = FALSE; } } if(calculate_eld(port, timing_info)){ EMGD_DEBUG("Fail to calculate ELD"); } /* turn on all ports */ EMGD_DEBUG("turn on displays plane_pipe_ports %d..%d", (p0 + 1), (pn-1)); for (p = (p0 + 1); p <= pn; p++) { if (DC_PORT_NUMBER(dc, p)) { port = context->mod_dispatch.dsp_port_list[DC_PORT_NUMBER(dc, p)]; display->allocated = 1; /* Update mode info for the port */ if (p == (p0 + 1)) { update_flags = MODE_UPDATE_PLANE | MODE_UPDATE_PIPE | MODE_UPDATE_PORT; } else { update_flags = MODE_UPDATE_PORT; } ret = mode_update_plane_pipe_ports(display, DC_PORT_NUMBER(dc, p), timing_info, fb_info, pt_info, update_flags); if (ret) { /* * This could happen if there was no memory for the * framebuffer or the FB was an invalid format. The * first is a too bad failure. The second should have * been checked by the IAL. */ EMGD_ERROR_EXIT("mode_update_plane_pipe_ports returned error " "%d", ret); port->pt_info->flags &= ~IGD_DISPLAY_ENABLE; return ret; } /* Program the port registers */ if(seamless == TRUE) { /* Don't have to program the registers, Since it's * all updated. Just return 0 */ ret = 0; } else { ret = mode_context->dispatch->program_port(display, DC_PORT_NUMBER(dc, p), TRUE); } if (ret == 0) { port->inuse = 1; } else { port->pt_info->flags &= ~IGD_DISPLAY_ENABLE; } } } EMGD_DEBUG("done - turn on displays plane_pipe_ports %d", p); /* Clear the Framebuffer after the planes, pipes and ports are * disabled and before they are enabled. */ if (flags & IGD_CLEAR_FB) { OPT_MICRO_VOID_CALL(full_clear_fb(mode_context, fb_info, NULL)); } /* program the pipe/plane/port if seamless is FALSE */ if(seamless == FALSE) { EMGD_DEBUG("Seamless is FALSE"); ret = TRUE; do{ /* turn on pipe */ mode_context->dispatch->program_pipe(display, TRUE); /* turn on plane */ mode_context->dispatch->program_plane(display, TRUE); /* turn on port */ for (p = pn; p > p0; p--) { if (DC_PORT_NUMBER(dc, p)) { mode_context->dispatch->post_program_port(display, DC_PORT_NUMBER(dc, p), TRUE); } } /* Check is display working fine */ OPT_MICRO_CALL_RET(ret, mode_context->dispatch-> check_display(display, DC_PORT_NUMBER(dc, p),TRUE)); if(!ret){ /* turn off plane and pipe */ mode_context->dispatch->program_plane(display, FALSE); mode_context->dispatch->program_pipe(display, FALSE); } }while(!ret); } #ifndef CONFIG_MICRO else { /* Seamless is TRUE */ /* Updating the plane registers, does not require us to * turn-off the pipe and we can still have seamless * because the display is not turned-off */ EMGD_DEBUG(" Seamless is TRUE"); if(mode_context->fw_info->program_plane == 1) { /* This means we have to update the plane registers * with the new values.eg. Change in pitch size between * firmware values and driver values. But we MUST also * update the palette registers for this to work and * palette registers are programmed when pipe is programmed. * This means we program the pipe , followed by the plane. */ /* By doing this, we update the palette */ mode_context->dispatch->program_pipe(display, TRUE); /* update the plane registers */ mode_context->dispatch->program_plane(display, TRUE); } } #endif EMGD_TRACE_EXIT; return 0; }
/*! * Calculates the Edid like data (ELD) if port supports audio transmission * * @param port * @param timing_info * * @return 0 */ static int calculate_eld( igd_display_port_t *port, igd_timing_info_t *timing_info) { /* Calculate for non-content protected & content protected(Array of 2) */ #ifdef CALCULATE_ELD_INFOFRAMES unsigned long cal_NPL[2]; /* Number of packer per line calculate*/ unsigned long poss_NPL[2]; /* Number of packer per line possible*/ unsigned long max_bitRate_2[2],max_bitRate_8[2]; unsigned long h_refresh, audio_freq; unsigned char input; int i,j,pix_rep; #endif cea_extension_t *temp_cea = NULL ; EMGD_TRACE_ENTER; /* Only calculate eld for HDMI port */ if((port->pd_driver->type != PD_DISPLAY_HDMI_EXT && port->pd_driver->type != PD_DISPLAY_HDMI_INT)){ return 0; } if(port->firmware_type == PI_FIRMWARE_EDID){ temp_cea = (cea_extension_t*)port->edid->cea; } /* Displayid unsupported for now. Uncomment this code when audio information is available for Display ID temp_cea = (&cea_extension_t)port->displayid->cea;*/ if(temp_cea == NULL){ /* CEA data unavailable, display does not have audio capability? */ /* We would allocate dummy edid structure and here and we should ony used canned ELD */ if(port->edid == NULL) { port->edid = (edid_t *) OS_ALLOC(sizeof(edid_t)); OS_MEMSET(port->edid, 0 , (sizeof(edid_t))); } port->edid->cea = (cea_extension_t *) OS_ALLOC(sizeof(cea_extension_t)); OS_MEMSET(port->edid->cea, 0 , (sizeof(cea_extension_t))); port->edid->cea->canned_eld = 1; temp_cea = (cea_extension_t*)port->edid->cea; port->callback->eld = &(port->edid->cea); } /* Default to canned ELD data */ temp_cea->LPCM_CAD[0] = 0x9; temp_cea->speaker_alloc_block[0] = 0x1; /* Default 0 Pixel replication */ port->edid->cea->pixel_rep = PIX_REPLICATION_0; /* Default */ port->edid->cea->colorimetry = HDMI_COLORIMETRY_NODATA; /* Default RGB 256 wuantization full range */ port->edid->cea->quantization = HDMI_QUANTIZATION_RGB_256; /* Default Unknown video code */ port->edid->cea->video_code = 0; port->edid->cea->aspect_ratio = (timing_info->mode_info_flags & PD_ASPECT_16_9) ? PD_ASPECT_RATIO_16_9 : PD_ASPECT_RATIO_4_3; #ifdef CALCULATE_ELD_INFOFRAMES calculate_infoframes(port,timing_info,temp_cea); /* If canned eld is not set and audio info from transmitter is available */ if(temp_cea->canned_eld != 1 && (temp_cea->audio_flag & PD_AUDIO_CHAR_AVAIL)){ pix_rep = port->edid->cea->pixel_rep; /*h_refresh = timing_info->dclk/timing_info->htotal;*/ h_refresh = timing_info->refresh; cal_NPL[0] = (pix_rep*(timing_info->hsync_end - timing_info->hsync_start) - port->edid->cea->K0) /32; cal_NPL[1] = (pix_rep*(timing_info->hsync_end - timing_info->hsync_start) - port->edid->cea->K1) /32; poss_NPL[0] = MODE_MIN(cal_NPL[0],port->edid->cea->NPL); poss_NPL[1] = MODE_MIN(cal_NPL[1],port->edid->cea->NPL); max_bitRate_2[0] = h_refresh * poss_NPL[0] - 1500; max_bitRate_2[1] = h_refresh * poss_NPL[1] - 1500; max_bitRate_8[0] = h_refresh * poss_NPL[0] * 4 - 1500; max_bitRate_8[1] = h_refresh * poss_NPL[1] * 4 - 1500; /* Loop trough Content Protection disabled then enabled */ for(i=0 ; i<2; i++){ for(j=0 ; j<3; j++){ input = 0; audio_freq = 48000 * (1<<j); /* 48Khz->96Khz->192Khz */ if(max_bitRate_8[i] >= audio_freq){ input = 7; }else if(max_bitRate_2[i] >= audio_freq){ input = 1; } /* take the minimum value min(transmitter, receiver) */ input = MODE_MIN(input,temp_cea->audio_cap[j].max_channels); temp_cea->LPCM_CAD[j] |= input<<((1-i)*3); if(temp_cea->audio_cap[j]._24bit){ temp_cea->LPCM_CAD[j] |= BIT(7); } if(temp_cea->audio_cap[j]._20bit){ temp_cea->LPCM_CAD[j] |= BIT(6); } } } /* TODO: Further construction of ELD from Monitor Name String begins here for now we only support VSDB */ /* By default we don send any vendor specific block unless latency value use for audio sync feature is available */ temp_cea->vsdbl = 0; /* This means the latecy field is available VSBD_LATENCY_FIELD = 8*/ if(temp_cea->vendor_block.vendor_block_size > VSBD_LATENCY_FIELD){ OS_MEMCPY(temp_cea->misc_data, temp_cea->vendor_data_block, temp_cea->vendor_block.vendor_block_size); temp_cea->vsdbl = temp_cea->vendor_block.vendor_block_size; /* If the VSBD has latency fields */ if(*(temp_cea->vendor_data_block + VSBD_LATENCY_FIELD - 1) & 0x80){ if(timing_info->mode_info_flags & IGD_SCAN_INTERLACE){ if(*(temp_cea->vendor_data_block + VSBD_LATENCY_FIELD - 1) & 0x40){ temp_cea->vendor_block.p_latency = 1; temp_cea->vendor_block.i_latency = 1; }else{ /* No latency available: Since it is an interlace mode but no vsbd_intlc_fld_present is available */ temp_cea->vendor_block.p_latency = 0; temp_cea->vendor_block.i_latency = 0; } }else{ temp_cea->vendor_block.p_latency = 1; temp_cea->vendor_block.i_latency = 0; } } } } #endif /* CALCULATE_ELD_INFOFRAMES */ temp_cea->audio_flag |= ELD_AVAIL; EMGD_TRACE_EXIT; return 0; }
/*! * Calculates infoframes information top be used by HDMI port drivers * * @param port * @param timing_info * @param temp_cea * * @return 0 */ static int calculate_infoframes( igd_display_port_t *port, igd_timing_info_t *timing_info, cea_extension_t *temp_cea) { pd_timing_t *cea_timings = NULL, *cea_timing_temp = NULL; EMGD_TRACE_ENTER; /* VBIOS has no access to CEA timing tables and this is not supported there as well */ if(timing_info->mode_info_flags & PD_MODE_CEA){ if(timing_info->width != 640 && timing_info->height != 480){ port->edid->cea->quantization = HDMI_QUANTIZATION_RGB_220; } /* Based on DPG algorithm. If monitors support more than 2 channels for 192Khz or/and 92Khz then set two pixel repeat one. KIV: Add pruning for pixel PIX_REPLICATION_3 if required */ if(temp_cea->audio_cap[CAP_192_KHZ].max_channels>2 || temp_cea->audio_cap[CAP_96_KHZ].max_channels>2){ port->edid->cea->pixel_rep = PIX_REPLICATION_1; } /* Based on HDMI spec 6.7.1 & 6.7.2 */ if ((timing_info->width == 720) && ((timing_info->height == 480) || (timing_info->height== 576))){ port->edid->cea->colorimetry = HDMI_COLORIMETRY_ITU601; } else if(((timing_info->width==1280) && (timing_info->height==720)) || ((timing_info->width == 1920) && (timing_info->height == 1080))){ port->edid->cea->colorimetry = HDMI_COLORIMETRY_ITU709; } cea_timings = (igd_timing_info_t *) OS_ALLOC(cea_timing_table_size); OS_MEMCPY(cea_timings, cea_timing_table, cea_timing_table_size); cea_timing_temp = cea_timings; while (cea_timings->width != IGD_TIMING_TABLE_END){ if(cea_timings->width == timing_info->width && cea_timings->height == timing_info->height && cea_timings->refresh == timing_info->refresh && cea_timings->dclk == timing_info->dclk && (cea_timings->mode_info_flags & (PD_ASPECT_16_9| IGD_SCAN_INTERLACE)) == (timing_info->mode_info_flags & (PD_ASPECT_16_9| IGD_SCAN_INTERLACE))){ port->edid->cea->video_code = cea_timings->mode_number; break; } cea_timings++; } OS_FREE(cea_timing_temp); } EMGD_TRACE_EXIT; return 0; }
/*! * Update internal data structures for the plane, pipe, and port as * requested. Allocate a new framebuffer if the new parameters do not * match the existing framebuffer. * * @param display * @param port_number * @param timing * @param pt_info User supplied timing info to check. * @param fb_info User supplied framebuffer info. * @param flags * * @return 0 on success * @return -IGD_ERROR_INVAL on failure */ static int mode_update_plane_pipe_ports( igd_display_context_t *display, unsigned short port_number, igd_timing_info_t *timing, igd_framebuffer_info_t *fb_info, igd_display_info_t *pt_info, unsigned long flags) { int ret; int alloc_fb; unsigned long size = 0; igd_framebuffer_info_t *plane_fb_info; igd_display_plane_t *mirror; EMGD_TRACE_ENTER; EMGD_DEBUG("Port Number (%d)", port_number); EMGD_ASSERT( (fb_info || pt_info), "ERROR: fb_info & pt_info are NULL", -IGD_ERROR_INVAL); EMGD_ASSERT( PLANE(display)->fb_info, "ERROR: fb_info in plane is NULL", -IGD_ERROR_INVAL); plane_fb_info = PLANE(display)->fb_info; mirror = PLANE(display)->mirror; /* * If there is a mirror plane (for Clone) and the mirror is populated * then update our plane from the mirror. If the mirror is not populated * then update the mirror from ours. */ if (mirror) { if(mirror->fb_info->flags) { OS_MEMCPY(plane_fb_info, mirror->fb_info, sizeof(igd_framebuffer_info_t)); } else { OS_MEMCPY(mirror->fb_info, plane_fb_info, sizeof(igd_framebuffer_info_t)); } } if (PORT(display, port_number)->pt_info == NULL) { if ((PORT(display, port_number)->pt_info = (igd_display_info_t *) OS_ALLOC(sizeof(igd_display_info_t))) == NULL) { EMGD_ERROR_EXIT("unable to alloc a pt_info struct in pipe."); return -IGD_ERROR_INVAL; } } /* * If the fb_info was provided, and either we were asked to update * the internal structures via the flags, or we are allocating a new * framebuffer. */ if(fb_info && (flags & MODE_UPDATE_PLANE)) { /* Assume we will be allocating a FB */ alloc_fb = 1; /* If the frambuffer parameters are unchanged then do not re-alloc */ if((fb_info->width == plane_fb_info->width) && (fb_info->height == plane_fb_info->height) && (fb_info->pixel_format == plane_fb_info->pixel_format) && (fb_info->flags == plane_fb_info->flags)) { alloc_fb = 0; } /* Do not re-alloc a framebuffer if the re-use flag is set. */ if(fb_info->flags & IGD_REUSE_FB) { alloc_fb = 0; /* May need to get the MIN_PITCH flags */ plane_fb_info->flags = (fb_info->flags & IGD_FB_FLAGS_MASK) | (plane_fb_info->flags & ~IGD_FB_FLAGS_MASK); } /* * If we don't have a framebuffer at all then we MUST allocate * one. */ if(!plane_fb_info->allocated && !fb_info->allocated) { alloc_fb = 1; } EMGD_DEBUG("plane_fb_info->fb_base_offset = 0x%08lx", plane_fb_info->fb_base_offset); if(alloc_fb) { if(plane_fb_info->allocated) { /* Free frame buffer memory */ display->context->dispatch.gmm_free( plane_fb_info->fb_base_offset); plane_fb_info->allocated = 0; } fb_info->fb_base_offset = plane_fb_info->fb_base_offset; /* * Keep the FB flags, add in Displayable flag and blank out * the rest. This insures that any tiled or usage flags from an * earlier call do not get reused. */ fb_info->flags = (fb_info->flags & IGD_FB_FLAGS_MASK) | IGD_SURFACE_DISPLAY; /* * Framebuffer allocations must always come from a reservation * if the IAL changes the address the new address must also be * from a reservation. */ GMM_SET_DEBUG_NAME("Framebuffer"); ret = display->context->dispatch.gmm_alloc_surface( &fb_info->fb_base_offset, fb_info->pixel_format, &fb_info->width, &fb_info->height, &fb_info->screen_pitch, &size, IGD_GMM_ALLOC_TYPE_RESERVATION, &fb_info->flags); if(ret) { EMGD_ERROR_EXIT("Allocation of Front buffer failed: %d", ret); return ret; } fb_info->allocated = 1; /* Set the visible offset to the newly-allocated offset: */ fb_info->visible_offset = fb_info->fb_base_offset; } else { /* If not reallocating, use back the offset in plane_fb_info */ fb_info->fb_base_offset = plane_fb_info->fb_base_offset; } OS_MEMCPY(plane_fb_info, fb_info, sizeof(igd_framebuffer_info_t)); plane_fb_info->allocated = 1; EMGD_DEBUG("plane_fb_info->fb_base_offset = 0x%08lx", plane_fb_info->fb_base_offset); } if(timing && (flags & MODE_UPDATE_PIPE)) { EMGD_DEBUG("Updating pipe timing."); PIPE(display)->timing = timing; PIPE(display)->owner = display; } if(pt_info && (flags & MODE_UPDATE_PORT)) { EMGD_DEBUG("OLD_PT========NEW PT "); IGD_PRINTK_PTINFO_2(PORT(display, port_number)->pt_info, pt_info); OS_MEMCPY(PORT(display, port_number)->pt_info, pt_info, sizeof(igd_display_info_t)); } EMGD_TRACE_EXIT; return 0; } /* end mode_update_plane_pipe_ports() */