/*! * Non-Optional Init Module Components * * @param found_device * @param pdev * * @return 0 on success * @return -IGD_ERROR_NODEV on failure */ static int detect_device(iegd_pci_t **found_device, os_pci_dev_t *pdev) { int i; /* Scan the PCI bus for supported device */ for(i = 0; i < MAX_PCI_DEVICE_SUPPORTED; i++) { *pdev = OS_PCI_FIND_DEVICE(intel_pci_device_table[i].vendor_id, intel_pci_device_table[i].device_id, intel_pci_device_table[i].bus, intel_pci_device_table[i].dev, intel_pci_device_table[i].func, (os_pci_dev_t)0); if(*pdev) { *found_device = &intel_pci_device_table[i]; break; } } if(!*pdev) { EMGD_ERROR("No supported VGA devices found."); return -IGD_ERROR_NODEV; } EMGD_DEBUG("VGA device found: 0x%x", (*found_device)->device_id); return 0; }
/*! * Status is currently not used * * @param display * @param port_number * @param status * * @return 0 on success * @return 1 on failure */ static int post_program_port_plb(igd_display_context_t *display, unsigned short port_number, unsigned long status) { int ret; igd_display_port_t *port; igd_timing_info_t *timings; unsigned long portreg; EMGD_TRACE_ENTER; port = PORT(display, port_number); timings = PIPE(display)->timing; /* * Writing the sDVO control register here works around a problem * where the sDVO port is not turning on when using a CH7308 * card in a 915 GM based system and the port order is 5200. * * In addition, post_set_mode() below, will report that the * "inputs are not trained", however, this does not seem to * have any negative effects. */ portreg = READ_MMIO_REG(display, port->port_reg); WRITE_MMIO_REG(display, port->port_reg, (portreg & ~BIT31)); WRITE_MMIO_REG(display, port->port_reg, portreg); /* Reenable/Redisable other port */ if (port->port_reg == 0x61140) { WRITE_MMIO_REG(display, 0x61160, READ_MMIO_REG(display, 0x61160)); } else { WRITE_MMIO_REG(display, 0x61140, READ_MMIO_REG(display, 0x61140)); } /* * Added for Lakeport A0 * Port clock multiplier bits 4-7, needs to be rewritten */ WRITE_MMIO_REG(display, PIPE(display)->clock_reg->dpll_control, READ_MMIO_REG(display, PIPE(display)->clock_reg->dpll_control)); /* We must wait for 150 us for the dpll clock to warm up */ OS_SLEEP(150); ret = 0; /* call post_set_mode() if exists */ if (port->pd_driver->post_set_mode) { ret = port->pd_driver->post_set_mode(port->pd_context, timings, 1<<PIPE(display)->pipe_num); if (ret) { EMGD_ERROR("PD post_set_mode returned: 0x%x", ret); } } EMGD_TRACE_EXIT; return ret; }
/*! * This function is used to initialize any module/dsp * module specific structures or tables etc. * * @param context SS level igd_context. * * @return 0 on success. * @return -IGD_INVAL or -IGD_ERROR_NODEV on failure */ int mode_init(igd_context_t *context) { igd_dispatch_t *dispatch = &context->dispatch; inter_module_dispatch_t *md; EMGD_TRACE_ENTER; EMGD_DEBUG("Allocating a mode context..."); /* Clear the allocated memory for mode context */ OS_MEMSET((void *)mode_context, 0, sizeof(mode_context_t)); /* Set the pointer to igd level context */ mode_context->context = context; mode_context->first_alter = TRUE; mode_context->display_color = context->mod_dispatch.init_params->display_color; mode_context->ref_freq = context->mod_dispatch.init_params->ref_freq; mode_context->tuning_wa = context->mod_dispatch.init_params->tuning_wa; /* Get mode's dispatch table */ mode_context->dispatch = (mode_dispatch_t *) dispatch_acquire(context, mode_dispatch); if(!mode_context->dispatch) { EMGD_ERROR_EXIT("Unsupported Device"); return -IGD_ERROR_NODEV; } md = &context->mod_dispatch; /* Set the fw_info to 0 */ mode_context->fw_info = NULL; /* Hook up the IGD dispatch table entires for mode */ dispatch->get_EDID_block = igd_get_EDID_block; dispatch->power_display = igd_power_display; dispatch->query_mode_list = igd_query_mode_list; dispatch->alter_displays = igd_alter_displays; OPT_MICRO_CALL(full_mode_init(context, mode_context)); /* Hook up inter-module dispatch functions */ md->mode_get_gpio_sets = mode_context->dispatch->get_gpio_sets; md->mode_reset_plane_pipe_ports = mode_context->dispatch->reset_plane_pipe_ports; md->filter_modes = mode_context->dispatch->filter_modes; /* Hook up Core specific IGD dispatch table entries */ dispatch->set_palette_entries = mode_context->dispatch->full->set_palette_entries; dispatch->set_palette_entry = mode_context->dispatch->set_palette_entry; dispatch->get_palette_entry = mode_context->dispatch->get_palette_entry; dispatch->wait_vblank = mode_context->dispatch->wait_vblank; /* Initialize dsp module */ if (dsp_init(context)) { EMGD_ERROR("dsp_init() failed."); return -IGD_INVAL; } /* Initialze port interface (pi) module */ if (pi_init(context)) { EMGD_ERROR_EXIT("pi_init() failed."); if(md->dsp_shutdown) { md->dsp_shutdown(context); } return -IGD_ERROR_INVAL; } if (mode_context->dispatch->full && md->reg_get_mod_state) { /* Save mode state */ module_state_h *state = NULL; unsigned long *flags = NULL; md->reg_get_mod_state(REG_MODE_STATE, &state, &flags); md->mode_save(context, state, flags); } /* Initialize the Display Configuration List */ /* FIXME: This should be done in dsp init */ dsp_dc_init(context); EMGD_TRACE_EXIT; return 0; }
/*! * 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; }
/*! * This function sets up planes, pipes, and ports * with the configuration passed in and returnes either one * or two display handle lists. * * @param driver_handle from igd_init_driver(). * @param primary on return, this points to a list of displays. * @param primary_ptinfo incoming timing info for the primary. * @param primary_fbinfo incoming framebuffer info. * @param secondary on return, this points to a list of displays. * @param secondary_fbinfo incoming framebuffer info. * @param dc display configuration * @param flags modify function behavior * * @return 0 on success * @return -IGD_INVAL on failure */ int igd_alter_displays( igd_driver_h driver_handle, igd_display_h *_primary, igd_display_info_t *primary_pt_info, igd_framebuffer_info_t *primary_fb_info, igd_display_h *_secondary, igd_display_info_t *secondary_pt_info, igd_framebuffer_info_t *secondary_fb_info, unsigned long dc, unsigned long flags) { igd_context_t *context = (igd_context_t *)driver_handle; igd_display_context_t **primary = (igd_display_context_t **)_primary; igd_display_context_t **secondary = (igd_display_context_t **)_secondary; igd_framebuffer_info_t *fb_info = NULL; igd_display_context_t *display = NULL,*tv_display=NULL; int p; int ret; unsigned short tv_port_num=0; int p_chng = 1, s_chng = 1; unsigned char disable_plane_pipe = 0; unsigned long current_dc; #if 0 /* Ian Elliott is taking this out ... see comment below */ #ifndef CONFIG_MICRO igd_framebuffer_info_t *plane_fb_info = NULL; #endif #endif /* 0 -- Ian is taking this out */ EMGD_TRACE_ENTER; /* * Make sure the DC is valid * * vBIOS won't be able to do this every time, for now only have * the drivers's do the check. */ #ifndef CONFIG_MICRO if (dc && !dsp_valid_dc(dc, 0)) { EMGD_ERROR_EXIT("Invalid display configuration: 0x%08lx", dc); return -IGD_ERROR_INVAL; } #endif /* * Can all display_info's and fb_info's be NULL? I.E. make this * function do an alloc of display handles only? If so, then * check for that condition and return without error. Otherwise * return an error. */ if (dc && (!primary_pt_info && !primary_fb_info) && (!secondary_pt_info && !secondary_fb_info)) { EMGD_ERROR_EXIT("Invalid timing and framebuffer info"); return -IGD_ERROR_INVAL; } #ifndef CONFIG_MICRO /* FIXME: GDK Change this to dispatch->idle() */ if (dsp_wait_rb(mode_context->context) != 0) { return -IGD_ERROR_INVAL; } #endif /* If seamless request is NOT set , then do reset_plane_pipe_ports * else delay it until we cannot support it. * If seamless is requested by the user and we CAN support it * then we need to make sure reset_plane_pipe_ports is NOT * called. That's the whole point anyway. Not to reset anything * during seamless transition */ if(mode_context->seamless != TRUE) { /* 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; } } current_dc = *(context->mod_dispatch.dsp_current_dc); #ifndef CONFIG_MICRO /* Check if platform needs force alter * to make sure we run tuning code. This * is for TNC-B0 workaround.*/ if (mode_context->dispatch->dsp_is_force_alter_required){ if (mode_context->dispatch-> dsp_is_force_alter_required(context-> mod_dispatch.dsp_display_list[IGD_DC_PRIMARY(current_dc)], current_dc, dc)){ flags |= IGD_FORCE_ALTER; } } #endif /* * Turn off the planes, pipes, and ports associated with the current * DC. However, limit the change to the primary if the secondary * display handle is NULL or limit the change to the secondary if the * the pimary display handle is NULL. */ for (p = 7; p > 0; p--) { if (p > 4) { display = NULL; if (DC_PORT_NUMBER(current_dc, p)) { display = context->mod_dispatch. dsp_display_list[IGD_DC_SECONDARY(current_dc)]; s_chng = TIMING_CHANGED(display, dc, current_dc, secondary_pt_info, secondary_fb_info, (unsigned long)0xfff00000, flags); } if (s_chng && display && secondary) { /* if the port is TV, then don't set the power to S3 as this causes * blank screen and system hang on LVDS on FSDOS, probably because the * external clock needs to be on till the pipes and * DPLLs are off */ if(PORT(display,DC_PORT_NUMBER(current_dc, p))->pd_type == PD_DISPLAY_TVOUT) { tv_display = display; tv_port_num = DC_PORT_NUMBER(current_dc, p); } else { ret = mode_context->dispatch->program_port(display, DC_PORT_NUMBER(current_dc, p), FALSE); } /* The secondary pipe master */ if (p == 5) { disable_plane_pipe = 1; } } } else { display = NULL; if (DC_PORT_NUMBER(current_dc, p)) { display = context->mod_dispatch. dsp_display_list[IGD_DC_PRIMARY(current_dc)]; p_chng = TIMING_CHANGED(display, dc, current_dc, primary_pt_info, primary_fb_info, (unsigned long)0x000ffff0, flags); } if (p_chng && display && primary) { /* if the port is TV, then don't set the power to S3 as this causes * blank screen and system hang on LVDS on FSDOS, probably because the * external clock needs to be on till the pipes and * DPLLs are off */ if(PORT(display,DC_PORT_NUMBER(current_dc, p))->pd_type == PD_DISPLAY_TVOUT) { tv_display = display; tv_port_num = DC_PORT_NUMBER(current_dc, p); } else { ret = mode_context->dispatch->program_port(display, DC_PORT_NUMBER(current_dc, p), FALSE); } /* The primary pipe master */ if (p == 1) { disable_plane_pipe = 1; } } } /* Disable plane and pipe after disabling the ports */ if (disable_plane_pipe) { if(mode_context->dispatch->full) { mode_context->dispatch->full->program_cursor(display, FALSE); } mode_context->dispatch->program_plane(display, FALSE); mode_context->dispatch->program_pipe(display, FALSE); /*pipes and dplls are off, now turn off tv port */ if(tv_display) { ret = mode_context->dispatch->program_port(tv_display, tv_port_num, FALSE); tv_display = NULL; } disable_plane_pipe = 0; } } #ifndef CONFIG_MICRO /* If DC is zero, then return here. A zero dc turns everything off */ /* This never happens for VBIOS since it only always calls * * alter_displays at the same point with the same valid DC */ if (!dc) { int i; mode_context->dispatch->reset_plane_pipe_ports(mode_context->context); /* Should de-allocate everything here */ dsp_alloc(driver_handle, dc, flags); /* * FIXME: This should be done inside dsp alloc, mode module does * not own this information. * When dc = 0, set all displays allocated to 0. */ for (i=0; i<IGD_MAX_PORTS+1; i++) { if (context->mod_dispatch.dsp_display_list[i]) { context->mod_dispatch.dsp_display_list[i]->allocated = 0; } context->mod_dispatch.dsp_display_list[i] = NULL; } return 0; } #endif /* * Check the DC (display configuration). If it is the same as the * current configuration, then don't change any allocations, only * modify the framebuffers and timings. */ if (dc != current_dc) { EMGD_DEBUG("Allocate display handles based on DC"); #ifndef CONFIG_MICRO if (swap_required(current_dc, dc, primary)) { swap_fb_cursor(); } #endif /* * This function should never be called after VBIOS initialization * * The dsp_alloc is discarded after VBIOS init and is over- * * written by font tables. Thus in VBIOS IAL, alter_displays * * is never get called with a different DC from the 1st time * */ dsp_alloc(driver_handle, dc, flags); } /* Attach the displays to the caller's pointers */ if (primary) { *primary = context->mod_dispatch.dsp_display_list[IGD_DC_PRIMARY(dc)]; #ifndef CONFIG_MICRO if (*primary && context->mod_dispatch.alloc_queues) { ret = context->mod_dispatch.alloc_queues(driver_handle, (*primary)->pipe, flags); if (ret) { EMGD_ERROR("unable to allocate command queues"); } } #endif } if (secondary) { EMGD_DEBUG("Attaching display 1 to secondary pointer"); *secondary = context->mod_dispatch. dsp_display_list[IGD_DC_SECONDARY(dc)]; #ifndef CONFIG_MICRO if (*secondary && context->mod_dispatch.alloc_queues) { ret = context->mod_dispatch.alloc_queues(driver_handle, (*secondary)->pipe, flags); if (ret) { EMGD_ERROR("unable to allocate command queues"); } } #endif } /* * Configure the primary display. This configures the timings and the * framebuffer. Once configured, it turns everythying on. */ if(primary && *primary && (primary_pt_info || primary_fb_info) && p_chng) { EMGD_DEBUG("Configure primary timings"); /* make framebuffer changes */ if (primary_fb_info) { /* set up new frame buffer info */ fb_info = primary_fb_info; } else { fb_info = PLANE(*primary)->fb_info; } ret = configure_display(driver_handle, (igd_display_context_t *)(*primary), primary_pt_info, fb_info, dc, 0, 4, flags); if (ret) { EMGD_DEBUG("Primary display disabled."); } } /* * Configure the secondary display. This configures the timings and the * framebuffer. Once configured, it turns everythying on. * * How close is this code to the code for the primary? Could this * be moved to a separate function? */ if (secondary != NULL) { #ifndef CONFIG_MICRO /* * In the case where we are in extended or clone and our pipe is not * turned on, we need to turn the pipes on. * We can run into this situation on pre-Cantiga Gen platforms on Linux * where LVDS was the primary display and was assigned PIPE B. Then we * are switching from LVDS to another display and that other display * wants to take PIPE A. In this case PIPE B will be turned on, the * display's new port will take PIPE A and turn on PIPE A. The second * display thinks it is still PIPE A and nothing has changed for it. * In this case where our pipe is not turned on, we need to let the * system know that something has changed. */ if ((IGD_DC_CLONE(dc) || IGD_DC_EXTENDED(dc)) && !(EMGD_READ32(MMIO(*secondary) + PIPE(*secondary)->pipe_reg) & 0x80000000)) { s_chng = 1; } #endif EMGD_DEBUG("Starting secondary pipe programming"); if ((*secondary != NULL) && (secondary_pt_info || secondary_fb_info) && s_chng){ /* * Configure the framebuffer. For clone, it is the same * as the primary. For DIH, it is a unique fb. */ EMGD_DEBUG("configure secondary framebuffer"); if (dc & IGD_DISPLAY_CONFIG_CLONE) { fb_info = PLANE(*primary)->fb_info; } else { if (secondary_fb_info) { fb_info = secondary_fb_info; } else { fb_info = PLANE(*secondary)->fb_info; } } ret = configure_display(driver_handle, (igd_display_context_t *)(*secondary), secondary_pt_info, fb_info, dc, 4, 7, flags); if (ret) { EMGD_DEBUG("Secondary display disabled."); EMGD_ERROR("Secondary display disabled."); } } } else { EMGD_DEBUG("Skipped secondary programming, NULL handle"); } /* * Workaround: wait for Vblank to avoid people accessing display * plane registers before the register is updated properly. */ if (primary && *primary) { EMGD_DEBUG("Wait for vblank on primary display (%p)", primary); EMGD_DEBUG("Wait for vblank on primary display (%p)", *primary); mode_context->dispatch->wait_vblank(*primary); } else if (secondary && *secondary) { EMGD_DEBUG("Wait for vblank on secondary display"); mode_context->dispatch->wait_vblank(*secondary); } EMGD_TRACE_EXIT; return 0; }