/* * mdp5_ctl_pair() - Associate 2 booked CTLs for single FLUSH */ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable) { struct mdp5_ctl_manager *ctl_mgr = ctlx->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); /* do nothing silently if hw doesn't support */ if (!ctl_mgr->single_flush_supported) return 0; if (!enable) { ctlx->pair = NULL; ctly->pair = NULL; mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, 0); return 0; } else if ((ctlx->pair != NULL) || (ctly->pair != NULL)) { DRM_DEV_ERROR(ctl_mgr->dev->dev, "CTLs already paired\n"); return -EINVAL; } else if (!(ctlx->status & ctly->status & CTL_STAT_BOOKED)) { DRM_DEV_ERROR(ctl_mgr->dev->dev, "Only pair booked CTLs\n"); return -EINVAL; } ctlx->pair = ctly; ctly->pair = ctlx; mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, MDP5_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); return 0; }
void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, uint32_t old_irqmask) { mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_CLEAR, irqmask ^ (irqmask & old_irqmask)); mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_EN, irqmask); }
void mdp5_irq_preinstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); mdp5_enable(mdp5_kms); mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, 0xffffffff); mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); mdp5_disable(mdp5_kms); }
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 void set_display_intf(struct mdp5_kms *mdp5_kms, struct mdp5_interface *intf) { unsigned long flags; u32 intf_sel; spin_lock_irqsave(&mdp5_kms->resource_lock, flags); intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL); switch (intf->num) { case 0: intf_sel &= ~MDP5_DISP_INTF_SEL_INTF0__MASK; intf_sel |= MDP5_DISP_INTF_SEL_INTF0(intf->type); break; case 1: intf_sel &= ~MDP5_DISP_INTF_SEL_INTF1__MASK; intf_sel |= MDP5_DISP_INTF_SEL_INTF1(intf->type); break; case 2: intf_sel &= ~MDP5_DISP_INTF_SEL_INTF2__MASK; intf_sel |= MDP5_DISP_INTF_SEL_INTF2(intf->type); break; case 3: intf_sel &= ~MDP5_DISP_INTF_SEL_INTF3__MASK; intf_sel |= MDP5_DISP_INTF_SEL_INTF3(intf->type); break; default: BUG(); break; } mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel); spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); }
static int pingpong_tearcheck_enable(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; int ret; ret = clk_set_rate(mdp5_kms->vsync_clk, clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE)); if (ret) { DRM_DEV_ERROR(encoder->dev->dev, "vsync_clk clk_set_rate failed, %d\n", ret); return ret; } ret = clk_prepare_enable(mdp5_kms->vsync_clk); if (ret) { DRM_DEV_ERROR(encoder->dev->dev, "vsync_clk clk_prepare_enable failed, %d\n", ret); return ret; } mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1); return 0; }
void mdp5_irq_uninstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); mdp5_enable(mdp5_kms); mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_EN(0), 0x00000000); mdp5_disable(mdp5_kms); }
static inline void ctl_write(struct mdp5_ctl *ctl, u32 reg, u32 data) { struct mdp5_kms *mdp5_kms = get_kms(ctl->ctlm); (void)ctl->reg_offset; /* TODO use this instead of mdp5_write */ mdp5_write(mdp5_kms, reg, data); }
static void pingpong_tearcheck_disable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); clk_disable_unprepare(mdp5_kms->vsync_clk); }
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); }
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; }
static void set_scanout_locked(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); enum mdp5_pipe pipe = mdp5_plane->pipe; mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), msm_framebuffer_iova(fb, mdp5_kms->id, 0)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), msm_framebuffer_iova(fb, mdp5_kms->id, 1)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), msm_framebuffer_iova(fb, mdp5_kms->id, 2)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), msm_framebuffer_iova(fb, mdp5_kms->id, 4)); plane->fb = fb; }
void mdp5_plane_set_scanout(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); enum mdp5_pipe pipe = mdp5_plane->pipe; uint32_t nplanes = drm_format_num_planes(fb->pixel_format); uint32_t iova[4]; int i; for (i = 0; i < nplanes; i++) { struct drm_gem_object *bo = msm_framebuffer_bo(fb, i); msm_gem_get_iova(bo, mdp5_kms->id, &iova[i]); } for (; i < 4; i++) iova[i] = 0; mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), iova[0]); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), iova[1]); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), iova[2]); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), iova[3]); plane->fb = fb; }
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 int mdp5_hw_init(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct device *dev = &mdp5_kms->pdev->dev; unsigned long flags; pm_runtime_get_sync(dev); /* Magic unknown register writes: * * W VBIF:0x004 00000001 (mdss_mdp.c:839) * W MDP5:0x2e0 0xe9 (mdss_mdp.c:839) * W MDP5:0x2e4 0x55 (mdss_mdp.c:839) * W MDP5:0x3ac 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3b4 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3bc 0xcccccc (mdss_mdp.c:839) * W MDP5:0x4a8 0xcccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b0 0xccccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b8 0xccccc000 (mdss_mdp.c:839) * * Downstream fbdev driver gets these register offsets/values * from DT.. not really sure what these registers are or if * different values for different boards/SoC's, etc. I guess * they are the golden registers. * * Not setting these does not seem to cause any problem. But * we may be getting lucky with the bootloader initializing * them for us. OTOH, if we can always count on the bootloader * setting the golden registers, then perhaps we don't need to * care. */ spin_lock_irqsave(&mdp5_kms->resource_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0); spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); mdp5_ctlm_hw_reset(mdp5_kms->ctlm); pm_runtime_put_sync(dev); return 0; }
static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; unsigned int id; uint32_t status, enable; enable = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_EN(0)); status = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_STATUS(0)) & enable; mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_CLEAR(0), status); VERB("status=%08x", status); mdp_dispatch_irqs(mdp_kms, status); for (id = 0; id < priv->num_crtcs; id++) if (status & mdp5_crtc_vblank(priv->crtcs[id])) drm_handle_vblank(dev, id); }
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; }
static int pingpong_tearcheck_enable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); int ret; ret = clk_set_rate(mdp5_kms->vsync_clk, clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE)); if (ret) { dev_err(encoder->dev->dev, "vsync_clk clk_set_rate failed, %d\n", ret); return ret; } ret = clk_prepare_enable(mdp5_kms->vsync_clk); if (ret) { dev_err(encoder->dev->dev, "vsync_clk clk_prepare_enable failed, %d\n", ret); return ret; } mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1); return 0; }
struct msm_kms *mdp5_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; struct mdp5_platform_config *config = mdp5_get_config(pdev); struct mdp5_kms *mdp5_kms; struct msm_kms *kms = NULL; struct msm_mmu *mmu; int ret; mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); if (!mdp5_kms) { dev_err(dev->dev, "failed to allocate kms\n"); ret = -ENOMEM; goto fail; } mdp_kms_init(&mdp5_kms->base, &kms_funcs); kms = &mdp5_kms->base.base; mdp5_kms->dev = dev; mdp5_kms->smp_blk_cnt = config->smp_blk_cnt; mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); if (IS_ERR(mdp5_kms->mmio)) { ret = PTR_ERR(mdp5_kms->mmio); goto fail; } mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); if (IS_ERR(mdp5_kms->vbif)) { ret = PTR_ERR(mdp5_kms->vbif); goto fail; } mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(mdp5_kms->vdd)) { ret = PTR_ERR(mdp5_kms->vdd); goto fail; } ret = regulator_enable(mdp5_kms->vdd); if (ret) { dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); goto fail; } ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk"); if (ret) goto fail; ret = clk_set_rate(mdp5_kms->src_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): */ mdp5_enable(mdp5_kms); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(0), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(1), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(2), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(3), 0); mdp5_disable(mdp5_kms); mdelay(16); if (config->iommu) { mmu = msm_iommu_new(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; } mdp5_kms->id = msm_register_mmu(dev, mmu); if (mdp5_kms->id < 0) { ret = mdp5_kms->id; dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret); goto fail; } ret = modeset_init(mdp5_kms); if (ret) { dev_err(dev->dev, "modeset_init failed: %d\n", ret); goto fail; } return kms; fail: if (kms) mdp5_destroy(kms); return ERR_PTR(ret); }
struct msm_kms *mdp5_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; struct mdp5_cfg *config; struct mdp5_kms *mdp5_kms; struct msm_kms *kms = NULL; struct msm_mmu *mmu; uint32_t major, minor; int i, ret; mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); if (!mdp5_kms) { dev_err(dev->dev, "failed to allocate kms\n"); ret = -ENOMEM; goto fail; } spin_lock_init(&mdp5_kms->resource_lock); mdp_kms_init(&mdp5_kms->base, &kms_funcs); kms = &mdp5_kms->base.base; mdp5_kms->dev = dev; /* mdp5_kms->mmio actually represents the MDSS base address */ mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); if (IS_ERR(mdp5_kms->mmio)) { ret = PTR_ERR(mdp5_kms->mmio); goto fail; } mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); if (IS_ERR(mdp5_kms->vbif)) { ret = PTR_ERR(mdp5_kms->vbif); goto fail; } mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(mdp5_kms->vdd)) { ret = PTR_ERR(mdp5_kms->vdd); goto fail; } ret = regulator_enable(mdp5_kms->vdd); if (ret) { dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); goto fail; } /* mandatory clocks: */ ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk", true); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk", true); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src", true); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk", true); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk", true); if (ret) goto fail; /* optional clocks: */ get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk", false); /* we need to set a default rate before enabling. Set a safe * rate first, then figure out hw revision, and then set a * more optimal rate: */ clk_set_rate(mdp5_kms->src_clk, 200000000); read_hw_revision(mdp5_kms, &major, &minor); mdp5_kms->cfg = mdp5_cfg_init(mdp5_kms, major, minor); if (IS_ERR(mdp5_kms->cfg)) { ret = PTR_ERR(mdp5_kms->cfg); mdp5_kms->cfg = NULL; goto fail; } config = mdp5_cfg_get_config(mdp5_kms->cfg); mdp5_kms->caps = config->hw->mdp.caps; /* TODO: compute core clock rate at runtime */ clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk); /* * Some chipsets have a Shared Memory Pool (SMP), while others * have dedicated latency buffering per source pipe instead; * this section initializes the SMP: */ if (mdp5_kms->caps & MDP_CAP_SMP) { mdp5_kms->smp = mdp5_smp_init(mdp5_kms->dev, &config->hw->smp); if (IS_ERR(mdp5_kms->smp)) { ret = PTR_ERR(mdp5_kms->smp); mdp5_kms->smp = NULL; goto fail; } } mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, mdp5_kms->cfg); if (IS_ERR(mdp5_kms->ctlm)) { ret = PTR_ERR(mdp5_kms->ctlm); mdp5_kms->ctlm = NULL; goto fail; } /* 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): */ mdp5_enable(mdp5_kms); for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) || !config->hw->intf.base[i]) continue; mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3); } mdp5_disable(mdp5_kms); mdelay(16); if (config->platform.iommu) { mmu = msm_iommu_new(&pdev->dev, config->platform.iommu); if (IS_ERR(mmu)) { ret = PTR_ERR(mmu); dev_err(dev->dev, "failed to init iommu: %d\n", ret); iommu_domain_free(config->platform.iommu); goto fail; } ret = mmu->funcs->attach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); if (ret) { dev_err(dev->dev, "failed to attach iommu: %d\n", ret); mmu->funcs->destroy(mmu); goto fail; } } else { dev_info(dev->dev, "no iommu, fallback to phys " "contig buffers for scanout\n"); mmu = NULL; } mdp5_kms->mmu = mmu; mdp5_kms->id = msm_register_mmu(dev, mmu); if (mdp5_kms->id < 0) { ret = mdp5_kms->id; dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret); goto fail; } ret = modeset_init(mdp5_kms); if (ret) { dev_err(dev->dev, "modeset_init failed: %d\n", ret); goto fail; } dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; dev->mode_config.max_width = config->hw->lm.max_width; dev->mode_config.max_height = config->hw->lm.max_height; dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp; dev->driver->get_scanout_position = mdp5_get_scanoutpos; dev->driver->get_vblank_counter = mdp5_get_vblank_counter; dev->max_vblank_count = 0xffffffff; dev->vblank_disable_immediate = true; return kms; fail: if (kms) mdp5_destroy(kms); return ERR_PTR(ret); }
struct msm_kms *mdp5_kms_init(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev; struct mdp5_kms *mdp5_kms; struct mdp5_cfg *config; struct msm_kms *kms; struct msm_gem_address_space *aspace; int irq, i, ret; /* priv->kms would have been populated by the MDP5 driver */ kms = priv->kms; if (!kms) return NULL; mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); mdp_kms_init(&mdp5_kms->base, &kms_funcs); pdev = mdp5_kms->pdev; irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (irq < 0) { ret = irq; dev_err(&pdev->dev, "failed to get irq: %d\n", ret); goto fail; } kms->irq = irq; config = mdp5_cfg_get_config(mdp5_kms->cfg); /* 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): */ pm_runtime_get_sync(&pdev->dev); for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) || !config->hw->intf.base[i]) continue; mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3); } mdelay(16); if (config->platform.iommu) { aspace = msm_gem_address_space_create(&pdev->dev, config->platform.iommu, "mdp5"); 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) { dev_err(&pdev->dev, "failed to attach iommu: %d\n", ret); goto fail; } } else { dev_info(&pdev->dev, "no iommu, fallback to phys contig buffers for scanout\n"); aspace = NULL; } pm_runtime_put_sync(&pdev->dev); ret = modeset_init(mdp5_kms); if (ret) { dev_err(&pdev->dev, "modeset_init failed: %d\n", ret); goto fail; } dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; dev->mode_config.max_width = 0xffff; dev->mode_config.max_height = 0xffff; dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos; dev->driver->get_scanout_position = mdp5_get_scanoutpos; dev->driver->get_vblank_counter = mdp5_get_vblank_counter; dev->max_vblank_count = 0xffffffff; dev->vblank_disable_immediate = true; return kms; fail: if (kms) mdp5_kms_destroy(kms); return ERR_PTR(ret); }
int mdp5_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 mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; const struct mdp_format *format; uint32_t nplanes, config = 0; uint32_t phasex_step = 0, phasey_step = 0; uint32_t hdecm = 0, vdecm = 0; int i, nblks; nplanes = drm_format_num_planes(fb->pixel_format); /* bad formats should already be rejected: */ if (WARN_ON(nplanes > pipe2nclients(pipe))) return -EINVAL; /* 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", mdp5_plane->name, fb->base.id, src_x, src_y, src_w, src_h, crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); /* * Calculate and request required # of smp blocks: */ nblks = request_smp_blocks(plane, fb->pixel_format, nplanes, src_w); if (nblks < 0) return nblks; /* * Currently we update the hw for allocations/requests immediately, * but once atomic modeset/pageflip is in place, the allocation * would move into atomic->check_plane_state(), while updating the * hw would remain here: */ for (i = 0; i < pipe2nclients(pipe); i++) mdp5_smp_configure(mdp5_kms, pipe2client(pipe, i)); if (src_w != crtc_w) { config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN; /* TODO calc phasex_step, hdecm */ } if (src_h != crtc_h) { config |= MDP5_PIPE_SCALE_CONFIG_SCALEY_EN; /* TODO calc phasey_step, vdecm */ } mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), MDP5_PIPE_SRC_XY_X(src_x) | MDP5_PIPE_SRC_XY_Y(src_y)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), MDP5_PIPE_OUT_XY_X(crtc_x) | MDP5_PIPE_OUT_XY_Y(crtc_y)); mdp5_plane_set_scanout(plane, fb); format = to_mdp_format(msm_framebuffer_format(fb)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | MDP5_PIPE_SRC_FORMAT_NUM_PLANES(nplanes - 1) | MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(CHROMA_RGB)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); /* not using secure mode: */ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), phasey_step); mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), MDP5_PIPE_DECIMATION_VERT(vdecm) | MDP5_PIPE_DECIMATION_HORZ(hdecm)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST)); set_fifo_thresholds(plane, nblks); /* TODO detach from old crtc (if we had more than one) */ mdp5_crtc_attach(crtc, plane); return 0; }
static int mdp5_hw_init(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct drm_device *dev = mdp5_kms->dev; uint32_t version, major, minor; int ret = 0; pm_runtime_get_sync(dev->dev); mdp5_enable(mdp5_kms); version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION); mdp5_disable(mdp5_kms); major = FIELD(version, MDP5_MDP_VERSION_MAJOR); minor = FIELD(version, MDP5_MDP_VERSION_MINOR); DBG("found MDP5 version v%d.%d", major, minor); if ((major != 1) || ((minor != 0) && (minor != 2))) { dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", major, minor); ret = -ENXIO; goto out; } mdp5_kms->rev = minor; /* Magic unknown register writes: * * W VBIF:0x004 00000001 (mdss_mdp.c:839) * W MDP5:0x2e0 0xe9 (mdss_mdp.c:839) * W MDP5:0x2e4 0x55 (mdss_mdp.c:839) * W MDP5:0x3ac 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3b4 0xc0000ccc (mdss_mdp.c:839) * W MDP5:0x3bc 0xcccccc (mdss_mdp.c:839) * W MDP5:0x4a8 0xcccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b0 0xccccc0c0 (mdss_mdp.c:839) * W MDP5:0x4b8 0xccccc000 (mdss_mdp.c:839) * * Downstream fbdev driver gets these register offsets/values * from DT.. not really sure what these registers are or if * different values for different boards/SoC's, etc. I guess * they are the golden registers. * * Not setting these does not seem to cause any problem. But * we may be getting lucky with the bootloader initializing * them for us. OTOH, if we can always count on the bootloader * setting the golden registers, then perhaps we don't need to * care. */ mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0); mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(0), 0); mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(1), 0); mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(2), 0); mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(3), 0); out: pm_runtime_put_sync(dev->dev); return ret; }
static void mdp5_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); int intf = mdp5_encoder->intf; 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; uint32_t format; 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); ctrl_pol = 0; if (mode->flags & DRM_MODE_FLAG_NHSYNC) ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; if (mode->flags & DRM_MODE_FLAG_NVSYNC) ctrl_pol |= MDP5_INTF_POLARITY_CTL_VSYNC_LOW; /* probably need to get DATA_EN polarity from panel.. */ dtv_hsync_skew = 0; /* get this from panel? */ format = 0x213f; /* 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; mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf), MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) | MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal)); mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_PERIOD_F0(intf), vsync_period); mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_LEN_F0(intf), vsync_len); mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_HCTL(intf), MDP5_INTF_DISPLAY_HCTL_START(hsync_start_x) | MDP5_INTF_DISPLAY_HCTL_END(hsync_end_x)); mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VSTART_F0(intf), display_v_start); mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VEND_F0(intf), display_v_end); mdp5_write(mdp5_kms, REG_MDP5_INTF_BORDER_COLOR(intf), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_UNDERFLOW_COLOR(intf), 0xff); mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_SKEW(intf), dtv_hsync_skew); mdp5_write(mdp5_kms, REG_MDP5_INTF_POLARITY_CTL(intf), ctrl_pol); mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_HCTL(intf), MDP5_INTF_ACTIVE_HCTL_START(0) | MDP5_INTF_ACTIVE_HCTL_END(0)); mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VSTART_F0(intf), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0); mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format); mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3); /* frame+line? */ }
struct msm_kms *mdp5_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; struct mdp5_cfg *config; struct mdp5_kms *mdp5_kms; struct msm_kms *kms = NULL; struct msm_mmu *mmu; uint32_t major, minor; int i, ret; mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); if (!mdp5_kms) { dev_err(dev->dev, "failed to allocate kms\n"); ret = -ENOMEM; goto fail; } spin_lock_init(&mdp5_kms->resource_lock); mdp_kms_init(&mdp5_kms->base, &kms_funcs); kms = &mdp5_kms->base.base; mdp5_kms->dev = dev; /* mdp5_kms->mmio actually represents the MDSS base address */ mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); if (IS_ERR(mdp5_kms->mmio)) { ret = PTR_ERR(mdp5_kms->mmio); goto fail; } mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); if (IS_ERR(mdp5_kms->vbif)) { ret = PTR_ERR(mdp5_kms->vbif); goto fail; } mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(mdp5_kms->vdd)) { ret = PTR_ERR(mdp5_kms->vdd); goto fail; } ret = regulator_enable(mdp5_kms->vdd); if (ret) { dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); goto fail; } ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk"); if (ret) goto fail; ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk"); if (ret) goto fail; /* we need to set a default rate before enabling. Set a safe * rate first, then figure out hw revision, and then set a * more optimal rate: */ clk_set_rate(mdp5_kms->src_clk, 200000000); read_hw_revision(mdp5_kms, &major, &minor); mdp5_kms->cfg = mdp5_cfg_init(mdp5_kms, major, minor); if (IS_ERR(mdp5_kms->cfg)) { ret = PTR_ERR(mdp5_kms->cfg); mdp5_kms->cfg = NULL; goto fail; } config = mdp5_cfg_get_config(mdp5_kms->cfg); /* TODO: compute core clock rate at runtime */ clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk); mdp5_kms->smp = mdp5_smp_init(mdp5_kms->dev, &config->hw->smp); if (IS_ERR(mdp5_kms->smp)) { ret = PTR_ERR(mdp5_kms->smp); mdp5_kms->smp = NULL; goto fail; } mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, config->hw); if (IS_ERR(mdp5_kms->ctlm)) { ret = PTR_ERR(mdp5_kms->ctlm); mdp5_kms->ctlm = NULL; goto fail; } /* 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): */ mdp5_enable(mdp5_kms); for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { if (!config->hw->intf.base[i] || mdp5_cfg_intf_is_virtual(config->hw->intfs[i])) continue; mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); } mdp5_disable(mdp5_kms); mdelay(16); if (config->platform.iommu) { mmu = msm_iommu_new(&pdev->dev, config->platform.iommu); if (IS_ERR(mmu)) { ret = PTR_ERR(mmu); dev_err(dev->dev, "failed to init iommu: %d\n", ret); goto fail; } ret = mmu->funcs->attach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); if (ret) { dev_err(dev->dev, "failed to attach iommu: %d\n", ret); mmu->funcs->destroy(mmu); goto fail; } } else { dev_info(dev->dev, "no iommu, fallback to phys " "contig buffers for scanout\n"); mmu = NULL; } mdp5_kms->mmu = mmu; mdp5_kms->id = msm_register_mmu(dev, mmu); if (mdp5_kms->id < 0) { ret = mdp5_kms->id; dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret); goto fail; } ret = modeset_init(mdp5_kms); if (ret) { dev_err(dev->dev, "modeset_init failed: %d\n", ret); goto fail; } return kms; fail: if (kms) mdp5_destroy(kms); return ERR_PTR(ret); }