723 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2016 Marek Vasut <marex@denx.de>4 *5 * This code is based on drivers/video/fbdev/mxsfb.c :6 * Copyright (C) 2010 Juergen Beisert, Pengutronix7 * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.8 * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.9 */10 11#include <linux/clk.h>12#include <linux/io.h>13#include <linux/iopoll.h>14#include <linux/media-bus-format.h>15#include <linux/pm_runtime.h>16#include <linux/spinlock.h>17 18#include <drm/drm_atomic.h>19#include <drm/drm_atomic_helper.h>20#include <drm/drm_bridge.h>21#include <drm/drm_crtc.h>22#include <drm/drm_encoder.h>23#include <drm/drm_fb_dma_helper.h>24#include <drm/drm_fourcc.h>25#include <drm/drm_framebuffer.h>26#include <drm/drm_gem_atomic_helper.h>27#include <drm/drm_gem_dma_helper.h>28#include <drm/drm_plane.h>29#include <drm/drm_vblank.h>30 31#include "mxsfb_drv.h"32#include "mxsfb_regs.h"33 34/* 1 second delay should be plenty of time for block reset */35#define RESET_TIMEOUT 100000036 37/* -----------------------------------------------------------------------------38 * CRTC39 */40 41static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)42{43 return (val & mxsfb->devdata->hs_wdth_mask) <<44 mxsfb->devdata->hs_wdth_shift;45}46 47/*48 * Setup the MXSFB registers for decoding the pixels out of the framebuffer and49 * outputting them on the bus.50 */51static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb,52 const u32 bus_format)53{54 struct drm_device *drm = mxsfb->drm;55 const u32 format = mxsfb->crtc.primary->state->fb->format->format;56 u32 ctrl, ctrl1;57 58 DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",59 bus_format);60 61 ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;62 63 /* CTRL1 contains IRQ config and status bits, preserve those. */64 ctrl1 = readl(mxsfb->base + LCDC_CTRL1);65 ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;66 67 switch (format) {68 case DRM_FORMAT_RGB565:69 dev_dbg(drm->dev, "Setting up RGB565 mode\n");70 ctrl |= CTRL_WORD_LENGTH_16;71 ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);72 break;73 case DRM_FORMAT_XRGB8888:74 dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");75 ctrl |= CTRL_WORD_LENGTH_24;76 /* Do not use packed pixels = one pixel per word instead. */77 ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);78 break;79 }80 81 switch (bus_format) {82 case MEDIA_BUS_FMT_RGB565_1X16:83 ctrl |= CTRL_BUS_WIDTH_16;84 break;85 case MEDIA_BUS_FMT_RGB666_1X18:86 ctrl |= CTRL_BUS_WIDTH_18;87 break;88 case MEDIA_BUS_FMT_RGB888_1X24:89 ctrl |= CTRL_BUS_WIDTH_24;90 break;91 default:92 dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format);93 break;94 }95 96 writel(ctrl1, mxsfb->base + LCDC_CTRL1);97 writel(ctrl, mxsfb->base + LCDC_CTRL);98}99 100static void mxsfb_set_mode(struct mxsfb_drm_private *mxsfb, u32 bus_flags)101{102 struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;103 u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;104 105 writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |106 TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),107 mxsfb->base + mxsfb->devdata->transfer_count);108 109 vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;110 111 vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */112 VDCTRL0_VSYNC_PERIOD_UNIT |113 VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |114 VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);115 if (m->flags & DRM_MODE_FLAG_PHSYNC)116 vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;117 if (m->flags & DRM_MODE_FLAG_PVSYNC)118 vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;119 /* Make sure Data Enable is high active by default */120 if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))121 vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;122 /*123 * DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,124 * controllers VDCTRL0_DOTCLK is display centric.125 * Drive on positive edge -> display samples on falling edge126 * DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING127 */128 if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)129 vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;130 131 writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);132 133 /* Frame length in lines. */134 writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);135 136 /* Line length in units of clocks or pixels. */137 hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;138 writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |139 VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),140 mxsfb->base + LCDC_VDCTRL2);141 142 writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |143 SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),144 mxsfb->base + LCDC_VDCTRL3);145 146 writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),147 mxsfb->base + LCDC_VDCTRL4);148 149}150 151static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)152{153 u32 reg;154 155 if (mxsfb->clk_disp_axi)156 clk_prepare_enable(mxsfb->clk_disp_axi);157 clk_prepare_enable(mxsfb->clk);158 159 /* Increase number of outstanding requests on all supported IPs */160 if (mxsfb->devdata->has_ctrl2) {161 reg = readl(mxsfb->base + LCDC_V4_CTRL2);162 reg &= ~CTRL2_SET_OUTSTANDING_REQS_MASK;163 reg |= CTRL2_SET_OUTSTANDING_REQS_16;164 writel(reg, mxsfb->base + LCDC_V4_CTRL2);165 }166 167 /* If it was disabled, re-enable the mode again */168 writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);169 170 /* Enable the SYNC signals first, then the DMA engine */171 reg = readl(mxsfb->base + LCDC_VDCTRL4);172 reg |= VDCTRL4_SYNC_SIGNALS_ON;173 writel(reg, mxsfb->base + LCDC_VDCTRL4);174 175 /*176 * Enable recovery on underflow.177 *178 * There is some sort of corner case behavior of the controller,179 * which could rarely be triggered at least on i.MX6SX connected180 * to 800x480 DPI panel and i.MX8MM connected to DPI->DSI->LVDS181 * bridged 1920x1080 panel (and likely on other setups too), where182 * the image on the panel shifts to the right and wraps around.183 * This happens either when the controller is enabled on boot or184 * even later during run time. The condition does not correct185 * itself automatically, i.e. the display image remains shifted.186 *187 * It seems this problem is known and is due to sporadic underflows188 * of the LCDIF FIFO. While the LCDIF IP does have underflow/overflow189 * IRQs, neither of the IRQs trigger and neither IRQ status bit is190 * asserted when this condition occurs.191 *192 * All known revisions of the LCDIF IP have CTRL1 RECOVER_ON_UNDERFLOW193 * bit, which is described in the reference manual since i.MX23 as194 * "195 * Set this bit to enable the LCDIF block to recover in the next196 * field/frame if there was an underflow in the current field/frame.197 * "198 * Enable this bit to mitigate the sporadic underflows.199 */200 reg = readl(mxsfb->base + LCDC_CTRL1);201 reg |= CTRL1_RECOVER_ON_UNDERFLOW;202 writel(reg, mxsfb->base + LCDC_CTRL1);203 204 writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);205}206 207static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)208{209 u32 reg;210 211 /*212 * Even if we disable the controller here, it will still continue213 * until its FIFOs are running out of data214 */215 writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);216 217 readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),218 0, 1000);219 220 reg = readl(mxsfb->base + LCDC_VDCTRL4);221 reg &= ~VDCTRL4_SYNC_SIGNALS_ON;222 writel(reg, mxsfb->base + LCDC_VDCTRL4);223 224 clk_disable_unprepare(mxsfb->clk);225 if (mxsfb->clk_disp_axi)226 clk_disable_unprepare(mxsfb->clk_disp_axi);227}228 229/*230 * Clear the bit and poll it cleared. This is usually called with231 * a reset address and mask being either SFTRST(bit 31) or CLKGATE232 * (bit 30).233 */234static int clear_poll_bit(void __iomem *addr, u32 mask)235{236 u32 reg;237 238 writel(mask, addr + REG_CLR);239 return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);240}241 242static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)243{244 int ret;245 246 /*247 * It seems, you can't re-program the controller if it is still248 * running. This may lead to shifted pictures (FIFO issue?), so249 * first stop the controller and drain its FIFOs.250 */251 252 ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);253 if (ret)254 return ret;255 256 writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);257 258 ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);259 if (ret)260 return ret;261 262 ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);263 if (ret)264 return ret;265 266 /* Clear the FIFOs */267 writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);268 readl(mxsfb->base + LCDC_CTRL1);269 writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_CLR);270 readl(mxsfb->base + LCDC_CTRL1);271 272 if (mxsfb->devdata->has_overlay)273 writel(0, mxsfb->base + LCDC_AS_CTRL);274 275 return 0;276}277 278static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb,279 struct drm_bridge_state *bridge_state,280 const u32 bus_format)281{282 struct drm_device *drm = mxsfb->crtc.dev;283 struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;284 u32 bus_flags = mxsfb->connector->display_info.bus_flags;285 int err;286 287 if (mxsfb->bridge && mxsfb->bridge->timings)288 bus_flags = mxsfb->bridge->timings->input_bus_flags;289 else if (bridge_state)290 bus_flags = bridge_state->input_bus_cfg.flags;291 292 DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",293 m->crtc_clock,294 (int)(clk_get_rate(mxsfb->clk) / 1000));295 DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",296 bus_flags);297 DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);298 299 /* Mandatory eLCDIF reset as per the Reference Manual */300 err = mxsfb_reset_block(mxsfb);301 if (err)302 return;303 304 mxsfb_set_formats(mxsfb, bus_format);305 306 clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);307 308 mxsfb_set_mode(mxsfb, bus_flags);309}310 311static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,312 struct drm_atomic_state *state)313{314 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,315 crtc);316 bool has_primary = crtc_state->plane_mask &317 drm_plane_mask(crtc->primary);318 319 /* The primary plane has to be enabled when the CRTC is active. */320 if (crtc_state->active && !has_primary)321 return -EINVAL;322 323 /* TODO: Is this needed ? */324 return drm_atomic_add_affected_planes(state, crtc);325}326 327static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,328 struct drm_atomic_state *state)329{330 struct drm_pending_vblank_event *event;331 332 event = crtc->state->event;333 crtc->state->event = NULL;334 335 if (!event)336 return;337 338 spin_lock_irq(&crtc->dev->event_lock);339 if (drm_crtc_vblank_get(crtc) == 0)340 drm_crtc_arm_vblank_event(crtc, event);341 else342 drm_crtc_send_vblank_event(crtc, event);343 spin_unlock_irq(&crtc->dev->event_lock);344}345 346static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,347 struct drm_atomic_state *state)348{349 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);350 struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,351 crtc->primary);352 struct drm_bridge_state *bridge_state = NULL;353 struct drm_device *drm = mxsfb->drm;354 u32 bus_format = 0;355 dma_addr_t dma_addr;356 357 pm_runtime_get_sync(drm->dev);358 mxsfb_enable_axi_clk(mxsfb);359 360 drm_crtc_vblank_on(crtc);361 362 /* If there is a bridge attached to the LCDIF, use its bus format */363 if (mxsfb->bridge) {364 bridge_state =365 drm_atomic_get_new_bridge_state(state,366 mxsfb->bridge);367 if (!bridge_state)368 bus_format = MEDIA_BUS_FMT_FIXED;369 else370 bus_format = bridge_state->input_bus_cfg.format;371 372 if (bus_format == MEDIA_BUS_FMT_FIXED) {373 dev_warn_once(drm->dev,374 "Bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"375 "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n");376 bus_format = MEDIA_BUS_FMT_RGB888_1X24;377 }378 }379 380 /* If there is no bridge, use bus format from connector */381 if (!bus_format && mxsfb->connector->display_info.num_bus_formats)382 bus_format = mxsfb->connector->display_info.bus_formats[0];383 384 /* If all else fails, default to RGB888_1X24 */385 if (!bus_format)386 bus_format = MEDIA_BUS_FMT_RGB888_1X24;387 388 mxsfb_crtc_mode_set_nofb(mxsfb, bridge_state, bus_format);389 390 /* Write cur_buf as well to avoid an initial corrupt frame */391 dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);392 if (dma_addr) {393 writel(dma_addr, mxsfb->base + mxsfb->devdata->cur_buf);394 writel(dma_addr, mxsfb->base + mxsfb->devdata->next_buf);395 }396 397 mxsfb_enable_controller(mxsfb);398}399 400static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,401 struct drm_atomic_state *state)402{403 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);404 struct drm_device *drm = mxsfb->drm;405 struct drm_pending_vblank_event *event;406 407 mxsfb_disable_controller(mxsfb);408 409 spin_lock_irq(&drm->event_lock);410 event = crtc->state->event;411 if (event) {412 crtc->state->event = NULL;413 drm_crtc_send_vblank_event(crtc, event);414 }415 spin_unlock_irq(&drm->event_lock);416 417 drm_crtc_vblank_off(crtc);418 419 mxsfb_disable_axi_clk(mxsfb);420 pm_runtime_put_sync(drm->dev);421}422 423static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)424{425 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);426 427 /* Clear and enable VBLANK IRQ */428 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);429 writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);430 431 return 0;432}433 434static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)435{436 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);437 438 /* Disable and clear VBLANK IRQ */439 writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);440 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);441}442 443static int mxsfb_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)444{445 struct mxsfb_drm_private *mxsfb;446 447 if (!crtc)448 return -ENODEV;449 450 mxsfb = to_mxsfb_drm_private(crtc->dev);451 452 if (source && strcmp(source, "auto") == 0)453 mxsfb->crc_active = true;454 else if (!source)455 mxsfb->crc_active = false;456 else457 return -EINVAL;458 459 return 0;460}461 462static int mxsfb_crtc_verify_crc_source(struct drm_crtc *crtc,463 const char *source, size_t *values_cnt)464{465 if (!crtc)466 return -ENODEV;467 468 if (source && strcmp(source, "auto") != 0) {469 DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",470 source, crtc->name);471 return -EINVAL;472 }473 474 *values_cnt = 1;475 return 0;476}477 478static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {479 .atomic_check = mxsfb_crtc_atomic_check,480 .atomic_flush = mxsfb_crtc_atomic_flush,481 .atomic_enable = mxsfb_crtc_atomic_enable,482 .atomic_disable = mxsfb_crtc_atomic_disable,483};484 485static const struct drm_crtc_funcs mxsfb_crtc_funcs = {486 .reset = drm_atomic_helper_crtc_reset,487 .destroy = drm_crtc_cleanup,488 .set_config = drm_atomic_helper_set_config,489 .page_flip = drm_atomic_helper_page_flip,490 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,491 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,492 .enable_vblank = mxsfb_crtc_enable_vblank,493 .disable_vblank = mxsfb_crtc_disable_vblank,494};495 496static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = {497 .reset = drm_atomic_helper_crtc_reset,498 .destroy = drm_crtc_cleanup,499 .set_config = drm_atomic_helper_set_config,500 .page_flip = drm_atomic_helper_page_flip,501 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,502 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,503 .enable_vblank = mxsfb_crtc_enable_vblank,504 .disable_vblank = mxsfb_crtc_disable_vblank,505 .set_crc_source = mxsfb_crtc_set_crc_source,506 .verify_crc_source = mxsfb_crtc_verify_crc_source,507};508 509/* -----------------------------------------------------------------------------510 * Encoder511 */512 513static const struct drm_encoder_funcs mxsfb_encoder_funcs = {514 .destroy = drm_encoder_cleanup,515};516 517/* -----------------------------------------------------------------------------518 * Planes519 */520 521static int mxsfb_plane_atomic_check(struct drm_plane *plane,522 struct drm_atomic_state *state)523{524 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,525 plane);526 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);527 struct drm_crtc_state *crtc_state;528 529 crtc_state = drm_atomic_get_new_crtc_state(state,530 &mxsfb->crtc);531 532 return drm_atomic_helper_check_plane_state(plane_state, crtc_state,533 DRM_PLANE_NO_SCALING,534 DRM_PLANE_NO_SCALING,535 false, true);536}537 538static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,539 struct drm_atomic_state *state)540{541 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);542 struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,543 plane);544 dma_addr_t dma_addr;545 546 dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);547 if (dma_addr)548 writel(dma_addr, mxsfb->base + mxsfb->devdata->next_buf);549}550 551static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,552 struct drm_atomic_state *state)553{554 struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,555 plane);556 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);557 struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,558 plane);559 dma_addr_t dma_addr;560 u32 ctrl;561 562 dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);563 if (!dma_addr) {564 writel(0, mxsfb->base + LCDC_AS_CTRL);565 return;566 }567 568 /*569 * HACK: The hardware seems to output 64 bytes of data of unknown570 * origin, and then to proceed with the framebuffer. Until the reason571 * is understood, live with the 16 initial invalid pixels on the first572 * line and start 64 bytes within the framebuffer.573 */574 dma_addr += 64;575 576 writel(dma_addr, mxsfb->base + LCDC_AS_NEXT_BUF);577 578 /*579 * If the plane was previously disabled, write LCDC_AS_BUF as well to580 * provide the first buffer.581 */582 if (!old_pstate->fb)583 writel(dma_addr, mxsfb->base + LCDC_AS_BUF);584 585 ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);586 587 switch (new_pstate->fb->format->format) {588 case DRM_FORMAT_XRGB4444:589 ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;590 break;591 case DRM_FORMAT_ARGB4444:592 ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;593 break;594 case DRM_FORMAT_XRGB1555:595 ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;596 break;597 case DRM_FORMAT_ARGB1555:598 ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;599 break;600 case DRM_FORMAT_RGB565:601 ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;602 break;603 case DRM_FORMAT_XRGB8888:604 ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;605 break;606 case DRM_FORMAT_ARGB8888:607 ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;608 break;609 }610 611 writel(ctrl, mxsfb->base + LCDC_AS_CTRL);612}613 614static void mxsfb_plane_overlay_atomic_disable(struct drm_plane *plane,615 struct drm_atomic_state *state)616{617 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);618 619 writel(0, mxsfb->base + LCDC_AS_CTRL);620}621 622static bool mxsfb_format_mod_supported(struct drm_plane *plane,623 uint32_t format,624 uint64_t modifier)625{626 return modifier == DRM_FORMAT_MOD_LINEAR;627}628 629static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {630 .atomic_check = mxsfb_plane_atomic_check,631 .atomic_update = mxsfb_plane_primary_atomic_update,632};633 634static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {635 .atomic_check = mxsfb_plane_atomic_check,636 .atomic_update = mxsfb_plane_overlay_atomic_update,637 .atomic_disable = mxsfb_plane_overlay_atomic_disable,638};639 640static const struct drm_plane_funcs mxsfb_plane_funcs = {641 .format_mod_supported = mxsfb_format_mod_supported,642 .update_plane = drm_atomic_helper_update_plane,643 .disable_plane = drm_atomic_helper_disable_plane,644 .destroy = drm_plane_cleanup,645 .reset = drm_atomic_helper_plane_reset,646 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,647 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,648};649 650static const uint32_t mxsfb_primary_plane_formats[] = {651 DRM_FORMAT_RGB565,652 DRM_FORMAT_XRGB8888,653};654 655static const uint32_t mxsfb_overlay_plane_formats[] = {656 DRM_FORMAT_XRGB4444,657 DRM_FORMAT_ARGB4444,658 DRM_FORMAT_XRGB1555,659 DRM_FORMAT_ARGB1555,660 DRM_FORMAT_RGB565,661 DRM_FORMAT_XRGB8888,662 DRM_FORMAT_ARGB8888,663};664 665static const uint64_t mxsfb_modifiers[] = {666 DRM_FORMAT_MOD_LINEAR,667 DRM_FORMAT_MOD_INVALID668};669 670/* -----------------------------------------------------------------------------671 * Initialization672 */673 674int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)675{676 struct drm_encoder *encoder = &mxsfb->encoder;677 struct drm_crtc *crtc = &mxsfb->crtc;678 int ret;679 680 drm_plane_helper_add(&mxsfb->planes.primary,681 &mxsfb_plane_primary_helper_funcs);682 ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,683 &mxsfb_plane_funcs,684 mxsfb_primary_plane_formats,685 ARRAY_SIZE(mxsfb_primary_plane_formats),686 mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,687 NULL);688 if (ret)689 return ret;690 691 if (mxsfb->devdata->has_overlay) {692 drm_plane_helper_add(&mxsfb->planes.overlay,693 &mxsfb_plane_overlay_helper_funcs);694 ret = drm_universal_plane_init(mxsfb->drm,695 &mxsfb->planes.overlay, 1,696 &mxsfb_plane_funcs,697 mxsfb_overlay_plane_formats,698 ARRAY_SIZE(mxsfb_overlay_plane_formats),699 mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,700 NULL);701 if (ret)702 return ret;703 }704 705 drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);706 if (mxsfb->devdata->has_crc32) {707 ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,708 &mxsfb->planes.primary, NULL,709 &mxsfb_crtc_with_crc_funcs,710 NULL);711 } else {712 ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,713 &mxsfb->planes.primary, NULL,714 &mxsfb_crtc_funcs, NULL);715 }716 if (ret)717 return ret;718 719 encoder->possible_crtcs = drm_crtc_mask(crtc);720 return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,721 DRM_MODE_ENCODER_NONE, NULL);722}723