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; }
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; }
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; }
/* 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); } }
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; }
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; }
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; }
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; }