162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * i.MX IPUv3 Graphics driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Sascha Hauer, Pengutronix 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/component.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <video/imx-ipu-v3.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_managed.h> 2362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2462306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "imx-drm.h" 2762306a36Sopenharmony_ci#include "ipuv3-plane.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DRIVER_DESC "i.MX IPUv3 Graphics" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct ipu_crtc { 3262306a36Sopenharmony_ci struct device *dev; 3362306a36Sopenharmony_ci struct drm_crtc base; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* plane[0] is the full plane, plane[1] is the partial plane */ 3662306a36Sopenharmony_ci struct ipu_plane *plane[2]; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci struct ipu_dc *dc; 3962306a36Sopenharmony_ci struct ipu_di *di; 4062306a36Sopenharmony_ci int irq; 4162306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic inline struct ipu_crtc *to_ipu_crtc(struct drm_crtc *crtc) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return container_of(crtc, struct ipu_crtc, base); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void ipu_crtc_atomic_enable(struct drm_crtc *crtc, 5062306a36Sopenharmony_ci struct drm_atomic_state *state) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 5362306a36Sopenharmony_ci struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ipu_prg_enable(ipu); 5662306a36Sopenharmony_ci ipu_dc_enable(ipu); 5762306a36Sopenharmony_ci ipu_dc_enable_channel(ipu_crtc->dc); 5862306a36Sopenharmony_ci ipu_di_enable(ipu_crtc->di); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void ipu_crtc_disable_planes(struct ipu_crtc *ipu_crtc, 6262306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci bool disable_partial = false; 6562306a36Sopenharmony_ci bool disable_full = false; 6662306a36Sopenharmony_ci struct drm_plane *plane; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) { 6962306a36Sopenharmony_ci if (plane == &ipu_crtc->plane[0]->base) 7062306a36Sopenharmony_ci disable_full = true; 7162306a36Sopenharmony_ci if (ipu_crtc->plane[1] && plane == &ipu_crtc->plane[1]->base) 7262306a36Sopenharmony_ci disable_partial = true; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (disable_partial) 7662306a36Sopenharmony_ci ipu_plane_disable(ipu_crtc->plane[1], true); 7762306a36Sopenharmony_ci if (disable_full) 7862306a36Sopenharmony_ci ipu_plane_disable(ipu_crtc->plane[0], true); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void ipu_crtc_atomic_disable(struct drm_crtc *crtc, 8262306a36Sopenharmony_ci struct drm_atomic_state *state) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 8562306a36Sopenharmony_ci crtc); 8662306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 8762306a36Sopenharmony_ci struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ipu_dc_disable_channel(ipu_crtc->dc); 9062306a36Sopenharmony_ci ipu_di_disable(ipu_crtc->di); 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * Planes must be disabled before DC clock is removed, as otherwise the 9362306a36Sopenharmony_ci * attached IDMACs will be left in undefined state, possibly hanging 9462306a36Sopenharmony_ci * the IPU or even system. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci ipu_crtc_disable_planes(ipu_crtc, old_crtc_state); 9762306a36Sopenharmony_ci ipu_dc_disable(ipu); 9862306a36Sopenharmony_ci ipu_prg_disable(ipu); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 10362306a36Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 10462306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 10562306a36Sopenharmony_ci crtc->state->event = NULL; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void imx_drm_crtc_reset(struct drm_crtc *crtc) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct imx_crtc_state *state; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (crtc->state) 11562306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci kfree(to_imx_crtc_state(crtc->state)); 11862306a36Sopenharmony_ci crtc->state = NULL; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 12162306a36Sopenharmony_ci if (state) 12262306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &state->base); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct imx_crtc_state *state; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!state) 13162306a36Sopenharmony_ci return NULL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci WARN_ON(state->base.crtc != crtc); 13662306a36Sopenharmony_ci state->base.crtc = crtc; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return &state->base; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void imx_drm_crtc_destroy_state(struct drm_crtc *crtc, 14262306a36Sopenharmony_ci struct drm_crtc_state *state) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(state); 14562306a36Sopenharmony_ci kfree(to_imx_crtc_state(state)); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int ipu_enable_vblank(struct drm_crtc *crtc) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci enable_irq(ipu_crtc->irq); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void ipu_disable_vblank(struct drm_crtc *crtc) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci disable_irq_nosync(ipu_crtc->irq); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const struct drm_crtc_funcs ipu_crtc_funcs = { 16562306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 16662306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 16762306a36Sopenharmony_ci .reset = imx_drm_crtc_reset, 16862306a36Sopenharmony_ci .atomic_duplicate_state = imx_drm_crtc_duplicate_state, 16962306a36Sopenharmony_ci .atomic_destroy_state = imx_drm_crtc_destroy_state, 17062306a36Sopenharmony_ci .enable_vblank = ipu_enable_vblank, 17162306a36Sopenharmony_ci .disable_vblank = ipu_disable_vblank, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic irqreturn_t ipu_irq_handler(int irq, void *dev_id) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = dev_id; 17762306a36Sopenharmony_ci struct drm_crtc *crtc = &ipu_crtc->base; 17862306a36Sopenharmony_ci unsigned long flags; 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci drm_crtc_handle_vblank(crtc); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (ipu_crtc->event) { 18462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ipu_crtc->plane); i++) { 18562306a36Sopenharmony_ci struct ipu_plane *plane = ipu_crtc->plane[i]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!plane) 18862306a36Sopenharmony_ci continue; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (ipu_plane_atomic_update_pending(&plane->base)) 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (i == ARRAY_SIZE(ipu_crtc->plane)) { 19562306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 19662306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, ipu_crtc->event); 19762306a36Sopenharmony_ci ipu_crtc->event = NULL; 19862306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 19962306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return IRQ_HANDLED; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic bool ipu_crtc_mode_fixup(struct drm_crtc *crtc, 20762306a36Sopenharmony_ci const struct drm_display_mode *mode, 20862306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 21162306a36Sopenharmony_ci struct videomode vm; 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci drm_display_mode_to_videomode(adjusted_mode, &vm); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = ipu_di_adjust_videomode(ipu_crtc->di, &vm); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci return false; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if ((vm.vsync_len == 0) || (vm.hsync_len == 0)) 22162306a36Sopenharmony_ci return false; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci drm_display_mode_from_videomode(&vm, adjusted_mode); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return true; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int ipu_crtc_atomic_check(struct drm_crtc *crtc, 22962306a36Sopenharmony_ci struct drm_atomic_state *state) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 23262306a36Sopenharmony_ci crtc); 23362306a36Sopenharmony_ci u32 primary_plane_mask = drm_plane_mask(crtc->primary); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0) 23662306a36Sopenharmony_ci return -EINVAL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void ipu_crtc_atomic_begin(struct drm_crtc *crtc, 24262306a36Sopenharmony_ci struct drm_atomic_state *state) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic void ipu_crtc_atomic_flush(struct drm_crtc *crtc, 24862306a36Sopenharmony_ci struct drm_atomic_state *state) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 25162306a36Sopenharmony_ci if (crtc->state->event) { 25262306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc)); 25562306a36Sopenharmony_ci ipu_crtc->event = crtc->state->event; 25662306a36Sopenharmony_ci crtc->state->event = NULL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 26462306a36Sopenharmony_ci struct drm_encoder *encoder; 26562306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 26662306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 26762306a36Sopenharmony_ci struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state); 26862306a36Sopenharmony_ci struct ipu_di_signal_cfg sig_cfg = {}; 26962306a36Sopenharmony_ci unsigned long encoder_types = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__, 27262306a36Sopenharmony_ci mode->hdisplay); 27362306a36Sopenharmony_ci dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__, 27462306a36Sopenharmony_ci mode->vdisplay); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 27762306a36Sopenharmony_ci if (encoder->crtc == crtc) 27862306a36Sopenharmony_ci encoder_types |= BIT(encoder->encoder_type); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", 28262306a36Sopenharmony_ci __func__, encoder_types); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * If we have DAC or LDB, then we need the IPU DI clock to be 28662306a36Sopenharmony_ci * the same as the LDB DI clock. For TVDAC, derive the IPU DI 28762306a36Sopenharmony_ci * clock from 27 MHz TVE_DI clock, but allow to divide it. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) | 29062306a36Sopenharmony_ci BIT(DRM_MODE_ENCODER_LVDS))) 29162306a36Sopenharmony_ci sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; 29262306a36Sopenharmony_ci else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC)) 29362306a36Sopenharmony_ci sig_cfg.clkflags = IPU_DI_CLKMODE_EXT; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci sig_cfg.clkflags = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci sig_cfg.enable_pol = !(imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_LOW); 29862306a36Sopenharmony_ci /* Default to driving pixel data on negative clock edges */ 29962306a36Sopenharmony_ci sig_cfg.clk_pol = !!(imx_crtc_state->bus_flags & 30062306a36Sopenharmony_ci DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE); 30162306a36Sopenharmony_ci sig_cfg.bus_format = imx_crtc_state->bus_format; 30262306a36Sopenharmony_ci sig_cfg.v_to_h_sync = 0; 30362306a36Sopenharmony_ci sig_cfg.hsync_pin = imx_crtc_state->di_hsync_pin; 30462306a36Sopenharmony_ci sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci drm_display_mode_to_videomode(mode, &sig_cfg.mode); 30762306a36Sopenharmony_ci if (!IS_ALIGNED(sig_cfg.mode.hactive, 8)) { 30862306a36Sopenharmony_ci unsigned int new_hactive = ALIGN(sig_cfg.mode.hactive, 8); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci dev_warn(ipu_crtc->dev, "8-pixel align hactive %d -> %d\n", 31162306a36Sopenharmony_ci sig_cfg.mode.hactive, new_hactive); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci sig_cfg.mode.hfront_porch -= new_hactive - sig_cfg.mode.hactive; 31462306a36Sopenharmony_ci sig_cfg.mode.hactive = new_hactive; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, 31862306a36Sopenharmony_ci mode->flags & DRM_MODE_FLAG_INTERLACE, 31962306a36Sopenharmony_ci imx_crtc_state->bus_format, sig_cfg.mode.hactive); 32062306a36Sopenharmony_ci ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs ipu_helper_funcs = { 32462306a36Sopenharmony_ci .mode_fixup = ipu_crtc_mode_fixup, 32562306a36Sopenharmony_ci .mode_set_nofb = ipu_crtc_mode_set_nofb, 32662306a36Sopenharmony_ci .atomic_check = ipu_crtc_atomic_check, 32762306a36Sopenharmony_ci .atomic_begin = ipu_crtc_atomic_begin, 32862306a36Sopenharmony_ci .atomic_flush = ipu_crtc_atomic_flush, 32962306a36Sopenharmony_ci .atomic_disable = ipu_crtc_atomic_disable, 33062306a36Sopenharmony_ci .atomic_enable = ipu_crtc_atomic_enable, 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void ipu_put_resources(struct drm_device *dev, void *ptr) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc = ptr; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(ipu_crtc->dc)) 33862306a36Sopenharmony_ci ipu_dc_put(ipu_crtc->dc); 33962306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(ipu_crtc->di)) 34062306a36Sopenharmony_ci ipu_di_put(ipu_crtc->di); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int ipu_get_resources(struct drm_device *dev, struct ipu_crtc *ipu_crtc, 34462306a36Sopenharmony_ci struct ipu_client_platformdata *pdata) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); 34762306a36Sopenharmony_ci int ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc); 35062306a36Sopenharmony_ci if (IS_ERR(ipu_crtc->dc)) 35162306a36Sopenharmony_ci return PTR_ERR(ipu_crtc->dc); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = drmm_add_action_or_reset(dev, ipu_put_resources, ipu_crtc); 35462306a36Sopenharmony_ci if (ret) 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ipu_crtc->di = ipu_di_get(ipu, pdata->di); 35862306a36Sopenharmony_ci if (IS_ERR(ipu_crtc->di)) 35962306a36Sopenharmony_ci return PTR_ERR(ipu_crtc->di); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int ipu_drm_bind(struct device *dev, struct device *master, void *data) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct ipu_client_platformdata *pdata = dev->platform_data; 36762306a36Sopenharmony_ci struct ipu_soc *ipu = dev_get_drvdata(dev->parent); 36862306a36Sopenharmony_ci struct drm_device *drm = data; 36962306a36Sopenharmony_ci struct ipu_plane *primary_plane; 37062306a36Sopenharmony_ci struct ipu_crtc *ipu_crtc; 37162306a36Sopenharmony_ci struct drm_crtc *crtc; 37262306a36Sopenharmony_ci int dp = -EINVAL; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (pdata->dp >= 0) 37662306a36Sopenharmony_ci dp = IPU_DP_FLOW_SYNC_BG; 37762306a36Sopenharmony_ci primary_plane = ipu_plane_init(drm, ipu, pdata->dma[0], dp, 0, 37862306a36Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY); 37962306a36Sopenharmony_ci if (IS_ERR(primary_plane)) 38062306a36Sopenharmony_ci return PTR_ERR(primary_plane); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ipu_crtc = drmm_crtc_alloc_with_planes(drm, struct ipu_crtc, base, 38362306a36Sopenharmony_ci &primary_plane->base, NULL, 38462306a36Sopenharmony_ci &ipu_crtc_funcs, NULL); 38562306a36Sopenharmony_ci if (IS_ERR(ipu_crtc)) 38662306a36Sopenharmony_ci return PTR_ERR(ipu_crtc); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ipu_crtc->dev = dev; 38962306a36Sopenharmony_ci ipu_crtc->plane[0] = primary_plane; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci crtc = &ipu_crtc->base; 39262306a36Sopenharmony_ci crtc->port = pdata->of_node; 39362306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &ipu_helper_funcs); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = ipu_get_resources(drm, ipu_crtc, pdata); 39662306a36Sopenharmony_ci if (ret) { 39762306a36Sopenharmony_ci dev_err(ipu_crtc->dev, "getting resources failed with %d.\n", 39862306a36Sopenharmony_ci ret); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* If this crtc is using the DP, add an overlay plane */ 40362306a36Sopenharmony_ci if (pdata->dp >= 0 && pdata->dma[1] > 0) { 40462306a36Sopenharmony_ci ipu_crtc->plane[1] = ipu_plane_init(drm, ipu, pdata->dma[1], 40562306a36Sopenharmony_ci IPU_DP_FLOW_SYNC_FG, 40662306a36Sopenharmony_ci drm_crtc_mask(&ipu_crtc->base), 40762306a36Sopenharmony_ci DRM_PLANE_TYPE_OVERLAY); 40862306a36Sopenharmony_ci if (IS_ERR(ipu_crtc->plane[1])) 40962306a36Sopenharmony_ci ipu_crtc->plane[1] = NULL; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]); 41362306a36Sopenharmony_ci ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0, 41462306a36Sopenharmony_ci "imx_drm", ipu_crtc); 41562306a36Sopenharmony_ci if (ret < 0) { 41662306a36Sopenharmony_ci dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci /* Only enable IRQ when we actually need it to trigger work. */ 42062306a36Sopenharmony_ci disable_irq(ipu_crtc->irq); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct component_ops ipu_crtc_ops = { 42662306a36Sopenharmony_ci .bind = ipu_drm_bind, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int ipu_drm_probe(struct platform_device *pdev) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!dev->platform_data) 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); 43862306a36Sopenharmony_ci if (ret) 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return component_add(dev, &ipu_crtc_ops); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int ipu_drm_remove(struct platform_device *pdev) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci component_del(&pdev->dev, &ipu_crtc_ops); 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistruct platform_driver ipu_drm_driver = { 45162306a36Sopenharmony_ci .driver = { 45262306a36Sopenharmony_ci .name = "imx-ipuv3-crtc", 45362306a36Sopenharmony_ci }, 45462306a36Sopenharmony_ci .probe = ipu_drm_probe, 45562306a36Sopenharmony_ci .remove = ipu_drm_remove, 45662306a36Sopenharmony_ci}; 457