static int mdp5_plane_prepare_fb(struct drm_plane *plane, struct drm_framebuffer *fb) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id); return msm_framebuffer_prepare(fb, mdp5_kms->id); }
static void pingpong_tearcheck_disable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); int pp_id = mixer->pp; mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); clk_disable_unprepare(mdp5_kms->vsync_clk); }
static int mdp4_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); struct drm_device *dev = crtc->dev; struct drm_gem_object *cursor_bo, *old_bo; unsigned long flags; uint32_t iova; int ret; if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height); return -EINVAL; } if (handle) { cursor_bo = drm_gem_object_lookup(dev, file_priv, handle); if (!cursor_bo) return -ENOENT; } else { cursor_bo = NULL; } if (cursor_bo) { ret = msm_gem_get_iova(cursor_bo, mdp4_kms->id, &iova); if (ret) goto fail; } else { iova = 0; } spin_lock_irqsave(&mdp4_crtc->cursor.lock, flags); old_bo = mdp4_crtc->cursor.next_bo; mdp4_crtc->cursor.next_bo = cursor_bo; mdp4_crtc->cursor.next_iova = iova; mdp4_crtc->cursor.width = width; mdp4_crtc->cursor.height = height; mdp4_crtc->cursor.stale = true; spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags); if (old_bo) { /* drop our previous reference: */ msm_gem_put_iova(old_bo, mdp4_kms->id); drm_gem_object_unreference_unlocked(old_bo); } request_pending(crtc, PENDING_CURSOR); return 0; fail: drm_gem_object_unreference_unlocked(cursor_bo); return ret; }
void mdp5_plane_complete_flip(struct drm_plane *plane) { struct mdp5_kms *mdp5_kms = get_kms(plane); struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; DBG("%s: complete flip", mdp5_plane->name); mdp5_smp_commit(mdp5_kms->smp, pipe); to_mdp5_plane_state(plane->state)->pending = false; }
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); }
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; }
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline) { struct mdp5_kms *mdp5_kms = get_kms(ctl->ctlm); struct mdp5_interface *intf = pipeline->intf; /* Virtual interfaces need not set a display intf (e.g.: Writeback) */ if (!mdp5_cfg_intf_is_virtual(intf->type)) set_display_intf(mdp5_kms, intf); set_ctl_op(ctl, pipeline); return 0; }
static int mdp4_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); struct drm_framebuffer *fb = new_state->fb; if (!fb) return 0; DBG("%s: prepare: FB[%u]", mdp4_plane->name, fb->base.id); return msm_framebuffer_prepare(fb, mdp4_kms->id); }
static void mdp4_plane_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); struct drm_framebuffer *fb = old_state->fb; if (!fb) return; DBG("%s: cleanup: FB[%u]", mdp4_plane->name, fb->base.id); msm_framebuffer_cleanup(fb, mdp4_kms->id); }
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; }
static void set_fifo_thresholds(struct drm_plane *plane, int nblks) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; uint32_t val; /* 1/4 of SMP pool that is being fetched */ val = (nblks * SMP_ENTRIES_PER_BLK) / 4; mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1); mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2); mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3); }
static int mdp5_plane_disable(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; DBG("%s: disable", mdp5_plane->name); if (mdp5_kms) { /* Release the memory we requested earlier from the SMP: */ mdp5_smp_release(mdp5_kms->smp, pipe); } return 0; }
static int pingpong_tearcheck_setup(struct drm_encoder *encoder, struct drm_display_mode *mode) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct device *dev = encoder->dev->dev; u32 total_lines_x100, vclks_line, cfg; long vsync_clk_speed; struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); int pp_id = mixer->pp; if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) { DRM_DEV_ERROR(dev, "vsync_clk is not initialized\n"); return -EINVAL; } total_lines_x100 = mode->vtotal * drm_mode_vrefresh(mode); if (!total_lines_x100) { DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n", __func__, mode->vtotal, drm_mode_vrefresh(mode)); return -EINVAL; } vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE); if (vsync_clk_speed <= 0) { DRM_DEV_ERROR(dev, "vsync_clk round rate failed %ld\n", vsync_clk_speed); return -EINVAL; } vclks_line = vsync_clk_speed * 100 / total_lines_x100; cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN; cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0); mdp5_write(mdp5_kms, REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay); mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1); mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay); mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id), MDP5_PP_SYNC_THRESH_START(4) | MDP5_PP_SYNC_THRESH_CONTINUE(4)); return 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); }
int mdp5_ctl_set_intf(struct mdp5_ctl *ctl, struct mdp5_interface *intf) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); memcpy(&ctl->pipeline.intf, intf, sizeof(*intf)); ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(ctl->lm) | mdp_ctl_flush_mask_encoder(intf); /* Virtual interfaces need not set a display intf (e.g.: Writeback) */ if (!mdp5_cfg_intf_is_virtual(intf->type)) set_display_intf(mdp5_kms, intf); set_ctl_op(ctl, intf); return 0; }
static void mdp4_crtc_dpms(struct drm_crtc *crtc, int mode) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); bool enabled = (mode == DRM_MODE_DPMS_ON); DBG("%s: mode=%d", mdp4_crtc->name, mode); if (enabled != mdp4_crtc->enabled) { if (enabled) { mdp4_enable(mdp4_kms); mdp_irq_register(&mdp4_kms->base, &mdp4_crtc->err); } else { mdp_irq_unregister(&mdp4_kms->base, &mdp4_crtc->err); mdp4_disable(mdp4_kms); } mdp4_crtc->enabled = enabled; } }
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); }
static void update_fb(struct drm_crtc *crtc, bool async, struct drm_framebuffer *new_fb) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_framebuffer *old_fb = mdp4_crtc->fb; if (old_fb) drm_flip_work_queue(&mdp4_crtc->unref_fb_work, old_fb); /* grab reference to incoming scanout fb: */ drm_framebuffer_reference(new_fb); mdp4_crtc->base.fb = new_fb; mdp4_crtc->fb = new_fb; if (!async) { /* enable vblank to pick up the old_fb */ mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank); } }
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; }
/* 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); }
int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, struct drm_encoder *slave_encoder) { struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms; struct device *dev; int intf_num; u32 data = 0; if (!encoder || !slave_encoder) return -EINVAL; mdp5_kms = get_kms(encoder); intf_num = mdp5_cmd_enc->intf->num; /* Switch slave encoder's trigger MUX, to use the master's * start signal for the slave encoder */ if (intf_num == 1) data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX; else if (intf_num == 2) data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX; else return -EINVAL; /* Smart Panel, Sync mode */ data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL; dev = &mdp5_kms->pdev->dev; /* Make sure clocks are on when connectors calling this function. */ pm_runtime_get_sync(dev); mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data); mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, MDP5_SPLIT_DPL_LOWER_SMART_PANEL); mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); pm_runtime_put_sync(dev); return 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; }
static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) { struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, vblank); struct drm_crtc *crtc = &mdp4_crtc->base; struct msm_drm_private *priv = crtc->dev->dev_private; unsigned pending; mdp_irq_unregister(&get_kms(crtc)->base, &mdp4_crtc->vblank); pending = atomic_xchg(&mdp4_crtc->pending, 0); if (pending & PENDING_FLIP) { complete_flip(crtc, NULL); drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq); } if (pending & PENDING_CURSOR) { update_cursor(crtc); drm_flip_work_commit(&mdp4_crtc->unref_cursor_work, priv->wq); } }
/* NOTE: looks like if horizontal decimation is used (if we supported that) * then the width used to calculate SMP block requirements is the post- * decimated width. Ie. SMP buffering sits downstream of decimation (which * presumably happens during the dma from scanout buffer). */ static int request_smp_blocks(struct drm_plane *plane, uint32_t format, uint32_t nplanes, uint32_t width) { struct drm_device *dev = plane->dev; struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; int i, hsub, nlines, nblks, ret; hsub = drm_format_horz_chroma_subsampling(format); /* different if BWC (compressed framebuffer?) enabled: */ nlines = 2; for (i = 0, nblks = 0; i < nplanes; i++) { int n, fetch_stride, cpp; cpp = drm_format_plane_cpp(format, i); fetch_stride = width * cpp / (i ? hsub : 1); n = DIV_ROUND_UP(fetch_stride * nlines, SMP_BLK_SIZE); /* for hw rev v1.00 */ if (mdp5_kms->rev == 0) n = roundup_pow_of_two(n); DBG("%s[%d]: request %d SMP blocks", mdp5_plane->name, i, n); ret = mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), n); if (ret) { dev_err(dev->dev, "Could not allocate %d SMP blocks: %d\n", n, ret); return ret; } nblks += n; } /* in success case, return total # of blocks allocated: */ return nblks; }
static void mdp4_dsi_encoder_disable(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_write(mdp4_kms, REG_MDP4_DSI_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_PRIMARY_VSYNC); mdp4_dsi_encoder->enabled = false; }
static void mdp5_encoder_dpms(struct drm_encoder *encoder, int mode) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); int intf = mdp5_encoder->intf; bool enabled = (mode == DRM_MODE_DPMS_ON); DBG("mode=%d", mode); if (enabled == mdp5_encoder->enabled) return; if (enabled) { bs_set(mdp5_encoder, 1); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1); } else { mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 0); bs_set(mdp5_encoder, 0); } mdp5_encoder->enabled = enabled; }
static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) { struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc); struct mdp5_interface *intf = &mdp5_cmd_enc->intf; int lm = mdp5_crtc_get_lm(encoder->crtc); if (WARN_ON(!mdp5_cmd_enc->enabled)) return; /* Wait for the last frame done */ mdp_irq_wait(&mdp5_kms->base, lm2ppdone(lm)); pingpong_tearcheck_disable(encoder); mdp5_ctl_set_encoder_state(ctl, false); mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); bs_set(mdp5_cmd_enc, 0); mdp5_cmd_enc->enabled = false; }
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; }
static int mdp5_plane_disable(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; int i; DBG("%s: disable", mdp5_plane->name); /* update our SMP request to zero (release all our blks): */ for (i = 0; i < pipe2nclients(pipe); i++) mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), 0); /* TODO detaching now will cause us not to get the last * vblank and mdp5_smp_commit().. so other planes will * still see smp blocks previously allocated to us as * in-use.. */ if (plane->crtc) mdp5_crtc_detach(plane->crtc, plane); return 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; }