Example #1
0
/**
 * sti_hqvdp_disable
 * @hqvdp: hqvdp pointer
 *
 * Disables the HQVDP plane
 */
static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp)
{
	int i;

	DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&hqvdp->plane));

	/* Unregister VTG Vsync callback */
	if (sti_vtg_unregister_client(hqvdp->vtg, &hqvdp->vtg_nb))
		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");

	/* Set next cmd to NULL */
	writel(0, hqvdp->regs + HQVDP_MBX_NEXT_CMD);

	for (i = 0; i < POLL_MAX_ATTEMPT; i++) {
		if (readl(hqvdp->regs + HQVDP_MBX_INFO_XP70)
				& INFO_XP70_FW_READY)
			break;
		msleep(POLL_DELAY_MS);
	}

	/* VTG can stop now */
	clk_disable_unprepare(hqvdp->clk_pix_main);

	if (i == POLL_MAX_ATTEMPT)
		DRM_ERROR("XP70 could not revert to idle\n");

	hqvdp->plane.status = STI_PLANE_DISABLED;
}
Example #2
0
int sti_mixer_set_plane_depth(struct sti_mixer *mixer, struct sti_plane *plane)
{
	int plane_id, depth = plane->drm_plane.state->normalized_zpos;
	unsigned int i;
	u32 mask, val;

	switch (plane->desc) {
	case STI_GDP_0:
		plane_id = GAM_DEPTH_GDP0_ID;
		break;
	case STI_GDP_1:
		plane_id = GAM_DEPTH_GDP1_ID;
		break;
	case STI_GDP_2:
		plane_id = GAM_DEPTH_GDP2_ID;
		break;
	case STI_GDP_3:
		plane_id = GAM_DEPTH_GDP3_ID;
		break;
	case STI_HQVDP_0:
		plane_id = GAM_DEPTH_VID0_ID;
		break;
	case STI_CURSOR:
		/* no need to set depth for cursor */
		return 0;
	default:
		DRM_ERROR("Unknown plane %d\n", plane->desc);
		return 1;
	}

	/* Search if a previous depth was already assigned to the plane */
	val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB);
	for (i = 0; i < GAM_MIXER_NB_DEPTH_LEVEL; i++) {
		mask = GAM_DEPTH_MASK_ID << (3 * i);
		if ((val & mask) == plane_id << (3 * i))
			break;
	}

	mask |= GAM_DEPTH_MASK_ID << (3 * depth);
	plane_id = plane_id << (3 * depth);

	DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer),
			 sti_plane_to_str(plane), depth);
	dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
		plane_id, mask);

	val &= ~mask;
	val |= plane_id;
	sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val);

	dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n",
		sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
	return 0;
}
Example #3
0
/**
 * sti_vdp_vtg_cb
 * @nb: notifier block
 * @evt: event message
 * @data: private data
 *
 * Handle VTG Vsync event, display pending bottom field
 *
 * RETURNS:
 * 0 on success.
 */
int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
{
	struct sti_hqvdp *hqvdp = container_of(nb, struct sti_hqvdp, vtg_nb);
	int btm_cmd_offset, top_cmd_offest;
	struct sti_hqvdp_cmd *btm_cmd, *top_cmd;

	if ((evt != VTG_TOP_FIELD_EVENT) && (evt != VTG_BOTTOM_FIELD_EVENT)) {
		DRM_DEBUG_DRIVER("Unknown event\n");
		return 0;
	}

	if (hqvdp->plane.status == STI_PLANE_FLUSHING) {
		/* disable need to be synchronize on vsync event */
		DRM_DEBUG_DRIVER("Vsync event received => disable %s\n",
				 sti_plane_to_str(&hqvdp->plane));

		sti_hqvdp_disable(hqvdp);
	}

	if (hqvdp->btm_field_pending) {
		/* Create the btm field command from the current one */
		btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp);
		top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp);
		if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) {
			DRM_DEBUG_DRIVER("Warning: no cmd, will skip field\n");
			return -EBUSY;
		}

		btm_cmd = hqvdp->hqvdp_cmd + btm_cmd_offset;
		top_cmd = hqvdp->hqvdp_cmd + top_cmd_offest;

		memcpy(btm_cmd, top_cmd, sizeof(*btm_cmd));

		btm_cmd->top.config = TOP_CONFIG_INTER_BTM;
		btm_cmd->top.current_luma +=
				btm_cmd->top.luma_src_pitch / 2;
		btm_cmd->top.current_chroma +=
				btm_cmd->top.chroma_src_pitch / 2;

		/* Post the command to mailbox */
		writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset,
				hqvdp->regs + HQVDP_MBX_NEXT_CMD);

		hqvdp->btm_field_pending = false;

		dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n",
				__func__, hqvdp->hqvdp_cmd_paddr);

		sti_plane_update_fps(&hqvdp->plane, false, true);
	}

	return 0;
}
Example #4
0
static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane,
				     struct drm_plane_state *oldstate)
{
	struct sti_plane *plane = to_sti_plane(drm_plane);

	if (!drm_plane->crtc) {
		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
				 drm_plane->base.id);
		return;
	}

	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
			 drm_plane->crtc->base.id,
			 sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
			 drm_plane->base.id, sti_plane_to_str(plane));

	plane->status = STI_PLANE_DISABLING;
}
Example #5
0
int sti_mixer_set_plane_status(struct sti_mixer *mixer,
			       struct sti_plane *plane, bool status)
{
	u32 mask, val;

	DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable",
			 sti_mixer_to_str(mixer), sti_plane_to_str(plane));

	mask = sti_mixer_get_plane_mask(plane);
	if (!mask) {
		DRM_ERROR("Can't find layer mask\n");
		return -EINVAL;
	}

	val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
	val &= ~mask;
	val |= status ? mask : 0;
	sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);

	return 0;
}
Example #6
0
static int hqvdp_dbg_show(struct seq_file *s, void *data)
{
	struct drm_info_node *node = s->private;
	struct sti_hqvdp *hqvdp = (struct sti_hqvdp *)node->info_ent->data;
	int cmd, cmd_offset, infoxp70;
	void *virt;

	seq_printf(s, "%s: (vaddr = 0x%p)",
		   sti_plane_to_str(&hqvdp->plane), hqvdp->regs);

	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_XP70);
	DBGFS_DUMP(HQVDP_MBX_INFO_HOST);
	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_HOST);
	DBGFS_DUMP(HQVDP_MBX_INFO_XP70);
	infoxp70 = readl(hqvdp->regs + HQVDP_MBX_INFO_XP70);
	seq_puts(s, "\tFirmware state: ");
	if (infoxp70 & INFO_XP70_FW_READY)
		seq_puts(s, "idle and ready");
	else if (infoxp70 & INFO_XP70_FW_PROCESSING)
		seq_puts(s, "processing a picture");
	else if (infoxp70 & INFO_XP70_FW_INITQUEUES)
		seq_puts(s, "programming queues");
	else
		seq_puts(s, "NOT READY");

	DBGFS_DUMP(HQVDP_MBX_SW_RESET_CTRL);
	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL1);
	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1)
					& STARTUP_CTRL1_RST_DONE)
		seq_puts(s, "\tReset is done");
	else
		seq_puts(s, "\tReset is NOT done");
	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL2);
	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2)
					& STARTUP_CTRL2_FETCH_EN)
		seq_puts(s, "\tFetch is enabled");
	else
		seq_puts(s, "\tFetch is NOT enabled");
	DBGFS_DUMP(HQVDP_MBX_GP_STATUS);
	DBGFS_DUMP(HQVDP_MBX_NEXT_CMD);
	DBGFS_DUMP(HQVDP_MBX_CURRENT_CMD);
	DBGFS_DUMP(HQVDP_MBX_SOFT_VSYNC);
	if (!(readl(hqvdp->regs + HQVDP_MBX_SOFT_VSYNC) & 3))
		seq_puts(s, "\tHW Vsync");
	else
		seq_puts(s, "\tSW Vsync ?!?!");

	/* Last command */
	cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);
	cmd_offset = sti_hqvdp_get_curr_cmd(hqvdp);
	if (cmd_offset == -1) {
		seq_puts(s, "\n\n  Last command: unknown");
	} else {
		virt = hqvdp->hqvdp_cmd + cmd_offset;
		seq_printf(s, "\n\n  Last command: address @ 0x%x (0x%p)",
			   cmd, virt);
		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
	}

	/* Next command */
	cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);
	cmd_offset = sti_hqvdp_get_next_cmd(hqvdp);
	if (cmd_offset == -1) {
		seq_puts(s, "\n\n  Next command: unknown");
	} else {
		virt = hqvdp->hqvdp_cmd + cmd_offset;
		seq_printf(s, "\n\n  Next command address: @ 0x%x (0x%p)",
			   cmd, virt);
		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
	}

	seq_puts(s, "\n");

	return 0;
}
Example #7
0
static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
				  struct drm_plane_state *state)
{
	struct sti_plane *plane = to_sti_plane(drm_plane);
	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);
	struct drm_crtc *crtc = state->crtc;
	struct drm_framebuffer *fb = state->fb;
	bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
	struct drm_crtc_state *crtc_state;
	struct drm_display_mode *mode;
	int dst_x, dst_y, dst_w, dst_h;
	int src_x, src_y, src_w, src_h;

	/* no need for further checks if the plane is being disabled */
	if (!crtc || !fb)
		return 0;

	crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
	mode = &crtc_state->mode;
	dst_x = state->crtc_x;
	dst_y = state->crtc_y;
	dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
	dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
	/* src_x are in 16.16 format */
	src_x = state->src_x >> 16;
	src_y = state->src_y >> 16;
	src_w = state->src_w >> 16;
	src_h = state->src_h >> 16;

	if (!sti_hqvdp_check_hw_scaling(hqvdp, mode,
					src_w, src_h,
					dst_w, dst_h)) {
		DRM_ERROR("Scaling beyond HW capabilities\n");
		return -EINVAL;
	}

	if (!drm_fb_cma_get_gem_obj(fb, 0)) {
		DRM_ERROR("Can't get CMA GEM object for fb\n");
		return -EINVAL;
	}

	/*
	 * Input / output size
	 * Align to upper even value
	 */
	dst_w = ALIGN(dst_w, 2);
	dst_h = ALIGN(dst_h, 2);

	if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) ||
	    (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) ||
	    (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) ||
	    (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) {
		DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n",
			  src_w, src_h,
			  dst_w, dst_h);
		return -EINVAL;
	}

	if (first_prepare) {
		/* Start HQVDP XP70 coprocessor */
		sti_hqvdp_start_xp70(hqvdp);

		/* Prevent VTG shutdown */
		if (clk_prepare_enable(hqvdp->clk_pix_main)) {
			DRM_ERROR("Failed to prepare/enable pix main clk\n");
			return -EINVAL;
		}

		/* Register VTG Vsync callback to handle bottom fields */
		if (sti_vtg_register_client(hqvdp->vtg,
					    &hqvdp->vtg_nb,
					    crtc)) {
			DRM_ERROR("Cannot register VTG notifier\n");
			return -EINVAL;
		}
	}

	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
		      crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)),
		      drm_plane->base.id, sti_plane_to_str(plane));
	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
		      sti_plane_to_str(plane),
		      dst_w, dst_h, dst_x, dst_y,
		      src_w, src_h, src_x, src_y);

	return 0;
}