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); }
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]); } }
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); }
/* 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); }
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; }
/* 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); }
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; }
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; }
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_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; }
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 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); }
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; }
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 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; }
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); }
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); }
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); }
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); }
void mdp4_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask) { mdp4_write(to_mdp4_kms(mdp_kms), REG_MDP4_INTR_ENABLE, irqmask); }
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; }
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; }
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); }
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; }
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); }
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); }
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; }