// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org>
* Parts of this file were based on the MCDE driver by Marcus Lorentzon
* (C) ST-Ericsson SA 2013
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <video/mipi_display.h>
#include "mcde_drm.h"
#include "mcde_display_regs.h"
enum mcde_fifo {
MCDE_FIFO_A,
MCDE_FIFO_B,
/* TODO: implement FIFO C0 and FIFO C1 */
};
enum mcde_channel {
MCDE_CHANNEL_0 = 0,
MCDE_CHANNEL_1,
MCDE_CHANNEL_2,
MCDE_CHANNEL_3,
};
enum mcde_extsrc {
MCDE_EXTSRC_0 = 0,
MCDE_EXTSRC_1,
MCDE_EXTSRC_2,
MCDE_EXTSRC_3,
MCDE_EXTSRC_4,
MCDE_EXTSRC_5,
MCDE_EXTSRC_6,
MCDE_EXTSRC_7,
MCDE_EXTSRC_8,
MCDE_EXTSRC_9,
};
enum mcde_overlay {
MCDE_OVERLAY_0 = 0,
MCDE_OVERLAY_1,
MCDE_OVERLAY_2,
MCDE_OVERLAY_3,
MCDE_OVERLAY_4,
MCDE_OVERLAY_5,
};
enum mcde_dsi_formatter {
MCDE_DSI_FORMATTER_0 = 0,
MCDE_DSI_FORMATTER_1,
MCDE_DSI_FORMATTER_2,
};
void mcde_display_irq(struct mcde *mcde)
{
u32 mispp, misovl, mischnl;
bool vblank = false;
/* Handle display IRQs */
mispp = readl(mcde->regs + MCDE_MISPP);
misovl = readl(mcde->regs + MCDE_MISOVL);
mischnl = readl(mcde->regs + MCDE_MISCHNL);
/*
* Handle IRQs from the DSI link. All IRQs from the DSI links
* are just latched onto the MCDE IRQ line, so we need to traverse
* any active DSI masters and check if an IRQ is originating from
* them.
*
* TODO: Currently only one DSI link is supported.
*/
if (mcde_dsi_irq(mcde->mdsi)) {
u32 val;
/*
* In oneshot mode we do not send continuous updates
* to the display, instead we only push out updates when
* the update function is called, then we disable the
* flow on the channel once we get the TE IRQ.
*/
if (mcde->oneshot_mode) {
spin_lock(&mcde->flow_lock);
if (--mcde->flow_active == 0) {
dev_dbg(mcde->dev, "TE0 IRQ\n");
/* Disable FIFO A flow */
val = readl(mcde->regs + MCDE_CRA0);
val &= ~MCDE_CRX0_FLOEN;
writel(val, mcde->regs + MCDE_CRA0);
}
spin_unlock(&mcde->flow_lock);
}
}
/* Vblank from one of the channels */
if (mispp & MCDE_PP_VCMPA) {
dev_dbg(mcde->dev, "chnl A vblank IRQ\n");
vblank = true;
}
if (mispp & MCDE_PP_VCMPB) {
dev_dbg(mcde->dev, "chnl B vblank IRQ\n");
vblank = true;
}
if (mispp & MCDE_PP_VCMPC0)
dev_dbg(mcde->dev, "chnl C0 vblank IRQ\n");
if (mispp & MCDE_PP_VCMPC1)
dev_dbg(mcde->dev, "chnl C1 vblank IRQ\n");
if (mispp & MCDE_PP_VSCC0)
dev_dbg(mcde->dev, "chnl C0 TE IRQ\n");
if (mispp & MCDE_PP_VSCC1)
dev_dbg(mcde->dev, "chnl C1 TE IRQ\n");
writel(mispp, mcde->regs + MCDE_RISPP);
if (vblank)
drm_crtc_handle_vblank(&mcde->pipe.crtc);
if (misovl)
dev_info(mcde->dev, "some stray overlay IRQ %08x\n", misovl);
writel(misovl, mcde->regs + MCDE_RISOVL);
if (mischnl)
dev_info(mcde->dev, "some stray channel error IRQ %08x\n",
mischnl);
writel(mischnl, mcde->regs + MCDE_RISCHNL);
}
void mcde_display_disable_irqs(struct mcde *mcde)
{
/* Disable all IRQs */
writel(0, mcde->regs + MCDE_IMSCPP);
writel(0, mcde->regs + MCDE_IMSCOVL);
writel(0, mcde->regs + MCDE_IMSCCHNL);
/* Clear any pending IRQs */
writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP);
writel(0xFFFFFFFF, mcde->regs + MCDE_RISOVL);
writel(0xFFFFFFFF<