/** * Set the display to the surface specified by buffer. * * @param swap_chain (IN) Pointer to the swap chain associated with buffer. * @param buffer (IN) Pointer to the buffer to display. */ void emgddc_flip(emgddc_swapchain_t *swap_chain, emgddc_buffer_t *buffer) { drm_emgd_priv_t *priv = buffer->priv; igd_context_t *context = priv->context; igd_surface_t surf; int ret; EMGD_TRACE_ENTER; EMGD_DEBUG("Parameters: swap_chain=0x%p", swap_chain); EMGD_DEBUG(" buffer=0x%p, *buffer->offset=0x%08lx", buffer, buffer->offset); if (EMGD_FALSE == swap_chain->valid) { EMGD_DEBUG("Not flipping--swap chain invalidated by a mode change."); EMGD_TRACE_EXIT; return; } surf.offset = buffer->offset; surf.pitch = buffer->pitch; surf.width = buffer->width; surf.height = buffer->height; surf.pixel_format = buffer->pixel_format; surf.flags = IGD_SURFACE_RENDER | IGD_SURFACE_DISPLAY; /* Flip the primary surface. Select a different EMGD display depending on * the DC & devinfo: */ if (IGD_DC_EXTENDED(priv->dc) && (swap_chain->devinfo->which_devinfo == 1)) { ret = context->dispatch.set_surface(priv->secondary, IGD_PRIORITY_NORMAL, IGD_BUFFER_DISPLAY, &surf, NULL, 0); if (ret != 0) { printk(KERN_ERR "%s: set_surface() returned %d", __FUNCTION__, ret); } } else { ret = context->dispatch.set_surface(priv->primary, IGD_PRIORITY_NORMAL, IGD_BUFFER_DISPLAY, &surf, NULL, 0); if (ret != 0) { printk(KERN_ERR "%s: set_surface() returned %d", __FUNCTION__, ret); } } /* Flip the secondary surface: */ if (IGD_DC_CLONE(priv->dc)) { /* If in clone mode, flip the other pipe too: */ ret = context->dispatch.set_surface(priv->secondary, IGD_PRIORITY_NORMAL, IGD_BUFFER_DISPLAY, &surf, NULL, 0); if (ret != 0) { printk(KERN_ERR "%s: set_surface() returned %d", __FUNCTION__, ret); } } EMGD_TRACE_EXIT; } /* emgddc_flip() */
/*! * This function checks whether a frame buffer swap and cursor * swap is required based on given current_dc and new_dc. * * @param current_dc current_dc the system is in. * @param dc new requested dc. * @param dsp pointer to a display context pointer * * @return 0 if no swap is required * @return 1 if swap is required */ int swap_required( unsigned long current_dc, unsigned long dc, igd_display_context_t **dsp) { /* * Note: Preserve the order of conditions as is. Changing the order * of below conditions require relook into the whole function to * make sure sematics match to return right value. */ /* Do not swap if no dc or current_dc */ if (!dc || !current_dc) { return 0; } /* * Do NOT swap the frame buffer and cursor if dc is going * from twin to extended and not all the ports are moving to the other * pipe. If all the ports are moving to the other pipe, then DO swap * the frame buffer and cursor. * We also need to make sure we are not in the special case where our * extended mode contains LVDS and we are on a platform which does not * support LVDS on PIPE A. In this case we do not want to swap here because * we will not be swapping later since the twin configuration will stay on * PIPE A so that the LVDS can move to PIPE B. */ if (IGD_DC_TWIN(current_dc) && IGD_DC_EXTENDED(dc)) { if (dsp && *dsp && all_ports_moving(current_dc, dc) && !(dc_contains_port_type(dc, IGD_PORT_TYPE_LVDS) && !(PIPE(*dsp)->pipe_features & IGD_PORT_SHARE_LVDS))) { return 1; } else { return 0; } } /* * Do NOT swap if dc is changing from Single to clone or * vice versa. In this case there is only 1 fb and no swap is required * and it stays with primary display. */ if ((IGD_DC_SINGLE(current_dc) && IGD_DC_CLONE(dc)) || (IGD_DC_CLONE(current_dc) && IGD_DC_SINGLE(dc))) { return 0; } /* * If secondary master port is moving to primary master or * primary master is moving to secondary master, try * and keep the framebuffer address and cursor the same. */ if (DC_PORT_NUMBER(current_dc, 5) == DC_PORT_NUMBER(dc, 1) || (DC_PORT_NUMBER(current_dc, 1) == DC_PORT_NUMBER(dc, 5))) { /* Note: This was previosuly return 1 to indicate swap required, which will cause a frame buffer swap when changing display from twin to clone. If the display is in extended mode before changing from twin to clone, then both Plane A and Plane B will point to the secondary frame buffer offset adress in clone mode and cause blank screen (. */ return 0; } 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; }