Exemple #1
0
int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num)
{
	u32 irq;
	unsigned long irq_flags;
	int ret = 0;

	irq = mdss_mdp_irq_mask(intr_type, intf_num);

	spin_lock_irqsave(&mdp_lock, irq_flags);
	if (mdss_res->mdp_irq_mask & irq) {
		pr_warn("MDSS MDP IRQ-0x%x is already set, mask=%x\n",
				irq, mdss_res->mdp_irq_mask);
		ret = -EBUSY;
	} else {
		pr_debug("MDP IRQ mask old=%x new=%x\n",
				mdss_res->mdp_irq_mask, irq);
		mdss_res->mdp_irq_mask |= irq;
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, irq);
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
				mdss_res->mdp_irq_mask);
		mdss_enable_irq(&mdss_mdp_hw);
	}
	spin_unlock_irqrestore(&mdp_lock, irq_flags);

	return ret;
}
Exemple #2
0
static void mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp)
{
	u32 mmb, off, data, s;

	for_each_set_bit(mmb, smp, SMP_MB_CNT) {
		off = (mmb / 3) * 4;
		s = (mmb % 3) * 8;
		data = MDSS_MDP_REG_READ(MDSS_MDP_REG_SMP_ALLOC_W0 + off);
		data &= ~(0xFF << s);
		data |= client_id << s;
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_W0 + off, data);
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_R0 + off, data);
	}
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
	struct mdss_mdp_video_ctx *ctx;
	u32 intr_type = MDSS_MDP_IRQ_INTF_VSYNC;

	pr_debug("kickoff ctl=%d\n", ctl->num);

	ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
	if (!ctx) {
		pr_err("invalid ctx\n");
		return -ENODEV;
	}
	mdss_mdp_set_intr_callback(intr_type, ctl->intf_num,
				   mdss_mdp_video_vsync_intr_done, ctx);
	mdss_mdp_irq_enable(intr_type, ctl->intf_num);

	if (!ctx->timegen_en) {
		int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);

		pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num);

		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
		ctx->timegen_en = true;
		wmb();
	}

	wait_for_completion_interruptible(&ctx->vsync_comp);
	mdss_mdp_irq_disable(intr_type, ctl->intf_num);

	return 0;
}
Exemple #4
0
static int mdss_hw_init(struct mdss_data_type *mdata)
{
	char *base = mdata->vbif_base;

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
	/* Setup VBIF QoS settings*/
	MDSS_MDP_REG_WRITE(0x2E0, 0x000000AA);
	MDSS_MDP_REG_WRITE(0x2E4, 0x00000055);
	writel_relaxed(0x00000001, base + 0x004);
	writel_relaxed(0x00000707, base + 0x0D8);
	writel_relaxed(0x00000030, base + 0x0F0);
	writel_relaxed(0x00000001, base + 0x124);
	writel_relaxed(0x00000FFF, base + 0x178);
	writel_relaxed(0x0FFF0FFF, base + 0x17C);
	writel_relaxed(0x22222222, base + 0x160);
	writel_relaxed(0x00002222, base + 0x164);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
	pr_debug("MDP hw init done\n");

	return 0;
}
irqreturn_t mdss_mdp_isr(int irq, void *ptr)
{
	u32 isr, mask;


	isr = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_STATUS);

	pr_debug("isr=%x\n", isr);

	if (isr == 0)
		goto done;

	mask = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_EN);
	MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, isr);

	isr &= mask;
	if (isr == 0)
		goto done;

	if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_0);

	if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_1);

	if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_2);

	if (isr & MDSS_MDP_INTR_INTF_0_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);

	if (isr & MDSS_MDP_INTR_INTF_1_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1);

	if (isr & MDSS_MDP_INTR_INTF_2_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2);

	if (isr & MDSS_MDP_INTR_INTF_3_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3);

	if (isr & MDSS_MDP_INTR_WB_0_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_0);

	if (isr & MDSS_MDP_INTR_WB_1_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_1);

	if (isr & MDSS_MDP_INTR_WB_2_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_2);

done:
	return IRQ_HANDLED;
}
Exemple #6
0
/* called from interrupt context */
void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num)
{
	u32 irq;

	irq = mdss_mdp_irq_mask(intr_type, intf_num);

	if (!(mdss_res->mdp_irq_mask & irq)) {
		pr_warn("MDSS MDP IRQ-%x is NOT set, mask=%x\n",
				irq, mdss_res->mdp_irq_mask);
	} else {
		mdss_res->mdp_irq_mask &= ~irq;
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
				mdss_res->mdp_irq_mask);
		if ((mdss_res->mdp_irq_mask == 0) &&
			(mdss_res->mdp_hist_irq_mask == 0))
			mdss_disable_irq_nosync(&mdss_mdp_hw);
	}
}
Exemple #7
0
void mdss_mdp_hist_irq_disable(u32 irq)
{
	unsigned long irq_flags;

	spin_lock_irqsave(&mdp_lock, irq_flags);
	if (!(mdss_res->mdp_hist_irq_mask & irq)) {
		pr_warn("MDSS MDP IRQ-%x is NOT set, mask=%x\n",
				irq, mdss_res->mdp_hist_irq_mask);
	} else {
		mdss_res->mdp_hist_irq_mask &= ~irq;
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_HIST_INTR_EN,
				mdss_res->mdp_hist_irq_mask);
		if ((mdss_res->mdp_irq_mask == 0) &&
			(mdss_res->mdp_hist_irq_mask == 0))
			mdss_disable_irq(&mdss_mdp_hw);
	}
	spin_unlock_irqrestore(&mdp_lock, irq_flags);
}
void mdss_mdp_irq_disable(u32 intr_type, u32 intf_num)
{
	u32 irq;
	unsigned long irq_flags;

	irq = mdss_mdp_irq_mask(intr_type, intf_num);

	spin_lock_irqsave(&mdp_lock, irq_flags);
	if (!(mdss_res->mdp_irq_mask & irq)) {
		pr_warn("MDSS MDP IRQ-%x is NOT set, mask=%x\n",
				irq, mdss_res->mdp_irq_mask);
	} else {
		mdss_res->mdp_irq_mask &= ~irq;
		MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
				mdss_res->mdp_irq_mask);
		mdss_disable_irq(&mdss_mdp_hw);
	}
	spin_unlock_irqrestore(&mdp_lock, irq_flags);
}
static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
{
	struct mdss_mdp_video_ctx *ctx;
	int off;

	pr_debug("stop ctl=%d\n", ctl->num);

	ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
	if (!ctx) {
		pr_err("invalid ctx for ctl=%d\n", ctl->num);
		return -ENODEV;
	}

	if (ctx->timegen_en) {
		off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
		ctx->timegen_en = false;
	}

	memset(ctx, 0, sizeof(*ctx));

	return 0;
}
Exemple #10
0
void htc_set_pp_pa(struct mdss_mdp_ctl *ctl)
{
	struct mdss_data_type *mdata;
	struct mdss_mdp_mixer *mixer;
	u32 base = 0, opmode;
	char __iomem *basel;

	
	if (htc_mdss_pp_pa[HUE_INDEX].req_value == htc_mdss_pp_pa[HUE_INDEX].cur_value)
		return;

	if (htc_mdss_pp_pa[HUE_INDEX].req_value >= HUE_MAX)
		return;

	mdata = mdss_mdp_get_mdata();
	mixer = mdata->mixer_intf;

	base = MDSS_MDP_REG_DSPP_OFFSET(0);
	basel = mixer->dspp_base;

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);

	MDSS_MDP_REG_WRITE(base + MDSS_MDP_REG_DSPP_PA_BASE, htc_mdss_pp_pa[HUE_INDEX].req_value);

	opmode = MDSS_MDP_REG_READ(base);
	opmode |= (1 << 20); 
	writel_relaxed(opmode, basel + MDSS_MDP_REG_DSPP_OP_MODE);

	ctl->flush_bits |= BIT(13);

	wmb();
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);

	htc_mdss_pp_pa[HUE_INDEX].cur_value = htc_mdss_pp_pa[HUE_INDEX].req_value;
	PR_DISP_INFO("%s pp_hue = 0x%x\n", __func__, htc_mdss_pp_pa[HUE_INDEX].req_value);
}
static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
					struct intf_timing_params *p)
{
	u32 hsync_period, vsync_period;
	u32 hsync_start_x, hsync_end_x, display_v_start, display_v_end;
	u32 active_h_start, active_h_end, active_v_start, active_v_end;
	u32 display_hctl, active_hctl, hsync_ctl, polarity_ctl;
	int off;

	off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);

	hsync_period = p->hsync_pulse_width + p->h_back_porch +
			p->width + p->h_front_porch;
	vsync_period = p->vsync_pulse_width + p->v_back_porch +
			p->height + p->v_front_porch;

	display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
			hsync_period) + p->hsync_skew;
	display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
			p->hsync_skew - 1;

	if (ctl->intf_type == MDSS_INTF_EDP) {
		display_v_start += p->hsync_pulse_width + p->h_back_porch;
		display_v_end -= p->h_front_porch;
	}

	hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
	hsync_end_x = hsync_period - p->h_front_porch - 1;

	if (p->width != p->xres) {
		active_h_start = hsync_start_x;
		active_h_end = active_h_start + p->xres - 1;
	} else {
		active_h_start = 0;
		active_h_end = 0;
	}

	if (p->height != p->yres) {
		active_v_start = display_v_start;
		active_v_end = active_v_start + (p->yres * hsync_period) - 1;
	} else {
		active_v_start = 0;
		active_v_end = 0;
	}


	if (active_h_end) {
		active_hctl = (active_h_end << 16) | active_h_start;
		active_hctl |= BIT(31);	/* ACTIVE_H_ENABLE */
	} else {
		active_hctl = 0;
	}

	if (active_v_end)
		active_v_start |= BIT(31); /* ACTIVE_V_ENABLE */

	hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
	display_hctl = (hsync_end_x << 16) | hsync_start_x;
	polarity_ctl = (0 << 2) |	/* DEN Polarity */
		       (0 << 1) |      /* VSYNC Polarity */
		       (0);	       /* HSYNC Polarity */

	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_CTL, hsync_ctl);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
			   vsync_period * hsync_period);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0,
			   p->vsync_pulse_width * hsync_period);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_HCTL,
			   display_hctl);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_START_F0,
			   display_v_start);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_END_F0,
			   display_v_end);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_HCTL, active_hctl);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_START_F0,
			   active_v_start);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_END_F0,
			   active_v_end);

	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_BORDER_COLOR,
			   p->border_clr);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_UNDERFLOW_COLOR,
			   p->underflow_clr);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_SKEW,
			   p->hsync_skew);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL,
			   polarity_ctl);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
			   MDSS_MDP_PANEL_FORMAT_RGB888);

	return 0;
}
Exemple #12
0
static int mdss_mdp_ctl_init(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_ctl *ctl;
	struct mdss_panel_data *pdata;
	u32 width, height;
	int ret = 0;

	if (!mfd)
		return -ENODEV;

	pdata = dev_get_platdata(&mfd->pdev->dev);
	if (!pdata) {
		pr_err("no panel connected for fb%d\n", mfd->index);
		return -ENODEV;
	}

	width = pdata->panel_info.xres;
	height = pdata->panel_info.yres;

	if (width > (2 * MAX_MIXER_WIDTH)) {
		pr_err("unsupported resolution\n");
		return -EINVAL;
	}

	if (!mfd->ctl) {
		ctl = mdss_mdp_ctl_alloc();
		if (!ctl) {
			pr_err("unable to allocate ctl\n");
			return -ENOMEM;
		}
		ctl->mfd = mfd;
		mfd->ctl = ctl;
		ctl->panel_data = pdata;
	} else {
		ctl = mfd->ctl;
	}

	ctl->width = width;
	ctl->height = height;

	if (!ctl->mixer_left) {
		ctl->mixer_left =
			mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF);
		if (!ctl->mixer_left) {
			pr_err("unable to allocate layer mixer\n");
			ret = -ENOMEM;
			goto ctl_init_fail;
		}
	}

	if (width > MAX_MIXER_WIDTH)
		width /= 2;

	ctl->mixer_left->width = width;
	ctl->mixer_left->height = height;
	ctl->mixer_left->ctl = ctl;

	if (width < ctl->width) {
		if (ctl->mixer_right == NULL) {
			ctl->mixer_right =
				mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF);
			if (!ctl->mixer_right) {
				pr_err("unable to allocate right mixer\n");
				ret = -ENOMEM;
				goto ctl_init_fail;
			}
		}
		ctl->mixer_right->width = width;
		ctl->mixer_right->height = height;
		ctl->mixer_right->ctl = ctl;
	} else if (ctl->mixer_right) {
		mdss_mdp_mixer_free(ctl->mixer_right);
	}

	switch (pdata->panel_info.type) {
	case EDP_PANEL:
		ctl->intf_num = MDSS_MDP_INTF0;
		ctl->intf_type = MDSS_INTF_EDP;
		ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
		ctl->start_fnc = mdss_mdp_video_start;
		break;
	case MIPI_VIDEO_PANEL:
		if (pdata->panel_info.pdest == DISPLAY_1)
			ctl->intf_num = MDSS_MDP_INTF1;
		else
			ctl->intf_num = MDSS_MDP_INTF2;
		ctl->intf_type = MDSS_INTF_DSI;
		ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
		ctl->start_fnc = mdss_mdp_video_start;
		break;
	case DTV_PANEL:
		ctl->intf_num = MDSS_MDP_INTF3;
		ctl->intf_type = MDSS_INTF_HDMI;
		ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
		ctl->start_fnc = mdss_mdp_video_start;
		break;
	case WRITEBACK_PANEL:
		ctl->intf_num = MDSS_MDP_NO_INTF;
		ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE;
		ctl->start_fnc = mdss_mdp_writeback_start;
		break;
	default:
		pr_err("unsupported panel type (%d)\n", pdata->panel_info.type);
		ret = -EINVAL;
		goto ctl_init_fail;
	}

	ctl->opmode |= (ctl->intf_num << 4);

	if (ctl->intf_num == MDSS_MDP_NO_INTF) {
		ctl->dst_format = pdata->panel_info.out_format;
	} else {
		struct mdp_dither_cfg_data dither = {
			.block = mfd->index + MDP_LOGICAL_BLOCK_DISP_0,
			.flags = MDP_PP_OPS_DISABLE,
		};

		switch (pdata->panel_info.bpp) {
		case 18:
			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666;
			dither.flags = MDP_PP_OPS_ENABLE | MDP_PP_OPS_WRITE;
			dither.g_y_depth = 2;
			dither.r_cr_depth = 2;
			dither.b_cb_depth = 2;
			break;
		case 24:
		default:
			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB888;
			break;
		}
		mdss_mdp_dither_config(&dither, NULL);
	}

	if (ctl->mixer_right) {
		ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
			       MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT;
	}

ctl_init_fail:
	if (IS_ERR_VALUE(ret)) {
		if (ctl->mixer_left)
			mdss_mdp_mixer_free(ctl->mixer_left);
		if (ctl->mixer_right)
			mdss_mdp_mixer_free(ctl->mixer_right);
		mdss_mdp_ctl_free(ctl);
		mfd->ctl = NULL;
	}

	return ret;
}

static int mdss_mdp_ctl_destroy(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_ctl *ctl;
	if (!mfd || !mfd->ctl)
		return -ENODEV;

	ctl = mfd->ctl;
	mfd->ctl = NULL;

	if (ctl->mixer_left)
		mdss_mdp_mixer_free(ctl->mixer_left);
	if (ctl->mixer_right)
		mdss_mdp_mixer_free(ctl->mixer_right);
	mdss_mdp_ctl_free(ctl);

	return 0;
}

int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg)
{
	struct mdss_panel_data *pdata;
	if (!ctl || !ctl->panel_data)
		return -ENODEV;

	pdata = ctl->panel_data;

	pr_debug("sending ctl=%d event=%d\n", ctl->num, event);

	if (pdata->event_handler)
		return pdata->event_handler(pdata, event, arg);

	return 0;
}

int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_ctl *ctl;
	struct mdss_mdp_mixer *mixer;
	u32 outsize, temp, off;
	int ret = 0;

	if (!mfd)
		return -ENODEV;

	if (mfd->key != MFD_KEY)
		return -EINVAL;

	if (mdss_mdp_ctl_init(mfd)) {
		pr_err("unable to initialize ctl\n");
		return -ENODEV;
	}

	ctl = mfd->ctl;

	if (ctl->power_on) {
		WARN(1, "already on!\n");
		return 0;
	}

	mutex_lock(&ctl->lock);

	ctl->power_on = true;
	ctl->bus_ab_quota = 0;
	ctl->bus_ib_quota = 0;
	ctl->clk_rate = 0;

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
	ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_RESET, NULL);
	if (ret) {
		pr_err("panel power on failed ctl=%d\n", ctl->num);
		goto start_fail;
	}

	if (ctl->start_fnc)
		ret = ctl->start_fnc(ctl);
	else
		pr_warn("no start function for ctl=%d type=%d\n", ctl->num,
				ctl->panel_data->panel_info.type);

	if (ret) {
		pr_err("unable to start intf\n");
		goto start_fail;
	}

	pr_debug("ctl_num=%d\n", ctl->num);

	mixer = ctl->mixer_left;
	mixer->params_changed++;

	temp = MDSS_MDP_REG_READ(MDSS_MDP_REG_DISP_INTF_SEL);
	temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8));
	MDSS_MDP_REG_WRITE(MDSS_MDP_REG_DISP_INTF_SEL, temp);

	if (ctl->intf_num != MDSS_MDP_NO_INTF) {
		off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
				   ctl->dst_format);
	}

	outsize = (mixer->height << 16) | mixer->width;
	off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);

	if (ctl->mixer_right) {
		mixer = ctl->mixer_right;
		mixer->params_changed++;
		outsize = (mixer->height << 16) | mixer->width;
		off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);
		mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0);
	}

start_fail:
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
	mutex_unlock(&ctl->lock);
	if (ret)
		mdss_mdp_ctl_destroy(mfd);

	return ret;
}

int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_ctl *ctl;
	int ret = 0;

	if (!mfd)
		return -ENODEV;

	if (mfd->key != MFD_KEY)
		return -EINVAL;

	if (!mfd->ctl) {
		pr_err("ctl not initialized\n");
		return -ENODEV;
	}

	ctl = mfd->ctl;

	if (!ctl->power_on) {
		WARN(1, "already off!\n");
		return 0;
	}

	pr_debug("ctl_num=%d\n", mfd->ctl->num);

	mutex_lock(&ctl->lock);

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);

	if (ctl->stop_fnc)
		ret = ctl->stop_fnc(ctl);
	else
		pr_warn("no stop func for ctl=%d\n", ctl->num);

	if (ret) {
		pr_warn("error powering off intf ctl=%d\n", ctl->num);
	} else {
		ctl->power_on = false;
		ctl->play_cnt = 0;
		ctl->clk_rate = 0;
		mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
	}

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);

	mutex_unlock(&ctl->lock);

	if (!ret && !mfd->ref_cnt) {
		ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CLOSE, NULL);
		WARN(ret, "unable to close intf %d\n", ctl->intf_num);
		mdss_mdp_ctl_destroy(mfd);
	}

	return ret;
}

static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl,
				struct mdss_mdp_mixer *mixer)
{
	struct mdss_mdp_pipe *pipe;
	u32 off, blend_op, blend_stage;
	u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
	int stage;

	if (!mixer)
		return -ENODEV;

	pr_debug("setup mixer=%d\n", mixer->num);

	pipe = mixer->stage_pipe[MDSS_MDP_STAGE_BASE];
	if (pipe == NULL) {
		mixercfg = MDSS_MDP_LM_BORDER_COLOR;
	} else {
		mixercfg = 1 << (3 * pipe->num);
		if (pipe->src_fmt->alpha_enable)
			bgalpha = 1;
	}

	for (stage = MDSS_MDP_STAGE_0; stage < MDSS_MDP_MAX_STAGE; stage++) {
		pipe = mixer->stage_pipe[stage];
		if (pipe == NULL)
			continue;

		if (stage != pipe->mixer_stage) {
			mixer->stage_pipe[stage] = NULL;
			continue;
		}

		blend_stage = stage - MDSS_MDP_STAGE_0;
		off = MDSS_MDP_REG_LM_OFFSET(mixer->num) +
		      MDSS_MDP_REG_LM_BLEND_OFFSET(blend_stage);

		if (pipe->is_fg) {
			bgalpha = 0;
			mixercfg = MDSS_MDP_LM_BORDER_COLOR;

			blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
				    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
			/* keep fg alpha */
			blend_color_out |= 1 << (blend_stage + 1);

			pr_debug("pnum=%d stg=%d alpha=IS_FG\n", pipe->num,
					stage);
		} else if (pipe->src_fmt->alpha_enable) {
			bgalpha = 0;
			blend_op = (MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL |
				    MDSS_MDP_BLEND_BG_INV_ALPHA);
			/* keep fg alpha */
			blend_color_out |= 1 << (blend_stage + 1);

			pr_debug("pnum=%d stg=%d alpha=FG PIXEL\n", pipe->num,
					stage);
		} else if (bgalpha) {
			blend_op = (MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL |
				    MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL |
				    MDSS_MDP_BLEND_FG_INV_ALPHA);
			/* keep bg alpha */
			pr_debug("pnum=%d stg=%d alpha=BG_PIXEL\n", pipe->num,
					stage);
		} else {
			blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
				    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
			pr_debug("pnum=%d stg=%d alpha=CONST\n", pipe->num,
					stage);
		}

		mixercfg |= stage << (3 * pipe->num);

		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA,
				   pipe->alpha);
		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_BG_ALPHA,
				   0xFF - pipe->alpha);
	}

	if (mixer->cursor_enabled)
		mixercfg |= MDSS_MDP_LM_CURSOR_OUT;

	pr_debug("mixer=%d mixer_cfg=%x\n", mixer->num, mixercfg);

	ctl->flush_bits |= BIT(6) << mixer->num;	/* LAYER_MIXER */

	off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_color_out);
	mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(mixer->num), mixercfg);

	return 0;
}

struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux)
{
	struct mdss_mdp_mixer *mixer = NULL;
	if (!ctl)
		return NULL;

	switch (mux) {
	case MDSS_MDP_MIXER_MUX_DEFAULT:
	case MDSS_MDP_MIXER_MUX_LEFT:
		mixer = ctl->mixer_left;
		break;
	case MDSS_MDP_MIXER_MUX_RIGHT:
		mixer = ctl->mixer_right;
		break;
	}

	return mixer;
}

struct mdss_mdp_pipe *mdss_mdp_mixer_stage_pipe(struct mdss_mdp_ctl *ctl,
						int mux, int stage)
{
	struct mdss_mdp_pipe *pipe = NULL;
	struct mdss_mdp_mixer *mixer;
	if (!ctl)
		return NULL;

	if (mutex_lock_interruptible(&ctl->lock))
		return NULL;

	mixer = mdss_mdp_mixer_get(ctl, mux);
	if (mixer)
		pipe = mixer->stage_pipe[stage];
	mutex_unlock(&ctl->lock);

	return pipe;
}

int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed)
{
	struct mdss_mdp_ctl *ctl;
	struct mdss_mdp_mixer *mixer;

	if (!pipe)
		return -EINVAL;
	mixer = pipe->mixer;
	if (!mixer)
		return -EINVAL;
	ctl = mixer->ctl;
	if (!ctl)
		return -EINVAL;

	if (pipe->mixer_stage >= MDSS_MDP_MAX_STAGE) {
		pr_err("invalid mixer stage\n");
		return -EINVAL;
	}

	pr_debug("pnum=%x mixer=%d stage=%d\n", pipe->num, mixer->num,
			pipe->mixer_stage);

	if (mutex_lock_interruptible(&ctl->lock))
		return -EINTR;

	if (params_changed) {
		mixer->params_changed++;
		mixer->stage_pipe[pipe->mixer_stage] = pipe;
	}

	if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA)
		ctl->flush_bits |= BIT(pipe->num) << 5;
	else /* RGB/VIG pipe */
		ctl->flush_bits |= BIT(pipe->num);

	mutex_unlock(&ctl->lock);

	return 0;
}

int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe)
{
	struct mdss_mdp_ctl *ctl;
	struct mdss_mdp_mixer *mixer;

	if (!pipe)
		return -EINVAL;
	mixer = pipe->mixer;
	if (!mixer)
		return -EINVAL;
	ctl = mixer->ctl;
	if (!ctl)
		return -EINVAL;

	pr_debug("unstage pnum=%d stage=%d mixer=%d\n", pipe->num,
			pipe->mixer_stage, mixer->num);

	if (mutex_lock_interruptible(&ctl->lock))
		return -EINTR;

	mixer->params_changed++;
	mixer->stage_pipe[pipe->mixer_stage] = NULL;

	mutex_unlock(&ctl->lock);

	return 0;
}

static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer)
{
	mixer->params_changed = 0;

	/* skip mixer setup for rotator */
	if (!mixer->rotator_mode)
		mdss_mdp_mixer_setup(mixer->ctl, mixer);

	return 0;
}
Exemple #13
0
static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
                                     struct fb_cursor *cursor)
{
    struct mdss_mdp_mixer *mixer;
    struct fb_image *img = &cursor->image;
    u32 blendcfg;
    int off, ret = 0;

    if (!mfd->cursor_buf) {
        mfd->cursor_buf = dma_alloc_coherent(NULL, MDSS_MDP_CURSOR_SIZE,
                                             (dma_addr_t *) &mfd->cursor_buf_phys,
                                             GFP_KERNEL);
        if (!mfd->cursor_buf) {
            pr_err("can't allocate cursor buffer\n");
            return -ENOMEM;
        }
    }

    mixer = mdss_mdp_mixer_get(mfd->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
    off = MDSS_MDP_REG_LM_OFFSET(mixer->num);

    if ((img->width > MDSS_MDP_CURSOR_WIDTH) ||
            (img->height > MDSS_MDP_CURSOR_HEIGHT) ||
            (img->depth != 32))
        return -EINVAL;

    pr_debug("mixer=%d enable=%x set=%x\n", mixer->num, cursor->enable,
             cursor->set);

    mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
    blendcfg = MDSS_MDP_REG_READ(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG);

    if (cursor->set & FB_CUR_SETPOS)
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_START_XY,
                           (img->dy << 16) | img->dx);

    if (cursor->set & FB_CUR_SETIMAGE) {
        int calpha_en, transp_en, alpha, size;
        ret = copy_from_user(mfd->cursor_buf, img->data,
                             img->width * img->height * 4);
        if (ret)
            return ret;

        if (img->bg_color == 0xffffffff)
            transp_en = 0;
        else
            transp_en = 1;

        alpha = (img->fg_color & 0xff000000) >> 24;

        if (alpha)
            calpha_en = 0x0; /* xrgb */
        else
            calpha_en = 0x2; /* argb */

        size = (img->height << 16) | img->width;
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_IMG_SIZE, size);
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_SIZE, size);
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_STRIDE,
                           img->width * 4);
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BASE_ADDR,
                           mfd->cursor_buf_phys);

        wmb();

        blendcfg &= ~0x1;
        blendcfg |= (transp_en << 3) | (calpha_en << 1);
        MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG,
                           blendcfg);
        if (calpha_en)
            MDSS_MDP_REG_WRITE(off +
                               MDSS_MDP_REG_LM_CURSOR_BLEND_PARAM,
                               alpha);

        if (transp_en) {
            MDSS_MDP_REG_WRITE(off +
                               MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW0,
                               ((img->bg_color & 0xff00) << 8) |
                               (img->bg_color & 0xff));
            MDSS_MDP_REG_WRITE(off +
                               MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW1,
                               ((img->bg_color & 0xff0000) >> 16));
            MDSS_MDP_REG_WRITE(off +
                               MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH0,
                               ((img->bg_color & 0xff00) << 8) |
                               (img->bg_color & 0xff));
            MDSS_MDP_REG_WRITE(off +
                               MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1,
                               ((img->bg_color & 0xff0000) >> 16));
        }
    }
irqreturn_t mdss_mdp_isr(int irq, void *ptr)
{
	u32 isr, mask, hist_isr, hist_mask;


	isr = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_STATUS);

	if (isr == 0)
		goto mdp_isr_done;


	mask = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_EN);
	MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, isr);

	pr_debug("%s: isr=%x mask=%x\n", __func__, isr, mask);

	isr &= mask;
	if (isr == 0)
		goto mdp_isr_done;

	if (isr & MDSS_MDP_INTR_INTF_0_UNDERRUN)
		mdss_mdp_intr_done(MDP_INTR_UNDERRUN_INTF_0);

	if (isr & MDSS_MDP_INTR_INTF_1_UNDERRUN)
		mdss_mdp_intr_done(MDP_INTR_UNDERRUN_INTF_1);

	if (isr & MDSS_MDP_INTR_INTF_2_UNDERRUN)
		mdss_mdp_intr_done(MDP_INTR_UNDERRUN_INTF_2);

	if (isr & MDSS_MDP_INTR_INTF_3_UNDERRUN)
		mdss_mdp_intr_done(MDP_INTR_UNDERRUN_INTF_3);

	if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_0);

	if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_1);

	if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_2);

	if (isr & MDSS_MDP_INTR_PING_PONG_0_RD_PTR)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_0_RD_PTR);

	if (isr & MDSS_MDP_INTR_PING_PONG_1_RD_PTR)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_1_RD_PTR);

	if (isr & MDSS_MDP_INTR_PING_PONG_2_RD_PTR)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_2_RD_PTR);

	if (isr & MDSS_MDP_INTR_INTF_0_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);

	if (isr & MDSS_MDP_INTR_INTF_1_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1);

	if (isr & MDSS_MDP_INTR_INTF_2_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2);

	if (isr & MDSS_MDP_INTR_INTF_3_VSYNC)
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3);

	if (isr & MDSS_MDP_INTR_WB_0_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_0);

	if (isr & MDSS_MDP_INTR_WB_1_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_1);

	if (isr & MDSS_MDP_INTR_WB_2_DONE)
		mdss_mdp_intr_done(MDP_INTR_WB_2);

mdp_isr_done:
	hist_isr = MDSS_MDP_REG_READ(MDSS_MDP_REG_HIST_INTR_STATUS);
	if (hist_isr == 0)
		goto hist_isr_done;
	hist_mask = MDSS_MDP_REG_READ(MDSS_MDP_REG_HIST_INTR_EN);
	MDSS_MDP_REG_WRITE(MDSS_MDP_REG_HIST_INTR_CLEAR, hist_isr);
	hist_isr &= hist_mask;
	if (hist_isr == 0)
		goto hist_isr_done;
	mdss_mdp_hist_intr_done(hist_isr);
hist_isr_done:
	return IRQ_HANDLED;
}
static int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
				   struct mdp_csc_cfg *data)
{
	int i, ret = 0;
	u32 *off, base, val = 0;

	if (data == NULL) {
		pr_err("no csc matrix specified\n");
		return -EINVAL;
	}

	switch (block) {
	case MDSS_MDP_BLOCK_SSPP:
		if (blk_idx < MDSS_MDP_SSPP_RGB0) {
			base = MDSS_MDP_REG_SSPP_OFFSET(blk_idx);
			if (tbl_idx == 1)
				base += MDSS_MDP_REG_VIG_CSC_1_BASE;
			else
				base += MDSS_MDP_REG_VIG_CSC_0_BASE;
		} else {
			ret = -EINVAL;
		}
		break;
	case MDSS_MDP_BLOCK_WB:
		if (blk_idx < MDSS_MDP_MAX_WRITEBACK) {
			base = MDSS_MDP_REG_WB_OFFSET(blk_idx) +
			       MDSS_MDP_REG_WB_CSC_BASE;
		} else {
			ret = -EINVAL;
		}
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret != 0) {
		pr_err("unsupported block id for csc\n");
		return ret;
	}

	off = (u32 *) (base + CSC_MV_OFF);
	for (i = 0; i < 9; i++) {
		if (i & 0x1) {
			val |= data->csc_mv[i] << 16;
			MDSS_MDP_REG_WRITE(off, val);
			off++;
		} else {
			val = data->csc_mv[i];
		}
	}
	MDSS_MDP_REG_WRITE(off, val); /* COEFF_33 */

	off = (u32 *) (base + CSC_BV_OFF);
	for (i = 0; i < 3; i++) {
		MDSS_MDP_REG_WRITE(off, data->csc_pre_bv[i]);
		MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF),
				   data->csc_post_bv[i]);
		off++;
	}

	off = (u32 *) (base + CSC_LV_OFF);
	for (i = 0; i < 6; i += 2) {
		val = (data->csc_pre_lv[i] << 8) | data->csc_pre_lv[i+1];
		MDSS_MDP_REG_WRITE(off, val);

		val = (data->csc_post_lv[i] << 8) | data->csc_post_lv[i+1];
		MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF), val);
		off++;
	}

	return ret;
}