18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Marek Vasut <marex@denx.de>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This code is based on drivers/video/fbdev/mxsfb.c :
68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Juergen Beisert, Pengutronix
78c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
88c2ecf20Sopenharmony_ci * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
158c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_encoder.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
248c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h>
258c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
268c2ecf20Sopenharmony_ci#include <drm/drm_plane.h>
278c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h>
288c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "mxsfb_drv.h"
318c2ecf20Sopenharmony_ci#include "mxsfb_regs.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* 1 second delay should be plenty of time for block reset */
348c2ecf20Sopenharmony_ci#define RESET_TIMEOUT		1000000
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
378c2ecf20Sopenharmony_ci * CRTC
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return (val & mxsfb->devdata->hs_wdth_mask) <<
438c2ecf20Sopenharmony_ci		mxsfb->devdata->hs_wdth_shift;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * Setup the MXSFB registers for decoding the pixels out of the framebuffer and
488c2ecf20Sopenharmony_ci * outputting them on the bus.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistatic void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct drm_device *drm = mxsfb->drm;
538c2ecf20Sopenharmony_ci	const u32 format = mxsfb->crtc.primary->state->fb->format->format;
548c2ecf20Sopenharmony_ci	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
558c2ecf20Sopenharmony_ci	u32 ctrl, ctrl1;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (mxsfb->connector->display_info.num_bus_formats)
588c2ecf20Sopenharmony_ci		bus_format = mxsfb->connector->display_info.bus_formats[0];
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
618c2ecf20Sopenharmony_ci			     bus_format);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* CTRL1 contains IRQ config and status bits, preserve those. */
668c2ecf20Sopenharmony_ci	ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
678c2ecf20Sopenharmony_ci	ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	switch (format) {
708c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
718c2ecf20Sopenharmony_ci		dev_dbg(drm->dev, "Setting up RGB565 mode\n");
728c2ecf20Sopenharmony_ci		ctrl |= CTRL_WORD_LENGTH_16;
738c2ecf20Sopenharmony_ci		ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
748c2ecf20Sopenharmony_ci		break;
758c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
768c2ecf20Sopenharmony_ci		dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
778c2ecf20Sopenharmony_ci		ctrl |= CTRL_WORD_LENGTH_24;
788c2ecf20Sopenharmony_ci		/* Do not use packed pixels = one pixel per word instead. */
798c2ecf20Sopenharmony_ci		ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
808c2ecf20Sopenharmony_ci		break;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	switch (bus_format) {
848c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_RGB565_1X16:
858c2ecf20Sopenharmony_ci		ctrl |= CTRL_BUS_WIDTH_16;
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_RGB666_1X18:
888c2ecf20Sopenharmony_ci		ctrl |= CTRL_BUS_WIDTH_18;
898c2ecf20Sopenharmony_ci		break;
908c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_RGB888_1X24:
918c2ecf20Sopenharmony_ci		ctrl |= CTRL_BUS_WIDTH_24;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	default:
948c2ecf20Sopenharmony_ci		dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
958c2ecf20Sopenharmony_ci		break;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	writel(ctrl1, mxsfb->base + LCDC_CTRL1);
998c2ecf20Sopenharmony_ci	writel(ctrl, mxsfb->base + LCDC_CTRL);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 reg;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (mxsfb->clk_disp_axi)
1078c2ecf20Sopenharmony_ci		clk_prepare_enable(mxsfb->clk_disp_axi);
1088c2ecf20Sopenharmony_ci	clk_prepare_enable(mxsfb->clk);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Increase number of outstanding requests on all supported IPs */
1118c2ecf20Sopenharmony_ci	if (mxsfb->devdata->has_ctrl2) {
1128c2ecf20Sopenharmony_ci		reg = readl(mxsfb->base + LCDC_V4_CTRL2);
1138c2ecf20Sopenharmony_ci		reg &= ~CTRL2_SET_OUTSTANDING_REQS_MASK;
1148c2ecf20Sopenharmony_ci		reg |= CTRL2_SET_OUTSTANDING_REQS_16;
1158c2ecf20Sopenharmony_ci		writel(reg, mxsfb->base + LCDC_V4_CTRL2);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* If it was disabled, re-enable the mode again */
1198c2ecf20Sopenharmony_ci	writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Enable the SYNC signals first, then the DMA engine */
1228c2ecf20Sopenharmony_ci	reg = readl(mxsfb->base + LCDC_VDCTRL4);
1238c2ecf20Sopenharmony_ci	reg |= VDCTRL4_SYNC_SIGNALS_ON;
1248c2ecf20Sopenharmony_ci	writel(reg, mxsfb->base + LCDC_VDCTRL4);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/*
1278c2ecf20Sopenharmony_ci	 * Enable recovery on underflow.
1288c2ecf20Sopenharmony_ci	 *
1298c2ecf20Sopenharmony_ci	 * There is some sort of corner case behavior of the controller,
1308c2ecf20Sopenharmony_ci	 * which could rarely be triggered at least on i.MX6SX connected
1318c2ecf20Sopenharmony_ci	 * to 800x480 DPI panel and i.MX8MM connected to DPI->DSI->LVDS
1328c2ecf20Sopenharmony_ci	 * bridged 1920x1080 panel (and likely on other setups too), where
1338c2ecf20Sopenharmony_ci	 * the image on the panel shifts to the right and wraps around.
1348c2ecf20Sopenharmony_ci	 * This happens either when the controller is enabled on boot or
1358c2ecf20Sopenharmony_ci	 * even later during run time. The condition does not correct
1368c2ecf20Sopenharmony_ci	 * itself automatically, i.e. the display image remains shifted.
1378c2ecf20Sopenharmony_ci	 *
1388c2ecf20Sopenharmony_ci	 * It seems this problem is known and is due to sporadic underflows
1398c2ecf20Sopenharmony_ci	 * of the LCDIF FIFO. While the LCDIF IP does have underflow/overflow
1408c2ecf20Sopenharmony_ci	 * IRQs, neither of the IRQs trigger and neither IRQ status bit is
1418c2ecf20Sopenharmony_ci	 * asserted when this condition occurs.
1428c2ecf20Sopenharmony_ci	 *
1438c2ecf20Sopenharmony_ci	 * All known revisions of the LCDIF IP have CTRL1 RECOVER_ON_UNDERFLOW
1448c2ecf20Sopenharmony_ci	 * bit, which is described in the reference manual since i.MX23 as
1458c2ecf20Sopenharmony_ci	 * "
1468c2ecf20Sopenharmony_ci	 *   Set this bit to enable the LCDIF block to recover in the next
1478c2ecf20Sopenharmony_ci	 *   field/frame if there was an underflow in the current field/frame.
1488c2ecf20Sopenharmony_ci	 * "
1498c2ecf20Sopenharmony_ci	 * Enable this bit to mitigate the sporadic underflows.
1508c2ecf20Sopenharmony_ci	 */
1518c2ecf20Sopenharmony_ci	reg = readl(mxsfb->base + LCDC_CTRL1);
1528c2ecf20Sopenharmony_ci	reg |= CTRL1_RECOVER_ON_UNDERFLOW;
1538c2ecf20Sopenharmony_ci	writel(reg, mxsfb->base + LCDC_CTRL1);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	u32 reg;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/*
1638c2ecf20Sopenharmony_ci	 * Even if we disable the controller here, it will still continue
1648c2ecf20Sopenharmony_ci	 * until its FIFOs are running out of data
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
1698c2ecf20Sopenharmony_ci			   0, 1000);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	reg = readl(mxsfb->base + LCDC_VDCTRL4);
1728c2ecf20Sopenharmony_ci	reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
1738c2ecf20Sopenharmony_ci	writel(reg, mxsfb->base + LCDC_VDCTRL4);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	clk_disable_unprepare(mxsfb->clk);
1768c2ecf20Sopenharmony_ci	if (mxsfb->clk_disp_axi)
1778c2ecf20Sopenharmony_ci		clk_disable_unprepare(mxsfb->clk_disp_axi);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/*
1818c2ecf20Sopenharmony_ci * Clear the bit and poll it cleared.  This is usually called with
1828c2ecf20Sopenharmony_ci * a reset address and mask being either SFTRST(bit 31) or CLKGATE
1838c2ecf20Sopenharmony_ci * (bit 30).
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistatic int clear_poll_bit(void __iomem *addr, u32 mask)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	u32 reg;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	writel(mask, addr + REG_CLR);
1908c2ecf20Sopenharmony_ci	return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
1988c2ecf20Sopenharmony_ci	if (ret)
1998c2ecf20Sopenharmony_ci		return ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
2048c2ecf20Sopenharmony_ci	if (ret)
2058c2ecf20Sopenharmony_ci		return ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = plane->state->fb;
2138c2ecf20Sopenharmony_ci	struct drm_gem_cma_object *gem;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (!fb)
2168c2ecf20Sopenharmony_ci		return 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	gem = drm_fb_cma_get_gem_obj(fb, 0);
2198c2ecf20Sopenharmony_ci	if (!gem)
2208c2ecf20Sopenharmony_ci		return 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return gem->paddr;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct drm_device *drm = mxsfb->crtc.dev;
2288c2ecf20Sopenharmony_ci	struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
2298c2ecf20Sopenharmony_ci	u32 bus_flags = mxsfb->connector->display_info.bus_flags;
2308c2ecf20Sopenharmony_ci	u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
2318c2ecf20Sopenharmony_ci	int err;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/*
2348c2ecf20Sopenharmony_ci	 * It seems, you can't re-program the controller if it is still
2358c2ecf20Sopenharmony_ci	 * running. This may lead to shifted pictures (FIFO issue?), so
2368c2ecf20Sopenharmony_ci	 * first stop the controller and drain its FIFOs.
2378c2ecf20Sopenharmony_ci	 */
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Mandatory eLCDIF reset as per the Reference Manual */
2408c2ecf20Sopenharmony_ci	err = mxsfb_reset_block(mxsfb);
2418c2ecf20Sopenharmony_ci	if (err)
2428c2ecf20Sopenharmony_ci		return;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* Clear the FIFOs */
2458c2ecf20Sopenharmony_ci	writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
2468c2ecf20Sopenharmony_ci	readl(mxsfb->base + LCDC_CTRL1);
2478c2ecf20Sopenharmony_ci	writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_CLR);
2488c2ecf20Sopenharmony_ci	readl(mxsfb->base + LCDC_CTRL1);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (mxsfb->devdata->has_overlay)
2518c2ecf20Sopenharmony_ci		writel(0, mxsfb->base + LCDC_AS_CTRL);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	mxsfb_set_formats(mxsfb);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (mxsfb->bridge && mxsfb->bridge->timings)
2588c2ecf20Sopenharmony_ci		bus_flags = mxsfb->bridge->timings->input_bus_flags;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
2618c2ecf20Sopenharmony_ci			     m->crtc_clock,
2628c2ecf20Sopenharmony_ci			     (int)(clk_get_rate(mxsfb->clk) / 1000));
2638c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
2648c2ecf20Sopenharmony_ci			     bus_flags);
2658c2ecf20Sopenharmony_ci	DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
2688c2ecf20Sopenharmony_ci	       TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
2698c2ecf20Sopenharmony_ci	       mxsfb->base + mxsfb->devdata->transfer_count);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	vdctrl0 = VDCTRL0_ENABLE_PRESENT |	/* Always in DOTCLOCK mode */
2748c2ecf20Sopenharmony_ci		  VDCTRL0_VSYNC_PERIOD_UNIT |
2758c2ecf20Sopenharmony_ci		  VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
2768c2ecf20Sopenharmony_ci		  VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
2778c2ecf20Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_PHSYNC)
2788c2ecf20Sopenharmony_ci		vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
2798c2ecf20Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_PVSYNC)
2808c2ecf20Sopenharmony_ci		vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
2818c2ecf20Sopenharmony_ci	/* Make sure Data Enable is high active by default */
2828c2ecf20Sopenharmony_ci	if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
2838c2ecf20Sopenharmony_ci		vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
2848c2ecf20Sopenharmony_ci	/*
2858c2ecf20Sopenharmony_ci	 * DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
2868c2ecf20Sopenharmony_ci	 * controllers VDCTRL0_DOTCLK is display centric.
2878c2ecf20Sopenharmony_ci	 * Drive on positive edge       -> display samples on falling edge
2888c2ecf20Sopenharmony_ci	 * DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
2898c2ecf20Sopenharmony_ci	 */
2908c2ecf20Sopenharmony_ci	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
2918c2ecf20Sopenharmony_ci		vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Frame length in lines. */
2968c2ecf20Sopenharmony_ci	writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Line length in units of clocks or pixels. */
2998c2ecf20Sopenharmony_ci	hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
3008c2ecf20Sopenharmony_ci	writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
3018c2ecf20Sopenharmony_ci	       VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
3028c2ecf20Sopenharmony_ci	       mxsfb->base + LCDC_VDCTRL2);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
3058c2ecf20Sopenharmony_ci	       SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
3068c2ecf20Sopenharmony_ci	       mxsfb->base + LCDC_VDCTRL3);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
3098c2ecf20Sopenharmony_ci	       mxsfb->base + LCDC_VDCTRL4);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
3138c2ecf20Sopenharmony_ci				   struct drm_crtc_state *state)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	bool has_primary = state->plane_mask &
3168c2ecf20Sopenharmony_ci			   drm_plane_mask(crtc->primary);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* The primary plane has to be enabled when the CRTC is active. */
3198c2ecf20Sopenharmony_ci	if (state->active && !has_primary)
3208c2ecf20Sopenharmony_ci		return -EINVAL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* TODO: Is this needed ? */
3238c2ecf20Sopenharmony_ci	return drm_atomic_add_affected_planes(state->state, crtc);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
3278c2ecf20Sopenharmony_ci				    struct drm_crtc_state *old_state)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *event;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	event = crtc->state->event;
3328c2ecf20Sopenharmony_ci	crtc->state->event = NULL;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!event)
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
3388c2ecf20Sopenharmony_ci	if (drm_crtc_vblank_get(crtc) == 0)
3398c2ecf20Sopenharmony_ci		drm_crtc_arm_vblank_event(crtc, event);
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, event);
3428c2ecf20Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
3468c2ecf20Sopenharmony_ci				     struct drm_crtc_state *old_state)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
3498c2ecf20Sopenharmony_ci	struct drm_device *drm = mxsfb->drm;
3508c2ecf20Sopenharmony_ci	dma_addr_t paddr;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	pm_runtime_get_sync(drm->dev);
3538c2ecf20Sopenharmony_ci	mxsfb_enable_axi_clk(mxsfb);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	drm_crtc_vblank_on(crtc);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	mxsfb_crtc_mode_set_nofb(mxsfb);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Write cur_buf as well to avoid an initial corrupt frame */
3608c2ecf20Sopenharmony_ci	paddr = mxsfb_get_fb_paddr(crtc->primary);
3618c2ecf20Sopenharmony_ci	if (paddr) {
3628c2ecf20Sopenharmony_ci		writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
3638c2ecf20Sopenharmony_ci		writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	mxsfb_enable_controller(mxsfb);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
3708c2ecf20Sopenharmony_ci				      struct drm_crtc_state *old_state)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
3738c2ecf20Sopenharmony_ci	struct drm_device *drm = mxsfb->drm;
3748c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *event;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	mxsfb_disable_controller(mxsfb);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	spin_lock_irq(&drm->event_lock);
3798c2ecf20Sopenharmony_ci	event = crtc->state->event;
3808c2ecf20Sopenharmony_ci	if (event) {
3818c2ecf20Sopenharmony_ci		crtc->state->event = NULL;
3828c2ecf20Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, event);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci	spin_unlock_irq(&drm->event_lock);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	drm_crtc_vblank_off(crtc);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	mxsfb_disable_axi_clk(mxsfb);
3898c2ecf20Sopenharmony_ci	pm_runtime_put_sync(drm->dev);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* Clear and enable VBLANK IRQ */
3978c2ecf20Sopenharmony_ci	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
3988c2ecf20Sopenharmony_ci	writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* Disable and clear VBLANK IRQ */
4088c2ecf20Sopenharmony_ci	writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
4098c2ecf20Sopenharmony_ci	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
4138c2ecf20Sopenharmony_ci	.atomic_check = mxsfb_crtc_atomic_check,
4148c2ecf20Sopenharmony_ci	.atomic_flush = mxsfb_crtc_atomic_flush,
4158c2ecf20Sopenharmony_ci	.atomic_enable = mxsfb_crtc_atomic_enable,
4168c2ecf20Sopenharmony_ci	.atomic_disable = mxsfb_crtc_atomic_disable,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs mxsfb_crtc_funcs = {
4208c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
4218c2ecf20Sopenharmony_ci	.destroy = drm_crtc_cleanup,
4228c2ecf20Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
4238c2ecf20Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
4248c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
4258c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
4268c2ecf20Sopenharmony_ci	.enable_vblank = mxsfb_crtc_enable_vblank,
4278c2ecf20Sopenharmony_ci	.disable_vblank = mxsfb_crtc_disable_vblank,
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
4318c2ecf20Sopenharmony_ci * Encoder
4328c2ecf20Sopenharmony_ci */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs mxsfb_encoder_funcs = {
4358c2ecf20Sopenharmony_ci	.destroy = drm_encoder_cleanup,
4368c2ecf20Sopenharmony_ci};
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
4398c2ecf20Sopenharmony_ci * Planes
4408c2ecf20Sopenharmony_ci */
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int mxsfb_plane_atomic_check(struct drm_plane *plane,
4438c2ecf20Sopenharmony_ci				    struct drm_plane_state *plane_state)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
4468c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
4498c2ecf20Sopenharmony_ci						   &mxsfb->crtc);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
4528c2ecf20Sopenharmony_ci						   DRM_PLANE_HELPER_NO_SCALING,
4538c2ecf20Sopenharmony_ci						   DRM_PLANE_HELPER_NO_SCALING,
4548c2ecf20Sopenharmony_ci						   false, true);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
4588c2ecf20Sopenharmony_ci					      struct drm_plane_state *old_pstate)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
4618c2ecf20Sopenharmony_ci	dma_addr_t paddr;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	paddr = mxsfb_get_fb_paddr(plane);
4648c2ecf20Sopenharmony_ci	if (paddr)
4658c2ecf20Sopenharmony_ci		writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
4698c2ecf20Sopenharmony_ci					      struct drm_plane_state *old_pstate)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
4728c2ecf20Sopenharmony_ci	struct drm_plane_state *state = plane->state;
4738c2ecf20Sopenharmony_ci	dma_addr_t paddr;
4748c2ecf20Sopenharmony_ci	u32 ctrl;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	paddr = mxsfb_get_fb_paddr(plane);
4778c2ecf20Sopenharmony_ci	if (!paddr) {
4788c2ecf20Sopenharmony_ci		writel(0, mxsfb->base + LCDC_AS_CTRL);
4798c2ecf20Sopenharmony_ci		return;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * HACK: The hardware seems to output 64 bytes of data of unknown
4848c2ecf20Sopenharmony_ci	 * origin, and then to proceed with the framebuffer. Until the reason
4858c2ecf20Sopenharmony_ci	 * is understood, live with the 16 initial invalid pixels on the first
4868c2ecf20Sopenharmony_ci	 * line and start 64 bytes within the framebuffer.
4878c2ecf20Sopenharmony_ci	 */
4888c2ecf20Sopenharmony_ci	paddr += 64;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/*
4938c2ecf20Sopenharmony_ci	 * If the plane was previously disabled, write LCDC_AS_BUF as well to
4948c2ecf20Sopenharmony_ci	 * provide the first buffer.
4958c2ecf20Sopenharmony_ci	 */
4968c2ecf20Sopenharmony_ci	if (!old_pstate->fb)
4978c2ecf20Sopenharmony_ci		writel(paddr, mxsfb->base + LCDC_AS_BUF);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	switch (state->fb->format->format) {
5028c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB4444:
5038c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
5048c2ecf20Sopenharmony_ci		break;
5058c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB4444:
5068c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
5098c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
5128c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
5138c2ecf20Sopenharmony_ci		break;
5148c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
5158c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
5188c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
5198c2ecf20Sopenharmony_ci		break;
5208c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
5218c2ecf20Sopenharmony_ci		ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic bool mxsfb_format_mod_supported(struct drm_plane *plane,
5298c2ecf20Sopenharmony_ci				       uint32_t format,
5308c2ecf20Sopenharmony_ci				       uint64_t modifier)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	return modifier == DRM_FORMAT_MOD_LINEAR;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
5368c2ecf20Sopenharmony_ci	.prepare_fb = drm_gem_fb_prepare_fb,
5378c2ecf20Sopenharmony_ci	.atomic_check = mxsfb_plane_atomic_check,
5388c2ecf20Sopenharmony_ci	.atomic_update = mxsfb_plane_primary_atomic_update,
5398c2ecf20Sopenharmony_ci};
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
5428c2ecf20Sopenharmony_ci	.prepare_fb = drm_gem_fb_prepare_fb,
5438c2ecf20Sopenharmony_ci	.atomic_check = mxsfb_plane_atomic_check,
5448c2ecf20Sopenharmony_ci	.atomic_update = mxsfb_plane_overlay_atomic_update,
5458c2ecf20Sopenharmony_ci};
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs mxsfb_plane_funcs = {
5488c2ecf20Sopenharmony_ci	.format_mod_supported	= mxsfb_format_mod_supported,
5498c2ecf20Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
5508c2ecf20Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
5518c2ecf20Sopenharmony_ci	.destroy		= drm_plane_cleanup,
5528c2ecf20Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
5538c2ecf20Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
5548c2ecf20Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
5558c2ecf20Sopenharmony_ci};
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic const uint32_t mxsfb_primary_plane_formats[] = {
5588c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
5598c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
5608c2ecf20Sopenharmony_ci};
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic const uint32_t mxsfb_overlay_plane_formats[] = {
5638c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB4444,
5648c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB4444,
5658c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB1555,
5668c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB1555,
5678c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
5688c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
5698c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB8888,
5708c2ecf20Sopenharmony_ci};
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic const uint64_t mxsfb_modifiers[] = {
5738c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
5748c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
5758c2ecf20Sopenharmony_ci};
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
5788c2ecf20Sopenharmony_ci * Initialization
5798c2ecf20Sopenharmony_ci */
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ciint mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = &mxsfb->encoder;
5848c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &mxsfb->crtc;
5858c2ecf20Sopenharmony_ci	int ret;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	drm_plane_helper_add(&mxsfb->planes.primary,
5888c2ecf20Sopenharmony_ci			     &mxsfb_plane_primary_helper_funcs);
5898c2ecf20Sopenharmony_ci	ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
5908c2ecf20Sopenharmony_ci				       &mxsfb_plane_funcs,
5918c2ecf20Sopenharmony_ci				       mxsfb_primary_plane_formats,
5928c2ecf20Sopenharmony_ci				       ARRAY_SIZE(mxsfb_primary_plane_formats),
5938c2ecf20Sopenharmony_ci				       mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,
5948c2ecf20Sopenharmony_ci				       NULL);
5958c2ecf20Sopenharmony_ci	if (ret)
5968c2ecf20Sopenharmony_ci		return ret;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (mxsfb->devdata->has_overlay) {
5998c2ecf20Sopenharmony_ci		drm_plane_helper_add(&mxsfb->planes.overlay,
6008c2ecf20Sopenharmony_ci				     &mxsfb_plane_overlay_helper_funcs);
6018c2ecf20Sopenharmony_ci		ret = drm_universal_plane_init(mxsfb->drm,
6028c2ecf20Sopenharmony_ci					       &mxsfb->planes.overlay, 1,
6038c2ecf20Sopenharmony_ci					       &mxsfb_plane_funcs,
6048c2ecf20Sopenharmony_ci					       mxsfb_overlay_plane_formats,
6058c2ecf20Sopenharmony_ci					       ARRAY_SIZE(mxsfb_overlay_plane_formats),
6068c2ecf20Sopenharmony_ci					       mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,
6078c2ecf20Sopenharmony_ci					       NULL);
6088c2ecf20Sopenharmony_ci		if (ret)
6098c2ecf20Sopenharmony_ci			return ret;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
6138c2ecf20Sopenharmony_ci	ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
6148c2ecf20Sopenharmony_ci					&mxsfb->planes.primary, NULL,
6158c2ecf20Sopenharmony_ci					&mxsfb_crtc_funcs, NULL);
6168c2ecf20Sopenharmony_ci	if (ret)
6178c2ecf20Sopenharmony_ci		return ret;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	encoder->possible_crtcs = drm_crtc_mask(crtc);
6208c2ecf20Sopenharmony_ci	return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
6218c2ecf20Sopenharmony_ci				DRM_MODE_ENCODER_NONE, NULL);
6228c2ecf20Sopenharmony_ci}
623