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