/**
 * 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() */
Beispiel #2
0
/*!
 * 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;
}
Beispiel #3
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;
}