static int ipu_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); int ret; if (ipu_crtc->newfb) return -EBUSY; ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); if (ret) { dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); list_del(&event->base.link); return ret; } ipu_crtc->newfb = fb; ipu_crtc->page_flip_event = event; crtc->primary->fb = fb; return 0; }
static void ipu_disable_vblank(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_crtc->page_flip_event = NULL; ipu_crtc->newfb = NULL; }
static int ipu_drm_set_base(struct drm_crtc *crtc, int x, int y) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct drm_gem_cma_object *cma_obj; struct drm_framebuffer *fb = crtc->fb; unsigned long phys; cma_obj = drm_fb_cma_get_gem_obj(fb, 0); if (!cma_obj) { DRM_LOG_KMS("entry is null.\n"); return -EFAULT; } phys = cma_obj->paddr; phys += x * (fb->bits_per_pixel >> 3); phys += y * fb->pitches[0]; dev_dbg(ipu_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys); dev_dbg(ipu_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y); ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitches[0]); ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch), 0, phys); return 0; }
static int ipu_enable_vblank(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); enable_irq(ipu_crtc->irq); return 0; }
static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 pixfmt, int hsync_pin, int vsync_pin) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_crtc->interface_pix_fmt = pixfmt; ipu_crtc->di_hsync_pin = hsync_pin; ipu_crtc->di_vsync_pin = vsync_pin; return 0; }
static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode); switch (mode) { case DRM_MODE_DPMS_ON: ipu_fb_enable(ipu_crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: ipu_fb_disable(ipu_crtc); break; } }
static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct videomode vm; int ret; drm_display_mode_to_videomode(adjusted_mode, &vm); ret = ipu_di_adjust_videomode(ipu_crtc->di, &vm); if (ret) return false; drm_display_mode_from_videomode(&vm, adjusted_mode); return true; }
static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, u32 pixfmt) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_crtc->interface_pix_fmt = pixfmt; switch (encoder_type) { case DRM_MODE_ENCODER_LVDS: ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; break; case DRM_MODE_ENCODER_NONE: ipu_crtc->di_clkflags = 0; break; } return 0; }
static void ipu_crtc_commit(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_fb_enable(ipu_crtc); }
static void ipu_crtc_prepare(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_fb_disable(ipu_crtc); }
static int ipu_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *orig_mode, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct drm_encoder *encoder; struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_di_signal_cfg sig_cfg = {}; unsigned long encoder_types = 0; u32 out_pixel_fmt; int ret; dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, mode->hdisplay); dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, mode->vdisplay); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) if (encoder->crtc == crtc) encoder_types |= BIT(encoder->encoder_type); dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", __func__, encoder_types); /* * If we have DAC, TVDAC or LDB, then we need the IPU DI clock * to be the same as the LDB DI clock. */ if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) | BIT(DRM_MODE_ENCODER_TVDAC) | BIT(DRM_MODE_ENCODER_LVDS))) sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; else sig_cfg.clkflags = 0; out_pixel_fmt = ipu_crtc->interface_pix_fmt; sig_cfg.enable_pol = 1; sig_cfg.clk_pol = 0; sig_cfg.pixel_fmt = out_pixel_fmt; sig_cfg.v_to_h_sync = 0; sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; drm_display_mode_to_videomode(mode, &sig_cfg.mode); ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, mode->flags & DRM_MODE_FLAG_INTERLACE, out_pixel_fmt, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing display controller failed with %d\n", ret); return ret; } ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); if (ret) { dev_err(ipu_crtc->dev, "initializing panel failed with %d\n", ret); return ret; } return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->primary->fb, 0, 0, mode->hdisplay, mode->vdisplay, x, y, mode->hdisplay, mode->vdisplay); }
static void ipu_disable_vblank(struct drm_crtc *crtc) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); disable_irq(ipu_crtc->irq); }
static int ipu_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *orig_mode, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct drm_framebuffer *fb = ipu_crtc->base.fb; int ret; struct ipu_di_signal_cfg sig_cfg = {}; u32 out_pixel_fmt; struct ipu_ch_param __iomem *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch); int bpp; u32 v4l2_fmt; dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, mode->hdisplay); dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, mode->vdisplay); ipu_ch_param_zero(cpmem); switch (fb->pixel_format) { case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: v4l2_fmt = V4L2_PIX_FMT_RGB32; bpp = 32; break; case DRM_FORMAT_RGB565: v4l2_fmt = V4L2_PIX_FMT_RGB565; bpp = 16; break; case DRM_FORMAT_RGB888: v4l2_fmt = V4L2_PIX_FMT_RGB24; bpp = 24; break; default: dev_err(ipu_crtc->dev, "unsupported pixel format 0x%08x\n", fb->pixel_format); return -EINVAL; } out_pixel_fmt = ipu_crtc->interface_pix_fmt; if (mode->flags & DRM_MODE_FLAG_INTERLACE) sig_cfg.interlaced = 1; if (mode->flags & DRM_MODE_FLAG_PHSYNC) sig_cfg.Hsync_pol = 1; if (mode->flags & DRM_MODE_FLAG_PVSYNC) sig_cfg.Vsync_pol = 1; sig_cfg.enable_pol = 1; sig_cfg.clk_pol = 0; sig_cfg.width = mode->hdisplay; sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; sig_cfg.h_start_width = mode->htotal - mode->hsync_end; sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; sig_cfg.pixelclock = mode->clock * 1000; sig_cfg.clkflags = ipu_crtc->di_clkflags; sig_cfg.v_to_h_sync = 0; if (ipu_crtc->dp) { ret = ipu_dp_setup_channel(ipu_crtc->dp, IPUV3_COLORSPACE_RGB, IPUV3_COLORSPACE_RGB); if (ret) { dev_err(ipu_crtc->dev, "initializing display processor failed with %d\n", ret); return ret; } ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1); } ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, out_pixel_fmt, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing display controller failed with %d\n", ret); return ret; } ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); if (ret) { dev_err(ipu_crtc->dev, "initializing panel failed with %d\n", ret); return ret; } ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay); ipu_cpmem_set_fmt(cpmem, v4l2_fmt); ipu_cpmem_set_high_priority(ipu_crtc->ipu_ch); ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing dmfc channel failed with %d\n", ret); return ret; } ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc, calc_bandwidth(mode, calc_vref(mode)), 64); if (ret) { dev_err(ipu_crtc->dev, "allocating dmfc bandwidth failed with %d\n", ret); return ret; } ipu_drm_set_base(crtc, x, y); return 0; }
static int ipu_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *orig_mode, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); int ret; struct ipu_di_signal_cfg sig_cfg = {}; u32 out_pixel_fmt; dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, mode->hdisplay); dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, mode->vdisplay); out_pixel_fmt = ipu_crtc->interface_pix_fmt; if (mode->flags & DRM_MODE_FLAG_INTERLACE) sig_cfg.interlaced = 1; if (mode->flags & DRM_MODE_FLAG_PHSYNC) sig_cfg.Hsync_pol = 1; if (mode->flags & DRM_MODE_FLAG_PVSYNC) sig_cfg.Vsync_pol = 1; sig_cfg.enable_pol = 1; sig_cfg.clk_pol = 1; sig_cfg.width = mode->hdisplay; sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; sig_cfg.h_start_width = mode->htotal - mode->hsync_end; sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start; sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay; sig_cfg.v_start_width = mode->vtotal - mode->vsync_end; sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start; sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay; sig_cfg.pixelclock = mode->clock * 1000; sig_cfg.clkflags = ipu_crtc->di_clkflags; sig_cfg.v_to_h_sync = 0; sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin; ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced, out_pixel_fmt, mode->hdisplay); if (ret) { dev_err(ipu_crtc->dev, "initializing display controller failed with %d\n", ret); return ret; } ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); if (ret) { dev_err(ipu_crtc->dev, "initializing panel failed with %d\n", ret); return ret; } return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, x, y, mode->hdisplay, mode->vdisplay); }
static int ipu_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags) { struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_flip_work *flip_work; int ret; if (ipu_crtc->flip_state != IPU_FLIP_NONE) return -EBUSY; ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); if (ret) { dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); list_del(&event->base.link); return ret; } flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL); if (!flip_work) { ret = -ENOMEM; goto put_vblank; } INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func); flip_work->page_flip_event = event; /* get BO backing the old framebuffer and take a reference */ flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base; drm_gem_object_reference(flip_work->bo); ipu_crtc->flip_work = flip_work; /* * If the object has a DMABUF attached, we need to wait on its fences * if there are any. */ if (cma_obj->base.dma_buf) { INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func); flip_work->crtc = ipu_crtc; ret = reservation_object_get_fences_rcu( cma_obj->base.dma_buf->resv, &flip_work->excl, &flip_work->shared_count, &flip_work->shared); if (unlikely(ret)) { DRM_ERROR("failed to get fences for buffer\n"); goto free_flip_work; } /* No need to queue the worker if the are no fences */ if (!flip_work->excl && !flip_work->shared_count) { ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; } else { ipu_crtc->flip_state = IPU_FLIP_PENDING; queue_work(ipu_crtc->flip_queue, &flip_work->fence_work); } } else { ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; } return 0; free_flip_work: drm_gem_object_unreference_unlocked(flip_work->bo); kfree(flip_work); ipu_crtc->flip_work = NULL; put_vblank: imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); return ret; }