Пример #1
0
void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask,
		uint32_t old_irqmask)
{
	mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_CLEAR,
		irqmask ^ (irqmask & old_irqmask));
	mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_ENABLE, irqmask);
}
Пример #2
0
static void mdp4_write_csc_config(struct mdp4_kms *mdp4_kms,
		enum mdp4_pipe pipe, struct csc_cfg *csc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(csc->matrix); i++) {
		mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_MV(pipe, i),
				csc->matrix[i]);
	}

	for (i = 0; i < ARRAY_SIZE(csc->post_bias) ; i++) {
		mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_PRE_BV(pipe, i),
				csc->pre_bias[i]);

		mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_POST_BV(pipe, i),
				csc->post_bias[i]);
	}

	for (i = 0; i < ARRAY_SIZE(csc->post_clamp) ; i++) {
		mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_PRE_LV(pipe, i),
				csc->pre_clamp[i]);

		mdp4_write(mdp4_kms, REG_MDP4_PIPE_CSC_POST_LV(pipe, i),
				csc->post_clamp[i]);
	}
}
Пример #3
0
void mdp4_irq_preinstall(struct msm_kms *kms)
{
	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
	mdp4_enable(mdp4_kms);
	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff);
	mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
	mdp4_disable(mdp4_kms);
}
Пример #4
0
/* set dma config, ie. the format the encoder wants. */
void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);

	mdp4_write(mdp4_kms, REG_MDP4_DMA_CONFIG(mdp4_crtc->dma), config);
}
Пример #5
0
static void mdp4_dtv_encoder_disable(struct drm_encoder *encoder)
{
	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);

	if (WARN_ON(!mdp4_dtv_encoder->enabled))
		return;

	mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);

	/*
	 * Wait for a vsync so we know the ENABLE=0 latched before
	 * the (connector) source of the vsync's gets disabled,
	 * otherwise we end up in a funny state if we re-enable
	 * before the disable latches, which results that some of
	 * the settings changes for the new modeset (like new
	 * scanout buffer) don't latch properly..
	 */
	mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_EXTERNAL_VSYNC);

	clk_disable_unprepare(mdp4_dtv_encoder->src_clk);
	clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk);
	clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk);

	bs_set(mdp4_dtv_encoder, 0);

	mdp4_dtv_encoder->enabled = false;
}
Пример #6
0
/* called from IRQ to update cursor related registers (if needed).  The
 * cursor registers, other than x/y position, appear not to be double
 * buffered, and changing them other than from vblank seems to trigger
 * underflow.
 */
static void update_cursor(struct drm_crtc *crtc)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	enum mdp4_dma dma = mdp4_crtc->dma;
	unsigned long flags;

	spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags);
	if (mdp4_crtc->cursor.stale) {
		struct drm_gem_object *next_bo = mdp4_crtc->cursor.next_bo;
		struct drm_gem_object *prev_bo = mdp4_crtc->cursor.scanout_bo;
		uint32_t iova = mdp4_crtc->cursor.next_iova;

		if (next_bo) {
			/* take a obj ref + iova ref when we start scanning out: */
			drm_gem_object_reference(next_bo);
			msm_gem_get_iova_locked(next_bo, mdp4_kms->id, &iova);

			/* enable cursor: */
			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_SIZE(dma),
					MDP4_DMA_CURSOR_SIZE_WIDTH(mdp4_crtc->cursor.width) |
					MDP4_DMA_CURSOR_SIZE_HEIGHT(mdp4_crtc->cursor.height));
			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma), iova);
			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BLEND_CONFIG(dma),
					MDP4_DMA_CURSOR_BLEND_CONFIG_FORMAT(CURSOR_ARGB) |
					MDP4_DMA_CURSOR_BLEND_CONFIG_CURSOR_EN);
		} else {
			/* disable cursor: */
			mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_BASE(dma),
					mdp4_kms->blank_cursor_iova);
		}

		/* and drop the iova ref + obj rev when done scanning out: */
		if (prev_bo)
			drm_flip_work_queue(&mdp4_crtc->unref_cursor_work, prev_bo);

		mdp4_crtc->cursor.scanout_bo = next_bo;
		mdp4_crtc->cursor.stale = false;
	}

	mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma),
			MDP4_DMA_CURSOR_POS_X(mdp4_crtc->cursor.x) |
			MDP4_DMA_CURSOR_POS_Y(mdp4_crtc->cursor.y));

	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
}
Пример #7
0
static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
			to_mdp4_lcdc_encoder(encoder);
	unsigned long pc = mdp4_lcdc_encoder->pixclock;
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	struct drm_panel *panel;
	uint32_t config;
	int i, ret;

	if (WARN_ON(mdp4_lcdc_encoder->enabled))
		return;

	/* TODO: hard-coded for 18bpp: */
	config =
		MDP4_DMA_CONFIG_R_BPC(BPC6) |
		MDP4_DMA_CONFIG_G_BPC(BPC6) |
		MDP4_DMA_CONFIG_B_BPC(BPC6) |
		MDP4_DMA_CONFIG_PACK(0x21) |
		MDP4_DMA_CONFIG_DEFLKR_EN |
		MDP4_DMA_CONFIG_DITHER_EN;

	if (!of_property_read_bool(dev->dev->of_node, "qcom,lcdc-align-lsb"))
		config |= MDP4_DMA_CONFIG_PACK_ALIGN_MSB;

	mdp4_crtc_set_config(encoder->crtc, config);
	mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0);

	bs_set(mdp4_lcdc_encoder, 1);

	for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
		ret = regulator_enable(mdp4_lcdc_encoder->regs[i]);
		if (ret)
			DRM_DEV_ERROR(dev->dev, "failed to enable regulator: %d\n", ret);
	}

	DBG("setting lcdc_clk=%lu", pc);
	ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc);
	if (ret)
		DRM_DEV_ERROR(dev->dev, "failed to configure lcdc_clk: %d\n", ret);
	ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk);
	if (ret)
		DRM_DEV_ERROR(dev->dev, "failed to enable lcdc_clk: %d\n", ret);

	panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
	if (!IS_ERR(panel)) {
		drm_panel_prepare(panel);
		drm_panel_enable(panel);
	}

	setup_phy(encoder);

	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);

	mdp4_lcdc_encoder->enabled = true;
}
Пример #8
0
static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	enum mdp4_dma dma = mdp4_crtc->dma;

	mdp4_write(mdp4_kms, REG_MDP4_DMA_CURSOR_POS(dma),
			MDP4_DMA_CURSOR_POS_X(x) |
			MDP4_DMA_CURSOR_POS_Y(y));

	return 0;
}
Пример #9
0
void mdp4_plane_set_scanout(struct drm_plane *plane,
		struct drm_framebuffer *fb)
{
	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
	struct mdp4_kms *mdp4_kms = get_kms(plane);
	enum mpd4_pipe pipe = mdp4_plane->pipe;
	uint32_t iova;

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe),
			MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
			MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe),
			MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
			MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));

	msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);

	plane->fb = fb;
}
Пример #10
0
static void mdp4_plane_set_scanout(struct drm_plane *plane,
		struct drm_framebuffer *fb)
{
	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
	struct mdp4_kms *mdp4_kms = get_kms(plane);
	enum mdp4_pipe pipe = mdp4_plane->pipe;

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_A(pipe),
			MDP4_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
			MDP4_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_STRIDE_B(pipe),
			MDP4_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
			MDP4_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe),
			msm_framebuffer_iova(fb, mdp4_kms->id, 0));
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP1_BASE(pipe),
			msm_framebuffer_iova(fb, mdp4_kms->id, 1));
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP2_BASE(pipe),
			msm_framebuffer_iova(fb, mdp4_kms->id, 2));
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP3_BASE(pipe),
			msm_framebuffer_iova(fb, mdp4_kms->id, 3));

	plane->fb = fb;
}
static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
			to_mdp4_lcdc_encoder(encoder);
	unsigned long pc = mdp4_lcdc_encoder->pixclock;
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	struct drm_panel *panel = mdp4_lcdc_encoder->panel;
	int i, ret;

	if (WARN_ON(mdp4_lcdc_encoder->enabled))
		return;

	/* TODO: hard-coded for 18bpp: */
	mdp4_crtc_set_config(encoder->crtc,
			MDP4_DMA_CONFIG_R_BPC(BPC6) |
			MDP4_DMA_CONFIG_G_BPC(BPC6) |
			MDP4_DMA_CONFIG_B_BPC(BPC6) |
			MDP4_DMA_CONFIG_PACK_ALIGN_MSB |
			MDP4_DMA_CONFIG_PACK(0x21) |
			MDP4_DMA_CONFIG_DEFLKR_EN |
			MDP4_DMA_CONFIG_DITHER_EN);
	mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0);

	bs_set(mdp4_lcdc_encoder, 1);

	for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
		ret = regulator_enable(mdp4_lcdc_encoder->regs[i]);
		if (ret)
			dev_err(dev->dev, "failed to enable regulator: %d\n", ret);
	}

	DBG("setting lcdc_clk=%lu", pc);
	ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc);
	if (ret)
		dev_err(dev->dev, "failed to configure lcdc_clk: %d\n", ret);
	ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk);
	if (ret)
		dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret);

	if (panel) {
		drm_panel_prepare(panel);
		drm_panel_enable(panel);
	}

	setup_phy(encoder);

	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);

	mdp4_lcdc_encoder->enabled = true;
}
Пример #12
0
static void crtc_flush(struct drm_crtc *crtc)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	uint32_t i, flush = 0;

	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
		struct drm_plane *plane = mdp4_crtc->planes[i];
		if (plane) {
			enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane);
			flush |= pipe2flush(pipe_id);
		}
	}
	flush |= ovlp2flush(mdp4_crtc->ovlp);

	DBG("%s: flush=%08x", mdp4_crtc->name, flush);

	mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush);
}
Пример #13
0
static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
			to_mdp4_lcdc_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	struct drm_panel *panel;
	int i, ret;

	if (WARN_ON(!mdp4_lcdc_encoder->enabled))
		return;

	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);

	panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node);
	if (!IS_ERR(panel)) {
		drm_panel_disable(panel);
		drm_panel_unprepare(panel);
	}

	/*
	 * Wait for a vsync so we know the ENABLE=0 latched before
	 * the (connector) source of the vsync's gets disabled,
	 * otherwise we end up in a funny state if we re-enable
	 * before the disable latches, which results that some of
	 * the settings changes for the new modeset (like new
	 * scanout buffer) don't latch properly..
	 */
	mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC);

	clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk);

	for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
		ret = regulator_disable(mdp4_lcdc_encoder->regs[i]);
		if (ret)
			DRM_DEV_ERROR(dev->dev, "failed to disable regulator: %d\n", ret);
	}

	bs_set(mdp4_lcdc_encoder, 0);

	mdp4_lcdc_encoder->enabled = false;
}
Пример #14
0
/* set interface for routing crtc->encoder: */
void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	uint32_t intf_sel;

	intf_sel = mdp4_read(mdp4_kms, REG_MDP4_DISP_INTF_SEL);

	switch (mdp4_crtc->dma) {
	case DMA_P:
		intf_sel &= ~MDP4_DISP_INTF_SEL_PRIM__MASK;
		intf_sel |= MDP4_DISP_INTF_SEL_PRIM(intf);
		break;
	case DMA_S:
		intf_sel &= ~MDP4_DISP_INTF_SEL_SEC__MASK;
		intf_sel |= MDP4_DISP_INTF_SEL_SEC(intf);
		break;
	case DMA_E:
		intf_sel &= ~MDP4_DISP_INTF_SEL_EXT__MASK;
		intf_sel |= MDP4_DISP_INTF_SEL_EXT(intf);
		break;
	}

	if (intf == INTF_DSI_VIDEO) {
		intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD;
		intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO;
		mdp4_crtc->mixer = 0;
	} else if (intf == INTF_DSI_CMD) {
		intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO;
		intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD;
		mdp4_crtc->mixer = 0;
	} else if (intf == INTF_LCDC_DTV){
		mdp4_crtc->mixer = 1;
	}

	blend_setup(crtc);

	DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel);

	mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);
}
Пример #15
0
irqreturn_t mdp4_irq(struct msm_kms *kms)
{
	struct mdp_kms *mdp_kms = to_mdp_kms(kms);
	struct mdp4_kms *mdp4_kms = to_mdp4_kms(mdp_kms);
	struct drm_device *dev = mdp4_kms->dev;
	struct msm_drm_private *priv = dev->dev_private;
	unsigned int id;
	uint32_t status;

	status = mdp4_read(mdp4_kms, REG_MDP4_INTR_STATUS);
	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, status);

	VERB("status=%08x", status);

	mdp_dispatch_irqs(mdp_kms, status);

	for (id = 0; id < priv->num_crtcs; id++)
		if (status & mdp4_crtc_vblank(priv->crtcs[id]))
			drm_handle_vblank(dev, id);

	return IRQ_HANDLED;
}
Пример #16
0
static void mdp4_dsi_encoder_enable(struct drm_encoder *encoder)
{
	struct mdp4_dsi_encoder *mdp4_dsi_encoder = to_mdp4_dsi_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);

	if (mdp4_dsi_encoder->enabled)
		return;

	 mdp4_crtc_set_config(encoder->crtc,
			MDP4_DMA_CONFIG_PACK_ALIGN_MSB |
			MDP4_DMA_CONFIG_DEFLKR_EN |
			MDP4_DMA_CONFIG_DITHER_EN |
			MDP4_DMA_CONFIG_R_BPC(BPC8) |
			MDP4_DMA_CONFIG_G_BPC(BPC8) |
			MDP4_DMA_CONFIG_B_BPC(BPC8) |
			MDP4_DMA_CONFIG_PACK(0x21));

	mdp4_crtc_set_intf(encoder->crtc, INTF_DSI_VIDEO, 0);

	mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 1);

	mdp4_dsi_encoder->enabled = true;
}
Пример #17
0
static void mdp4_dtv_encoder_enable(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	unsigned long pc = mdp4_dtv_encoder->pixclock;
	int ret;

	if (WARN_ON(mdp4_dtv_encoder->enabled))
		return;

	mdp4_crtc_set_config(encoder->crtc,
			MDP4_DMA_CONFIG_R_BPC(BPC8) |
			MDP4_DMA_CONFIG_G_BPC(BPC8) |
			MDP4_DMA_CONFIG_B_BPC(BPC8) |
			MDP4_DMA_CONFIG_PACK(0x21));
	mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 1);

	bs_set(mdp4_dtv_encoder, 1);

	DBG("setting src_clk=%lu", pc);

	ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc);
	if (ret)
		dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret);
	clk_prepare_enable(mdp4_dtv_encoder->src_clk);
	ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk);
	if (ret)
		dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret);
	ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk);
	if (ret)
		dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret);

	mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1);

	mdp4_dtv_encoder->enabled = true;
}
Пример #18
0
static void blend_setup(struct drm_crtc *crtc)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	int i, ovlp = mdp4_crtc->ovlp;
	uint32_t mixer_cfg = 0;
	static const enum mdp_mixer_stage_id stages[] = {
			STAGE_BASE, STAGE0, STAGE1, STAGE2, STAGE3,
	};
	/* statically (for now) map planes to mixer stage (z-order): */
	static const int idxs[] = {
			[VG1]  = 1,
			[VG2]  = 2,
			[RGB1] = 0,
			[RGB2] = 0,
			[RGB3] = 0,
			[VG3]  = 3,
			[VG4]  = 4,

	};
	bool alpha[4]= { false, false, false, false };

	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0);

	/* TODO single register for all CRTCs, so this won't work properly
	 * when multiple CRTCs are active..
	 */
	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
		struct drm_plane *plane = mdp4_crtc->planes[i];
		if (plane) {
			enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane);
			int idx = idxs[pipe_id];
			if (idx > 0) {
				const struct mdp_format *format =
					to_mdp_format(msm_framebuffer_format(plane->fb));
				alpha[idx-1] = format->alpha_enable;
			}
			mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
		}
	}

	/* this shouldn't happen.. and seems to cause underflow: */
	WARN_ON(!mixer_cfg);

	for (i = 0; i < 4; i++) {
		uint32_t op;

		if (alpha[i]) {
			op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_PIXEL) |
					MDP4_OVLP_STAGE_OP_BG_ALPHA(FG_PIXEL) |
					MDP4_OVLP_STAGE_OP_BG_INV_ALPHA;
		} else {
			op = MDP4_OVLP_STAGE_OP_FG_ALPHA(FG_CONST) |
					MDP4_OVLP_STAGE_OP_BG_ALPHA(BG_CONST);
		}

		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_FG_ALPHA(ovlp, i), 0xff);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_BG_ALPHA(ovlp, i), 0x00);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_OP(ovlp, i), op);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_CO3(ovlp, i), 1);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW0(ovlp, i), 0);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_LOW1(ovlp, i), 0);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH0(ovlp, i), 0);
		mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);
	}

	mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);
}
static void setup_phy(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct drm_connector *connector = get_connector(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0;
	int bpp, nchan, swap;

	if (!connector)
		return;

	bpp = 3 * connector->display_info.bpc;

	if (!bpp)
		bpp = 18;

	/* TODO, these should come from panel somehow: */
	nchan = 1;
	swap = 0;

	switch (bpp) {
	case 24:
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06));
		if (nchan == 2) {
			lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
		} else {
			lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
		}
		break;

	case 18:
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
				MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17));
		mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) |
				MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14));
		if (nchan == 2) {
			lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
		} else {
			lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
					MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
		}
		lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT;
		break;

	default:
		dev_err(dev->dev, "unknown bpp: %d\n", bpp);
		return;
	}

	switch (nchan) {
	case 1:
		lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0;
		lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN |
				MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL;
		break;
	case 2:
		lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 |
				MDP4_LVDS_PHY_CFG0_CHANNEL1;
		lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN |
				MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN;
		break;
	default:
		dev_err(dev->dev, "unknown # of channels: %d\n", nchan);
		return;
	}

	if (swap)
		lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP;

	lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE;

	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
	mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf);
	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30);

	mb();
	udelay(1);
	lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE;
	mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
}
Пример #20
0
struct msm_kms *mdp4_kms_init(struct drm_device *dev)
{
	struct platform_device *pdev = dev->platformdev;
	struct mdp4_platform_config *config = mdp4_get_config(pdev);
	struct mdp4_kms *mdp4_kms;
	struct msm_kms *kms = NULL;
	struct msm_mmu *mmu;
	int ret;

	mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
	if (!mdp4_kms) {
		dev_err(dev->dev, "failed to allocate kms\n");
		ret = -ENOMEM;
		goto fail;
	}

	mdp_kms_init(&mdp4_kms->base, &kms_funcs);

	kms = &mdp4_kms->base.base;

	mdp4_kms->dev = dev;

	mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4");
	if (IS_ERR(mdp4_kms->mmio)) {
		ret = PTR_ERR(mdp4_kms->mmio);
		goto fail;
	}

	mdp4_kms->dsi_pll_vdda =
			devm_regulator_get_optional(&pdev->dev, "dsi_pll_vdda");
	if (IS_ERR(mdp4_kms->dsi_pll_vdda))
		mdp4_kms->dsi_pll_vdda = NULL;

	mdp4_kms->dsi_pll_vddio =
			devm_regulator_get_optional(&pdev->dev, "dsi_pll_vddio");
	if (IS_ERR(mdp4_kms->dsi_pll_vddio))
		mdp4_kms->dsi_pll_vddio = NULL;

	mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd");
	if (IS_ERR(mdp4_kms->vdd))
		mdp4_kms->vdd = NULL;

	if (mdp4_kms->vdd) {
		ret = regulator_enable(mdp4_kms->vdd);
		if (ret) {
			dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret);
			goto fail;
		}
	}

	mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk");
	if (IS_ERR(mdp4_kms->clk)) {
		dev_err(dev->dev, "failed to get core_clk\n");
		ret = PTR_ERR(mdp4_kms->clk);
		goto fail;
	}

	mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk");
	if (IS_ERR(mdp4_kms->pclk))
		mdp4_kms->pclk = NULL;

	// XXX if (rev >= MDP_REV_42) { ???
	mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk");
	if (IS_ERR(mdp4_kms->lut_clk)) {
		dev_err(dev->dev, "failed to get lut_clk\n");
		ret = PTR_ERR(mdp4_kms->lut_clk);
		goto fail;
	}

	mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "mdp_axi_clk");
	if (IS_ERR(mdp4_kms->axi_clk)) {
		dev_err(dev->dev, "failed to get axi_clk\n");
		ret = PTR_ERR(mdp4_kms->axi_clk);
		goto fail;
	}

	clk_set_rate(mdp4_kms->clk, config->max_clk);
	clk_set_rate(mdp4_kms->lut_clk, config->max_clk);

	/* make sure things are off before attaching iommu (bootloader could
	 * have left things on, in which case we'll start getting faults if
	 * we don't disable):
	 */
	mdp4_enable(mdp4_kms);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
	mdp4_disable(mdp4_kms);
	mdelay(16);

	if (config->iommu) {
		mmu = msm_iommu_new(&pdev->dev, config->iommu);
		if (IS_ERR(mmu)) {
			ret = PTR_ERR(mmu);
			goto fail;
		}
		ret = mmu->funcs->attach(mmu, iommu_ports,
				ARRAY_SIZE(iommu_ports));
		if (ret)
			goto fail;
	} else {
		dev_info(dev->dev, "no iommu, fallback to phys "
				"contig buffers for scanout\n");
		mmu = NULL;
	}

	mdp4_kms->id = msm_register_mmu(dev, mmu);
	if (mdp4_kms->id < 0) {
		ret = mdp4_kms->id;
		dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret);
		goto fail;
	}

	ret = modeset_init(mdp4_kms);
	if (ret) {
		dev_err(dev->dev, "modeset_init failed: %d\n", ret);
		goto fail;
	}

	mutex_lock(&dev->struct_mutex);
	mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC);
	mutex_unlock(&dev->struct_mutex);
	if (IS_ERR(mdp4_kms->blank_cursor_bo)) {
		ret = PTR_ERR(mdp4_kms->blank_cursor_bo);
		dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret);
		mdp4_kms->blank_cursor_bo = NULL;
		goto fail;
	}

	ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id,
			&mdp4_kms->blank_cursor_iova);
	if (ret) {
		dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret);
		goto fail;
	}

	return kms;

fail:
	if (kms)
		mdp4_destroy(kms);
	return ERR_PTR(ret);
}
Пример #21
0
void mdp4_irq_uninstall(struct msm_kms *kms)
{
	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
	mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
}
Пример #22
0
void mdp4_irq_preinstall(struct msm_kms *kms)
{
	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
	mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff);
}
Пример #23
0
void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask)
{
	mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_ENABLE, irqmask);
}
Пример #24
0
static int mdp4_plane_mode_set(struct drm_plane *plane,
		struct drm_crtc *crtc, struct drm_framebuffer *fb,
		int crtc_x, int crtc_y,
		unsigned int crtc_w, unsigned int crtc_h,
		uint32_t src_x, uint32_t src_y,
		uint32_t src_w, uint32_t src_h)
{
	struct drm_device *dev = plane->dev;
	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
	struct mdp4_kms *mdp4_kms = get_kms(plane);
	enum mdp4_pipe pipe = mdp4_plane->pipe;
	const struct mdp_format *format;
	uint32_t op_mode = 0;
	uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
	uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
	enum mdp4_frame_format frame_type;

	if (!(crtc && fb)) {
		DBG("%s: disabled!", mdp4_plane->name);
		return 0;
	}

	frame_type = mdp4_get_frame_format(fb);

	/* src values are in Q16 fixed point, convert to integer: */
	src_x = src_x >> 16;
	src_y = src_y >> 16;
	src_w = src_w >> 16;
	src_h = src_h >> 16;

	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp4_plane->name,
			fb->base.id, src_x, src_y, src_w, src_h,
			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);

	format = to_mdp_format(msm_framebuffer_format(fb));

	if (src_w > (crtc_w * DOWN_SCALE_MAX)) {
		dev_err(dev->dev, "Width down scaling exceeds limits!\n");
		return -ERANGE;
	}

	if (src_h > (crtc_h * DOWN_SCALE_MAX)) {
		dev_err(dev->dev, "Height down scaling exceeds limits!\n");
		return -ERANGE;
	}

	if (crtc_w > (src_w * UP_SCALE_MAX)) {
		dev_err(dev->dev, "Width up scaling exceeds limits!\n");
		return -ERANGE;
	}

	if (crtc_h > (src_h * UP_SCALE_MAX)) {
		dev_err(dev->dev, "Height up scaling exceeds limits!\n");
		return -ERANGE;
	}

	if (src_w != crtc_w) {
		uint32_t sel_unit = SCALE_FIR;
		op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;

		if (MDP_FORMAT_IS_YUV(format)) {
			if (crtc_w > src_w)
				sel_unit = SCALE_PIXEL_RPT;
			else if (crtc_w <= (src_w / 4))
				sel_unit = SCALE_MN_PHASE;

			op_mode |= MDP4_PIPE_OP_MODE_SCALEX_UNIT_SEL(sel_unit);
			phasex_step = mult_frac(MDP4_VG_PHASE_STEP_DEFAULT,
					src_w, crtc_w);
		}
	}

	if (src_h != crtc_h) {
		uint32_t sel_unit = SCALE_FIR;
		op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN;

		if (MDP_FORMAT_IS_YUV(format)) {

			if (crtc_h > src_h)
				sel_unit = SCALE_PIXEL_RPT;
			else if (crtc_h <= (src_h / 4))
				sel_unit = SCALE_MN_PHASE;

			op_mode |= MDP4_PIPE_OP_MODE_SCALEY_UNIT_SEL(sel_unit);
			phasey_step = mult_frac(MDP4_VG_PHASE_STEP_DEFAULT,
					src_h, crtc_h);
		}
	}

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe),
			MDP4_PIPE_SRC_SIZE_WIDTH(src_w) |
			MDP4_PIPE_SRC_SIZE_HEIGHT(src_h));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe),
			MDP4_PIPE_SRC_XY_X(src_x) |
			MDP4_PIPE_SRC_XY_Y(src_y));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe),
			MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) |
			MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe),
			MDP4_PIPE_DST_XY_X(crtc_x) |
			MDP4_PIPE_DST_XY_Y(crtc_y));

	mdp4_plane_set_scanout(plane, fb);

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe),
			MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
			MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
			MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
			MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
			COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
			MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
			MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
			MDP4_PIPE_SRC_FORMAT_FETCH_PLANES(format->fetch_type) |
			MDP4_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample) |
			MDP4_PIPE_SRC_FORMAT_FRAME_FORMAT(frame_type) |
			COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe),
			MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
			MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
			MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
			MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));

	if (MDP_FORMAT_IS_YUV(format)) {
		struct csc_cfg *csc = mdp_get_default_csc_cfg(CSC_YUV2RGB);

		op_mode |= MDP4_PIPE_OP_MODE_SRC_YCBCR;
		op_mode |= MDP4_PIPE_OP_MODE_CSC_EN;
		mdp4_write_csc_config(mdp4_kms, pipe, csc);
	}

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step);

	if (frame_type != FRAME_LINEAR)
		mdp4_write(mdp4_kms, REG_MDP4_PIPE_SSTILE_FRAME_SIZE(pipe),
				MDP4_PIPE_SSTILE_FRAME_SIZE_WIDTH(src_w) |
				MDP4_PIPE_SSTILE_FRAME_SIZE_HEIGHT(src_h));

	return 0;
}
Пример #25
0
int mdp4_plane_mode_set(struct drm_plane *plane,
		struct drm_crtc *crtc, struct drm_framebuffer *fb,
		int crtc_x, int crtc_y,
		unsigned int crtc_w, unsigned int crtc_h,
		uint32_t src_x, uint32_t src_y,
		uint32_t src_w, uint32_t src_h)
{
	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
	struct mdp4_kms *mdp4_kms = get_kms(plane);
	enum mpd4_pipe pipe = mdp4_plane->pipe;
	const struct mdp4_format *format;
	uint32_t op_mode = 0;
	uint32_t phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
	uint32_t phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;

	/* src values are in Q16 fixed point, convert to integer: */
	src_x = src_x >> 16;
	src_y = src_y >> 16;
	src_w = src_w >> 16;
	src_h = src_h >> 16;

	if (src_w != crtc_w) {
		op_mode |= MDP4_PIPE_OP_MODE_SCALEX_EN;
		/* TODO calc phasex_step */
	}

	if (src_h != crtc_h) {
		op_mode |= MDP4_PIPE_OP_MODE_SCALEY_EN;
		/* TODO calc phasey_step */
	}

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_SIZE(pipe),
			MDP4_PIPE_SRC_SIZE_WIDTH(src_w) |
			MDP4_PIPE_SRC_SIZE_HEIGHT(src_h));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_XY(pipe),
			MDP4_PIPE_SRC_XY_X(src_x) |
			MDP4_PIPE_SRC_XY_Y(src_y));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_SIZE(pipe),
			MDP4_PIPE_DST_SIZE_WIDTH(crtc_w) |
			MDP4_PIPE_DST_SIZE_HEIGHT(crtc_h));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_DST_XY(pipe),
			MDP4_PIPE_SRC_XY_X(crtc_x) |
			MDP4_PIPE_SRC_XY_Y(crtc_y));

	mdp4_plane_set_scanout(plane, fb);

	format = to_mdp4_format(msm_framebuffer_format(fb));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_FORMAT(pipe),
			MDP4_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
			MDP4_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
			MDP4_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
			MDP4_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
			COND(format->alpha_enable, MDP4_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
			MDP4_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
			MDP4_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
			COND(format->unpack_tight, MDP4_PIPE_SRC_FORMAT_UNPACK_TIGHT));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRC_UNPACK(pipe),
			MDP4_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
			MDP4_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
			MDP4_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
			MDP4_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(pipe), op_mode);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEX_STEP(pipe), phasex_step);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_PHASEY_STEP(pipe), phasey_step);

	plane->crtc = crtc;

	return 0;
}
Пример #26
0
static void mdp4_dtv_encoder_mode_set(struct drm_encoder *encoder,
		struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder);
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol;
	uint32_t display_v_start, display_v_end;
	uint32_t hsync_start_x, hsync_end_x;

	mode = adjusted_mode;

	DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
			mode->base.id, mode->name,
			mode->vrefresh, mode->clock,
			mode->hdisplay, mode->hsync_start,
			mode->hsync_end, mode->htotal,
			mode->vdisplay, mode->vsync_start,
			mode->vsync_end, mode->vtotal,
			mode->type, mode->flags);

	mdp4_dtv_encoder->pixclock = mode->clock * 1000;

	DBG("pixclock=%lu", mdp4_dtv_encoder->pixclock);

	ctrl_pol = 0;
	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
		ctrl_pol |= MDP4_DTV_CTRL_POLARITY_HSYNC_LOW;
	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
		ctrl_pol |= MDP4_DTV_CTRL_POLARITY_VSYNC_LOW;
	/* probably need to get DATA_EN polarity from panel.. */

	dtv_hsync_skew = 0;  /* get this from panel? */

	hsync_start_x = (mode->htotal - mode->hsync_start);
	hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;

	vsync_period = mode->vtotal * mode->htotal;
	vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
	display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew;
	display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1;

	mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_CTRL,
			MDP4_DTV_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
			MDP4_DTV_HSYNC_CTRL_PERIOD(mode->htotal));
	mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_PERIOD, vsync_period);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_VSYNC_LEN, vsync_len);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_HCTRL,
			MDP4_DTV_DISPLAY_HCTRL_START(hsync_start_x) |
			MDP4_DTV_DISPLAY_HCTRL_END(hsync_end_x));
	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VSTART, display_v_start);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_DISPLAY_VEND, display_v_end);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_BORDER_CLR, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_UNDERFLOW_CLR,
			MDP4_DTV_UNDERFLOW_CLR_ENABLE_RECOVERY |
			MDP4_DTV_UNDERFLOW_CLR_COLOR(0xff));
	mdp4_write(mdp4_kms, REG_MDP4_DTV_HSYNC_SKEW, dtv_hsync_skew);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_CTRL_POLARITY, ctrl_pol);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_HCTL,
			MDP4_DTV_ACTIVE_HCTL_START(0) |
			MDP4_DTV_ACTIVE_HCTL_END(0));
	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VSTART, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_ACTIVE_VEND, 0);
}
Пример #27
0
static int mdp4_hw_init(struct msm_kms *kms)
{
	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
	struct drm_device *dev = mdp4_kms->dev;
	uint32_t version, major, minor, dmap_cfg, vg_cfg;
	unsigned long clk;
	int ret = 0;

	pm_runtime_get_sync(dev->dev);

	mdp4_enable(mdp4_kms);
	version = mdp4_read(mdp4_kms, REG_MDP4_VERSION);
	mdp4_disable(mdp4_kms);

	major = FIELD(version, MDP4_VERSION_MAJOR);
	minor = FIELD(version, MDP4_VERSION_MINOR);

	DBG("found MDP4 version v%d.%d", major, minor);

	if (major != 4) {
		dev_err(dev->dev, "unexpected MDP version: v%d.%d\n",
				major, minor);
		ret = -ENXIO;
		goto out;
	}

	mdp4_kms->rev = minor;

	if (mdp4_kms->rev > 1) {
		mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff);
		mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f);
	}

	mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3);

	/* max read pending cmd config, 3 pending requests: */
	mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222);

	clk = clk_get_rate(mdp4_kms->clk);

	if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) {
		dmap_cfg = 0x47;     /* 16 bytes-burst x 8 req */
		vg_cfg = 0x47;       /* 16 bytes-burs x 8 req */
	} else {
		dmap_cfg = 0x27;     /* 8 bytes-burst x 8 req */
		vg_cfg = 0x43;       /* 16 bytes-burst x 4 req */
	}

	DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg);

	mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg);
	mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg);

	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg);

	if (mdp4_kms->rev >= 2)
		mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1);
	mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, 0);

	/* disable CSC matrix / YUV by default: */
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0);
	mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0);
	mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0);

	if (mdp4_kms->rev > 1)
		mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1);

	dev->mode_config.allow_fb_modifiers = true;

out:
	pm_runtime_put_sync(dev->dev);

	return ret;
}
Пример #28
0
static void mdp4_dsi_encoder_mode_set(struct drm_encoder *encoder,
				      struct drm_display_mode *mode,
				      struct drm_display_mode *adjusted_mode)
{
	struct mdp4_kms *mdp4_kms = get_kms(encoder);
	uint32_t dsi_hsync_skew, vsync_period, vsync_len, ctrl_pol;
	uint32_t display_v_start, display_v_end;
	uint32_t hsync_start_x, hsync_end_x;

	mode = adjusted_mode;

	DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));

	ctrl_pol = 0;
	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
		ctrl_pol |= MDP4_DSI_CTRL_POLARITY_HSYNC_LOW;
	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
		ctrl_pol |= MDP4_DSI_CTRL_POLARITY_VSYNC_LOW;
	/* probably need to get DATA_EN polarity from panel.. */

	dsi_hsync_skew = 0;  /* get this from panel? */

	hsync_start_x = (mode->htotal - mode->hsync_start);
	hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;

	vsync_period = mode->vtotal * mode->htotal;
	vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
	display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dsi_hsync_skew;
	display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dsi_hsync_skew - 1;

	mdp4_write(mdp4_kms, REG_MDP4_DSI_HSYNC_CTRL,
			MDP4_DSI_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
			MDP4_DSI_HSYNC_CTRL_PERIOD(mode->htotal));
	mdp4_write(mdp4_kms, REG_MDP4_DSI_VSYNC_PERIOD, vsync_period);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_VSYNC_LEN, vsync_len);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_HCTRL,
			MDP4_DSI_DISPLAY_HCTRL_START(hsync_start_x) |
			MDP4_DSI_DISPLAY_HCTRL_END(hsync_end_x));
	mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_VSTART, display_v_start);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_DISPLAY_VEND, display_v_end);

	mdp4_write(mdp4_kms, REG_MDP4_DSI_CTRL_POLARITY, ctrl_pol);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_UNDERFLOW_CLR,
			MDP4_DSI_UNDERFLOW_CLR_ENABLE_RECOVERY |
			MDP4_DSI_UNDERFLOW_CLR_COLOR(0xff));
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_HCTL,
			MDP4_DSI_ACTIVE_HCTL_START(0) |
			MDP4_DSI_ACTIVE_HCTL_END(0));
	mdp4_write(mdp4_kms, REG_MDP4_DSI_HSYNC_SKEW, dsi_hsync_skew);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_BORDER_CLR, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_VSTART, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ACTIVE_VEND, 0);
}
Пример #29
0
struct msm_kms *mdp4_kms_init(struct drm_device *dev)
{
	struct platform_device *pdev = to_platform_device(dev->dev);
	struct mdp4_platform_config *config = mdp4_get_config(pdev);
	struct mdp4_kms *mdp4_kms;
	struct msm_kms *kms = NULL;
	struct msm_gem_address_space *aspace;
	int irq, ret;

	mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
	if (!mdp4_kms) {
		dev_err(dev->dev, "failed to allocate kms\n");
		ret = -ENOMEM;
		goto fail;
	}

	mdp_kms_init(&mdp4_kms->base, &kms_funcs);

	kms = &mdp4_kms->base.base;

	mdp4_kms->dev = dev;

	mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4");
	if (IS_ERR(mdp4_kms->mmio)) {
		ret = PTR_ERR(mdp4_kms->mmio);
		goto fail;
	}

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		ret = irq;
		dev_err(dev->dev, "failed to get irq: %d\n", ret);
		goto fail;
	}

	kms->irq = irq;

	/* NOTE: driver for this regulator still missing upstream.. use
	 * _get_exclusive() and ignore the error if it does not exist
	 * (and hope that the bootloader left it on for us)
	 */
	mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd");
	if (IS_ERR(mdp4_kms->vdd))
		mdp4_kms->vdd = NULL;

	if (mdp4_kms->vdd) {
		ret = regulator_enable(mdp4_kms->vdd);
		if (ret) {
			dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret);
			goto fail;
		}
	}

	mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk");
	if (IS_ERR(mdp4_kms->clk)) {
		dev_err(dev->dev, "failed to get core_clk\n");
		ret = PTR_ERR(mdp4_kms->clk);
		goto fail;
	}

	mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk");
	if (IS_ERR(mdp4_kms->pclk))
		mdp4_kms->pclk = NULL;

	// XXX if (rev >= MDP_REV_42) { ???
	mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk");
	if (IS_ERR(mdp4_kms->lut_clk)) {
		dev_err(dev->dev, "failed to get lut_clk\n");
		ret = PTR_ERR(mdp4_kms->lut_clk);
		goto fail;
	}

	mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
	if (IS_ERR(mdp4_kms->axi_clk)) {
		dev_err(dev->dev, "failed to get axi_clk\n");
		ret = PTR_ERR(mdp4_kms->axi_clk);
		goto fail;
	}

	clk_set_rate(mdp4_kms->clk, config->max_clk);
	clk_set_rate(mdp4_kms->lut_clk, config->max_clk);

	pm_runtime_enable(dev->dev);
	mdp4_kms->rpm_enabled = true;

	/* make sure things are off before attaching iommu (bootloader could
	 * have left things on, in which case we'll start getting faults if
	 * we don't disable):
	 */
	mdp4_enable(mdp4_kms);
	mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
	mdp4_disable(mdp4_kms);
	mdelay(16);

	if (config->iommu) {
		aspace = msm_gem_address_space_create(&pdev->dev,
				config->iommu, "mdp4");
		if (IS_ERR(aspace)) {
			ret = PTR_ERR(aspace);
			goto fail;
		}

		kms->aspace = aspace;

		ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
				ARRAY_SIZE(iommu_ports));
		if (ret)
			goto fail;
	} else {
		dev_info(dev->dev, "no iommu, fallback to phys "
				"contig buffers for scanout\n");
		aspace = NULL;
	}

	ret = modeset_init(mdp4_kms);
	if (ret) {
		dev_err(dev->dev, "modeset_init failed: %d\n", ret);
		goto fail;
	}

	mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC);
	if (IS_ERR(mdp4_kms->blank_cursor_bo)) {
		ret = PTR_ERR(mdp4_kms->blank_cursor_bo);
		dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret);
		mdp4_kms->blank_cursor_bo = NULL;
		goto fail;
	}

	ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, kms->aspace,
			&mdp4_kms->blank_cursor_iova);
	if (ret) {
		dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret);
		goto fail;
	}

	dev->mode_config.min_width = 0;
	dev->mode_config.min_height = 0;
	dev->mode_config.max_width = 2048;
	dev->mode_config.max_height = 2048;

	return kms;

fail:
	if (kms)
		mdp4_destroy(kms);
	return ERR_PTR(ret);
}
Пример #30
0
static int mdp4_crtc_mode_set(struct drm_crtc *crtc,
		struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode,
		int x, int y,
		struct drm_framebuffer *old_fb)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct mdp4_kms *mdp4_kms = get_kms(crtc);
	enum mdp4_dma dma = mdp4_crtc->dma;
	int ret, ovlp = mdp4_crtc->ovlp;

	mode = adjusted_mode;

	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
			mdp4_crtc->name, mode->base.id, mode->name,
			mode->vrefresh, mode->clock,
			mode->hdisplay, mode->hsync_start,
			mode->hsync_end, mode->htotal,
			mode->vdisplay, mode->vsync_start,
			mode->vsync_end, mode->vtotal,
			mode->type, mode->flags);

	/* grab extra ref for update_scanout() */
	drm_framebuffer_reference(crtc->primary->fb);

	ret = mdp4_plane_mode_set(mdp4_crtc->plane, crtc, crtc->primary->fb,
			0, 0, mode->hdisplay, mode->vdisplay,
			x << 16, y << 16,
			mode->hdisplay << 16, mode->vdisplay << 16);
	if (ret) {
		drm_framebuffer_unreference(crtc->primary->fb);
		dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n",
				mdp4_crtc->name, ret);
		return ret;
	}

	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_SIZE(dma),
			MDP4_DMA_SRC_SIZE_WIDTH(mode->hdisplay) |
			MDP4_DMA_SRC_SIZE_HEIGHT(mode->vdisplay));

	/* take data from pipe: */
	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_BASE(dma), 0);
	mdp4_write(mdp4_kms, REG_MDP4_DMA_SRC_STRIDE(dma),
			crtc->primary->fb->pitches[0]);
	mdp4_write(mdp4_kms, REG_MDP4_DMA_DST_SIZE(dma),
			MDP4_DMA_DST_SIZE_WIDTH(0) |
			MDP4_DMA_DST_SIZE_HEIGHT(0));

	mdp4_write(mdp4_kms, REG_MDP4_OVLP_BASE(ovlp), 0);
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_SIZE(ovlp),
			MDP4_OVLP_SIZE_WIDTH(mode->hdisplay) |
			MDP4_OVLP_SIZE_HEIGHT(mode->vdisplay));
	mdp4_write(mdp4_kms, REG_MDP4_OVLP_STRIDE(ovlp),
			crtc->primary->fb->pitches[0]);

	mdp4_write(mdp4_kms, REG_MDP4_OVLP_CFG(ovlp), 1);

	if (dma == DMA_E) {
		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(0), 0x00ff0000);
		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(1), 0x00ff0000);
		mdp4_write(mdp4_kms, REG_MDP4_DMA_E_QUANT(2), 0x00ff0000);
	}

	update_fb(crtc, crtc->primary->fb);
	update_scanout(crtc, crtc->primary->fb);

	return 0;
}