Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_MEDIATEK
tristate "DRM Support for Mediatek SoCs"
depends on DRM
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on COMMON_CLK
depends on HAVE_ARM_SMCCC || COMPILE_TEST
depends on OF
depends on MTK_MMSYS
select DRM_CLIENT_SELECTION
select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
select DRM_DISPLAY_HELPER
select DRM_BRIDGE_CONNECTOR
select DRM_MIPI_DSI
select DRM_PANEL
select VIDEOMODE_HELPERS
help
Choose this option if you have a Mediatek SoCs.
The module will be called mediatek-drm
This driver provides kernel mode setting and
buffer management to userspace.
config DRM_MEDIATEK_DP
tristate "DRM DPTX Support for MediaTek SoCs"
depends on DRM_MEDIATEK
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DP_AUX_BUS
help
DRM/KMS Display Port driver for MediaTek SoCs.
config DRM_MEDIATEK_HDMI_COMMON
tristate
depends on DRM_MEDIATEK
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HELPER
select SND_SOC_HDMI_CODEC if SND_SOC
help
MediaTek SoC HDMI common library
config DRM_MEDIATEK_HDMI
tristate "DRM HDMI Support for Mediatek SoCs"
depends on DRM_MEDIATEK
select DRM_MEDIATEK_HDMI_COMMON
help
DRM/KMS HDMI driver for Mediatek SoCs
config DRM_MEDIATEK_HDMI_V2
tristate "DRM HDMI v2 IP support for MediaTek SoCs"
depends on DRM_MEDIATEK
select DRM_MEDIATEK_HDMI_COMMON
help
Say yes here to enable support for the HDMIv2 IP and related
DDCv2 as found in the MediaTek MT8195, MT8188 SoCs and other
variants.
This driver can also be built as a module. If so, the HDMIv2
module will be called "mtk_hdmi_v2", and the DDCv2 module
will be called "mtk_hdmi_ddc_v2".
@@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0
mediatek-drm-y := mtk_crtc.o \
mtk_ddp_comp.o \
mtk_disp_aal.o \
mtk_disp_ccorr.o \
mtk_disp_color.o \
mtk_disp_gamma.o \
mtk_disp_merge.o \
mtk_disp_ovl.o \
mtk_disp_ovl_adaptor.o \
mtk_disp_rdma.o \
mtk_drm_drv.o \
mtk_dsi.o \
mtk_dpi.o \
mtk_ethdr.o \
mtk_mdp_rdma.o \
mtk_padding.o \
mtk_plane.o
obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI_COMMON) += mtk_hdmi_common.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_cec.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi_ddc.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_v2.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_ddc_v2.o
obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Jie Qiu <jie.qiu@mediatek.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "mtk_cec.h"
#include "mtk_drm_drv.h"
#define TR_CONFIG 0x00
#define CLEAR_CEC_IRQ BIT(15)
#define CEC_CKGEN 0x04
#define CEC_32K_PDN BIT(19)
#define PDN BIT(16)
#define RX_EVENT 0x54
#define HDMI_PORD BIT(25)
#define HDMI_HTPLG BIT(24)
#define HDMI_PORD_INT_EN BIT(9)
#define HDMI_HTPLG_INT_EN BIT(8)
#define RX_GEN_WD 0x58
#define HDMI_PORD_INT_32K_STATUS BIT(26)
#define RX_RISC_INT_32K_STATUS BIT(25)
#define HDMI_HTPLG_INT_32K_STATUS BIT(24)
#define HDMI_PORD_INT_32K_CLR BIT(18)
#define RX_INT_32K_CLR BIT(17)
#define HDMI_HTPLG_INT_32K_CLR BIT(16)
#define HDMI_PORD_INT_32K_STA_MASK BIT(10)
#define RX_RISC_INT_32K_STA_MASK BIT(9)
#define HDMI_HTPLG_INT_32K_STA_MASK BIT(8)
#define HDMI_PORD_INT_32K_EN BIT(2)
#define RX_INT_32K_EN BIT(1)
#define HDMI_HTPLG_INT_32K_EN BIT(0)
#define NORMAL_INT_CTRL 0x5C
#define HDMI_HTPLG_INT_STA BIT(0)
#define HDMI_PORD_INT_STA BIT(1)
#define HDMI_HTPLG_INT_CLR BIT(16)
#define HDMI_PORD_INT_CLR BIT(17)
#define HDMI_FULL_INT_CLR BIT(20)
struct mtk_cec {
void __iomem *regs;
struct clk *clk;
int irq;
bool hpd;
void (*hpd_event)(bool hpd, struct device *dev);
struct device *hdmi_dev;
spinlock_t lock;
};
static void mtk_cec_clear_bits(struct mtk_cec *cec, unsigned int offset,
unsigned int bits)
{
void __iomem *reg = cec->regs + offset;
u32 tmp;
tmp = readl(reg);
tmp &= ~bits;
writel(tmp, reg);
}
static void mtk_cec_set_bits(struct mtk_cec *cec, unsigned int offset,
unsigned int bits)
{
void __iomem *reg = cec->regs + offset;
u32 tmp;
tmp = readl(reg);
tmp |= bits;
writel(tmp, reg);
}
static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset,
unsigned int val, unsigned int mask)
{
u32 tmp = readl(cec->regs + offset) & ~mask;
tmp |= val & mask;
writel(tmp, cec->regs + offset);
}
void mtk_cec_set_hpd_event(struct device *dev,
void (*hpd_event)(bool hpd, struct device *dev),
struct device *hdmi_dev)
{
struct mtk_cec *cec = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&cec->lock, flags);
cec->hdmi_dev = hdmi_dev;
cec->hpd_event = hpd_event;
spin_unlock_irqrestore(&cec->lock, flags);
}
EXPORT_SYMBOL_NS_GPL(mtk_cec_set_hpd_event, "DRM_MTK_HDMI_V1");
bool mtk_cec_hpd_high(struct device *dev)
{
struct mtk_cec *cec = dev_get_drvdata(dev);
unsigned int status;
status = readl(cec->regs + RX_EVENT);
return (status & (HDMI_PORD | HDMI_HTPLG)) == (HDMI_PORD | HDMI_HTPLG);
}
EXPORT_SYMBOL_NS_GPL(mtk_cec_hpd_high, "DRM_MTK_HDMI_V1");
static void mtk_cec_htplg_irq_init(struct mtk_cec *cec)
{
mtk_cec_mask(cec, CEC_CKGEN, 0 | CEC_32K_PDN, PDN | CEC_32K_PDN);
mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR | RX_INT_32K_CLR |
HDMI_HTPLG_INT_32K_CLR | HDMI_PORD_INT_32K_EN |
RX_INT_32K_EN | HDMI_HTPLG_INT_32K_EN);
}
static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec)
{
mtk_cec_set_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
}
static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec)
{
mtk_cec_clear_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN);
}
static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
{
mtk_cec_set_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
mtk_cec_set_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
usleep_range(5, 10);
mtk_cec_clear_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR |
HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR);
mtk_cec_clear_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ);
mtk_cec_clear_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR |
RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR);
}
static void mtk_cec_hpd_event(struct mtk_cec *cec, bool hpd)
{
void (*hpd_event)(bool hpd, struct device *dev);
struct device *hdmi_dev;
unsigned long flags;
spin_lock_irqsave(&cec->lock, flags);
hpd_event = cec->hpd_event;
hdmi_dev = cec->hdmi_dev;
spin_unlock_irqrestore(&cec->lock, flags);
if (hpd_event)
hpd_event(hpd, hdmi_dev);
}
static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
{
struct device *dev = arg;
struct mtk_cec *cec = dev_get_drvdata(dev);
bool hpd;
mtk_cec_clear_htplg_irq(cec);
hpd = mtk_cec_hpd_high(dev);
if (cec->hpd != hpd) {
dev_dbg(dev, "hotplug event! cur hpd = %d, hpd = %d\n",
cec->hpd, hpd);
cec->hpd = hpd;
mtk_cec_hpd_event(cec, hpd);
}
return IRQ_HANDLED;
}
static int mtk_cec_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_cec *cec;
int ret;
cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
if (!cec)
return -ENOMEM;
platform_set_drvdata(pdev, cec);
spin_lock_init(&cec->lock);
cec->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cec->regs))
return dev_err_probe(dev, PTR_ERR(cec->regs),
"Failed to ioremap cec\n");
cec->clk = devm_clk_get(dev, NULL);
if (IS_ERR(cec->clk))
return dev_err_probe(dev, PTR_ERR(cec->clk),
"Failed to get cec clock\n");
cec->irq = platform_get_irq(pdev, 0);
if (cec->irq < 0)
return cec->irq;
ret = devm_request_threaded_irq(dev, cec->irq, NULL,
mtk_cec_htplg_isr_thread,
IRQF_SHARED | IRQF_TRIGGER_LOW |
IRQF_ONESHOT, "hdmi hpd", dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to register cec irq\n");
ret = clk_prepare_enable(cec->clk);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable cec clock\n");
mtk_cec_htplg_irq_init(cec);
mtk_cec_htplg_irq_enable(cec);
return 0;
}
static void mtk_cec_remove(struct platform_device *pdev)
{
struct mtk_cec *cec = platform_get_drvdata(pdev);
mtk_cec_htplg_irq_disable(cec);
clk_disable_unprepare(cec->clk);
}
static const struct of_device_id mtk_cec_of_ids[] = {
{ .compatible = "mediatek,mt8173-cec", },
{}
};
MODULE_DEVICE_TABLE(of, mtk_cec_of_ids);
static struct platform_driver mtk_cec_driver = {
.probe = mtk_cec_probe,
.remove = mtk_cec_remove,
.driver = {
.name = "mediatek-cec",
.of_match_table = mtk_cec_of_ids,
},
};
module_platform_driver(mtk_cec_driver);
MODULE_DESCRIPTION("MediaTek HDMI CEC Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Jie Qiu <jie.qiu@mediatek.com>
*/
#ifndef _MTK_CEC_H
#define _MTK_CEC_H
#include <linux/types.h>
struct device;
void mtk_cec_set_hpd_event(struct device *dev,
void (*hotplug_event)(bool hpd, struct device *dev),
struct device *hdmi_dev);
bool mtk_cec_hpd_high(struct device *dev);
#endif /* _MTK_CEC_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015 MediaTek Inc.
*/
#ifndef MTK_CRTC_H
#define MTK_CRTC_H
#include <drm/drm_crtc.h>
#include "mtk_ddp_comp.h"
#include "mtk_drm_drv.h"
#include "mtk_plane.h"
#define MTK_MAX_BPC 10
#define MTK_MIN_BPC 3
void mtk_crtc_commit(struct drm_crtc *crtc);
int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
unsigned int path_len, int priv_data_index,
const struct mtk_drm_route *conn_routes,
unsigned int num_conn_routes);
int mtk_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
struct mtk_plane_state *state);
void mtk_crtc_plane_disable(struct drm_crtc *crtc, struct drm_plane *plane);
void mtk_crtc_async_update(struct drm_crtc *crtc, struct drm_plane *plane,
struct drm_atomic_commit *plane_state);
struct device *mtk_crtc_dma_dev_get(struct drm_crtc *crtc);
#endif /* MTK_CRTC_H */
@@ -0,0 +1,710 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 MediaTek Inc.
* Authors:
* YT Shen <yt.shen@mediatek.com>
* CK Hu <ck.hu@mediatek.com>
*/
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include <drm/drm_print.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#include "mtk_plane.h"
#define DISP_REG_DITHER_EN 0x0000
#define DITHER_EN BIT(0)
#define DISP_REG_DITHER_CFG 0x0020
#define DITHER_RELAY_MODE BIT(0)
#define DITHER_ENGINE_EN BIT(1)
#define DISP_DITHERING BIT(2)
#define DISP_REG_DITHER_SIZE 0x0030
#define DISP_REG_DITHER_5 0x0114
#define DISP_REG_DITHER_7 0x011c
#define DISP_REG_DITHER_15 0x013c
#define DITHER_LSB_ERR_SHIFT_R(x) (((x) & 0x7) << 28)
#define DITHER_ADD_LSHIFT_R(x) (((x) & 0x7) << 20)
#define DITHER_NEW_BIT_MODE BIT(0)
#define DISP_REG_DITHER_16 0x0140
#define DITHER_LSB_ERR_SHIFT_B(x) (((x) & 0x7) << 28)
#define DITHER_ADD_LSHIFT_B(x) (((x) & 0x7) << 20)
#define DITHER_LSB_ERR_SHIFT_G(x) (((x) & 0x7) << 12)
#define DITHER_ADD_LSHIFT_G(x) (((x) & 0x7) << 4)
#define DISP_REG_DSC_CON 0x0000
#define DSC_EN BIT(0)
#define DSC_DUAL_INOUT BIT(2)
#define DSC_BYPASS BIT(4)
#define DSC_UFOE_SEL BIT(16)
#define DISP_REG_OD_EN 0x0000
#define DISP_REG_OD_CFG 0x0020
#define OD_RELAYMODE BIT(0)
#define DISP_REG_OD_SIZE 0x0030
#define DISP_REG_POSTMASK_EN 0x0000
#define POSTMASK_EN BIT(0)
#define DISP_REG_POSTMASK_CFG 0x0020
#define POSTMASK_RELAY_MODE BIT(0)
#define DISP_REG_POSTMASK_SIZE 0x0030
#define DISP_REG_UFO_START 0x0000
#define UFO_BYPASS BIT(2)
struct mtk_ddp_comp_dev {
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
};
void mtk_ddp_write(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset)
{
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
if (cmdq_pkt)
cmdq_pkt_write(cmdq_pkt, cmdq_reg->subsys,
cmdq_reg->offset + offset, value);
else
#endif
writel(value, regs + offset);
}
void mtk_ddp_write_relaxed(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset)
{
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
if (cmdq_pkt)
cmdq_pkt_write(cmdq_pkt, cmdq_reg->subsys,
cmdq_reg->offset + offset, value);
else
#endif
writel_relaxed(value, regs + offset);
}
void mtk_ddp_write_mask(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset, unsigned int mask)
{
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
if (cmdq_pkt) {
cmdq_pkt_write_mask(cmdq_pkt, cmdq_reg->subsys,
cmdq_reg->offset + offset, value, mask);
} else {
#endif
u32 tmp = readl(regs + offset);
tmp = (tmp & ~mask) | (value & mask);
writel(tmp, regs + offset);
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
}
#endif
}
static int mtk_ddp_clk_enable(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
return clk_prepare_enable(priv->clk);
}
static void mtk_ddp_clk_disable(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
clk_disable_unprepare(priv->clk);
}
void mtk_dither_set_common(void __iomem *regs, struct cmdq_client_reg *cmdq_reg,
unsigned int bpc, unsigned int cfg,
unsigned int dither_en, struct cmdq_pkt *cmdq_pkt)
{
/* If bpc equal to 0, the dithering function didn't be enabled */
if (bpc == 0)
return;
if (bpc >= MTK_MIN_BPC) {
mtk_ddp_write(cmdq_pkt, 0, cmdq_reg, regs, DISP_REG_DITHER_5);
mtk_ddp_write(cmdq_pkt, 0, cmdq_reg, regs, DISP_REG_DITHER_7);
mtk_ddp_write(cmdq_pkt,
DITHER_LSB_ERR_SHIFT_R(MTK_MAX_BPC - bpc) |
DITHER_ADD_LSHIFT_R(MTK_MAX_BPC - bpc) |
DITHER_NEW_BIT_MODE,
cmdq_reg, regs, DISP_REG_DITHER_15);
mtk_ddp_write(cmdq_pkt,
DITHER_LSB_ERR_SHIFT_B(MTK_MAX_BPC - bpc) |
DITHER_ADD_LSHIFT_B(MTK_MAX_BPC - bpc) |
DITHER_LSB_ERR_SHIFT_G(MTK_MAX_BPC - bpc) |
DITHER_ADD_LSHIFT_G(MTK_MAX_BPC - bpc),
cmdq_reg, regs, DISP_REG_DITHER_16);
mtk_ddp_write(cmdq_pkt, dither_en, cmdq_reg, regs, cfg);
}
}
static void mtk_dither_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs, DISP_REG_DITHER_SIZE);
mtk_ddp_write(cmdq_pkt, DITHER_RELAY_MODE, &priv->cmdq_reg, priv->regs,
DISP_REG_DITHER_CFG);
mtk_dither_set_common(priv->regs, &priv->cmdq_reg, bpc, DISP_REG_DITHER_CFG,
DITHER_ENGINE_EN, cmdq_pkt);
}
static void mtk_dither_start(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel(DITHER_EN, priv->regs + DISP_REG_DITHER_EN);
}
static void mtk_dither_stop(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel_relaxed(0x0, priv->regs + DISP_REG_DITHER_EN);
}
static void mtk_dither_set(struct device *dev, unsigned int bpc,
unsigned int cfg, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
mtk_dither_set_common(priv->regs, &priv->cmdq_reg, bpc, cfg,
DISP_DITHERING, cmdq_pkt);
}
static void mtk_dsc_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
/* dsc bypass mode */
mtk_ddp_write_mask(cmdq_pkt, DSC_BYPASS, &priv->cmdq_reg, priv->regs,
DISP_REG_DSC_CON, DSC_BYPASS);
mtk_ddp_write_mask(cmdq_pkt, DSC_UFOE_SEL, &priv->cmdq_reg, priv->regs,
DISP_REG_DSC_CON, DSC_UFOE_SEL);
mtk_ddp_write_mask(cmdq_pkt, DSC_DUAL_INOUT, &priv->cmdq_reg, priv->regs,
DISP_REG_DSC_CON, DSC_DUAL_INOUT);
}
static void mtk_dsc_start(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
/* write with mask to reserve the value set in mtk_dsc_config */
mtk_ddp_write_mask(NULL, DSC_EN, &priv->cmdq_reg, priv->regs, DISP_REG_DSC_CON, DSC_EN);
}
static void mtk_dsc_stop(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel_relaxed(0x0, priv->regs + DISP_REG_DSC_CON);
}
static void mtk_od_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs, DISP_REG_OD_SIZE);
mtk_ddp_write(cmdq_pkt, OD_RELAYMODE, &priv->cmdq_reg, priv->regs, DISP_REG_OD_CFG);
mtk_dither_set(dev, bpc, DISP_REG_OD_CFG, cmdq_pkt);
}
static void mtk_od_start(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel(1, priv->regs + DISP_REG_OD_EN);
}
static void mtk_postmask_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs,
DISP_REG_POSTMASK_SIZE);
mtk_ddp_write(cmdq_pkt, POSTMASK_RELAY_MODE, &priv->cmdq_reg,
priv->regs, DISP_REG_POSTMASK_CFG);
}
static void mtk_postmask_start(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel(POSTMASK_EN, priv->regs + DISP_REG_POSTMASK_EN);
}
static void mtk_postmask_stop(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel_relaxed(0x0, priv->regs + DISP_REG_POSTMASK_EN);
}
static void mtk_ufoe_start(struct device *dev)
{
struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
writel(UFO_BYPASS, priv->regs + DISP_REG_UFO_START);
}
static const struct mtk_ddp_comp_funcs ddp_aal = {
.clk_enable = mtk_aal_clk_enable,
.clk_disable = mtk_aal_clk_disable,
.gamma_get_lut_size = mtk_aal_gamma_get_lut_size,
.gamma_set = mtk_aal_gamma_set,
.config = mtk_aal_config,
.start = mtk_aal_start,
.stop = mtk_aal_stop,
};
static const struct mtk_ddp_comp_funcs ddp_ccorr = {
.clk_enable = mtk_ccorr_clk_enable,
.clk_disable = mtk_ccorr_clk_disable,
.config = mtk_ccorr_config,
.start = mtk_ccorr_start,
.stop = mtk_ccorr_stop,
.ctm_set = mtk_ccorr_ctm_set,
};
static const struct mtk_ddp_comp_funcs ddp_color = {
.clk_enable = mtk_color_clk_enable,
.clk_disable = mtk_color_clk_disable,
.config = mtk_color_config,
.start = mtk_color_start,
};
static const struct mtk_ddp_comp_funcs ddp_dither = {
.clk_enable = mtk_ddp_clk_enable,
.clk_disable = mtk_ddp_clk_disable,
.config = mtk_dither_config,
.start = mtk_dither_start,
.stop = mtk_dither_stop,
};
static const struct mtk_ddp_comp_funcs ddp_dpi = {
.start = mtk_dpi_start,
.stop = mtk_dpi_stop,
.encoder_index = mtk_dpi_encoder_index,
};
static const struct mtk_ddp_comp_funcs ddp_dsc = {
.clk_enable = mtk_ddp_clk_enable,
.clk_disable = mtk_ddp_clk_disable,
.config = mtk_dsc_config,
.start = mtk_dsc_start,
.stop = mtk_dsc_stop,
};
static const struct mtk_ddp_comp_funcs ddp_dsi = {
.start = mtk_dsi_ddp_start,
.stop = mtk_dsi_ddp_stop,
.encoder_index = mtk_dsi_encoder_index,
};
static const struct mtk_ddp_comp_funcs ddp_gamma = {
.clk_enable = mtk_gamma_clk_enable,
.clk_disable = mtk_gamma_clk_disable,
.gamma_get_lut_size = mtk_gamma_get_lut_size,
.gamma_set = mtk_gamma_set,
.config = mtk_gamma_config,
.start = mtk_gamma_start,
.stop = mtk_gamma_stop,
};
static const struct mtk_ddp_comp_funcs ddp_merge = {
.clk_enable = mtk_merge_clk_enable,
.clk_disable = mtk_merge_clk_disable,
.start = mtk_merge_start,
.stop = mtk_merge_stop,
.config = mtk_merge_config,
};
static const struct mtk_ddp_comp_funcs ddp_od = {
.clk_enable = mtk_ddp_clk_enable,
.clk_disable = mtk_ddp_clk_disable,
.config = mtk_od_config,
.start = mtk_od_start,
};
static const struct mtk_ddp_comp_funcs ddp_ovl = {
.clk_enable = mtk_ovl_clk_enable,
.clk_disable = mtk_ovl_clk_disable,
.config = mtk_ovl_config,
.start = mtk_ovl_start,
.stop = mtk_ovl_stop,
.register_vblank_cb = mtk_ovl_register_vblank_cb,
.unregister_vblank_cb = mtk_ovl_unregister_vblank_cb,
.enable_vblank = mtk_ovl_enable_vblank,
.disable_vblank = mtk_ovl_disable_vblank,
.supported_rotations = mtk_ovl_supported_rotations,
.layer_nr = mtk_ovl_layer_nr,
.layer_check = mtk_ovl_layer_check,
.layer_config = mtk_ovl_layer_config,
.bgclr_in_on = mtk_ovl_bgclr_in_on,
.bgclr_in_off = mtk_ovl_bgclr_in_off,
.get_blend_modes = mtk_ovl_get_blend_modes,
.get_formats = mtk_ovl_get_formats,
.get_num_formats = mtk_ovl_get_num_formats,
.is_afbc_supported = mtk_ovl_is_afbc_supported,
};
static const struct mtk_ddp_comp_funcs ddp_postmask = {
.clk_enable = mtk_ddp_clk_enable,
.clk_disable = mtk_ddp_clk_disable,
.config = mtk_postmask_config,
.start = mtk_postmask_start,
.stop = mtk_postmask_stop,
};
static const struct mtk_ddp_comp_funcs ddp_rdma = {
.clk_enable = mtk_rdma_clk_enable,
.clk_disable = mtk_rdma_clk_disable,
.config = mtk_rdma_config,
.start = mtk_rdma_start,
.stop = mtk_rdma_stop,
.register_vblank_cb = mtk_rdma_register_vblank_cb,
.unregister_vblank_cb = mtk_rdma_unregister_vblank_cb,
.enable_vblank = mtk_rdma_enable_vblank,
.disable_vblank = mtk_rdma_disable_vblank,
.layer_nr = mtk_rdma_layer_nr,
.layer_config = mtk_rdma_layer_config,
.get_formats = mtk_rdma_get_formats,
.get_num_formats = mtk_rdma_get_num_formats,
};
static const struct mtk_ddp_comp_funcs ddp_ufoe = {
.clk_enable = mtk_ddp_clk_enable,
.clk_disable = mtk_ddp_clk_disable,
.start = mtk_ufoe_start,
};
static const struct mtk_ddp_comp_funcs ddp_ovl_adaptor = {
.power_on = mtk_ovl_adaptor_power_on,
.power_off = mtk_ovl_adaptor_power_off,
.clk_enable = mtk_ovl_adaptor_clk_enable,
.clk_disable = mtk_ovl_adaptor_clk_disable,
.config = mtk_ovl_adaptor_config,
.start = mtk_ovl_adaptor_start,
.stop = mtk_ovl_adaptor_stop,
.layer_nr = mtk_ovl_adaptor_layer_nr,
.layer_config = mtk_ovl_adaptor_layer_config,
.register_vblank_cb = mtk_ovl_adaptor_register_vblank_cb,
.unregister_vblank_cb = mtk_ovl_adaptor_unregister_vblank_cb,
.enable_vblank = mtk_ovl_adaptor_enable_vblank,
.disable_vblank = mtk_ovl_adaptor_disable_vblank,
.dma_dev_get = mtk_ovl_adaptor_dma_dev_get,
.connect = mtk_ovl_adaptor_connect,
.disconnect = mtk_ovl_adaptor_disconnect,
.add = mtk_ovl_adaptor_add_comp,
.remove = mtk_ovl_adaptor_remove_comp,
.get_blend_modes = mtk_ovl_adaptor_get_blend_modes,
.get_formats = mtk_ovl_adaptor_get_formats,
.get_num_formats = mtk_ovl_adaptor_get_num_formats,
.mode_valid = mtk_ovl_adaptor_mode_valid,
};
static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
[MTK_DISP_AAL] = "aal",
[MTK_DISP_BLS] = "bls",
[MTK_DISP_CCORR] = "ccorr",
[MTK_DISP_COLOR] = "color",
[MTK_DISP_DITHER] = "dither",
[MTK_DISP_DSC] = "dsc",
[MTK_DISP_GAMMA] = "gamma",
[MTK_DISP_MERGE] = "merge",
[MTK_DISP_MUTEX] = "mutex",
[MTK_DISP_OD] = "od",
[MTK_DISP_OVL] = "ovl",
[MTK_DISP_OVL_2L] = "ovl-2l",
[MTK_DISP_OVL_ADAPTOR] = "ovl_adaptor",
[MTK_DISP_POSTMASK] = "postmask",
[MTK_DISP_PWM] = "pwm",
[MTK_DISP_RDMA] = "rdma",
[MTK_DISP_UFOE] = "ufoe",
[MTK_DISP_WDMA] = "wdma",
[MTK_DP_INTF] = "dp-intf",
[MTK_DPI] = "dpi",
[MTK_DSI] = "dsi",
};
struct mtk_ddp_comp_match {
enum mtk_ddp_comp_type type;
int alias_id;
const struct mtk_ddp_comp_funcs *funcs;
};
static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_DRM_ID_MAX] = {
[DDP_COMPONENT_AAL0] = { MTK_DISP_AAL, 0, &ddp_aal },
[DDP_COMPONENT_AAL1] = { MTK_DISP_AAL, 1, &ddp_aal },
[DDP_COMPONENT_BLS] = { MTK_DISP_BLS, 0, NULL },
[DDP_COMPONENT_CCORR] = { MTK_DISP_CCORR, 0, &ddp_ccorr },
[DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color },
[DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color },
[DDP_COMPONENT_DITHER0] = { MTK_DISP_DITHER, 0, &ddp_dither },
[DDP_COMPONENT_DP_INTF0] = { MTK_DP_INTF, 0, &ddp_dpi },
[DDP_COMPONENT_DP_INTF1] = { MTK_DP_INTF, 1, &ddp_dpi },
[DDP_COMPONENT_DPI0] = { MTK_DPI, 0, &ddp_dpi },
[DDP_COMPONENT_DPI1] = { MTK_DPI, 1, &ddp_dpi },
[DDP_COMPONENT_DRM_OVL_ADAPTOR] = { MTK_DISP_OVL_ADAPTOR, 0, &ddp_ovl_adaptor },
[DDP_COMPONENT_DSC0] = { MTK_DISP_DSC, 0, &ddp_dsc },
[DDP_COMPONENT_DSC1] = { MTK_DISP_DSC, 1, &ddp_dsc },
[DDP_COMPONENT_DSI0] = { MTK_DSI, 0, &ddp_dsi },
[DDP_COMPONENT_DSI1] = { MTK_DSI, 1, &ddp_dsi },
[DDP_COMPONENT_DSI2] = { MTK_DSI, 2, &ddp_dsi },
[DDP_COMPONENT_DSI3] = { MTK_DSI, 3, &ddp_dsi },
[DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma },
[DDP_COMPONENT_MERGE0] = { MTK_DISP_MERGE, 0, &ddp_merge },
[DDP_COMPONENT_MERGE1] = { MTK_DISP_MERGE, 1, &ddp_merge },
[DDP_COMPONENT_MERGE2] = { MTK_DISP_MERGE, 2, &ddp_merge },
[DDP_COMPONENT_MERGE3] = { MTK_DISP_MERGE, 3, &ddp_merge },
[DDP_COMPONENT_MERGE4] = { MTK_DISP_MERGE, 4, &ddp_merge },
[DDP_COMPONENT_MERGE5] = { MTK_DISP_MERGE, 5, &ddp_merge },
[DDP_COMPONENT_OD0] = { MTK_DISP_OD, 0, &ddp_od },
[DDP_COMPONENT_OD1] = { MTK_DISP_OD, 1, &ddp_od },
[DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, &ddp_ovl },
[DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, &ddp_ovl },
[DDP_COMPONENT_OVL_2L0] = { MTK_DISP_OVL_2L, 0, &ddp_ovl },
[DDP_COMPONENT_OVL_2L1] = { MTK_DISP_OVL_2L, 1, &ddp_ovl },
[DDP_COMPONENT_OVL_2L2] = { MTK_DISP_OVL_2L, 2, &ddp_ovl },
[DDP_COMPONENT_POSTMASK0] = { MTK_DISP_POSTMASK, 0, &ddp_postmask },
[DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL },
[DDP_COMPONENT_PWM1] = { MTK_DISP_PWM, 1, NULL },
[DDP_COMPONENT_PWM2] = { MTK_DISP_PWM, 2, NULL },
[DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, &ddp_rdma },
[DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, &ddp_rdma },
[DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, &ddp_rdma },
[DDP_COMPONENT_RDMA4] = { MTK_DISP_RDMA, 4, &ddp_rdma },
[DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe },
[DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, NULL },
[DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL },
};
static bool mtk_ddp_comp_find(struct device *dev,
const unsigned int *path,
unsigned int path_len,
struct mtk_ddp_comp *ddp_comp)
{
unsigned int i;
if (path == NULL)
return false;
for (i = 0U; i < path_len; i++)
if (dev == ddp_comp[path[i]].dev)
return true;
return false;
}
static int mtk_ddp_comp_find_in_route(struct device *dev,
const struct mtk_drm_route *routes,
unsigned int num_routes,
struct mtk_ddp_comp *ddp_comp)
{
unsigned int i;
if (!routes)
return -EINVAL;
for (i = 0; i < num_routes; i++)
if (dev == ddp_comp[routes[i].route_ddp].dev)
return BIT(routes[i].crtc_id);
return -ENODEV;
}
static bool mtk_ddp_path_available(const unsigned int *path,
unsigned int path_len,
struct device_node **comp_node)
{
unsigned int i;
if (!path || !path_len)
return false;
for (i = 0U; i < path_len; i++) {
/* OVL_ADAPTOR doesn't have a device node */
if (path[i] == DDP_COMPONENT_DRM_OVL_ADAPTOR)
continue;
if (!comp_node[path[i]])
return false;
}
return true;
}
int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type)
{
int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]);
int i;
for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) {
if (comp_type == mtk_ddp_matches[i].type &&
(id < 0 || id == mtk_ddp_matches[i].alias_id))
return i;
}
return -EINVAL;
}
int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev)
{
struct mtk_drm_private *private = drm->dev_private;
const struct mtk_mmsys_driver_data *data;
struct mtk_drm_private *priv_n;
int i = 0, j;
int ret;
for (j = 0; j < private->data->mmsys_dev_num; j++) {
priv_n = private->all_drm_private[j];
data = priv_n->data;
if (mtk_ddp_path_available(data->main_path, data->main_len,
priv_n->comp_node)) {
if (mtk_ddp_comp_find(dev, data->main_path,
data->main_len,
priv_n->ddp_comp))
return BIT(i);
i++;
}
if (mtk_ddp_path_available(data->ext_path, data->ext_len,
priv_n->comp_node)) {
if (mtk_ddp_comp_find(dev, data->ext_path,
data->ext_len,
priv_n->ddp_comp))
return BIT(i);
i++;
}
if (mtk_ddp_path_available(data->third_path, data->third_len,
priv_n->comp_node)) {
if (mtk_ddp_comp_find(dev, data->third_path,
data->third_len,
priv_n->ddp_comp))
return BIT(i);
i++;
}
}
ret = mtk_ddp_comp_find_in_route(dev,
private->data->conn_routes,
private->data->num_conn_routes,
private->ddp_comp);
if (ret < 0)
DRM_INFO("Failed to find comp in ddp table, ret = %d\n", ret);
return ret;
}
static void mtk_ddp_comp_put_device(void *_dev)
{
struct device *dev = _dev;
put_device(dev);
}
static void mtk_ddp_comp_clk_put(void *_clk)
{
struct clk *clk = _clk;
clk_put(clk);
}
int mtk_ddp_comp_init(struct device *dev, struct device_node *node, struct mtk_ddp_comp *comp,
unsigned int comp_id)
{
struct platform_device *comp_pdev;
enum mtk_ddp_comp_type type;
struct mtk_ddp_comp_dev *priv;
int ret;
if (comp_id >= DDP_COMPONENT_DRM_ID_MAX)
return -EINVAL;
type = mtk_ddp_matches[comp_id].type;
comp->id = comp_id;
comp->funcs = mtk_ddp_matches[comp_id].funcs;
/* Not all drm components have a DTS device node, such as ovl_adaptor,
* which is the drm bring up sub driver
*/
if (!node)
return 0;
comp_pdev = of_find_device_by_node(node);
if (!comp_pdev) {
DRM_INFO("Waiting for device %s\n", node->full_name);
return -EPROBE_DEFER;
}
comp->dev = &comp_pdev->dev;
ret = devm_add_action_or_reset(dev, mtk_ddp_comp_put_device, comp->dev);
if (ret)
return ret;
if (type == MTK_DISP_AAL ||
type == MTK_DISP_BLS ||
type == MTK_DISP_CCORR ||
type == MTK_DISP_COLOR ||
type == MTK_DISP_GAMMA ||
type == MTK_DISP_MERGE ||
type == MTK_DISP_OVL ||
type == MTK_DISP_OVL_2L ||
type == MTK_DISP_PWM ||
type == MTK_DISP_RDMA ||
type == MTK_DPI ||
type == MTK_DP_INTF ||
type == MTK_DSI)
return 0;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regs = devm_of_iomap(dev, node, 0, NULL);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
priv->clk = of_clk_get(node, 0);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
ret = devm_add_action_or_reset(dev, mtk_ddp_comp_clk_put, priv->clk);
if (ret)
return ret;
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(comp->dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(comp->dev, "get mediatek,gce-client-reg fail!\n");
#endif
platform_set_drvdata(comp_pdev, priv);
return 0;
}
@@ -0,0 +1,365 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015 MediaTek Inc.
*/
#ifndef MTK_DDP_COMP_H
#define MTK_DDP_COMP_H
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include <linux/soc/mediatek/mtk-mutex.h>
#include <drm/drm_modes.h>
struct device;
struct device_node;
struct drm_crtc;
struct drm_device;
struct mtk_plane_state;
struct drm_crtc_state;
enum mtk_ddp_comp_type {
MTK_DISP_AAL,
MTK_DISP_BLS,
MTK_DISP_CCORR,
MTK_DISP_COLOR,
MTK_DISP_DITHER,
MTK_DISP_DSC,
MTK_DISP_GAMMA,
MTK_DISP_MERGE,
MTK_DISP_MUTEX,
MTK_DISP_OD,
MTK_DISP_OVL,
MTK_DISP_OVL_2L,
MTK_DISP_OVL_ADAPTOR,
MTK_DISP_POSTMASK,
MTK_DISP_PWM,
MTK_DISP_RDMA,
MTK_DISP_UFOE,
MTK_DISP_WDMA,
MTK_DPI,
MTK_DP_INTF,
MTK_DSI,
MTK_DDP_COMP_TYPE_MAX,
};
struct mtk_ddp_comp;
struct cmdq_pkt;
struct mtk_ddp_comp_funcs {
int (*power_on)(struct device *dev);
void (*power_off)(struct device *dev);
int (*clk_enable)(struct device *dev);
void (*clk_disable)(struct device *dev);
void (*config)(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
void (*start)(struct device *dev);
void (*stop)(struct device *dev);
void (*register_vblank_cb)(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data);
void (*unregister_vblank_cb)(struct device *dev);
void (*enable_vblank)(struct device *dev);
void (*disable_vblank)(struct device *dev);
unsigned int (*supported_rotations)(struct device *dev);
unsigned int (*layer_nr)(struct device *dev);
int (*layer_check)(struct device *dev,
unsigned int idx,
struct mtk_plane_state *state);
void (*layer_config)(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt);
unsigned int (*gamma_get_lut_size)(struct device *dev);
void (*gamma_set)(struct device *dev,
struct drm_crtc_state *state);
void (*bgclr_in_on)(struct device *dev);
void (*bgclr_in_off)(struct device *dev);
void (*ctm_set)(struct device *dev,
struct drm_crtc_state *state);
struct device * (*dma_dev_get)(struct device *dev);
u32 (*get_blend_modes)(struct device *dev);
const u32 *(*get_formats)(struct device *dev);
size_t (*get_num_formats)(struct device *dev);
bool (*is_afbc_supported)(struct device *dev);
void (*connect)(struct device *dev, struct device *mmsys_dev, unsigned int next);
void (*disconnect)(struct device *dev, struct device *mmsys_dev, unsigned int next);
void (*add)(struct device *dev, struct mtk_mutex *mutex);
void (*remove)(struct device *dev, struct mtk_mutex *mutex);
unsigned int (*encoder_index)(struct device *dev);
enum drm_mode_status (*mode_valid)(struct device *dev, const struct drm_display_mode *mode);
};
struct mtk_ddp_comp {
struct device *dev;
int irq;
unsigned int id;
int encoder_index;
const struct mtk_ddp_comp_funcs *funcs;
};
static inline int mtk_ddp_comp_power_on(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->power_on)
return comp->funcs->power_on(comp->dev);
else
return pm_runtime_resume_and_get(comp->dev);
return 0;
}
static inline void mtk_ddp_comp_power_off(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->power_off)
comp->funcs->power_off(comp->dev);
else
pm_runtime_put(comp->dev);
}
static inline int mtk_ddp_comp_clk_enable(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->clk_enable)
return comp->funcs->clk_enable(comp->dev);
return 0;
}
static inline void mtk_ddp_comp_clk_disable(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->clk_disable)
comp->funcs->clk_disable(comp->dev);
}
static inline
enum drm_mode_status mtk_ddp_comp_mode_valid(struct mtk_ddp_comp *comp,
const struct drm_display_mode *mode)
{
if (comp && comp->funcs && comp->funcs->mode_valid)
return comp->funcs->mode_valid(comp->dev, mode);
return MODE_OK;
}
static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
unsigned int w, unsigned int h,
unsigned int vrefresh, unsigned int bpc,
struct cmdq_pkt *cmdq_pkt)
{
if (comp->funcs && comp->funcs->config)
comp->funcs->config(comp->dev, w, h, vrefresh, bpc, cmdq_pkt);
}
static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->start)
comp->funcs->start(comp->dev);
}
static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->stop)
comp->funcs->stop(comp->dev);
}
static inline void mtk_ddp_comp_register_vblank_cb(struct mtk_ddp_comp *comp,
void (*vblank_cb)(void *),
void *vblank_cb_data)
{
if (comp->funcs && comp->funcs->register_vblank_cb)
comp->funcs->register_vblank_cb(comp->dev, vblank_cb,
vblank_cb_data);
}
static inline void mtk_ddp_comp_unregister_vblank_cb(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->unregister_vblank_cb)
comp->funcs->unregister_vblank_cb(comp->dev);
}
static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->enable_vblank)
comp->funcs->enable_vblank(comp->dev);
}
static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->disable_vblank)
comp->funcs->disable_vblank(comp->dev);
}
static inline
unsigned int mtk_ddp_comp_supported_rotations(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->supported_rotations)
return comp->funcs->supported_rotations(comp->dev);
/*
* In order to pass IGT tests, DRM_MODE_ROTATE_0 is required when
* rotation is not supported.
*/
return DRM_MODE_ROTATE_0;
}
static inline unsigned int mtk_ddp_comp_layer_nr(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->layer_nr)
return comp->funcs->layer_nr(comp->dev);
return 0;
}
static inline int mtk_ddp_comp_layer_check(struct mtk_ddp_comp *comp,
unsigned int idx,
struct mtk_plane_state *state)
{
if (comp->funcs && comp->funcs->layer_check)
return comp->funcs->layer_check(comp->dev, idx, state);
return 0;
}
static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt)
{
if (comp->funcs && comp->funcs->layer_config)
comp->funcs->layer_config(comp->dev, idx, state, cmdq_pkt);
}
static inline unsigned int mtk_ddp_gamma_get_lut_size(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->gamma_get_lut_size)
return comp->funcs->gamma_get_lut_size(comp->dev);
return 0;
}
static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp,
struct drm_crtc_state *state)
{
if (comp->funcs && comp->funcs->gamma_set)
comp->funcs->gamma_set(comp->dev, state);
}
static inline void mtk_ddp_comp_bgclr_in_on(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->bgclr_in_on)
comp->funcs->bgclr_in_on(comp->dev);
}
static inline void mtk_ddp_comp_bgclr_in_off(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->bgclr_in_off)
comp->funcs->bgclr_in_off(comp->dev);
}
static inline void mtk_ddp_ctm_set(struct mtk_ddp_comp *comp,
struct drm_crtc_state *state)
{
if (comp->funcs && comp->funcs->ctm_set)
comp->funcs->ctm_set(comp->dev, state);
}
static inline struct device *mtk_ddp_comp_dma_dev_get(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->dma_dev_get)
return comp->funcs->dma_dev_get(comp->dev);
return comp->dev;
}
static inline
u32 mtk_ddp_comp_get_blend_modes(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->get_blend_modes)
return comp->funcs->get_blend_modes(comp->dev);
return 0;
}
static inline
const u32 *mtk_ddp_comp_get_formats(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->get_formats)
return comp->funcs->get_formats(comp->dev);
return NULL;
}
static inline
size_t mtk_ddp_comp_get_num_formats(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->get_num_formats)
return comp->funcs->get_num_formats(comp->dev);
return 0;
}
static inline bool mtk_ddp_comp_is_afbc_supported(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->is_afbc_supported)
return comp->funcs->is_afbc_supported(comp->dev);
return false;
}
static inline bool mtk_ddp_comp_add(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
{
if (comp->funcs && comp->funcs->add) {
comp->funcs->add(comp->dev, mutex);
return true;
}
return false;
}
static inline bool mtk_ddp_comp_remove(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
{
if (comp->funcs && comp->funcs->remove) {
comp->funcs->remove(comp->dev, mutex);
return true;
}
return false;
}
static inline bool mtk_ddp_comp_connect(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
unsigned int next)
{
if (comp->funcs && comp->funcs->connect) {
comp->funcs->connect(comp->dev, mmsys_dev, next);
return true;
}
return false;
}
static inline bool mtk_ddp_comp_disconnect(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
unsigned int next)
{
if (comp->funcs && comp->funcs->disconnect) {
comp->funcs->disconnect(comp->dev, mmsys_dev, next);
return true;
}
return false;
}
static inline void mtk_ddp_comp_encoder_index_set(struct mtk_ddp_comp *comp)
{
if (comp->funcs && comp->funcs->encoder_index)
comp->encoder_index = (int)comp->funcs->encoder_index(comp->dev);
}
int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type);
int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev);
int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node, struct mtk_ddp_comp *comp,
unsigned int comp_id);
enum mtk_ddp_comp_type mtk_ddp_comp_get_type(unsigned int comp_id);
void mtk_ddp_write(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset);
void mtk_ddp_write_relaxed(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset);
void mtk_ddp_write_mask(struct cmdq_pkt *cmdq_pkt, unsigned int value,
struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
unsigned int offset, unsigned int mask);
#endif /* MTK_DDP_COMP_H */
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_AAL_EN 0x0000
#define AAL_EN BIT(0)
#define DISP_AAL_CFG 0x0020
#define AAL_RELAY_MODE BIT(0)
#define AAL_GAMMA_LUT_EN BIT(1)
#define DISP_AAL_SIZE 0x0030
#define DISP_AAL_SIZE_HSIZE GENMASK(28, 16)
#define DISP_AAL_SIZE_VSIZE GENMASK(12, 0)
#define DISP_AAL_OUTPUT_SIZE 0x04d8
#define DISP_AAL_GAMMA_LUT 0x0700
#define DISP_AAL_GAMMA_LUT_R GENMASK(29, 20)
#define DISP_AAL_GAMMA_LUT_G GENMASK(19, 10)
#define DISP_AAL_GAMMA_LUT_B GENMASK(9, 0)
#define DISP_AAL_LUT_BITS 10
#define DISP_AAL_LUT_SIZE 512
struct mtk_disp_aal_data {
bool has_gamma;
};
/**
* struct mtk_disp_aal - Display Adaptive Ambient Light driver structure
* @clk: clock for DISP_AAL controller
* @regs: MMIO registers base
* @cmdq_reg: CMDQ Client register
* @data: platform specific data for DISP_AAL
*/
struct mtk_disp_aal {
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_aal_data *data;
};
int mtk_aal_clk_enable(struct device *dev)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
return clk_prepare_enable(aal->clk);
}
void mtk_aal_clk_disable(struct device *dev)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
clk_disable_unprepare(aal->clk);
}
void mtk_aal_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
u32 sz;
sz = FIELD_PREP(DISP_AAL_SIZE_HSIZE, w);
sz |= FIELD_PREP(DISP_AAL_SIZE_VSIZE, h);
mtk_ddp_write(cmdq_pkt, sz, &aal->cmdq_reg, aal->regs, DISP_AAL_SIZE);
mtk_ddp_write(cmdq_pkt, sz, &aal->cmdq_reg, aal->regs, DISP_AAL_OUTPUT_SIZE);
}
/**
* mtk_aal_gamma_get_lut_size() - Get gamma LUT size for AAL
* @dev: Pointer to struct device
*
* Return: 0 if gamma control not supported in AAL or gamma LUT size
*/
unsigned int mtk_aal_gamma_get_lut_size(struct device *dev)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
if (aal->data && aal->data->has_gamma)
return DISP_AAL_LUT_SIZE;
return 0;
}
void mtk_aal_gamma_set(struct device *dev, struct drm_crtc_state *state)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
struct drm_color_lut *lut;
unsigned int i;
u32 cfg_val;
/* If gamma is not supported in AAL, go out immediately */
if (!(aal->data && aal->data->has_gamma))
return;
/* Also, if there's no gamma lut there's nothing to do here. */
if (!state->gamma_lut)
return;
lut = (struct drm_color_lut *)state->gamma_lut->data;
for (i = 0; i < DISP_AAL_LUT_SIZE; i++) {
struct drm_color_lut hwlut = {
.red = drm_color_lut_extract(lut[i].red, DISP_AAL_LUT_BITS),
.green = drm_color_lut_extract(lut[i].green, DISP_AAL_LUT_BITS),
.blue = drm_color_lut_extract(lut[i].blue, DISP_AAL_LUT_BITS)
};
u32 word;
word = FIELD_PREP(DISP_AAL_GAMMA_LUT_R, hwlut.red);
word |= FIELD_PREP(DISP_AAL_GAMMA_LUT_G, hwlut.green);
word |= FIELD_PREP(DISP_AAL_GAMMA_LUT_B, hwlut.blue);
writel(word, aal->regs + DISP_AAL_GAMMA_LUT + i * 4);
}
cfg_val = readl(aal->regs + DISP_AAL_CFG);
/* Enable the gamma table */
cfg_val |= FIELD_PREP(AAL_GAMMA_LUT_EN, 1);
/* Disable RELAY mode to pass the processed image */
cfg_val &= ~AAL_RELAY_MODE;
writel(cfg_val, aal->regs + DISP_AAL_CFG);
}
void mtk_aal_start(struct device *dev)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
writel(AAL_EN, aal->regs + DISP_AAL_EN);
}
void mtk_aal_stop(struct device *dev)
{
struct mtk_disp_aal *aal = dev_get_drvdata(dev);
writel_relaxed(0x0, aal->regs + DISP_AAL_EN);
}
static int mtk_disp_aal_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_aal_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_aal_component_ops = {
.bind = mtk_disp_aal_bind,
.unbind = mtk_disp_aal_unbind,
};
static int mtk_disp_aal_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_aal *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get aal clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap aal\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_disp_aal_component_ops);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_disp_aal_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_aal_component_ops);
}
static const struct mtk_disp_aal_data mt8173_aal_driver_data = {
.has_gamma = true,
};
static const struct of_device_id mtk_disp_aal_driver_dt_match[] = {
{ .compatible = "mediatek,mt8173-disp-aal", .data = &mt8173_aal_driver_data },
{ .compatible = "mediatek,mt8183-disp-aal" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_disp_aal_driver_dt_match);
struct platform_driver mtk_disp_aal_driver = {
.probe = mtk_disp_aal_probe,
.remove = mtk_disp_aal_remove,
.driver = {
.name = "mediatek-disp-aal",
.of_match_table = mtk_disp_aal_driver_dt_match,
},
};
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_CCORR_EN 0x0000
#define CCORR_EN BIT(0)
#define DISP_CCORR_CFG 0x0020
#define CCORR_RELAY_MODE BIT(0)
#define CCORR_ENGINE_EN BIT(1)
#define CCORR_GAMMA_OFF BIT(2)
#define CCORR_WGAMUT_SRC_CLIP BIT(3)
#define DISP_CCORR_SIZE 0x0030
#define DISP_CCORR_COEF_0 0x0080
#define DISP_CCORR_COEF_1 0x0084
#define DISP_CCORR_COEF_2 0x0088
#define DISP_CCORR_COEF_3 0x008C
#define DISP_CCORR_COEF_4 0x0090
struct mtk_disp_ccorr_data {
u32 matrix_bits;
};
struct mtk_disp_ccorr {
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_ccorr_data *data;
};
int mtk_ccorr_clk_enable(struct device *dev)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
return clk_prepare_enable(ccorr->clk);
}
void mtk_ccorr_clk_disable(struct device *dev)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
clk_disable_unprepare(ccorr->clk);
}
void mtk_ccorr_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, w << 16 | h, &ccorr->cmdq_reg, ccorr->regs,
DISP_CCORR_SIZE);
mtk_ddp_write(cmdq_pkt, CCORR_ENGINE_EN, &ccorr->cmdq_reg, ccorr->regs,
DISP_CCORR_CFG);
}
void mtk_ccorr_start(struct device *dev)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
writel(CCORR_EN, ccorr->regs + DISP_CCORR_EN);
}
void mtk_ccorr_stop(struct device *dev)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
writel_relaxed(0x0, ccorr->regs + DISP_CCORR_EN);
}
void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
struct drm_property_blob *blob = state->ctm;
struct drm_color_ctm *ctm;
const u64 *input;
uint16_t coeffs[9] = { 0 };
int i;
struct cmdq_pkt *cmdq_pkt = NULL;
u32 matrix_bits = ccorr->data->matrix_bits;
if (!blob)
return;
ctm = (struct drm_color_ctm *)blob->data;
input = ctm->matrix;
for (i = 0; i < ARRAY_SIZE(coeffs); i++)
coeffs[i] = drm_color_ctm_s31_32_to_qm_n(input[i], 2, matrix_bits);
mtk_ddp_write(cmdq_pkt, coeffs[0] << 16 | coeffs[1],
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_0);
mtk_ddp_write(cmdq_pkt, coeffs[2] << 16 | coeffs[3],
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_1);
mtk_ddp_write(cmdq_pkt, coeffs[4] << 16 | coeffs[5],
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_2);
mtk_ddp_write(cmdq_pkt, coeffs[6] << 16 | coeffs[7],
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_3);
mtk_ddp_write(cmdq_pkt, coeffs[8] << 16,
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_4);
}
static int mtk_disp_ccorr_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_ccorr_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_ccorr_component_ops = {
.bind = mtk_disp_ccorr_bind,
.unbind = mtk_disp_ccorr_unbind,
};
static int mtk_disp_ccorr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_ccorr *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get ccorr clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap ccorr\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_disp_ccorr_component_ops);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_disp_ccorr_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_ccorr_component_ops);
}
static const struct mtk_disp_ccorr_data mt8183_ccorr_driver_data = {
.matrix_bits = 10,
};
static const struct mtk_disp_ccorr_data mt8192_ccorr_driver_data = {
.matrix_bits = 11,
};
static const struct of_device_id mtk_disp_ccorr_driver_dt_match[] = {
{ .compatible = "mediatek,mt8183-disp-ccorr",
.data = &mt8183_ccorr_driver_data},
{ .compatible = "mediatek,mt8192-disp-ccorr",
.data = &mt8192_ccorr_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_ccorr_driver_dt_match);
struct platform_driver mtk_disp_ccorr_driver = {
.probe = mtk_disp_ccorr_probe,
.remove = mtk_disp_ccorr_remove,
.driver = {
.name = "mediatek-disp-ccorr",
.of_match_table = mtk_disp_ccorr_driver_dt_match,
},
};
@@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_COLOR_CFG_MAIN 0x0400
#define DISP_COLOR_START_MT2701 0x0f00
#define DISP_COLOR_START_MT8167 0x0400
#define DISP_COLOR_START_MT8173 0x0c00
#define DISP_COLOR_START(comp) ((comp)->data->color_offset)
#define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50)
#define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54)
#define COLOR_BYPASS_ALL BIT(7)
#define COLOR_SEQ_SEL BIT(13)
struct mtk_disp_color_data {
unsigned int color_offset;
};
/*
* struct mtk_disp_color - DISP_COLOR driver structure
* @crtc: associated crtc to report irq events to
* @data: platform colour driver data
*/
struct mtk_disp_color {
struct drm_crtc *crtc;
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_color_data *data;
};
int mtk_color_clk_enable(struct device *dev)
{
struct mtk_disp_color *color = dev_get_drvdata(dev);
return clk_prepare_enable(color->clk);
}
void mtk_color_clk_disable(struct device *dev)
{
struct mtk_disp_color *color = dev_get_drvdata(dev);
clk_disable_unprepare(color->clk);
}
void mtk_color_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_color *color = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, w, &color->cmdq_reg, color->regs, DISP_COLOR_WIDTH(color));
mtk_ddp_write(cmdq_pkt, h, &color->cmdq_reg, color->regs, DISP_COLOR_HEIGHT(color));
}
void mtk_color_start(struct device *dev)
{
struct mtk_disp_color *color = dev_get_drvdata(dev);
writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
color->regs + DISP_COLOR_CFG_MAIN);
writel(0x1, color->regs + DISP_COLOR_START(color));
}
static int mtk_disp_color_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_color_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_color_component_ops = {
.bind = mtk_disp_color_bind,
.unbind = mtk_disp_color_unbind,
};
static int mtk_disp_color_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_color *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get color clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap color\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_disp_color_component_ops);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_disp_color_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_color_component_ops);
}
static const struct mtk_disp_color_data mt2701_color_driver_data = {
.color_offset = DISP_COLOR_START_MT2701,
};
static const struct mtk_disp_color_data mt8167_color_driver_data = {
.color_offset = DISP_COLOR_START_MT8167,
};
static const struct mtk_disp_color_data mt8173_color_driver_data = {
.color_offset = DISP_COLOR_START_MT8173,
};
static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
{ .compatible = "mediatek,mt2701-disp-color",
.data = &mt2701_color_driver_data},
{ .compatible = "mediatek,mt8167-disp-color",
.data = &mt8167_color_driver_data},
{ .compatible = "mediatek,mt8173-disp-color",
.data = &mt8173_color_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match);
struct platform_driver mtk_disp_color_driver = {
.probe = mtk_disp_color_probe,
.remove = mtk_disp_color_remove,
.driver = {
.name = "mediatek-disp-color",
.of_match_table = mtk_disp_color_driver_dt_match,
},
};
@@ -0,0 +1,179 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#ifndef _MTK_DISP_DRV_H_
#define _MTK_DISP_DRV_H_
#include <linux/soc/mediatek/mtk-cmdq.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include <linux/soc/mediatek/mtk-mutex.h>
#include "mtk_mdp_rdma.h"
#include "mtk_plane.h"
int mtk_aal_clk_enable(struct device *dev);
void mtk_aal_clk_disable(struct device *dev);
void mtk_aal_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
unsigned int mtk_aal_gamma_get_lut_size(struct device *dev);
void mtk_aal_gamma_set(struct device *dev, struct drm_crtc_state *state);
void mtk_aal_start(struct device *dev);
void mtk_aal_stop(struct device *dev);
void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state);
int mtk_ccorr_clk_enable(struct device *dev);
void mtk_ccorr_clk_disable(struct device *dev);
void mtk_ccorr_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
void mtk_ccorr_start(struct device *dev);
void mtk_ccorr_stop(struct device *dev);
void mtk_color_bypass_shadow(struct device *dev);
int mtk_color_clk_enable(struct device *dev);
void mtk_color_clk_disable(struct device *dev);
void mtk_color_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
void mtk_color_start(struct device *dev);
void mtk_dither_set_common(void __iomem *regs, struct cmdq_client_reg *cmdq_reg,
unsigned int bpc, unsigned int cfg,
unsigned int dither_en, struct cmdq_pkt *cmdq_pkt);
void mtk_dpi_start(struct device *dev);
void mtk_dpi_stop(struct device *dev);
unsigned int mtk_dpi_encoder_index(struct device *dev);
void mtk_dsi_ddp_start(struct device *dev);
void mtk_dsi_ddp_stop(struct device *dev);
unsigned int mtk_dsi_encoder_index(struct device *dev);
int mtk_gamma_clk_enable(struct device *dev);
void mtk_gamma_clk_disable(struct device *dev);
void mtk_gamma_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
unsigned int mtk_gamma_get_lut_size(struct device *dev);
void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state);
void mtk_gamma_start(struct device *dev);
void mtk_gamma_stop(struct device *dev);
int mtk_merge_clk_enable(struct device *dev);
void mtk_merge_clk_disable(struct device *dev);
void mtk_merge_config(struct device *dev, unsigned int width,
unsigned int height, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
void mtk_merge_start(struct device *dev);
void mtk_merge_stop(struct device *dev);
void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int r_w,
unsigned int h, unsigned int vrefresh, unsigned int bpc,
struct cmdq_pkt *cmdq_pkt);
void mtk_merge_start_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt);
void mtk_merge_stop_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt);
enum drm_mode_status mtk_merge_mode_valid(struct device *dev,
const struct drm_display_mode *mode);
void mtk_ovl_bgclr_in_on(struct device *dev);
void mtk_ovl_bgclr_in_off(struct device *dev);
void mtk_ovl_bypass_shadow(struct device *dev);
int mtk_ovl_clk_enable(struct device *dev);
void mtk_ovl_clk_disable(struct device *dev);
void mtk_ovl_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
int mtk_ovl_layer_check(struct device *dev, unsigned int idx,
struct mtk_plane_state *mtk_state);
void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt);
unsigned int mtk_ovl_layer_nr(struct device *dev);
void mtk_ovl_layer_on(struct device *dev, unsigned int idx,
struct cmdq_pkt *cmdq_pkt);
void mtk_ovl_layer_off(struct device *dev, unsigned int idx,
struct cmdq_pkt *cmdq_pkt);
void mtk_ovl_start(struct device *dev);
void mtk_ovl_stop(struct device *dev);
unsigned int mtk_ovl_supported_rotations(struct device *dev);
void mtk_ovl_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data);
void mtk_ovl_unregister_vblank_cb(struct device *dev);
void mtk_ovl_enable_vblank(struct device *dev);
void mtk_ovl_disable_vblank(struct device *dev);
u32 mtk_ovl_get_blend_modes(struct device *dev);
const u32 *mtk_ovl_get_formats(struct device *dev);
size_t mtk_ovl_get_num_formats(struct device *dev);
bool mtk_ovl_is_afbc_supported(struct device *dev);
void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex);
void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex);
bool mtk_ovl_adaptor_is_comp_present(struct device_node *node);
void mtk_ovl_adaptor_connect(struct device *dev, struct device *mmsys_dev,
unsigned int next);
void mtk_ovl_adaptor_disconnect(struct device *dev, struct device *mmsys_dev,
unsigned int next);
int mtk_ovl_adaptor_power_on(struct device *dev);
void mtk_ovl_adaptor_power_off(struct device *dev);
int mtk_ovl_adaptor_clk_enable(struct device *dev);
void mtk_ovl_adaptor_clk_disable(struct device *dev);
void mtk_ovl_adaptor_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
void mtk_ovl_adaptor_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt);
void mtk_ovl_adaptor_register_vblank_cb(struct device *dev, void (*vblank_cb)(void *),
void *vblank_cb_data);
void mtk_ovl_adaptor_unregister_vblank_cb(struct device *dev);
void mtk_ovl_adaptor_enable_vblank(struct device *dev);
void mtk_ovl_adaptor_disable_vblank(struct device *dev);
void mtk_ovl_adaptor_start(struct device *dev);
void mtk_ovl_adaptor_stop(struct device *dev);
unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev);
struct device *mtk_ovl_adaptor_dma_dev_get(struct device *dev);
u32 mtk_ovl_adaptor_get_blend_modes(struct device *dev);
const u32 *mtk_ovl_adaptor_get_formats(struct device *dev);
size_t mtk_ovl_adaptor_get_num_formats(struct device *dev);
enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev,
const struct drm_display_mode *mode);
void mtk_rdma_bypass_shadow(struct device *dev);
int mtk_rdma_clk_enable(struct device *dev);
void mtk_rdma_clk_disable(struct device *dev);
void mtk_rdma_config(struct device *dev, unsigned int width,
unsigned int height, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
unsigned int mtk_rdma_layer_nr(struct device *dev);
void mtk_rdma_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt);
void mtk_rdma_start(struct device *dev);
void mtk_rdma_stop(struct device *dev);
void mtk_rdma_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data);
void mtk_rdma_unregister_vblank_cb(struct device *dev);
void mtk_rdma_enable_vblank(struct device *dev);
void mtk_rdma_disable_vblank(struct device *dev);
const u32 *mtk_rdma_get_formats(struct device *dev);
size_t mtk_rdma_get_num_formats(struct device *dev);
int mtk_mdp_rdma_power_on(struct device *dev);
void mtk_mdp_rdma_power_off(struct device *dev);
int mtk_mdp_rdma_clk_enable(struct device *dev);
void mtk_mdp_rdma_clk_disable(struct device *dev);
void mtk_mdp_rdma_start(struct device *dev, struct cmdq_pkt *cmdq_pkt);
void mtk_mdp_rdma_stop(struct device *dev, struct cmdq_pkt *cmdq_pkt);
void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg,
struct cmdq_pkt *cmdq_pkt);
const u32 *mtk_mdp_rdma_get_formats(struct device *dev);
size_t mtk_mdp_rdma_get_num_formats(struct device *dev);
int mtk_padding_clk_enable(struct device *dev);
void mtk_padding_clk_disable(struct device *dev);
void mtk_padding_start(struct device *dev);
void mtk_padding_stop(struct device *dev);
#endif
@@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_GAMMA_EN 0x0000
#define GAMMA_EN BIT(0)
#define DISP_GAMMA_CFG 0x0020
#define GAMMA_RELAY_MODE BIT(0)
#define GAMMA_LUT_EN BIT(1)
#define GAMMA_DITHERING BIT(2)
#define GAMMA_LUT_TYPE BIT(2)
#define DISP_GAMMA_SIZE 0x0030
#define DISP_GAMMA_SIZE_HSIZE GENMASK(28, 16)
#define DISP_GAMMA_SIZE_VSIZE GENMASK(12, 0)
#define DISP_GAMMA_BANK 0x0100
#define DISP_GAMMA_BANK_BANK GENMASK(1, 0)
#define DISP_GAMMA_BANK_DATA_MODE BIT(2)
#define DISP_GAMMA_LUT 0x0700
#define DISP_GAMMA_LUT1 0x0b00
/* For 10 bit LUT layout, R/G/B are in the same register */
#define DISP_GAMMA_LUT_10BIT_R GENMASK(29, 20)
#define DISP_GAMMA_LUT_10BIT_G GENMASK(19, 10)
#define DISP_GAMMA_LUT_10BIT_B GENMASK(9, 0)
/* For 12 bit LUT layout, R/G are in LUT, B is in LUT1 */
#define DISP_GAMMA_LUT_12BIT_R GENMASK(11, 0)
#define DISP_GAMMA_LUT_12BIT_G GENMASK(23, 12)
#define DISP_GAMMA_LUT_12BIT_B GENMASK(11, 0)
struct mtk_disp_gamma_data {
bool has_dither;
bool lut_diff;
u16 lut_bank_size;
u16 lut_size;
u8 lut_bits;
};
/**
* struct mtk_disp_gamma - Display Gamma driver structure
* @clk: clock for DISP_GAMMA block
* @regs: MMIO registers base
* @cmdq_reg: CMDQ Client register
* @data: platform data for DISP_GAMMA
*/
struct mtk_disp_gamma {
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_gamma_data *data;
};
int mtk_gamma_clk_enable(struct device *dev)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
return clk_prepare_enable(gamma->clk);
}
void mtk_gamma_clk_disable(struct device *dev)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
clk_disable_unprepare(gamma->clk);
}
unsigned int mtk_gamma_get_lut_size(struct device *dev)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
if (gamma && gamma->data)
return gamma->data->lut_size;
return 0;
}
static bool mtk_gamma_lut_is_descending(struct drm_color_lut *lut, u32 lut_size)
{
u64 first, last;
int last_entry = lut_size - 1;
first = lut[0].red + lut[0].green + lut[0].blue;
last = lut[last_entry].red + lut[last_entry].green + lut[last_entry].blue;
return !!(first > last);
}
/*
* SoCs supporting 12-bits LUTs are using a new register layout that does
* always support (by HW) both 12-bits and 10-bits LUT but, on those, we
* ignore the support for 10-bits in this driver and always use 12-bits.
*
* Summarizing:
* - SoC HW support 9/10-bits LUT only
* - Old register layout
* - 10-bits LUT supported
* - 9-bits LUT not supported
* - SoC HW support both 10/12bits LUT
* - New register layout
* - 12-bits LUT supported
* - 10-its LUT not supported
*/
void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
void __iomem *lut0_base = gamma->regs + DISP_GAMMA_LUT;
void __iomem *lut1_base = gamma->regs + DISP_GAMMA_LUT1;
u32 cfg_val, data_mode, lbank_val, word[2];
u8 lut_bits = gamma->data->lut_bits;
int cur_bank, num_lut_banks;
struct drm_color_lut *lut;
unsigned int i;
/* If there's no gamma lut there's nothing to do here. */
if (!state->gamma_lut)
return;
num_lut_banks = gamma->data->lut_size / gamma->data->lut_bank_size;
lut = (struct drm_color_lut *)state->gamma_lut->data;
/* Switch to 12 bits data mode if supported */
data_mode = FIELD_PREP(DISP_GAMMA_BANK_DATA_MODE, !!(lut_bits == 12));
for (cur_bank = 0; cur_bank < num_lut_banks; cur_bank++) {
/* Switch gamma bank and set data mode before writing LUT */
if (num_lut_banks > 1) {
lbank_val = FIELD_PREP(DISP_GAMMA_BANK_BANK, cur_bank);
lbank_val |= data_mode;
writel(lbank_val, gamma->regs + DISP_GAMMA_BANK);
}
for (i = 0; i < gamma->data->lut_bank_size; i++) {
int n = cur_bank * gamma->data->lut_bank_size + i;
struct drm_color_lut diff, hwlut;
hwlut.red = drm_color_lut_extract(lut[n].red, lut_bits);
hwlut.green = drm_color_lut_extract(lut[n].green, lut_bits);
hwlut.blue = drm_color_lut_extract(lut[n].blue, lut_bits);
if (!gamma->data->lut_diff || (i % 2 == 0)) {
if (lut_bits == 12) {
word[0] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_R, hwlut.red);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_12BIT_G, hwlut.green);
word[1] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_B, hwlut.blue);
} else {
word[0] = FIELD_PREP(DISP_GAMMA_LUT_10BIT_R, hwlut.red);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_G, hwlut.green);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_B, hwlut.blue);
}
} else {
diff.red = lut[n].red - lut[n - 1].red;
diff.red = drm_color_lut_extract(diff.red, lut_bits);
diff.green = lut[n].green - lut[n - 1].green;
diff.green = drm_color_lut_extract(diff.green, lut_bits);
diff.blue = lut[n].blue - lut[n - 1].blue;
diff.blue = drm_color_lut_extract(diff.blue, lut_bits);
if (lut_bits == 12) {
word[0] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_R, diff.red);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_12BIT_G, diff.green);
word[1] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_B, diff.blue);
} else {
word[0] = FIELD_PREP(DISP_GAMMA_LUT_10BIT_R, diff.red);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_G, diff.green);
word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_B, diff.blue);
}
}
writel(word[0], lut0_base + i * 4);
if (lut_bits == 12)
writel(word[1], lut1_base + i * 4);
}
}
cfg_val = readl(gamma->regs + DISP_GAMMA_CFG);
if (!gamma->data->has_dither) {
/* Descending or Rising LUT */
if (mtk_gamma_lut_is_descending(lut, gamma->data->lut_size - 1))
cfg_val |= FIELD_PREP(GAMMA_LUT_TYPE, 1);
else
cfg_val &= ~GAMMA_LUT_TYPE;
}
/* Enable the gamma table */
cfg_val |= FIELD_PREP(GAMMA_LUT_EN, 1);
/* Disable RELAY mode to pass the processed image */
cfg_val &= ~GAMMA_RELAY_MODE;
writel(cfg_val, gamma->regs + DISP_GAMMA_CFG);
}
void mtk_gamma_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
u32 sz;
sz = FIELD_PREP(DISP_GAMMA_SIZE_HSIZE, w);
sz |= FIELD_PREP(DISP_GAMMA_SIZE_VSIZE, h);
mtk_ddp_write(cmdq_pkt, sz, &gamma->cmdq_reg, gamma->regs, DISP_GAMMA_SIZE);
if (gamma->data && gamma->data->has_dither)
mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc,
DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt);
}
void mtk_gamma_start(struct device *dev)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN);
}
void mtk_gamma_stop(struct device *dev)
{
struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN);
}
static int mtk_disp_gamma_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_gamma_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_gamma_component_ops = {
.bind = mtk_disp_gamma_bind,
.unbind = mtk_disp_gamma_unbind,
};
static int mtk_disp_gamma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_gamma *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get gamma clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap gamma\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_disp_gamma_component_ops);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_disp_gamma_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_gamma_component_ops);
}
static const struct mtk_disp_gamma_data mt8173_gamma_driver_data = {
.has_dither = true,
.lut_bank_size = 512,
.lut_bits = 10,
.lut_size = 512,
};
static const struct mtk_disp_gamma_data mt8183_gamma_driver_data = {
.lut_bank_size = 512,
.lut_bits = 10,
.lut_diff = true,
.lut_size = 512,
};
static const struct mtk_disp_gamma_data mt8195_gamma_driver_data = {
.lut_bank_size = 256,
.lut_bits = 12,
.lut_diff = true,
.lut_size = 1024,
};
static const struct of_device_id mtk_disp_gamma_driver_dt_match[] = {
{ .compatible = "mediatek,mt8173-disp-gamma",
.data = &mt8173_gamma_driver_data},
{ .compatible = "mediatek,mt8183-disp-gamma",
.data = &mt8183_gamma_driver_data},
{ .compatible = "mediatek,mt8195-disp-gamma",
.data = &mt8195_gamma_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match);
struct platform_driver mtk_disp_gamma_driver = {
.probe = mtk_disp_gamma_probe,
.remove = mtk_disp_gamma_remove,
.driver = {
.name = "mediatek-disp-gamma",
.of_match_table = mtk_disp_gamma_driver_dt_match,
},
};
@@ -0,0 +1,376 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_ddp_comp.h"
#include "mtk_drm_drv.h"
#include "mtk_disp_drv.h"
#define DISP_REG_MERGE_CTRL 0x000
#define MERGE_EN 1
#define DISP_REG_MERGE_CFG_0 0x010
#define DISP_REG_MERGE_CFG_1 0x014
#define DISP_REG_MERGE_CFG_4 0x020
#define DISP_REG_MERGE_CFG_10 0x038
/* no swap */
#define SWAP_MODE 0
#define FLD_SWAP_MODE GENMASK(4, 0)
#define DISP_REG_MERGE_CFG_12 0x040
#define CFG_10_10_1PI_2PO_BUF_MODE 6
#define CFG_10_10_2PI_2PO_BUF_MODE 8
#define CFG_11_10_1PI_2PO_MERGE 18
#define FLD_CFG_MERGE_MODE GENMASK(4, 0)
#define DISP_REG_MERGE_CFG_24 0x070
#define DISP_REG_MERGE_CFG_25 0x074
#define DISP_REG_MERGE_CFG_26 0x078
#define DISP_REG_MERGE_CFG_27 0x07c
#define DISP_REG_MERGE_CFG_36 0x0a0
#define ULTRA_EN BIT(0)
#define PREULTRA_EN BIT(4)
#define DISP_REG_MERGE_CFG_37 0x0a4
/* 0: Off, 1: SRAM0, 2: SRAM1, 3: SRAM0 + SRAM1 */
#define BUFFER_MODE 3
#define FLD_BUFFER_MODE GENMASK(1, 0)
/*
* For the ultra and preultra settings, 6us ~ 9us is experience value
* and the maximum frequency of mmsys clock is 594MHz.
*/
#define DISP_REG_MERGE_CFG_40 0x0b0
/* 6 us, 594M pixel/sec */
#define ULTRA_TH_LOW (6 * 594)
/* 8 us, 594M pixel/sec */
#define ULTRA_TH_HIGH (8 * 594)
#define FLD_ULTRA_TH_LOW GENMASK(15, 0)
#define FLD_ULTRA_TH_HIGH GENMASK(31, 16)
#define DISP_REG_MERGE_CFG_41 0x0b4
/* 8 us, 594M pixel/sec */
#define PREULTRA_TH_LOW (8 * 594)
/* 9 us, 594M pixel/sec */
#define PREULTRA_TH_HIGH (9 * 594)
#define FLD_PREULTRA_TH_LOW GENMASK(15, 0)
#define FLD_PREULTRA_TH_HIGH GENMASK(31, 16)
#define DISP_REG_MERGE_MUTE_0 0xf00
struct mtk_disp_merge {
void __iomem *regs;
struct clk *clk;
struct clk *async_clk;
struct cmdq_client_reg cmdq_reg;
bool fifo_en;
bool mute_support;
struct reset_control *reset_ctl;
};
void mtk_merge_start(struct device *dev)
{
mtk_merge_start_cmdq(dev, NULL);
}
void mtk_merge_stop(struct device *dev)
{
mtk_merge_stop_cmdq(dev, NULL);
}
void mtk_merge_start_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
if (priv->mute_support)
mtk_ddp_write(cmdq_pkt, 0x0, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_MUTE_0);
mtk_ddp_write(cmdq_pkt, 1, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CTRL);
}
void mtk_merge_stop_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
if (priv->mute_support)
mtk_ddp_write(cmdq_pkt, 0x1, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_MUTE_0);
mtk_ddp_write(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CTRL);
if (!cmdq_pkt && priv->async_clk)
reset_control_reset(priv->reset_ctl);
}
static void mtk_merge_fifo_setting(struct mtk_disp_merge *priv,
struct cmdq_pkt *cmdq_pkt)
{
mtk_ddp_write(cmdq_pkt, ULTRA_EN | PREULTRA_EN,
&priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_36);
mtk_ddp_write_mask(cmdq_pkt, BUFFER_MODE,
&priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_37,
FLD_BUFFER_MODE);
mtk_ddp_write_mask(cmdq_pkt, ULTRA_TH_LOW | ULTRA_TH_HIGH << 16,
&priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_40,
FLD_ULTRA_TH_LOW | FLD_ULTRA_TH_HIGH);
mtk_ddp_write_mask(cmdq_pkt, PREULTRA_TH_LOW | PREULTRA_TH_HIGH << 16,
&priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_41,
FLD_PREULTRA_TH_LOW | FLD_PREULTRA_TH_HIGH);
}
void mtk_merge_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
mtk_merge_advance_config(dev, w, 0, h, vrefresh, bpc, cmdq_pkt);
}
void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int r_w,
unsigned int h, unsigned int vrefresh, unsigned int bpc,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
unsigned int mode = CFG_10_10_1PI_2PO_BUF_MODE;
if (!h || !l_w) {
dev_err(dev, "%s: input width(%d) or height(%d) is invalid\n", __func__, l_w, h);
return;
}
if (priv->fifo_en) {
mtk_merge_fifo_setting(priv, cmdq_pkt);
mode = CFG_10_10_2PI_2PO_BUF_MODE;
}
if (r_w)
mode = CFG_11_10_1PI_2PO_MERGE;
mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_0);
mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_1);
mtk_ddp_write(cmdq_pkt, h << 16 | (l_w + r_w), &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_4);
/*
* DISP_REG_MERGE_CFG_24 is merge SRAM0 w/h
* DISP_REG_MERGE_CFG_25 is merge SRAM1 w/h.
* If r_w > 0, the merge is in merge mode (input0 and input1 merge together),
* the input0 goes to SRAM0, and input1 goes to SRAM1.
* If r_w = 0, the merge is in buffer mode, the input goes through SRAM0 and
* then to SRAM1. Both SRAM0 and SRAM1 are set to the same size.
*/
mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_24);
if (r_w)
mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_25);
else
mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_25);
/*
* DISP_REG_MERGE_CFG_26 and DISP_REG_MERGE_CFG_27 is only used in LR merge.
* Only take effect when the merge is setting to merge mode.
*/
mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_26);
mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_27);
mtk_ddp_write_mask(cmdq_pkt, SWAP_MODE, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_10, FLD_SWAP_MODE);
mtk_ddp_write_mask(cmdq_pkt, mode, &priv->cmdq_reg, priv->regs,
DISP_REG_MERGE_CFG_12, FLD_CFG_MERGE_MODE);
}
int mtk_merge_clk_enable(struct device *dev)
{
int ret = 0;
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "merge clk prepare enable failed\n");
return ret;
}
ret = clk_prepare_enable(priv->async_clk);
if (ret) {
/* should clean up the state of priv->clk */
clk_disable_unprepare(priv->clk);
dev_err(dev, "async clk prepare enable failed\n");
return ret;
}
return ret;
}
void mtk_merge_clk_disable(struct device *dev)
{
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
clk_disable_unprepare(priv->async_clk);
clk_disable_unprepare(priv->clk);
}
enum drm_mode_status mtk_merge_mode_valid(struct device *dev,
const struct drm_display_mode *mode)
{
struct mtk_disp_merge *priv = dev_get_drvdata(dev);
unsigned long rate;
rate = clk_get_rate(priv->clk);
/* Convert to KHz and round the number */
rate = (rate + 500) / 1000;
if (rate && mode->clock > rate) {
dev_dbg(dev, "invalid clock: %d (>%lu)\n", mode->clock, rate);
return MODE_CLOCK_HIGH;
}
/*
* Measure the bandwidth requirement of hardware prefetch (per frame)
*
* let N = prefetch buffer size in lines
* (ex. N=3, then prefetch buffer size = 3 lines)
*
* prefetch size = htotal * N (pixels)
* time per line = 1 / fps / vtotal (seconds)
* duration = vbp * time per line
* = vbp / fps / vtotal
*
* data rate = prefetch size / duration
* = htotal * N / (vbp / fps / vtotal)
* = htotal * vtotal * fps * N / vbp
* = clk * N / vbp (pixels per second)
*
* Say 4K60 (CEA-861) is the maximum mode supported by the SoC
* data rate = 594000K * N / 72 = 8250 (standard)
* (remove K * N due to the same unit)
*
* For 2560x1440@144 (clk=583600K, vbp=17):
* data rate = 583600 / 17 ~= 34329 > 8250 (NG)
*
* For 2560x1440@120 (clk=497760K, vbp=77):
* data rate = 497760 / 77 ~= 6464 < 8250 (OK)
*
* A non-standard 4K60 timing (clk=521280K, vbp=54)
* data rate = 521280 / 54 ~= 9653 > 8250 (NG)
*
* Bandwidth requirement of hardware prefetch increases significantly
* when the VBP decreases (more than 4x in this example).
*
* The proposed formula is only one way to estimate whether our SoC
* supports the mode setting. The basic idea behind it is just to check
* if the data rate requirement is too high (directly proportional to
* pixel clock, inversely proportional to vbp). Please adjust the
* function if it doesn't fit your situation in the future.
*/
rate = mode->clock / (mode->vtotal - mode->vsync_end);
if (rate > 8250) {
dev_dbg(dev, "invalid rate: %lu (>8250): " DRM_MODE_FMT "\n",
rate, DRM_MODE_ARG(mode));
return MODE_BAD;
}
return MODE_OK;
}
static int mtk_disp_merge_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_merge_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_merge_component_ops = {
.bind = mtk_disp_merge_bind,
.unbind = mtk_disp_merge_unbind,
};
static int mtk_disp_merge_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_merge *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap merge\n");
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get merge clk\n");
priv->async_clk = devm_clk_get_optional(dev, "merge_async");
if (IS_ERR(priv->async_clk))
return dev_err_probe(dev, PTR_ERR(priv->async_clk),
"failed to get merge async clock\n");
if (priv->async_clk) {
priv->reset_ctl = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(priv->reset_ctl))
return PTR_ERR(priv->reset_ctl);
}
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->fifo_en = of_property_read_bool(dev->of_node,
"mediatek,merge-fifo-en");
priv->mute_support = of_property_read_bool(dev->of_node,
"mediatek,merge-mute");
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_disp_merge_component_ops);
if (ret != 0)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_disp_merge_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_merge_component_ops);
}
static const struct of_device_id mtk_disp_merge_driver_dt_match[] = {
{ .compatible = "mediatek,mt8195-disp-merge", },
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_merge_driver_dt_match);
struct platform_driver mtk_disp_merge_driver = {
.probe = mtk_disp_merge_probe,
.remove = mtk_disp_merge_remove,
.driver = {
.name = "mediatek-disp-merge",
.of_match_table = mtk_disp_merge_driver_dt_match,
},
};
@@ -0,0 +1,780 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 MediaTek Inc.
*/
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_REG_OVL_INTEN 0x0004
#define OVL_FME_CPL_INT BIT(1)
#define DISP_REG_OVL_INTSTA 0x0008
#define DISP_REG_OVL_EN 0x000c
#define DISP_REG_OVL_RST 0x0014
#define DISP_REG_OVL_ROI_SIZE 0x0020
#define DISP_REG_OVL_DATAPATH_CON 0x0024
#define OVL_LAYER_SMI_ID_EN BIT(0)
#define OVL_BGCLR_SEL_IN BIT(2)
#define OVL_LAYER_AFBC_EN(n) BIT(4+n)
#define DISP_REG_OVL_ROI_BGCLR 0x0028
#define DISP_REG_OVL_SRC_CON 0x002c
#define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n))
#define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n))
#define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n))
#define DISP_REG_OVL_PITCH_MSB(n) (0x0040 + 0x20 * (n))
#define OVL_PITCH_MSB_2ND_SUBBUF BIT(16)
#define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n))
#define OVL_CONST_BLEND BIT(28)
#define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n))
#define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n))
#define DISP_REG_OVL_ADDR_MT2701 0x0040
#define DISP_REG_OVL_CLRFMT_EXT 0x02d0
#define OVL_CON_CLRFMT_BIT_DEPTH_MASK(n) (GENMASK(1, 0) << (4 * (n)))
#define OVL_CON_CLRFMT_BIT_DEPTH(depth, n) ((depth) << (4 * (n)))
#define OVL_CON_CLRFMT_8_BIT (0)
#define OVL_CON_CLRFMT_10_BIT (1)
#define DISP_REG_OVL_ADDR_MT8173 0x0f40
#define DISP_REG_OVL_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n))
#define DISP_REG_OVL_HDR_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n) + 0x04)
#define DISP_REG_OVL_HDR_PITCH(ovl, n) ((ovl)->data->addr + 0x20 * (n) + 0x08)
#define GMC_THRESHOLD_BITS 16
#define GMC_THRESHOLD_HIGH ((1 << GMC_THRESHOLD_BITS) / 4)
#define GMC_THRESHOLD_LOW ((1 << GMC_THRESHOLD_BITS) / 8)
#define OVL_CON_CLRFMT_MAN BIT(23)
#define OVL_CON_BYTE_SWAP BIT(24)
/* OVL_CON_RGB_SWAP works only if OVL_CON_CLRFMT_MAN is enabled */
#define OVL_CON_RGB_SWAP BIT(25)
#define OVL_CON_CLRFMT_RGB (1 << 12)
#define OVL_CON_CLRFMT_ARGB8888 (2 << 12)
#define OVL_CON_CLRFMT_RGBA8888 (3 << 12)
#define OVL_CON_CLRFMT_ABGR8888 (OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP)
#define OVL_CON_CLRFMT_BGRA8888 (OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP)
#define OVL_CON_CLRFMT_UYVY (4 << 12)
#define OVL_CON_CLRFMT_YUYV (5 << 12)
#define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
#define OVL_CON_CLRFMT_PARGB8888 ((3 << 12) | OVL_CON_CLRFMT_MAN)
#define OVL_CON_CLRFMT_PABGR8888 (OVL_CON_CLRFMT_PARGB8888 | OVL_CON_RGB_SWAP)
#define OVL_CON_CLRFMT_PBGRA8888 (OVL_CON_CLRFMT_PARGB8888 | OVL_CON_BYTE_SWAP)
#define OVL_CON_CLRFMT_PRGBA8888 (OVL_CON_CLRFMT_PABGR8888 | OVL_CON_BYTE_SWAP)
#define OVL_CON_CLRFMT_RGB565(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
0 : OVL_CON_CLRFMT_RGB)
#define OVL_CON_CLRFMT_RGB888(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
OVL_CON_CLRFMT_RGB : 0)
#define OVL_CON_AEN BIT(8)
#define OVL_CON_ALPHA 0xff
#define OVL_CON_VIRT_FLIP BIT(9)
#define OVL_CON_HORZ_FLIP BIT(10)
#define OVL_COLOR_ALPHA GENMASK(31, 24)
static inline bool is_10bit_rgb(u32 fmt)
{
switch (fmt) {
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_BGRA1010102:
return true;
}
return false;
}
static const u32 mt8173_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
};
static const u32 mt8195_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_BGRX1010102,
DRM_FORMAT_BGRA1010102,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_RGBX1010102,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
};
struct mtk_disp_ovl_data {
unsigned int addr;
unsigned int gmc_bits;
unsigned int layer_nr;
bool fmt_rgb565_is_0;
bool smi_id_en;
bool supports_afbc;
const u32 blend_modes;
const u32 *formats;
size_t num_formats;
bool supports_clrfmt_ext;
};
/*
* struct mtk_disp_ovl - DISP_OVL driver structure
* @crtc: associated crtc to report vblank events to
* @data: platform data
*/
struct mtk_disp_ovl {
struct drm_crtc *crtc;
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_ovl_data *data;
void (*vblank_cb)(void *data);
void *vblank_cb_data;
};
static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
{
struct mtk_disp_ovl *priv = dev_id;
/* Clear frame completion interrupt */
writel(0x0, priv->regs + DISP_REG_OVL_INTSTA);
if (!priv->vblank_cb)
return IRQ_NONE;
priv->vblank_cb(priv->vblank_cb_data);
return IRQ_HANDLED;
}
void mtk_ovl_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
ovl->vblank_cb = vblank_cb;
ovl->vblank_cb_data = vblank_cb_data;
}
void mtk_ovl_unregister_vblank_cb(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
ovl->vblank_cb = NULL;
ovl->vblank_cb_data = NULL;
}
void mtk_ovl_enable_vblank(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
writel_relaxed(OVL_FME_CPL_INT, ovl->regs + DISP_REG_OVL_INTEN);
}
void mtk_ovl_disable_vblank(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_INTEN);
}
u32 mtk_ovl_get_blend_modes(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return ovl->data->blend_modes;
}
const u32 *mtk_ovl_get_formats(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return ovl->data->formats;
}
size_t mtk_ovl_get_num_formats(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return ovl->data->num_formats;
}
bool mtk_ovl_is_afbc_supported(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return ovl->data->supports_afbc;
}
int mtk_ovl_clk_enable(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return clk_prepare_enable(ovl->clk);
}
void mtk_ovl_clk_disable(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
clk_disable_unprepare(ovl->clk);
}
void mtk_ovl_start(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
if (ovl->data->smi_id_en) {
unsigned int reg;
reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON);
reg = reg | OVL_LAYER_SMI_ID_EN;
writel_relaxed(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON);
}
writel_relaxed(0x1, ovl->regs + DISP_REG_OVL_EN);
}
void mtk_ovl_stop(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_EN);
if (ovl->data->smi_id_en) {
unsigned int reg;
reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON);
reg = reg & ~OVL_LAYER_SMI_ID_EN;
writel_relaxed(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON);
}
}
static void mtk_ovl_set_afbc(struct mtk_disp_ovl *ovl, struct cmdq_pkt *cmdq_pkt,
int idx, bool enabled)
{
mtk_ddp_write_mask(cmdq_pkt, enabled ? OVL_LAYER_AFBC_EN(idx) : 0,
&ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_DATAPATH_CON, OVL_LAYER_AFBC_EN(idx));
}
static void mtk_ovl_set_bit_depth(struct device *dev, int idx, u32 format,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
unsigned int bit_depth = OVL_CON_CLRFMT_8_BIT;
if (!ovl->data->supports_clrfmt_ext)
return;
if (is_10bit_rgb(format))
bit_depth = OVL_CON_CLRFMT_10_BIT;
mtk_ddp_write_mask(cmdq_pkt, OVL_CON_CLRFMT_BIT_DEPTH(bit_depth, idx),
&ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_CLRFMT_EXT,
OVL_CON_CLRFMT_BIT_DEPTH_MASK(idx));
}
void mtk_ovl_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
if (w != 0 && h != 0)
mtk_ddp_write_relaxed(cmdq_pkt, h << 16 | w, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_ROI_SIZE);
/*
* The background color must be opaque black (ARGB),
* otherwise the alpha blending will have no effect
*/
mtk_ddp_write_relaxed(cmdq_pkt, OVL_COLOR_ALPHA, &ovl->cmdq_reg,
ovl->regs, DISP_REG_OVL_ROI_BGCLR);
mtk_ddp_write(cmdq_pkt, 0x1, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RST);
mtk_ddp_write(cmdq_pkt, 0x0, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RST);
}
unsigned int mtk_ovl_layer_nr(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
return ovl->data->layer_nr;
}
unsigned int mtk_ovl_supported_rotations(struct device *dev)
{
return DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
}
int mtk_ovl_layer_check(struct device *dev, unsigned int idx,
struct mtk_plane_state *mtk_state)
{
struct drm_plane_state *state = &mtk_state->base;
/* check if any unsupported rotation is set */
if (state->rotation & ~mtk_ovl_supported_rotations(dev))
return -EINVAL;
/*
* TODO: Rotating/reflecting YUV buffers is not supported at this time.
* Only RGB[AX] variants are supported.
* Since DRM_MODE_ROTATE_0 means "no rotation", we should not
* reject layers with this property.
*/
if (state->fb->format->is_yuv && (state->rotation & ~DRM_MODE_ROTATE_0))
return -EINVAL;
return 0;
}
void mtk_ovl_layer_on(struct device *dev, unsigned int idx,
struct cmdq_pkt *cmdq_pkt)
{
unsigned int gmc_thrshd_l;
unsigned int gmc_thrshd_h;
unsigned int gmc_value;
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
mtk_ddp_write(cmdq_pkt, 0x1, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_RDMA_CTRL(idx));
gmc_thrshd_l = GMC_THRESHOLD_LOW >>
(GMC_THRESHOLD_BITS - ovl->data->gmc_bits);
gmc_thrshd_h = GMC_THRESHOLD_HIGH >>
(GMC_THRESHOLD_BITS - ovl->data->gmc_bits);
if (ovl->data->gmc_bits == 10)
gmc_value = gmc_thrshd_h | gmc_thrshd_h << 16;
else
gmc_value = gmc_thrshd_l | gmc_thrshd_l << 8 |
gmc_thrshd_h << 16 | gmc_thrshd_h << 24;
mtk_ddp_write(cmdq_pkt, gmc_value,
&ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RDMA_GMC(idx));
mtk_ddp_write_mask(cmdq_pkt, BIT(idx), &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_SRC_CON, BIT(idx));
}
void mtk_ovl_layer_off(struct device *dev, unsigned int idx,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
mtk_ddp_write_mask(cmdq_pkt, 0, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_SRC_CON, BIT(idx));
mtk_ddp_write(cmdq_pkt, 0, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_RDMA_CTRL(idx));
}
static unsigned int mtk_ovl_fmt_convert(struct mtk_disp_ovl *ovl,
struct mtk_plane_state *state)
{
unsigned int fmt = state->pending.format;
unsigned int blend_mode = DRM_MODE_BLEND_COVERAGE;
/*
* For the platforms where OVL_CON_CLRFMT_MAN is defined in the hardware data sheet
* and supports premultiplied color formats, such as OVL_CON_CLRFMT_PARGB8888.
*
* Check blend_modes in the driver data to see if premultiplied mode is supported.
* If not, use coverage mode instead to set it to the supported color formats.
*
* Current DRM assumption is that alpha is default premultiplied, so the bitmask of
* blend_modes must include BIT(DRM_MODE_BLEND_PREMULTI). Otherwise, mtk_plane_init()
* will get an error return from drm_plane_create_blend_mode_property() and
* state->base.pixel_blend_mode should not be used.
*/
if (ovl->data->blend_modes & BIT(DRM_MODE_BLEND_PREMULTI))
blend_mode = state->base.pixel_blend_mode;
switch (fmt) {
default:
case DRM_FORMAT_RGB565:
return OVL_CON_CLRFMT_RGB565(ovl);
case DRM_FORMAT_BGR565:
return OVL_CON_CLRFMT_RGB565(ovl) | OVL_CON_BYTE_SWAP;
case DRM_FORMAT_RGB888:
return OVL_CON_CLRFMT_RGB888(ovl);
case DRM_FORMAT_BGR888:
return OVL_CON_CLRFMT_RGB888(ovl) | OVL_CON_BYTE_SWAP;
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_RGBA1010102:
return blend_mode == DRM_MODE_BLEND_COVERAGE ?
OVL_CON_CLRFMT_RGBA8888 :
OVL_CON_CLRFMT_PRGBA8888;
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_BGRA1010102:
return blend_mode == DRM_MODE_BLEND_COVERAGE ?
OVL_CON_CLRFMT_BGRA8888 :
OVL_CON_CLRFMT_PBGRA8888;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_ARGB2101010:
return blend_mode == DRM_MODE_BLEND_COVERAGE ?
OVL_CON_CLRFMT_ARGB8888 :
OVL_CON_CLRFMT_PARGB8888;
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_ABGR2101010:
return blend_mode == DRM_MODE_BLEND_COVERAGE ?
OVL_CON_CLRFMT_ABGR8888 :
OVL_CON_CLRFMT_PABGR8888;
case DRM_FORMAT_UYVY:
return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
case DRM_FORMAT_YUYV:
return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
}
}
static void mtk_ovl_afbc_layer_config(struct mtk_disp_ovl *ovl,
unsigned int idx,
struct mtk_plane_pending_state *pending,
struct cmdq_pkt *cmdq_pkt)
{
unsigned int pitch_msb = pending->pitch >> 16;
unsigned int hdr_pitch = pending->hdr_pitch;
unsigned int hdr_addr = pending->hdr_addr;
if (pending->modifier != DRM_FORMAT_MOD_LINEAR) {
mtk_ddp_write_relaxed(cmdq_pkt, hdr_addr, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_HDR_ADDR(ovl, idx));
mtk_ddp_write_relaxed(cmdq_pkt,
OVL_PITCH_MSB_2ND_SUBBUF | pitch_msb,
&ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx));
mtk_ddp_write_relaxed(cmdq_pkt, hdr_pitch, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_HDR_PITCH(ovl, idx));
} else {
mtk_ddp_write_relaxed(cmdq_pkt, pitch_msb,
&ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx));
}
}
void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
struct mtk_plane_pending_state *pending = &state->pending;
unsigned int addr = pending->addr;
unsigned int pitch_lsb = pending->pitch & GENMASK(15, 0);
unsigned int fmt = pending->format;
unsigned int rotation = pending->rotation;
unsigned int offset = (pending->y << 16) | pending->x;
unsigned int src_size = (pending->height << 16) | pending->width;
unsigned int blend_mode = state->base.pixel_blend_mode;
unsigned int ignore_pixel_alpha = 0;
unsigned int con;
if (!pending->enable) {
mtk_ovl_layer_off(dev, idx, cmdq_pkt);
return;
}
con = mtk_ovl_fmt_convert(ovl, state);
if (state->base.fb) {
con |= state->base.alpha & OVL_CON_ALPHA;
/*
* For blend_modes supported SoCs, always enable alpha blending.
* For blend_modes unsupported SoCs, enable alpha blending when has_alpha is set.
*/
if (blend_mode || state->base.fb->format->has_alpha)
con |= OVL_CON_AEN;
/*
* Although the alpha channel can be ignored, CONST_BLD must be enabled
* for XRGB format, otherwise OVL will still read the value from memory.
* For RGB888 related formats, whether CONST_BLD is enabled or not won't
* affect the result. Therefore we use !has_alpha as the condition.
*/
if (blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !state->base.fb->format->has_alpha)
ignore_pixel_alpha = OVL_CONST_BLEND;
}
/*
* Treat rotate 180 as flip x + flip y, and XOR the original rotation value
* to flip x + flip y to support both in the same time.
*/
if (rotation & DRM_MODE_ROTATE_180)
rotation ^= DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
if (rotation & DRM_MODE_REFLECT_Y) {
con |= OVL_CON_VIRT_FLIP;
addr += (pending->height - 1) * pending->pitch;
}
if (rotation & DRM_MODE_REFLECT_X) {
con |= OVL_CON_HORZ_FLIP;
addr += pending->pitch - 1;
}
if (ovl->data->supports_afbc)
mtk_ovl_set_afbc(ovl, cmdq_pkt, idx,
pending->modifier != DRM_FORMAT_MOD_LINEAR);
mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_CON(idx));
mtk_ddp_write_relaxed(cmdq_pkt, pitch_lsb | ignore_pixel_alpha,
&ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH(idx));
mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_SRC_SIZE(idx));
mtk_ddp_write_relaxed(cmdq_pkt, offset, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_OFFSET(idx));
mtk_ddp_write_relaxed(cmdq_pkt, addr, &ovl->cmdq_reg, ovl->regs,
DISP_REG_OVL_ADDR(ovl, idx));
if (ovl->data->supports_afbc)
mtk_ovl_afbc_layer_config(ovl, idx, pending, cmdq_pkt);
mtk_ovl_set_bit_depth(dev, idx, fmt, cmdq_pkt);
mtk_ovl_layer_on(dev, idx, cmdq_pkt);
}
void mtk_ovl_bgclr_in_on(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
unsigned int reg;
reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON);
reg = reg | OVL_BGCLR_SEL_IN;
writel(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON);
}
void mtk_ovl_bgclr_in_off(struct device *dev)
{
struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
unsigned int reg;
reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON);
reg = reg & ~OVL_BGCLR_SEL_IN;
writel(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON);
}
static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_ovl_component_ops = {
.bind = mtk_disp_ovl_bind,
.unbind = mtk_disp_ovl_unbind,
};
static int mtk_disp_ovl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_ovl *priv;
int irq;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get ovl clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap ovl\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
IRQF_TRIGGER_NONE, dev_name(dev), priv);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to request irq %d\n", irq);
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_disp_ovl_component_ops);
if (ret) {
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
return 0;
}
static void mtk_disp_ovl_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
pm_runtime_disable(&pdev->dev);
}
static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT2701,
.gmc_bits = 8,
.layer_nr = 4,
.fmt_rgb565_is_0 = false,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8167_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 8,
.layer_nr = 4,
.fmt_rgb565_is_0 = true,
.smi_id_en = true,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 8,
.layer_nr = 4,
.fmt_rgb565_is_0 = true,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8183_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 10,
.layer_nr = 4,
.fmt_rgb565_is_0 = true,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8183_ovl_2l_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 10,
.layer_nr = 2,
.fmt_rgb565_is_0 = true,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8192_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 10,
.layer_nr = 4,
.fmt_rgb565_is_0 = true,
.smi_id_en = true,
.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE) |
BIT(DRM_MODE_BLEND_PIXEL_NONE),
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8192_ovl_2l_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 10,
.layer_nr = 2,
.fmt_rgb565_is_0 = true,
.smi_id_en = true,
.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE) |
BIT(DRM_MODE_BLEND_PIXEL_NONE),
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_ovl_data mt8195_ovl_driver_data = {
.addr = DISP_REG_OVL_ADDR_MT8173,
.gmc_bits = 10,
.layer_nr = 4,
.fmt_rgb565_is_0 = true,
.smi_id_en = true,
.supports_afbc = true,
.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE) |
BIT(DRM_MODE_BLEND_PIXEL_NONE),
.formats = mt8195_formats,
.num_formats = ARRAY_SIZE(mt8195_formats),
.supports_clrfmt_ext = true,
};
static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
{ .compatible = "mediatek,mt2701-disp-ovl",
.data = &mt2701_ovl_driver_data},
{ .compatible = "mediatek,mt8167-disp-ovl",
.data = &mt8167_ovl_driver_data},
{ .compatible = "mediatek,mt8173-disp-ovl",
.data = &mt8173_ovl_driver_data},
{ .compatible = "mediatek,mt8183-disp-ovl",
.data = &mt8183_ovl_driver_data},
{ .compatible = "mediatek,mt8183-disp-ovl-2l",
.data = &mt8183_ovl_2l_driver_data},
{ .compatible = "mediatek,mt8192-disp-ovl",
.data = &mt8192_ovl_driver_data},
{ .compatible = "mediatek,mt8192-disp-ovl-2l",
.data = &mt8192_ovl_2l_driver_data},
{ .compatible = "mediatek,mt8195-disp-ovl",
.data = &mt8195_ovl_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
struct platform_driver mtk_disp_ovl_driver = {
.probe = mtk_disp_ovl_probe,
.remove = mtk_disp_ovl_remove,
.driver = {
.name = "mediatek-disp-ovl",
.of_match_table = mtk_disp_ovl_driver_dt_match,
},
};
@@ -0,0 +1,684 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <drm/drm_fourcc.h>
#include <drm/drm_of.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include <linux/soc/mediatek/mtk-mutex.h>
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#include "mtk_ethdr.h"
#define MTK_OVL_ADAPTOR_RDMA_MAX_WIDTH 1920
#define MTK_OVL_ADAPTOR_LAYER_NUM 4
enum mtk_ovl_adaptor_comp_type {
OVL_ADAPTOR_TYPE_ETHDR,
OVL_ADAPTOR_TYPE_MDP_RDMA,
OVL_ADAPTOR_TYPE_MERGE,
OVL_ADAPTOR_TYPE_PADDING,
OVL_ADAPTOR_TYPE_NUM,
};
enum mtk_ovl_adaptor_comp_id {
OVL_ADAPTOR_ETHDR0,
OVL_ADAPTOR_MDP_RDMA0,
OVL_ADAPTOR_MDP_RDMA1,
OVL_ADAPTOR_MDP_RDMA2,
OVL_ADAPTOR_MDP_RDMA3,
OVL_ADAPTOR_MDP_RDMA4,
OVL_ADAPTOR_MDP_RDMA5,
OVL_ADAPTOR_MDP_RDMA6,
OVL_ADAPTOR_MDP_RDMA7,
OVL_ADAPTOR_MERGE0,
OVL_ADAPTOR_MERGE1,
OVL_ADAPTOR_MERGE2,
OVL_ADAPTOR_MERGE3,
OVL_ADAPTOR_PADDING0,
OVL_ADAPTOR_PADDING1,
OVL_ADAPTOR_PADDING2,
OVL_ADAPTOR_PADDING3,
OVL_ADAPTOR_PADDING4,
OVL_ADAPTOR_PADDING5,
OVL_ADAPTOR_PADDING6,
OVL_ADAPTOR_PADDING7,
OVL_ADAPTOR_ID_MAX
};
struct ovl_adaptor_comp_match {
enum mtk_ovl_adaptor_comp_type type;
enum mtk_ddp_comp_id comp_id;
int alias_id;
const struct mtk_ddp_comp_funcs *funcs;
};
struct mtk_disp_ovl_adaptor {
struct device *ovl_adaptor_comp[OVL_ADAPTOR_ID_MAX];
struct device *mmsys_dev;
bool children_bound;
};
static const char * const private_comp_stem[OVL_ADAPTOR_TYPE_NUM] = {
[OVL_ADAPTOR_TYPE_ETHDR] = "ethdr",
[OVL_ADAPTOR_TYPE_MDP_RDMA] = "vdo1-rdma",
[OVL_ADAPTOR_TYPE_MERGE] = "merge",
[OVL_ADAPTOR_TYPE_PADDING] = "padding",
};
static const struct mtk_ddp_comp_funcs ethdr = {
.clk_enable = mtk_ethdr_clk_enable,
.clk_disable = mtk_ethdr_clk_disable,
.start = mtk_ethdr_start,
.stop = mtk_ethdr_stop,
};
static const struct mtk_ddp_comp_funcs merge = {
.clk_enable = mtk_merge_clk_enable,
.clk_disable = mtk_merge_clk_disable,
.mode_valid = mtk_merge_mode_valid,
};
static const struct mtk_ddp_comp_funcs padding = {
.clk_enable = mtk_padding_clk_enable,
.clk_disable = mtk_padding_clk_disable,
.start = mtk_padding_start,
.stop = mtk_padding_stop,
};
static const struct mtk_ddp_comp_funcs rdma = {
.power_on = mtk_mdp_rdma_power_on,
.power_off = mtk_mdp_rdma_power_off,
.clk_enable = mtk_mdp_rdma_clk_enable,
.clk_disable = mtk_mdp_rdma_clk_disable,
};
static const struct ovl_adaptor_comp_match comp_matches[OVL_ADAPTOR_ID_MAX] = {
[OVL_ADAPTOR_ETHDR0] = { OVL_ADAPTOR_TYPE_ETHDR, DDP_COMPONENT_ETHDR_MIXER, 0, &ethdr },
[OVL_ADAPTOR_MDP_RDMA0] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA0, 0, &rdma },
[OVL_ADAPTOR_MDP_RDMA1] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA1, 1, &rdma },
[OVL_ADAPTOR_MDP_RDMA2] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA2, 2, &rdma },
[OVL_ADAPTOR_MDP_RDMA3] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA3, 3, &rdma },
[OVL_ADAPTOR_MDP_RDMA4] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA4, 4, &rdma },
[OVL_ADAPTOR_MDP_RDMA5] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA5, 5, &rdma },
[OVL_ADAPTOR_MDP_RDMA6] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA6, 6, &rdma },
[OVL_ADAPTOR_MDP_RDMA7] = { OVL_ADAPTOR_TYPE_MDP_RDMA, DDP_COMPONENT_MDP_RDMA7, 7, &rdma },
[OVL_ADAPTOR_MERGE0] = { OVL_ADAPTOR_TYPE_MERGE, DDP_COMPONENT_MERGE1, 1, &merge },
[OVL_ADAPTOR_MERGE1] = { OVL_ADAPTOR_TYPE_MERGE, DDP_COMPONENT_MERGE2, 2, &merge },
[OVL_ADAPTOR_MERGE2] = { OVL_ADAPTOR_TYPE_MERGE, DDP_COMPONENT_MERGE3, 3, &merge },
[OVL_ADAPTOR_MERGE3] = { OVL_ADAPTOR_TYPE_MERGE, DDP_COMPONENT_MERGE4, 4, &merge },
[OVL_ADAPTOR_PADDING0] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING0, 0, &padding },
[OVL_ADAPTOR_PADDING1] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING1, 1, &padding },
[OVL_ADAPTOR_PADDING2] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING2, 2, &padding },
[OVL_ADAPTOR_PADDING3] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING3, 3, &padding },
[OVL_ADAPTOR_PADDING4] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING4, 4, &padding },
[OVL_ADAPTOR_PADDING5] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING5, 5, &padding },
[OVL_ADAPTOR_PADDING6] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING6, 6, &padding },
[OVL_ADAPTOR_PADDING7] = { OVL_ADAPTOR_TYPE_PADDING, DDP_COMPONENT_PADDING7, 7, &padding },
};
void mtk_ovl_adaptor_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
struct mtk_plane_pending_state *pending = &state->pending;
struct mtk_mdp_rdma_cfg rdma_config = {0};
struct device *rdma_l;
struct device *rdma_r;
struct device *merge;
struct device *ethdr;
const struct drm_format_info *fmt_info = drm_format_info(pending->format);
bool use_dual_pipe = false;
unsigned int align_width;
unsigned int l_w = 0;
unsigned int r_w = 0;
dev_dbg(dev, "%s+ idx:%d, enable:%d, fmt:0x%x\n", __func__, idx,
pending->enable, pending->format);
dev_dbg(dev, "addr 0x%pad, fb w:%d, {%d,%d,%d,%d}\n",
&pending->addr, (pending->pitch / fmt_info->cpp[0]),
pending->x, pending->y, pending->width, pending->height);
rdma_l = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0 + 2 * idx];
rdma_r = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0 + 2 * idx + 1];
merge = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MERGE0 + idx];
ethdr = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0];
if (!pending->enable || !pending->width || !pending->height) {
mtk_merge_stop_cmdq(merge, cmdq_pkt);
mtk_mdp_rdma_stop(rdma_l, cmdq_pkt);
mtk_mdp_rdma_stop(rdma_r, cmdq_pkt);
mtk_ethdr_layer_config(ethdr, idx, state, cmdq_pkt);
return;
}
/* ETHDR is in 1T2P domain, width needs to be 2 pixels align */
align_width = ALIGN_DOWN(pending->width, 2);
if (align_width > MTK_OVL_ADAPTOR_RDMA_MAX_WIDTH)
use_dual_pipe = true;
if (use_dual_pipe) {
l_w = (align_width / 2) + ((pending->width / 2) % 2);
r_w = align_width - l_w;
} else {
l_w = align_width;
}
mtk_merge_advance_config(merge, l_w, r_w, pending->height, 0, 0, cmdq_pkt);
mtk_mmsys_merge_async_config(ovl_adaptor->mmsys_dev, idx, align_width / 2,
pending->height, cmdq_pkt);
rdma_config.width = l_w;
rdma_config.height = pending->height;
rdma_config.addr0 = pending->addr;
rdma_config.pitch = pending->pitch;
rdma_config.fmt = pending->format;
rdma_config.color_encoding = pending->color_encoding;
mtk_mdp_rdma_config(rdma_l, &rdma_config, cmdq_pkt);
if (use_dual_pipe) {
rdma_config.x_left = l_w;
rdma_config.width = r_w;
mtk_mdp_rdma_config(rdma_r, &rdma_config, cmdq_pkt);
}
mtk_merge_start_cmdq(merge, cmdq_pkt);
mtk_mdp_rdma_start(rdma_l, cmdq_pkt);
if (use_dual_pipe)
mtk_mdp_rdma_start(rdma_r, cmdq_pkt);
else
mtk_mdp_rdma_stop(rdma_r, cmdq_pkt);
mtk_ethdr_layer_config(ethdr, idx, state, cmdq_pkt);
}
void mtk_ovl_adaptor_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
mtk_ethdr_config(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0], w, h,
vrefresh, bpc, cmdq_pkt);
}
void mtk_ovl_adaptor_start(struct device *dev)
{
int i;
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
if (!ovl_adaptor->ovl_adaptor_comp[i] ||
!comp_matches[i].funcs->start)
continue;
comp_matches[i].funcs->start(ovl_adaptor->ovl_adaptor_comp[i]);
}
}
void mtk_ovl_adaptor_stop(struct device *dev)
{
int i;
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
if (!ovl_adaptor->ovl_adaptor_comp[i] ||
!comp_matches[i].funcs->stop)
continue;
comp_matches[i].funcs->stop(ovl_adaptor->ovl_adaptor_comp[i]);
}
}
/**
* power_off - Power off the devices in OVL adaptor
* @dev: Device to be powered off
* @num: Number of the devices to be powered off
*
* Calls the .power_off() ovl_adaptor component callback if it is present.
*/
static inline void power_off(struct device *dev, int num)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
int i;
if (num > OVL_ADAPTOR_ID_MAX)
num = OVL_ADAPTOR_ID_MAX;
for (i = num - 1; i >= 0; i--) {
if (!ovl_adaptor->ovl_adaptor_comp[i] ||
!comp_matches[i].funcs->power_off)
continue;
comp_matches[i].funcs->power_off(ovl_adaptor->ovl_adaptor_comp[i]);
}
}
/**
* mtk_ovl_adaptor_power_on - Power on the devices in OVL adaptor
* @dev: Device to be powered on
*
* Different from OVL, OVL adaptor is a pseudo device so
* we didn't define it in the device tree, pm_runtime_resume_and_get()
* called by .atomic_enable() power on no device in OVL adaptor,
* we have to implement a function to do the job instead.
*
* Return: Zero for success or negative number for failure.
*/
int mtk_ovl_adaptor_power_on(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
int i, ret;
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
if (!ovl_adaptor->ovl_adaptor_comp[i] ||
!comp_matches[i].funcs->power_on)
continue;
ret = comp_matches[i].funcs->power_on(ovl_adaptor->ovl_adaptor_comp[i]);
if (ret < 0) {
dev_err(dev, "Failed to enable power domain %d, err %d\n", i, ret);
power_off(dev, i);
return ret;
}
}
return 0;
}
void mtk_ovl_adaptor_power_off(struct device *dev)
{
power_off(dev, OVL_ADAPTOR_ID_MAX);
}
int mtk_ovl_adaptor_clk_enable(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
struct device *comp;
int ret;
int i;
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
comp = ovl_adaptor->ovl_adaptor_comp[i];
if (!comp || !comp_matches[i].funcs->clk_enable)
continue;
ret = comp_matches[i].funcs->clk_enable(comp);
if (ret) {
dev_err(dev, "Failed to enable clock %d, err %d\n", i, ret);
while (--i >= 0)
comp_matches[i].funcs->clk_disable(comp);
return ret;
}
}
return 0;
}
void mtk_ovl_adaptor_clk_disable(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
struct device *comp;
int i;
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
comp = ovl_adaptor->ovl_adaptor_comp[i];
if (!comp || !comp_matches[i].funcs->clk_disable)
continue;
comp_matches[i].funcs->clk_disable(comp);
if (i < OVL_ADAPTOR_MERGE0)
pm_runtime_put(comp);
}
}
enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev,
const struct drm_display_mode *mode)
{
int i;
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
dev = ovl_adaptor->ovl_adaptor_comp[i];
if (!dev || !comp_matches[i].funcs->mode_valid)
continue;
return comp_matches[i].funcs->mode_valid(dev, mode);
}
return MODE_OK;
}
unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev)
{
return MTK_OVL_ADAPTOR_LAYER_NUM;
}
struct device *mtk_ovl_adaptor_dma_dev_get(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
return ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0];
}
void mtk_ovl_adaptor_register_vblank_cb(struct device *dev, void (*vblank_cb)(void *),
void *vblank_cb_data)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
mtk_ethdr_register_vblank_cb(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0],
vblank_cb, vblank_cb_data);
}
void mtk_ovl_adaptor_unregister_vblank_cb(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
mtk_ethdr_unregister_vblank_cb(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]);
}
void mtk_ovl_adaptor_enable_vblank(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
mtk_ethdr_enable_vblank(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]);
}
void mtk_ovl_adaptor_disable_vblank(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
mtk_ethdr_disable_vblank(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]);
}
u32 mtk_ovl_adaptor_get_blend_modes(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
return mtk_ethdr_get_blend_modes(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]);
}
const u32 *mtk_ovl_adaptor_get_formats(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
return mtk_mdp_rdma_get_formats(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]);
}
size_t mtk_ovl_adaptor_get_num_formats(struct device *dev)
{
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
return mtk_mdp_rdma_get_num_formats(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]);
}
void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex)
{
int i;
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
if (!ovl_adaptor->ovl_adaptor_comp[i])
continue;
mtk_mutex_add_comp(mutex, comp_matches[i].comp_id);
}
}
void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex)
{
int i;
struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
if (!ovl_adaptor->ovl_adaptor_comp[i])
continue;
mtk_mutex_remove_comp(mutex, comp_matches[i].comp_id);
}
}
void mtk_ovl_adaptor_connect(struct device *dev, struct device *mmsys_dev, unsigned int next)
{
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_ETHDR_MIXER, next);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MDP_RDMA0, DDP_COMPONENT_MERGE1);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MDP_RDMA1, DDP_COMPONENT_MERGE1);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MDP_RDMA2, DDP_COMPONENT_MERGE2);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MERGE1, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MERGE2, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MERGE3, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_connect(mmsys_dev, DDP_COMPONENT_MERGE4, DDP_COMPONENT_ETHDR_MIXER);
}
void mtk_ovl_adaptor_disconnect(struct device *dev, struct device *mmsys_dev, unsigned int next)
{
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_ETHDR_MIXER, next);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MDP_RDMA0, DDP_COMPONENT_MERGE1);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MDP_RDMA1, DDP_COMPONENT_MERGE1);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MDP_RDMA2, DDP_COMPONENT_MERGE2);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MERGE1, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MERGE2, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MERGE3, DDP_COMPONENT_ETHDR_MIXER);
mtk_mmsys_ddp_disconnect(mmsys_dev, DDP_COMPONENT_MERGE4, DDP_COMPONENT_ETHDR_MIXER);
}
static int ovl_adaptor_comp_get_id(struct device *dev, struct device_node *node,
enum mtk_ovl_adaptor_comp_type type)
{
int alias_id = of_alias_get_id(node, private_comp_stem[type]);
int i;
for (i = 0; i < ARRAY_SIZE(comp_matches); i++)
if (comp_matches[i].type == type &&
comp_matches[i].alias_id == alias_id)
return i;
dev_warn(dev, "Failed to get id. type: %d, alias: %d\n", type, alias_id);
return -EINVAL;
}
static const struct of_device_id mtk_ovl_adaptor_comp_dt_ids[] = {
{ .compatible = "mediatek,mt8188-disp-padding", .data = (void *)OVL_ADAPTOR_TYPE_PADDING },
{ .compatible = "mediatek,mt8195-disp-ethdr", .data = (void *)OVL_ADAPTOR_TYPE_ETHDR },
{ .compatible = "mediatek,mt8195-disp-merge", .data = (void *)OVL_ADAPTOR_TYPE_MERGE },
{ .compatible = "mediatek,mt8195-vdo1-rdma", .data = (void *)OVL_ADAPTOR_TYPE_MDP_RDMA },
{ /* sentinel */ }
};
static int ovl_adaptor_of_get_ddp_comp_type(struct device_node *node,
enum mtk_ovl_adaptor_comp_type *ctype)
{
const struct of_device_id *of_id = of_match_node(mtk_ovl_adaptor_comp_dt_ids, node);
if (!of_id)
return -EINVAL;
*ctype = (enum mtk_ovl_adaptor_comp_type)((uintptr_t)of_id->data);
return 0;
}
bool mtk_ovl_adaptor_is_comp_present(struct device_node *node)
{
enum mtk_ovl_adaptor_comp_type type;
int ret;
ret = ovl_adaptor_of_get_ddp_comp_type(node, &type);
if (ret)
return false;
if (type >= OVL_ADAPTOR_TYPE_NUM)
return false;
/*
* In the context of mediatek-drm, ETHDR, MDP_RDMA and Padding are
* used exclusively by OVL Adaptor: if this component is not one of
* those, it's likely not an OVL Adaptor path.
*/
return type == OVL_ADAPTOR_TYPE_ETHDR ||
type == OVL_ADAPTOR_TYPE_MDP_RDMA ||
type == OVL_ADAPTOR_TYPE_PADDING;
}
static void ovl_adaptor_put_device(void *_dev)
{
struct device *dev = _dev;
put_device(dev);
}
static int ovl_adaptor_comp_init(struct device *dev, struct component_match **match)
{
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
struct device_node *parent;
struct platform_device *comp_pdev;
parent = dev->parent->parent->of_node->parent;
for_each_child_of_node_scoped(parent, node) {
enum mtk_ovl_adaptor_comp_type type;
int id, ret;
ret = ovl_adaptor_of_get_ddp_comp_type(node, &type);
if (ret)
continue;
if (!of_device_is_available(node)) {
dev_dbg(dev, "Skipping disabled component %pOF\n",
node);
continue;
}
id = ovl_adaptor_comp_get_id(dev, node, type);
if (id < 0) {
dev_warn(dev, "Skipping unknown component %pOF\n",
node);
continue;
}
comp_pdev = of_find_device_by_node(node);
if (!comp_pdev)
return -EPROBE_DEFER;
ret = devm_add_action_or_reset(dev, ovl_adaptor_put_device,
&comp_pdev->dev);
if (ret)
return ret;
priv->ovl_adaptor_comp[id] = &comp_pdev->dev;
drm_of_component_match_add(dev, match, component_compare_of, node);
dev_dbg(dev, "Adding component match for %pOF\n", node);
}
if (!*match) {
dev_err(dev, "No match device for ovl_adaptor\n");
return -ENODEV;
}
return 0;
}
static int mtk_disp_ovl_adaptor_comp_bind(struct device *dev, struct device *master,
void *data)
{
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
if (!priv->children_bound)
return -EPROBE_DEFER;
return 0;
}
static void mtk_disp_ovl_adaptor_comp_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_ovl_adaptor_comp_ops = {
.bind = mtk_disp_ovl_adaptor_comp_bind,
.unbind = mtk_disp_ovl_adaptor_comp_unbind,
};
static int mtk_disp_ovl_adaptor_master_bind(struct device *dev)
{
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
int ret;
ret = component_bind_all(dev, priv->mmsys_dev);
if (ret)
return dev_err_probe(dev, ret, "component_bind_all failed!\n");
priv->children_bound = true;
return 0;
}
static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev)
{
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
priv->children_bound = false;
component_unbind_all(dev, priv->mmsys_dev);
}
static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
.bind = mtk_disp_ovl_adaptor_master_bind,
.unbind = mtk_disp_ovl_adaptor_master_unbind,
};
static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
{
struct mtk_disp_ovl_adaptor *priv;
struct device *dev = &pdev->dev;
struct component_match *match = NULL;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
ret = ovl_adaptor_comp_init(dev, &match);
if (ret < 0)
return ret;
priv->mmsys_dev = pdev->dev.platform_data;
ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component master\n");
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops);
if (ret != 0) {
component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
return 0;
}
static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);
component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(&pdev->dev);
}
struct platform_driver mtk_disp_ovl_adaptor_driver = {
.probe = mtk_disp_ovl_adaptor_probe,
.remove = mtk_disp_ovl_adaptor_remove,
.driver = {
.name = "mediatek-disp-ovl-adaptor",
},
};
@@ -0,0 +1,423 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 MediaTek Inc.
*/
#include <drm/drm_fourcc.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#define DISP_REG_RDMA_INT_ENABLE 0x0000
#define DISP_REG_RDMA_INT_STATUS 0x0004
#define RDMA_TARGET_LINE_INT BIT(5)
#define RDMA_FIFO_UNDERFLOW_INT BIT(4)
#define RDMA_EOF_ABNORMAL_INT BIT(3)
#define RDMA_FRAME_END_INT BIT(2)
#define RDMA_FRAME_START_INT BIT(1)
#define RDMA_REG_UPDATE_INT BIT(0)
#define DISP_REG_RDMA_GLOBAL_CON 0x0010
#define RDMA_ENGINE_EN BIT(0)
#define RDMA_MODE_MEMORY BIT(1)
#define DISP_REG_RDMA_SIZE_CON_0 0x0014
#define RDMA_MATRIX_ENABLE BIT(17)
#define RDMA_MATRIX_INT_MTX_SEL GENMASK(23, 20)
#define RDMA_MATRIX_INT_MTX_BT601_to_RGB (6 << 20)
#define DISP_REG_RDMA_SIZE_CON_1 0x0018
#define DISP_REG_RDMA_TARGET_LINE 0x001c
#define DISP_RDMA_MEM_CON 0x0024
#define MEM_MODE_INPUT_FORMAT_RGB565 (0x000 << 4)
#define MEM_MODE_INPUT_FORMAT_RGB888 (0x001 << 4)
#define MEM_MODE_INPUT_FORMAT_RGBA8888 (0x002 << 4)
#define MEM_MODE_INPUT_FORMAT_ARGB8888 (0x003 << 4)
#define MEM_MODE_INPUT_FORMAT_UYVY (0x004 << 4)
#define MEM_MODE_INPUT_FORMAT_YUYV (0x005 << 4)
#define MEM_MODE_INPUT_SWAP BIT(8)
#define DISP_RDMA_MEM_SRC_PITCH 0x002c
#define DISP_RDMA_MEM_GMC_SETTING_0 0x0030
#define DISP_REG_RDMA_FIFO_CON 0x0040
#define RDMA_FIFO_UNDERFLOW_EN BIT(31)
#define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16)
#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16)
#define RDMA_FIFO_SIZE(rdma) ((rdma)->data->fifo_size)
#define DISP_RDMA_MEM_START_ADDR 0x0f00
#define RDMA_MEM_GMC 0x40402020
static const u32 mt8173_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
};
struct mtk_disp_rdma_data {
unsigned int fifo_size;
const u32 *formats;
size_t num_formats;
};
/*
* struct mtk_disp_rdma - DISP_RDMA driver structure
* @data: local driver data
*/
struct mtk_disp_rdma {
struct clk *clk;
void __iomem *regs;
struct cmdq_client_reg cmdq_reg;
const struct mtk_disp_rdma_data *data;
void (*vblank_cb)(void *data);
void *vblank_cb_data;
u32 fifo_size;
};
static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
{
struct mtk_disp_rdma *priv = dev_id;
/* Clear frame completion interrupt */
writel(0x0, priv->regs + DISP_REG_RDMA_INT_STATUS);
if (!priv->vblank_cb)
return IRQ_NONE;
priv->vblank_cb(priv->vblank_cb_data);
return IRQ_HANDLED;
}
static void rdma_update_bits(struct device *dev, unsigned int reg,
unsigned int mask, unsigned int val)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
unsigned int tmp = readl(rdma->regs + reg);
tmp = (tmp & ~mask) | (val & mask);
writel(tmp, rdma->regs + reg);
}
void mtk_rdma_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
rdma->vblank_cb = vblank_cb;
rdma->vblank_cb_data = vblank_cb_data;
}
void mtk_rdma_unregister_vblank_cb(struct device *dev)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
rdma->vblank_cb = NULL;
rdma->vblank_cb_data = NULL;
}
void mtk_rdma_enable_vblank(struct device *dev)
{
rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
RDMA_FRAME_END_INT);
}
void mtk_rdma_disable_vblank(struct device *dev)
{
rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
}
const u32 *mtk_rdma_get_formats(struct device *dev)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
return rdma->data->formats;
}
size_t mtk_rdma_get_num_formats(struct device *dev)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
return rdma->data->num_formats;
}
int mtk_rdma_clk_enable(struct device *dev)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
return clk_prepare_enable(rdma->clk);
}
void mtk_rdma_clk_disable(struct device *dev)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
clk_disable_unprepare(rdma->clk);
}
void mtk_rdma_start(struct device *dev)
{
rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
RDMA_ENGINE_EN);
}
void mtk_rdma_stop(struct device *dev)
{
rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
}
void mtk_rdma_config(struct device *dev, unsigned int width,
unsigned int height, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
unsigned int threshold;
unsigned int reg;
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
u32 rdma_fifo_size;
mtk_ddp_write_mask(cmdq_pkt, width, &rdma->cmdq_reg, rdma->regs,
DISP_REG_RDMA_SIZE_CON_0, 0xfff);
mtk_ddp_write_mask(cmdq_pkt, height, &rdma->cmdq_reg, rdma->regs,
DISP_REG_RDMA_SIZE_CON_1, 0xfffff);
if (rdma->fifo_size)
rdma_fifo_size = rdma->fifo_size;
else
rdma_fifo_size = RDMA_FIFO_SIZE(rdma);
/*
* Enable FIFO underflow since DSI and DPI can't be blocked.
* Keep the FIFO pseudo size reset default of 8 KiB. Set the
* output threshold to 70% of max fifo size to make sure the
* threhold will not overflow
*/
threshold = rdma_fifo_size * 7 / 10;
reg = RDMA_FIFO_UNDERFLOW_EN |
RDMA_FIFO_PSEUDO_SIZE(rdma_fifo_size) |
RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
mtk_ddp_write(cmdq_pkt, reg, &rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_FIFO_CON);
}
static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma,
unsigned int fmt)
{
/* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX"
* is defined in mediatek HW data sheet.
* The alphabet order in XXX is no relation to data
* arrangement in memory.
*/
switch (fmt) {
default:
case DRM_FORMAT_RGB565:
return MEM_MODE_INPUT_FORMAT_RGB565;
case DRM_FORMAT_BGR565:
return MEM_MODE_INPUT_FORMAT_RGB565 | MEM_MODE_INPUT_SWAP;
case DRM_FORMAT_RGB888:
return MEM_MODE_INPUT_FORMAT_RGB888;
case DRM_FORMAT_BGR888:
return MEM_MODE_INPUT_FORMAT_RGB888 | MEM_MODE_INPUT_SWAP;
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_RGBA8888:
return MEM_MODE_INPUT_FORMAT_ARGB8888;
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_BGRA8888:
return MEM_MODE_INPUT_FORMAT_ARGB8888 | MEM_MODE_INPUT_SWAP;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
return MEM_MODE_INPUT_FORMAT_RGBA8888;
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR8888:
return MEM_MODE_INPUT_FORMAT_RGBA8888 | MEM_MODE_INPUT_SWAP;
case DRM_FORMAT_UYVY:
return MEM_MODE_INPUT_FORMAT_UYVY;
case DRM_FORMAT_YUYV:
return MEM_MODE_INPUT_FORMAT_YUYV;
}
}
unsigned int mtk_rdma_layer_nr(struct device *dev)
{
return 1;
}
void mtk_rdma_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
struct mtk_plane_pending_state *pending = &state->pending;
unsigned int addr = pending->addr;
unsigned int pitch = pending->pitch & 0xffff;
unsigned int fmt = pending->format;
unsigned int con;
con = rdma_fmt_convert(rdma, fmt);
mtk_ddp_write_relaxed(cmdq_pkt, con, &rdma->cmdq_reg, rdma->regs, DISP_RDMA_MEM_CON);
if (fmt == DRM_FORMAT_UYVY || fmt == DRM_FORMAT_YUYV) {
mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_ENABLE, &rdma->cmdq_reg, rdma->regs,
DISP_REG_RDMA_SIZE_CON_0,
RDMA_MATRIX_ENABLE);
mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_INT_MTX_BT601_to_RGB,
&rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_SIZE_CON_0,
RDMA_MATRIX_INT_MTX_SEL);
} else {
mtk_ddp_write_mask(cmdq_pkt, 0, &rdma->cmdq_reg, rdma->regs,
DISP_REG_RDMA_SIZE_CON_0,
RDMA_MATRIX_ENABLE);
}
mtk_ddp_write_relaxed(cmdq_pkt, addr, &rdma->cmdq_reg, rdma->regs,
DISP_RDMA_MEM_START_ADDR);
mtk_ddp_write_relaxed(cmdq_pkt, pitch, &rdma->cmdq_reg, rdma->regs,
DISP_RDMA_MEM_SRC_PITCH);
mtk_ddp_write(cmdq_pkt, RDMA_MEM_GMC, &rdma->cmdq_reg, rdma->regs,
DISP_RDMA_MEM_GMC_SETTING_0);
mtk_ddp_write_mask(cmdq_pkt, RDMA_MODE_MEMORY, &rdma->cmdq_reg, rdma->regs,
DISP_REG_RDMA_GLOBAL_CON, RDMA_MODE_MEMORY);
}
static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_disp_rdma_component_ops = {
.bind = mtk_disp_rdma_bind,
.unbind = mtk_disp_rdma_unbind,
};
static int mtk_disp_rdma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_disp_rdma *priv;
int irq;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get rdma clk\n");
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap rdma\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
ret = of_property_read_u32(dev->of_node,
"mediatek,rdma-fifo-size",
&priv->fifo_size);
if (ret && (ret != -EINVAL))
return dev_err_probe(dev, ret, "Failed to get rdma fifo size\n");
/* Disable and clear pending interrupts */
writel(0x0, priv->regs + DISP_REG_RDMA_INT_ENABLE);
writel(0x0, priv->regs + DISP_REG_RDMA_INT_STATUS);
ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
IRQF_TRIGGER_NONE, dev_name(dev), priv);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to request irq %d\n", irq);
priv->data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, priv);
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_disp_rdma_component_ops);
if (ret) {
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
return 0;
}
static void mtk_disp_rdma_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
pm_runtime_disable(&pdev->dev);
}
static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = {
.fifo_size = SZ_4K,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = {
.fifo_size = SZ_8K,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = {
.fifo_size = 5 * SZ_1K,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = {
.fifo_size = 1920,
.formats = mt8173_formats,
.num_formats = ARRAY_SIZE(mt8173_formats),
};
static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
{ .compatible = "mediatek,mt2701-disp-rdma",
.data = &mt2701_rdma_driver_data},
{ .compatible = "mediatek,mt8173-disp-rdma",
.data = &mt8173_rdma_driver_data},
{ .compatible = "mediatek,mt8183-disp-rdma",
.data = &mt8183_rdma_driver_data},
{ .compatible = "mediatek,mt8195-disp-rdma",
.data = &mt8195_rdma_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
struct platform_driver mtk_disp_rdma_driver = {
.probe = mtk_disp_rdma_probe,
.remove = mtk_disp_rdma_remove,
.driver = {
.name = "mediatek-disp-rdma",
.of_match_table = mtk_disp_rdma_driver_dt_match,
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,362 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019-2022 MediaTek Inc.
* Copyright (c) 2022 BayLibre
*/
#ifndef _MTK_DP_REG_H_
#define _MTK_DP_REG_H_
#define SEC_OFFSET 0x4000
#define MTK_DP_HPD_DISCONNECT BIT(1)
#define MTK_DP_HPD_CONNECT BIT(2)
#define MTK_DP_HPD_INTERRUPT BIT(3)
/* offset: 0x0 */
#define DP_PHY_GLB_BIAS_GEN_00 0x0
#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(20, 16)
#define DP_PHY_GLB_DPAUX_TX 0x8
#define RG_CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
#define MTK_DP_0034 0x34
#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
#define DP_PHY_LANE_TX_0 0x104
#define RG_XTP_LN0_TX_IMPSEL_PMOS GENMASK(15, 12)
#define RG_XTP_LN0_TX_IMPSEL_NMOS GENMASK(19, 16)
#define DP_PHY_LANE_TX_1 0x204
#define RG_XTP_LN1_TX_IMPSEL_PMOS GENMASK(15, 12)
#define RG_XTP_LN1_TX_IMPSEL_NMOS GENMASK(19, 16)
#define DP_PHY_LANE_TX_2 0x304
#define RG_XTP_LN2_TX_IMPSEL_PMOS GENMASK(15, 12)
#define RG_XTP_LN2_TX_IMPSEL_NMOS GENMASK(19, 16)
#define DP_PHY_LANE_TX_3 0x404
#define RG_XTP_LN3_TX_IMPSEL_PMOS GENMASK(15, 12)
#define RG_XTP_LN3_TX_IMPSEL_NMOS GENMASK(19, 16)
#define MTK_DP_1040 0x1040
#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
#define RG_XTP_GLB_CKDET_EN BIT(1)
#define RG_DPAUX_RX_EN BIT(0)
/* offset: TOP_OFFSET (0x2000) */
#define MTK_DP_TOP_PWR_STATE 0x2000
#define DP_PWR_STATE_MASK GENMASK(1, 0)
#define DP_PWR_STATE_BANDGAP BIT(0)
#define DP_PWR_STATE_BANDGAP_TPLL BIT(1)
#define DP_PWR_STATE_BANDGAP_TPLL_LANE GENMASK(1, 0)
#define MTK_DP_TOP_SWING_EMP 0x2004
#define DP_TX0_VOLT_SWING_MASK GENMASK(1, 0)
#define DP_TX0_VOLT_SWING_SHIFT 0
#define DP_TX0_PRE_EMPH_MASK GENMASK(3, 2)
#define DP_TX0_PRE_EMPH_SHIFT 2
#define DP_TX1_VOLT_SWING_MASK GENMASK(9, 8)
#define DP_TX1_VOLT_SWING_SHIFT 8
#define DP_TX1_PRE_EMPH_MASK GENMASK(11, 10)
#define DP_TX2_VOLT_SWING_MASK GENMASK(17, 16)
#define DP_TX2_PRE_EMPH_MASK GENMASK(19, 18)
#define DP_TX3_VOLT_SWING_MASK GENMASK(25, 24)
#define DP_TX3_PRE_EMPH_MASK GENMASK(27, 26)
#define MTK_DP_TOP_RESET_AND_PROBE 0x2020
#define SW_RST_B_PHYD BIT(4)
#define MTK_DP_TOP_IRQ_MASK 0x202c
#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
#define MTK_DP_TOP_MEM_PD 0x2038
#define MEM_ISO_EN BIT(0)
#define FUSE_SEL BIT(2)
/* offset: ENC0_OFFSET (0x3000) */
#define MTK_DP_ENC0_P0_3000 0x3000
#define LANE_NUM_DP_ENC0_P0_MASK GENMASK(1, 0)
#define VIDEO_MUTE_SW_DP_ENC0_P0 BIT(2)
#define VIDEO_MUTE_SEL_DP_ENC0_P0 BIT(3)
#define ENHANCED_FRAME_EN_DP_ENC0_P0 BIT(4)
#define MTK_DP_ENC0_P0_3004 0x3004
#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK BIT(8)
#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0 BIT(9)
#define SDP_RESET_SW_DP_ENC0_P0 BIT(13)
#define MTK_DP_ENC0_P0_3010 0x3010
#define HTOTAL_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3014 0x3014
#define VTOTAL_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3018 0x3018
#define HSTART_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_301C 0x301c
#define VSTART_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3020 0x3020
#define HWIDTH_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3024 0x3024
#define VHEIGHT_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3028 0x3028
#define HSW_SW_DP_ENC0_P0_MASK GENMASK(14, 0)
#define HSP_SW_DP_ENC0_P0_MASK BIT(15)
#define MTK_DP_ENC0_P0_302C 0x302c
#define VSW_SW_DP_ENC0_P0_MASK GENMASK(14, 0)
#define VSP_SW_DP_ENC0_P0_MASK BIT(15)
#define MTK_DP_ENC0_P0_3030 0x3030
#define HTOTAL_SEL_DP_ENC0_P0 BIT(0)
#define VTOTAL_SEL_DP_ENC0_P0 BIT(1)
#define HSTART_SEL_DP_ENC0_P0 BIT(2)
#define VSTART_SEL_DP_ENC0_P0 BIT(3)
#define HWIDTH_SEL_DP_ENC0_P0 BIT(4)
#define VHEIGHT_SEL_DP_ENC0_P0 BIT(5)
#define HSP_SEL_DP_ENC0_P0 BIT(6)
#define HSW_SEL_DP_ENC0_P0 BIT(7)
#define VSP_SEL_DP_ENC0_P0 BIT(8)
#define VSW_SEL_DP_ENC0_P0 BIT(9)
#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 BIT(11)
#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0 BIT(12)
#define MTK_DP_ENC0_P0_3034 0x3034
#define MTK_DP_ENC0_P0_3038 0x3038
#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK BIT(11)
#define MTK_DP_ENC0_P0_303C 0x303c
#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK GENMASK(5, 0)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK GENMASK(10, 8)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT (0 << 8)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT (1 << 8)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT (2 << 8)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT (3 << 8)
#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT (4 << 8)
#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK GENMASK(14, 12)
#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB (0 << 12)
#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422 (1 << 12)
#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420 (2 << 12)
#define VIDEO_MN_GEN_EN_DP_ENC0_P0 BIT(15)
#define MTK_DP_ENC0_P0_3040 0x3040
#define SDP_DOWN_CNT_DP_ENC0_P0_VAL 0x20
#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK GENMASK(11, 0)
#define MTK_DP_ENC0_P0_304C 0x304c
#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK BIT(2)
#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK BIT(8)
#define MTK_DP_ENC0_P0_3064 0x3064
#define HDE_NUM_LAST_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3088 0x3088
#define AU_EN_DP_ENC0_P0 BIT(6)
#define AUDIO_8CH_EN_DP_ENC0_P0_MASK BIT(7)
#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK BIT(8)
#define AUDIO_2CH_EN_DP_ENC0_P0_MASK BIT(14)
#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK BIT(15)
#define MTK_DP_ENC0_P0_308C 0x308c
#define CH_STATUS_0_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3090 0x3090
#define CH_STATUS_1_DP_ENC0_P0_MASK GENMASK(15, 0)
#define MTK_DP_ENC0_P0_3094 0x3094
#define CH_STATUS_2_DP_ENC0_P0_MASK GENMASK(7, 0)
#define MTK_DP_ENC0_P0_30A4 0x30a4
#define AU_TS_CFG_DP_ENC0_P0_MASK GENMASK(7, 0)
#define MTK_DP_ENC0_P0_30A8 0x30a8
#define MTK_DP_ENC0_P0_30BC 0x30bc
#define ISRC_CONT_DP_ENC0_P0 BIT(0)
#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK GENMASK(10, 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2 (1 << 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4 (2 << 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8 (3 << 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2 (5 << 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4 (6 << 8)
#define MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8 (7 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2 (1 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4 (2 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8 (3 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2 (4 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4 (5 << 8)
#define MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8 (7 << 8)
#define MTK_DP_ENC0_P0_30D8 0x30d8
#define MTK_DP_ENC0_P0_312C 0x312c
#define ASP_HB2_DP_ENC0_P0_MASK GENMASK(7, 0)
#define ASP_HB3_DP_ENC0_P0_MASK GENMASK(15, 8)
#define MTK_DP_ENC0_P0_3154 0x3154
#define PGEN_HTOTAL_DP_ENC0_P0_MASK GENMASK(13, 0)
#define MTK_DP_ENC0_P0_3158 0x3158
#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK GENMASK(13, 0)
#define MTK_DP_ENC0_P0_315C 0x315c
#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK GENMASK(13, 0)
#define MTK_DP_ENC0_P0_3160 0x3160
#define PGEN_HFDE_START_DP_ENC0_P0_MASK GENMASK(13, 0)
#define MTK_DP_ENC0_P0_3164 0x3164
#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK GENMASK(13, 0)
#define MTK_DP_ENC0_P0_3168 0x3168
#define PGEN_VTOTAL_DP_ENC0_P0_MASK GENMASK(12, 0)
#define MTK_DP_ENC0_P0_316C 0x316c
#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK GENMASK(12, 0)
#define MTK_DP_ENC0_P0_3170 0x3170
#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK GENMASK(12, 0)
#define MTK_DP_ENC0_P0_3174 0x3174
#define PGEN_VFDE_START_DP_ENC0_P0_MASK GENMASK(12, 0)
#define MTK_DP_ENC0_P0_3178 0x3178
#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK GENMASK(12, 0)
#define MTK_DP_ENC0_P0_31B0 0x31b0
#define PGEN_PATTERN_SEL_VAL 4
#define PGEN_PATTERN_SEL_MASK GENMASK(6, 4)
#define MTK_DP_ENC0_P0_31EC 0x31ec
#define AUDIO_CH_SRC_SEL_DP_ENC0_P0 BIT(4)
#define ISRC1_HB3_DP_ENC0_P0_MASK GENMASK(15, 8)
/* offset: ENC1_OFFSET (0x3200) */
#define MTK_DP_ENC1_P0_3200 0x3200
#define MTK_DP_ENC1_P0_3280 0x3280
#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK GENMASK(4, 0)
#define SDP_PACKET_W_DP_ENC1_P0 BIT(5)
#define SDP_PACKET_W_DP_ENC1_P0_MASK BIT(5)
#define MTK_DP_ENC1_P0_3300 0x3300
#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL 2
#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK GENMASK(9, 8)
#define MTK_DP_ENC1_P0_3304 0x3304
#define AU_PRTY_REGEN_DP_ENC1_P0_MASK BIT(8)
#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK BIT(9)
#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK BIT(12)
#define MTK_DP_ENC1_P0_3324 0x3324
#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK GENMASK(9, 8)
#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX 0
#define MTK_DP_ENC1_P0_3364 0x3364
#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL 0x20
#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK GENMASK(11, 0)
#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL 4
#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK GENMASK(15, 12)
#define MTK_DP_ENC1_P0_3368 0x3368
#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 BIT(0)
#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 BIT(4)
#define SDP_DP13_EN_DP_ENC1_P0 BIT(8)
#define BS2BS_MODE_DP_ENC1_P0 BIT(12)
#define BS2BS_MODE_DP_ENC1_P0_MASK GENMASK(13, 12)
#define BS2BS_MODE_DP_ENC1_P0_VAL 1
#define DP_ENC1_P0_3368_VAL (VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
SDP_DP13_EN_DP_ENC1_P0 | \
BS2BS_MODE_DP_ENC1_P0)
#define MTK_DP_ENC1_P0_3374 0x3374
#define SDP_ASP_INSERT_IN_HBLANK_DP_ENC1_P0_MASK BIT(12)
#define SDP_DOWN_ASP_CNT_INIT_DP_ENC1_P0_MASK GENMASK(11, 0)
#define MTK_DP_ENC1_P0_33F4 0x33f4
#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN BIT(0)
#define DP_ENC_DUMMY_RW_1 BIT(9)
/* offset: TRANS_OFFSET (0x3400) */
#define MTK_DP_TRANS_P0_3400 0x3400
#define PATTERN1_EN_DP_TRANS_P0_MASK BIT(12)
#define PATTERN2_EN_DP_TRANS_P0_MASK BIT(13)
#define PATTERN3_EN_DP_TRANS_P0_MASK BIT(14)
#define PATTERN4_EN_DP_TRANS_P0_MASK BIT(15)
#define MTK_DP_TRANS_P0_3404 0x3404
#define DP_SCR_EN_DP_TRANS_P0_MASK BIT(0)
#define MTK_DP_TRANS_P0_340C 0x340c
#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0 BIT(13)
#define MTK_DP_TRANS_P0_3410 0x3410
#define HPD_DEB_THD_DP_TRANS_P0_MASK GENMASK(3, 0)
#define HPD_INT_THD_DP_TRANS_P0_MASK GENMASK(7, 4)
#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << 4)
#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US (2 << 6)
#define HPD_DISC_THD_DP_TRANS_P0_MASK GENMASK(11, 8)
#define HPD_CONN_THD_DP_TRANS_P0_MASK GENMASK(15, 12)
#define MTK_DP_TRANS_P0_3414 0x3414
#define HPD_DB_DP_TRANS_P0_MASK BIT(2)
#define MTK_DP_TRANS_P0_3418 0x3418
#define IRQ_CLR_DP_TRANS_P0_MASK GENMASK(3, 0)
#define IRQ_MASK_DP_TRANS_P0_MASK GENMASK(7, 4)
#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << 4)
#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << 4)
#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << 4)
#define IRQ_STATUS_DP_TRANS_P0_MASK GENMASK(15, 12)
#define MTK_DP_TRANS_P0_342C 0x342c
#define XTAL_FREQ_DP_TRANS_P0_DEFAULT (BIT(0) | BIT(3) | BIT(5) | BIT(6))
#define XTAL_FREQ_DP_TRANS_P0_MASK GENMASK(7, 0)
#define MTK_DP_TRANS_P0_3430 0x3430
#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK GENMASK(1, 0)
#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
#define MTK_DP_TRANS_P0_34A4 0x34a4
#define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2)
#define MTK_DP_TRANS_P0_3540 0x3540
#define FEC_EN_DP_TRANS_P0_MASK BIT(0)
#define FEC_CLOCK_EN_MODE_DP_TRANS_P0 BIT(3)
#define MTK_DP_TRANS_P0_3580 0x3580
#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK BIT(8)
#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK BIT(9)
#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK BIT(10)
#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK BIT(11)
#define MTK_DP_TRANS_P0_35C8 0x35c8
#define SW_IRQ_CLR_DP_TRANS_P0_MASK GENMASK(15, 0)
#define SW_IRQ_STATUS_DP_TRANS_P0_MASK GENMASK(15, 0)
#define MTK_DP_TRANS_P0_35D0 0x35d0
#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK GENMASK(15, 0)
#define MTK_DP_TRANS_P0_35F0 0x35f0
#define DP_TRANS_DUMMY_RW_0 BIT(3)
#define DP_TRANS_DUMMY_RW_0_MASK GENMASK(3, 2)
/* offset: AUX_OFFSET (0x3600) */
#define MTK_DP_AUX_P0_360C 0x360c
#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK GENMASK(12, 0)
#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL 0x1595
#define MTK_DP_AUX_P0_3614 0x3614
#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK GENMASK(6, 0)
#define AUX_RX_UI_CNT_THR_AUX_FOR_26M 13
#define MTK_DP_AUX_P0_3618 0x3618
#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK BIT(9)
#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK GENMASK(3, 0)
#define MTK_DP_AUX_P0_3620 0x3620
#define AUX_RD_MODE_AUX_TX_P0_MASK BIT(9)
#define AUX_RX_FIFO_READ_PULSE_TX_P0 BIT(8)
#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK GENMASK(7, 0)
#define MTK_DP_AUX_P0_3624 0x3624
#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK GENMASK(3, 0)
#define MTK_DP_AUX_P0_3628 0x3628
#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK GENMASK(9, 0)
#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE BIT(0)
#define MTK_DP_AUX_P0_362C 0x362c
#define AUX_NO_LENGTH_AUX_TX_P0 BIT(0)
#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK BIT(1)
#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK GENMASK(15, 2)
#define MTK_DP_AUX_P0_3630 0x3630
#define AUX_TX_REQUEST_READY_AUX_TX_P0 BIT(3)
#define MTK_DP_AUX_P0_3634 0x3634
#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK GENMASK(15, 8)
#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M 25
#define MTK_DP_AUX_P0_3640 0x3640
#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(6)
#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(5)
#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(4)
#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 BIT(3)
#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 BIT(2)
#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 BIT(1)
#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 BIT(0)
#define DP_AUX_P0_3640_VAL (AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
#define MTK_DP_AUX_P0_3644 0x3644
#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK GENMASK(3, 0)
#define MTK_DP_AUX_P0_3648 0x3648
#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK GENMASK(15, 0)
#define MTK_DP_AUX_P0_364C 0x364c
#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK GENMASK(3, 0)
#define MTK_DP_AUX_P0_3650 0x3650
#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK GENMASK(15, 12)
#define PHY_FIFO_RST_AUX_TX_P0_MASK BIT(9)
#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 BIT(8)
#define MTK_DP_AUX_P0_3658 0x3658
#define AUX_TX_OV_EN_AUX_TX_P0_MASK BIT(0)
#define MTK_DP_AUX_P0_3690 0x3690
#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0 BIT(8)
#define MTK_DP_AUX_P0_3704 0x3704
#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK BIT(1)
#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0 BIT(2)
#define MTK_DP_AUX_P0_3708 0x3708
#define MTK_DP_AUX_P0_37C8 0x37c8
#define MTK_ATOP_EN_AUX_TX_P0 BIT(0)
#endif /*_MTK_DP_REG_H_*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,247 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Jie Qiu <jie.qiu@mediatek.com>
*/
#ifndef __MTK_DPI_REGS_H
#define __MTK_DPI_REGS_H
#define DPI_EN 0x00
#define EN BIT(0)
#define DPI_RET 0x04
#define RST BIT(0)
#define DPI_INTEN 0x08
#define INT_VSYNC_EN BIT(0)
#define INT_VDE_EN BIT(1)
#define INT_UNDERFLOW_EN BIT(2)
#define DPI_INTSTA 0x0C
#define INT_VSYNC_STA BIT(0)
#define INT_VDE_STA BIT(1)
#define INT_UNDERFLOW_STA BIT(2)
#define DPI_CON 0x10
#define BG_ENABLE BIT(0)
#define IN_RB_SWAP BIT(1)
#define INTL_EN BIT(2)
#define TDFP_EN BIT(3)
#define CLPF_EN BIT(4)
#define YUV422_EN BIT(5)
#define CSC_ENABLE BIT(6)
#define R601_SEL BIT(7)
#define EMBSYNC_EN BIT(8)
#define VS_LODD_EN BIT(16)
#define VS_LEVEN_EN BIT(17)
#define VS_RODD_EN BIT(18)
#define VS_REVEN BIT(19)
#define FAKE_DE_LODD BIT(20)
#define FAKE_DE_LEVEN BIT(21)
#define FAKE_DE_RODD BIT(22)
#define FAKE_DE_REVEN BIT(23)
/* DPI_CON: DPI instances */
#define DPI_OUTPUT_1T1P_EN BIT(24)
#define DPI_INPUT_2P_EN BIT(25)
/* DPI_CON: DPINTF instances */
#define DPINTF_YUV422_EN BIT(24)
#define DPINTF_CSC_ENABLE BIT(26)
#define DPINTF_INPUT_2P_EN BIT(29)
#define DPI_OUTPUT_SETTING 0x14
#define CH_SWAP 0
#define DPINTF_CH_SWAP 1
#define CH_SWAP_MASK (0x7 << 0)
#define SWAP_RGB 0x00
#define SWAP_GBR 0x01
#define SWAP_BRG 0x02
#define SWAP_RBG 0x03
#define SWAP_GRB 0x04
#define SWAP_BGR 0x05
#define BIT_SWAP BIT(3)
#define B_MASK BIT(4)
#define G_MASK BIT(5)
#define R_MASK BIT(6)
#define DE_MASK BIT(8)
#define HS_MASK BIT(9)
#define VS_MASK BIT(10)
#define DE_POL BIT(12)
#define HSYNC_POL BIT(13)
#define VSYNC_POL BIT(14)
#define CK_POL BIT(15)
#define OEN_OFF BIT(16)
#define EDGE_SEL BIT(17)
#define OUT_BIT 18
#define OUT_BIT_MASK (0x3 << 18)
#define OUT_BIT_8 0x00
#define OUT_BIT_10 0x01
#define OUT_BIT_12 0x02
#define OUT_BIT_16 0x03
#define YC_MAP 20
#define YC_MAP_MASK (0x7 << 20)
#define YC_MAP_RGB 0x00
#define YC_MAP_CYCY 0x04
#define YC_MAP_YCYC 0x05
#define YC_MAP_CY 0x06
#define YC_MAP_YC 0x07
#define DPI_SIZE 0x18
#define HSIZE 0
#define HSIZE_MASK (0x1FFF << 0)
#define DPINTF_HSIZE_MASK (0xFFFF << 0)
#define VSIZE 16
#define VSIZE_MASK (0x1FFF << 16)
#define DPINTF_VSIZE_MASK (0xFFFF << 16)
#define DPI_DDR_SETTING 0x1C
#define DDR_EN BIT(0)
#define DDDR_SEL BIT(1)
#define DDR_4PHASE BIT(2)
#define DDR_WIDTH (0x3 << 4)
#define DDR_PAD_MODE (0x1 << 8)
#define DPI_TGEN_HWIDTH 0x20
#define HPW 0
#define HPW_MASK (0xFFF << 0)
#define DPINTF_HPW_MASK (0xFFFF << 0)
#define DPI_TGEN_HPORCH 0x24
#define HBP 0
#define HBP_MASK (0xFFF << 0)
#define DPINTF_HBP_MASK (0xFFFF << 0)
#define HFP 16
#define HFP_MASK (0xFFF << 16)
#define DPINTF_HFP_MASK (0xFFFF << 16)
#define DPI_TGEN_VWIDTH 0x28
#define DPI_TGEN_VPORCH 0x2C
#define VSYNC_WIDTH_SHIFT 0
#define VSYNC_WIDTH_MASK (0xFFF << 0)
#define DPINTF_VSYNC_WIDTH_MASK (0xFFFF << 0)
#define VSYNC_HALF_LINE_SHIFT 16
#define VSYNC_HALF_LINE_MASK BIT(16)
#define VSYNC_BACK_PORCH_SHIFT 0
#define VSYNC_BACK_PORCH_MASK (0xFFF << 0)
#define DPINTF_VSYNC_BACK_PORCH_MASK (0xFFFF << 0)
#define VSYNC_FRONT_PORCH_SHIFT 16
#define VSYNC_FRONT_PORCH_MASK (0xFFF << 16)
#define DPINTF_VSYNC_FRONT_PORCH_MASK (0xFFFF << 16)
#define DPI_BG_HCNTL 0x30
#define BG_RIGHT (0x1FFF << 0)
#define BG_LEFT (0x1FFF << 16)
#define DPI_BG_VCNTL 0x34
#define BG_BOT (0x1FFF << 0)
#define BG_TOP (0x1FFF << 16)
#define DPI_BG_COLOR 0x38
#define BG_B (0xF << 0)
#define BG_G (0xF << 8)
#define BG_R (0xF << 16)
#define DPI_FIFO_CTL 0x3C
#define FIFO_VALID_SET (0x1F << 0)
#define FIFO_RST_SEL (0x1 << 8)
#define DPI_STATUS 0x40
#define VCOUNTER (0x1FFF << 0)
#define DPI_BUSY BIT(16)
#define OUTEN BIT(17)
#define FIELD BIT(20)
#define TDLR BIT(21)
#define DPI_TMODE 0x44
#define DPI_OEN_ON BIT(0)
#define DPI_CHECKSUM 0x48
#define DPI_CHECKSUM_MASK (0xFFFFFF << 0)
#define DPI_CHECKSUM_READY BIT(30)
#define DPI_CHECKSUM_EN BIT(31)
#define DPI_DUMMY 0x50
#define DPI_DUMMY_MASK (0xFFFFFFFF << 0)
#define DPI_TGEN_VWIDTH_LEVEN 0x68
#define DPI_TGEN_VPORCH_LEVEN 0x6C
#define DPI_TGEN_VWIDTH_RODD 0x70
#define DPI_TGEN_VPORCH_RODD 0x74
#define DPI_TGEN_VWIDTH_REVEN 0x78
#define DPI_TGEN_VPORCH_REVEN 0x7C
#define DPI_ESAV_VTIMING_LODD 0x80
#define ESAV_VOFST_LODD (0xFFF << 0)
#define ESAV_VWID_LODD (0xFFF << 16)
#define DPI_ESAV_VTIMING_LEVEN 0x84
#define ESAV_VOFST_LEVEN (0xFFF << 0)
#define ESAV_VWID_LEVEN (0xFFF << 16)
#define DPI_ESAV_VTIMING_RODD 0x88
#define ESAV_VOFST_RODD (0xFFF << 0)
#define ESAV_VWID_RODD (0xFFF << 16)
#define DPI_ESAV_VTIMING_REVEN 0x8C
#define ESAV_VOFST_REVEN (0xFFF << 0)
#define ESAV_VWID_REVEN (0xFFF << 16)
#define DPI_ESAV_FTIMING 0x90
#define ESAV_FOFST_ODD (0xFFF << 0)
#define ESAV_FOFST_EVEN (0xFFF << 16)
#define DPI_CLPF_SETTING 0x94
#define CLPF_TYPE (0x3 << 0)
#define ROUND_EN BIT(4)
#define DPI_Y_LIMIT 0x98
#define Y_LIMINT_BOT 0
#define Y_LIMINT_BOT_MASK (0xFFF << 0)
#define Y_LIMINT_TOP 16
#define Y_LIMINT_TOP_MASK (0xFFF << 16)
#define DPI_C_LIMIT 0x9C
#define C_LIMIT_BOT 0
#define C_LIMIT_BOT_MASK (0xFFF << 0)
#define C_LIMIT_TOP 16
#define C_LIMIT_TOP_MASK (0xFFF << 16)
#define DPI_YUV422_SETTING 0xA0
#define UV_SWAP BIT(0)
#define CR_DELSEL BIT(4)
#define CB_DELSEL BIT(5)
#define Y_DELSEL BIT(6)
#define DE_DELSEL BIT(7)
#define DPI_EMBSYNC_SETTING 0xA4
#define EMBSYNC_R_CR_EN BIT(0)
#define EMPSYNC_G_Y_EN BIT(1)
#define EMPSYNC_B_CB_EN BIT(2)
#define ESAV_F_INV BIT(4)
#define ESAV_V_INV BIT(5)
#define ESAV_H_INV BIT(6)
#define ESAV_CODE_MAN BIT(8)
#define VS_OUT_SEL (0x7 << 12)
#define DPI_ESAV_CODE_SET0 0xA8
#define ESAV_CODE0 (0xFFF << 0)
#define ESAV_CODE1 (0xFFF << 16)
#define DPI_ESAV_CODE_SET1 0xAC
#define ESAV_CODE2 (0xFFF << 0)
#define ESAV_CODE3_MSB BIT(16)
#define EDGE_SEL_EN BIT(5)
#define H_FRE_2N BIT(25)
#define DPI_MATRIX_SET 0xB4
#define INT_MATRIX_SEL_MASK GENMASK(4, 0)
#define MATRIX_SEL_RGB_TO_JPEG 0
#define MATRIX_SEL_RGB_TO_BT601 2
#define DPI_PATTERN0 0xf00
#define DPI_PAT_EN BIT(0)
#define DPI_PAT_SEL GENMASK(6, 4)
#endif /* __MTK_DPI_REGS_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015 MediaTek Inc.
*/
#ifndef MTK_DRM_DRV_H
#define MTK_DRM_DRV_H
#include <linux/io.h>
#include "mtk_ddp_comp.h"
#define MAX_CONNECTOR 2
#define DDP_COMPONENT_DRM_OVL_ADAPTOR (DDP_COMPONENT_ID_MAX + 1)
#define DDP_COMPONENT_DRM_ID_MAX (DDP_COMPONENT_DRM_OVL_ADAPTOR + 1)
enum mtk_crtc_path {
CRTC_MAIN,
CRTC_EXT,
CRTC_THIRD,
MAX_CRTC,
};
struct device;
struct device_node;
struct drm_crtc;
struct drm_device;
struct drm_fb_helper;
struct drm_property;
struct regmap;
struct mtk_drm_route {
const unsigned int crtc_id;
const unsigned int route_ddp;
};
struct mtk_mmsys_driver_data {
const unsigned int *main_path;
unsigned int main_len;
const unsigned int *ext_path;
unsigned int ext_len;
const unsigned int *third_path;
unsigned int third_len;
const struct mtk_drm_route *conn_routes;
unsigned int num_conn_routes;
bool shadow_register;
unsigned int mmsys_id;
unsigned int mmsys_dev_num;
u16 max_width;
u16 min_width;
u16 min_height;
};
struct mtk_drm_private {
struct drm_device *drm;
bool mtk_drm_bound;
bool drm_master;
struct device *dev;
struct device_node *mutex_node;
struct device *mutex_dev;
struct device *mmsys_dev;
struct device_node *comp_node[DDP_COMPONENT_DRM_ID_MAX];
struct mtk_ddp_comp ddp_comp[DDP_COMPONENT_DRM_ID_MAX];
struct mtk_mmsys_driver_data *data;
struct drm_atomic_commit *suspend_state;
unsigned int mbox_index;
struct mtk_drm_private **all_drm_private;
};
extern struct platform_driver mtk_disp_aal_driver;
extern struct platform_driver mtk_disp_ccorr_driver;
extern struct platform_driver mtk_disp_color_driver;
extern struct platform_driver mtk_disp_gamma_driver;
extern struct platform_driver mtk_disp_merge_driver;
extern struct platform_driver mtk_disp_ovl_adaptor_driver;
extern struct platform_driver mtk_disp_ovl_driver;
extern struct platform_driver mtk_disp_rdma_driver;
extern struct platform_driver mtk_dpi_driver;
extern struct platform_driver mtk_dsi_driver;
extern struct platform_driver mtk_ethdr_driver;
extern struct platform_driver mtk_mdp_rdma_driver;
extern struct platform_driver mtk_padding_driver;
#endif /* MTK_DRM_DRV_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,396 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_drm_drv.h"
#include "mtk_ethdr.h"
#define MIX_INTEN 0x4
#define MIX_FME_CPL_INTEN BIT(1)
#define MIX_INTSTA 0x8
#define MIX_EN 0xc
#define MIX_RST 0x14
#define MIX_ROI_SIZE 0x18
#define MIX_DATAPATH_CON 0x1c
#define OUTPUT_NO_RND BIT(3)
#define SOURCE_RGB_SEL BIT(7)
#define BACKGROUND_RELAY (4 << 9)
#define MIX_ROI_BGCLR 0x20
#define BGCLR_BLACK 0xff000000
#define MIX_SRC_CON 0x24
#define MIX_SRC_L0_EN BIT(0)
#define MIX_L_SRC_CON(n) (0x28 + 0x18 * (n))
#define NON_PREMULTI_SOURCE (2 << 12)
#define PREMULTI_SOURCE (3 << 12)
#define MIX_L_SRC_SIZE(n) (0x30 + 0x18 * (n))
#define MIX_L_SRC_OFFSET(n) (0x34 + 0x18 * (n))
#define MIX_FUNC_DCM0 0x120
#define MIX_FUNC_DCM1 0x124
#define MIX_FUNC_DCM_ENABLE 0xffffffff
#define HDR_VDO_FE_0804_HDR_DM_FE 0x804
#define HDR_VDO_FE_0804_BYPASS_ALL 0xfd
#define HDR_GFX_FE_0204_GFX_HDR_FE 0x204
#define HDR_GFX_FE_0204_BYPASS_ALL 0xfd
#define HDR_VDO_BE_0204_VDO_DM_BE 0x204
#define HDR_VDO_BE_0204_BYPASS_ALL 0x7e
#define MIXER_INX_MODE_BYPASS 0
#define MIXER_INX_MODE_EVEN_EXTEND 1
#define MIXER_ALPHA_AEN BIT(8)
#define MIXER_ALPHA 0xff
#define ETHDR_CLK_NUM 13
enum mtk_ethdr_comp_id {
ETHDR_MIXER,
ETHDR_VDO_FE0,
ETHDR_VDO_FE1,
ETHDR_GFX_FE0,
ETHDR_GFX_FE1,
ETHDR_VDO_BE,
ETHDR_ADL_DS,
ETHDR_ID_MAX
};
struct mtk_ethdr_comp {
struct device *dev;
void __iomem *regs;
struct cmdq_client_reg cmdq_base;
};
struct mtk_ethdr {
struct mtk_ethdr_comp ethdr_comp[ETHDR_ID_MAX];
struct clk_bulk_data ethdr_clk[ETHDR_CLK_NUM];
struct device *mmsys_dev;
void (*vblank_cb)(void *data);
void *vblank_cb_data;
int irq;
struct reset_control *reset_ctl;
};
static const char * const ethdr_clk_str[] = {
"ethdr_top",
"mixer",
"vdo_fe0",
"vdo_fe1",
"gfx_fe0",
"gfx_fe1",
"vdo_be",
"adl_ds",
"vdo_fe0_async",
"vdo_fe1_async",
"gfx_fe0_async",
"gfx_fe1_async",
"vdo_be_async",
};
void mtk_ethdr_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
priv->vblank_cb = vblank_cb;
priv->vblank_cb_data = vblank_cb_data;
}
void mtk_ethdr_unregister_vblank_cb(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
priv->vblank_cb = NULL;
priv->vblank_cb_data = NULL;
}
void mtk_ethdr_enable_vblank(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
writel(MIX_FME_CPL_INTEN, priv->ethdr_comp[ETHDR_MIXER].regs + MIX_INTEN);
}
void mtk_ethdr_disable_vblank(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
writel(0x0, priv->ethdr_comp[ETHDR_MIXER].regs + MIX_INTEN);
}
static irqreturn_t mtk_ethdr_irq_handler(int irq, void *dev_id)
{
struct mtk_ethdr *priv = dev_id;
writel(0x0, priv->ethdr_comp[ETHDR_MIXER].regs + MIX_INTSTA);
if (!priv->vblank_cb)
return IRQ_NONE;
priv->vblank_cb(priv->vblank_cb_data);
return IRQ_HANDLED;
}
u32 mtk_ethdr_get_blend_modes(struct device *dev)
{
return BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE) |
BIT(DRM_MODE_BLEND_PIXEL_NONE);
}
void mtk_ethdr_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
struct mtk_ethdr_comp *mixer = &priv->ethdr_comp[ETHDR_MIXER];
struct mtk_plane_pending_state *pending = &state->pending;
unsigned int offset = (pending->x & 1) << 31 | pending->y << 16 | pending->x;
unsigned int align_width = ALIGN_DOWN(pending->width, 2);
unsigned int alpha_con = 0;
bool replace_src_a = false;
dev_dbg(dev, "%s+ idx:%d", __func__, idx);
if (idx >= 4)
return;
if (!pending->enable || !pending->width || !pending->height) {
/*
* instead of disabling layer with MIX_SRC_CON directly
* set the size to 0 to avoid screen shift due to mixer
* mode switch (hardware behavior)
*/
mtk_ddp_write(cmdq_pkt, 0, &mixer->cmdq_base, mixer->regs, MIX_L_SRC_SIZE(idx));
return;
}
if (state->base.fb) {
alpha_con |= MIXER_ALPHA_AEN;
alpha_con |= state->base.alpha & MIXER_ALPHA;
}
if (state->base.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
alpha_con |= PREMULTI_SOURCE;
else
alpha_con |= NON_PREMULTI_SOURCE;
if ((state->base.fb && !state->base.fb->format->has_alpha) ||
state->base.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) {
/*
* Mixer doesn't support CONST_BLD mode,
* use a trick to make the output equivalent
*/
replace_src_a = true;
}
mtk_mmsys_mixer_in_config(priv->mmsys_dev, idx + 1, replace_src_a,
MIXER_ALPHA,
pending->x & 1 ? MIXER_INX_MODE_EVEN_EXTEND :
MIXER_INX_MODE_BYPASS, align_width / 2 - 1, cmdq_pkt);
mtk_ddp_write(cmdq_pkt, pending->height << 16 | align_width, &mixer->cmdq_base,
mixer->regs, MIX_L_SRC_SIZE(idx));
mtk_ddp_write(cmdq_pkt, offset, &mixer->cmdq_base, mixer->regs, MIX_L_SRC_OFFSET(idx));
mtk_ddp_write(cmdq_pkt, alpha_con, &mixer->cmdq_base, mixer->regs, MIX_L_SRC_CON(idx));
mtk_ddp_write_mask(cmdq_pkt, BIT(idx), &mixer->cmdq_base, mixer->regs, MIX_SRC_CON,
BIT(idx));
}
void mtk_ethdr_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
struct mtk_ethdr_comp *vdo_fe0 = &priv->ethdr_comp[ETHDR_VDO_FE0];
struct mtk_ethdr_comp *vdo_fe1 = &priv->ethdr_comp[ETHDR_VDO_FE1];
struct mtk_ethdr_comp *gfx_fe0 = &priv->ethdr_comp[ETHDR_GFX_FE0];
struct mtk_ethdr_comp *gfx_fe1 = &priv->ethdr_comp[ETHDR_GFX_FE1];
struct mtk_ethdr_comp *vdo_be = &priv->ethdr_comp[ETHDR_VDO_BE];
struct mtk_ethdr_comp *mixer = &priv->ethdr_comp[ETHDR_MIXER];
dev_dbg(dev, "%s-w:%d, h:%d\n", __func__, w, h);
mtk_ddp_write(cmdq_pkt, HDR_VDO_FE_0804_BYPASS_ALL, &vdo_fe0->cmdq_base,
vdo_fe0->regs, HDR_VDO_FE_0804_HDR_DM_FE);
mtk_ddp_write(cmdq_pkt, HDR_VDO_FE_0804_BYPASS_ALL, &vdo_fe1->cmdq_base,
vdo_fe1->regs, HDR_VDO_FE_0804_HDR_DM_FE);
mtk_ddp_write(cmdq_pkt, HDR_GFX_FE_0204_BYPASS_ALL, &gfx_fe0->cmdq_base,
gfx_fe0->regs, HDR_GFX_FE_0204_GFX_HDR_FE);
mtk_ddp_write(cmdq_pkt, HDR_GFX_FE_0204_BYPASS_ALL, &gfx_fe1->cmdq_base,
gfx_fe1->regs, HDR_GFX_FE_0204_GFX_HDR_FE);
mtk_ddp_write(cmdq_pkt, HDR_VDO_BE_0204_BYPASS_ALL, &vdo_be->cmdq_base,
vdo_be->regs, HDR_VDO_BE_0204_VDO_DM_BE);
mtk_ddp_write(cmdq_pkt, MIX_FUNC_DCM_ENABLE, &mixer->cmdq_base, mixer->regs, MIX_FUNC_DCM0);
mtk_ddp_write(cmdq_pkt, MIX_FUNC_DCM_ENABLE, &mixer->cmdq_base, mixer->regs, MIX_FUNC_DCM1);
mtk_ddp_write(cmdq_pkt, h << 16 | w, &mixer->cmdq_base, mixer->regs, MIX_ROI_SIZE);
mtk_ddp_write(cmdq_pkt, BGCLR_BLACK, &mixer->cmdq_base, mixer->regs, MIX_ROI_BGCLR);
mtk_ddp_write(cmdq_pkt, NON_PREMULTI_SOURCE, &mixer->cmdq_base, mixer->regs,
MIX_L_SRC_CON(0));
mtk_ddp_write(cmdq_pkt, NON_PREMULTI_SOURCE, &mixer->cmdq_base, mixer->regs,
MIX_L_SRC_CON(1));
mtk_ddp_write(cmdq_pkt, NON_PREMULTI_SOURCE, &mixer->cmdq_base, mixer->regs,
MIX_L_SRC_CON(2));
mtk_ddp_write(cmdq_pkt, NON_PREMULTI_SOURCE, &mixer->cmdq_base, mixer->regs,
MIX_L_SRC_CON(3));
mtk_ddp_write(cmdq_pkt, 0x0, &mixer->cmdq_base, mixer->regs, MIX_L_SRC_SIZE(0));
mtk_ddp_write(cmdq_pkt, OUTPUT_NO_RND | SOURCE_RGB_SEL | BACKGROUND_RELAY,
&mixer->cmdq_base, mixer->regs, MIX_DATAPATH_CON);
mtk_ddp_write_mask(cmdq_pkt, MIX_SRC_L0_EN, &mixer->cmdq_base, mixer->regs,
MIX_SRC_CON, MIX_SRC_L0_EN);
mtk_mmsys_hdr_config(priv->mmsys_dev, w / 2, h, cmdq_pkt);
mtk_mmsys_mixer_in_channel_swap(priv->mmsys_dev, 4, 0, cmdq_pkt);
}
void mtk_ethdr_start(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
struct mtk_ethdr_comp *mixer = &priv->ethdr_comp[ETHDR_MIXER];
writel(1, mixer->regs + MIX_EN);
}
void mtk_ethdr_stop(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
struct mtk_ethdr_comp *mixer = &priv->ethdr_comp[ETHDR_MIXER];
writel(0, mixer->regs + MIX_EN);
writel(1, mixer->regs + MIX_RST);
reset_control_reset(priv->reset_ctl);
writel(0, mixer->regs + MIX_RST);
}
int mtk_ethdr_clk_enable(struct device *dev)
{
int ret;
struct mtk_ethdr *priv = dev_get_drvdata(dev);
ret = clk_bulk_prepare_enable(ETHDR_CLK_NUM, priv->ethdr_clk);
if (ret)
dev_err(dev,
"ethdr_clk prepare enable failed\n");
return ret;
}
void mtk_ethdr_clk_disable(struct device *dev)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
clk_bulk_disable_unprepare(ETHDR_CLK_NUM, priv->ethdr_clk);
}
static int mtk_ethdr_bind(struct device *dev, struct device *master,
void *data)
{
struct mtk_ethdr *priv = dev_get_drvdata(dev);
priv->mmsys_dev = data;
return 0;
}
static void mtk_ethdr_unbind(struct device *dev, struct device *master, void *data)
{
}
static const struct component_ops mtk_ethdr_component_ops = {
.bind = mtk_ethdr_bind,
.unbind = mtk_ethdr_unbind,
};
static int mtk_ethdr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_ethdr *priv;
int ret;
int i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
for (i = 0; i < ETHDR_ID_MAX; i++) {
priv->ethdr_comp[i].dev = dev;
priv->ethdr_comp[i].regs = of_iomap(dev->of_node, i);
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev,
&priv->ethdr_comp[i].cmdq_base, i);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
dev_dbg(dev, "[DRM]regs:0x%p, node:%d\n", priv->ethdr_comp[i].regs, i);
}
for (i = 0; i < ETHDR_CLK_NUM; i++)
priv->ethdr_clk[i].id = ethdr_clk_str[i];
ret = devm_clk_bulk_get_optional(dev, ETHDR_CLK_NUM, priv->ethdr_clk);
if (ret)
return ret;
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
priv->irq = 0;
if (priv->irq) {
ret = devm_request_irq(dev, priv->irq, mtk_ethdr_irq_handler,
IRQF_TRIGGER_NONE, dev_name(dev), priv);
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to request irq %d\n",
priv->irq);
}
priv->reset_ctl = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(priv->reset_ctl))
return dev_err_probe(dev, PTR_ERR(priv->reset_ctl),
"cannot get ethdr reset control\n");
platform_set_drvdata(pdev, priv);
ret = component_add(dev, &mtk_ethdr_component_ops);
if (ret)
return dev_err_probe(dev, ret, "Failed to add component\n");
return 0;
}
static void mtk_ethdr_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_ethdr_component_ops);
}
static const struct of_device_id mtk_ethdr_driver_dt_match[] = {
{ .compatible = "mediatek,mt8195-disp-ethdr"},
{},
};
MODULE_DEVICE_TABLE(of, mtk_ethdr_driver_dt_match);
struct platform_driver mtk_ethdr_driver = {
.probe = mtk_ethdr_probe,
.remove = mtk_ethdr_remove,
.driver = {
.name = "mediatek-disp-ethdr",
.of_match_table = mtk_ethdr_driver_dt_match,
},
};
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_ETHDR_H__
#define __MTK_ETHDR_H__
void mtk_ethdr_start(struct device *dev);
void mtk_ethdr_stop(struct device *dev);
int mtk_ethdr_clk_enable(struct device *dev);
void mtk_ethdr_clk_disable(struct device *dev);
void mtk_ethdr_config(struct device *dev, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
u32 mtk_ethdr_get_blend_modes(struct device *dev);
void mtk_ethdr_layer_config(struct device *dev, unsigned int idx,
struct mtk_plane_state *state,
struct cmdq_pkt *cmdq_pkt);
void mtk_ethdr_register_vblank_cb(struct device *dev,
void (*vblank_cb)(void *),
void *vblank_cb_data);
void mtk_ethdr_unregister_vblank_cb(struct device *dev);
void mtk_ethdr_enable_vblank(struct device *dev);
void mtk_ethdr_disable_vblank(struct device *dev);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,465 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <drm/drm_modes.h>
#include <linux/device.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/math.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <sound/hdmi-codec.h>
#include "mtk_hdmi_common.h"
struct hdmi_acr_n {
unsigned int clock;
unsigned int n[3];
};
/* Recommended N values from HDMI specification, tables 7-1 to 7-3 */
static const struct hdmi_acr_n hdmi_rec_n_table[] = {
/* Clock, N: 32kHz 44.1kHz 48kHz */
{ 25175, { 4576, 7007, 6864 } },
{ 74176, { 11648, 17836, 11648 } },
{ 148352, { 11648, 8918, 5824 } },
{ 296703, { 5824, 4459, 5824 } },
{ 297000, { 3072, 4704, 5120 } },
{ 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */
};
/**
* hdmi_recommended_n() - Return N value recommended by HDMI specification
* @freq: audio sample rate in Hz
* @clock: rounded TMDS clock in kHz
*/
static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock)
{
const struct hdmi_acr_n *recommended;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) {
if (clock == hdmi_rec_n_table[i].clock)
break;
}
recommended = hdmi_rec_n_table + i;
switch (freq) {
case 32000:
return recommended->n[0];
case 44100:
return recommended->n[1];
case 48000:
return recommended->n[2];
case 88200:
return recommended->n[1] * 2;
case 96000:
return recommended->n[2] * 2;
case 176400:
return recommended->n[1] * 4;
case 192000:
return recommended->n[2] * 4;
default:
return (128 * freq) / 1000;
}
}
static unsigned int hdmi_mode_clock_to_hz(unsigned int clock)
{
switch (clock) {
case 25175:
return 25174825; /* 25.2/1.001 MHz */
case 74176:
return 74175824; /* 74.25/1.001 MHz */
case 148352:
return 148351648; /* 148.5/1.001 MHz */
case 296703:
return 296703297; /* 297/1.001 MHz */
default:
return clock * 1000;
}
}
static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
unsigned int tmds_clock, unsigned int n)
{
return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
128 * audio_sample_rate);
}
void mtk_hdmi_get_ncts(unsigned int sample_rate, unsigned int clock,
unsigned int *n, unsigned int *cts)
{
*n = hdmi_recommended_n(sample_rate, clock);
*cts = hdmi_expected_cts(sample_rate, clock, *n);
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_get_ncts, "DRM_MTK_HDMI");
int mtk_hdmi_audio_params(struct mtk_hdmi *hdmi,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
struct hdmi_audio_param aud_params = { 0 };
unsigned int chan = params->cea.channels;
dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
params->sample_rate, params->sample_width, chan);
if (!hdmi->bridge.encoder)
return -ENODEV;
switch (chan) {
case 2:
aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
break;
case 4:
aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
break;
case 6:
aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
break;
case 8:
aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
break;
default:
dev_err(hdmi->dev, "channel[%d] not supported!\n", chan);
return -EINVAL;
}
switch (params->sample_rate) {
case 32000:
case 44100:
case 48000:
case 88200:
case 96000:
case 176400:
case 192000:
break;
default:
dev_err(hdmi->dev, "rate[%d] not supported!\n",
params->sample_rate);
return -EINVAL;
}
switch (daifmt->fmt) {
case HDMI_I2S:
aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
aud_params.aud_input_type = HDMI_AUD_INPUT_I2S;
aud_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
aud_params.aud_mclk = HDMI_AUD_MCLK_128FS;
break;
case HDMI_SPDIF:
aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
aud_params.aud_input_type = HDMI_AUD_INPUT_SPDIF;
break;
default:
dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__,
daifmt->fmt);
return -EINVAL;
}
memcpy(&aud_params.codec_params, params, sizeof(aud_params.codec_params));
memcpy(&hdmi->aud_param, &aud_params, sizeof(aud_params));
dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
aud_params.aud_codec, aud_params.aud_input_type,
aud_params.aud_input_chan_type, aud_params.codec_params.sample_rate);
return 0;
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_params, "DRM_MTK_HDMI");
int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
{
struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
if (hdmi->enabled)
memcpy(buf, hdmi->curr_conn->eld, min(sizeof(hdmi->curr_conn->eld), len));
else
memset(buf, 0, len);
return 0;
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_get_eld, "DRM_MTK_HDMI");
void mtk_hdmi_audio_set_plugged_cb(struct mtk_hdmi *hdmi, hdmi_codec_plugged_cb fn,
struct device *codec_dev)
{
mutex_lock(&hdmi->update_plugged_status_lock);
hdmi->plugged_cb = fn;
hdmi->codec_dev = codec_dev;
mutex_unlock(&hdmi->update_plugged_status_lock);
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_set_plugged_cb, "DRM_MTK_HDMI");
static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi, struct device_node *np,
const char * const *clock_names, size_t num_clocks)
{
int i;
for (i = 0; i < num_clocks; i++) {
hdmi->clk[i] = of_clk_get_by_name(np, clock_names[i]);
if (IS_ERR(hdmi->clk[i]))
return PTR_ERR(hdmi->clk[i]);
}
return 0;
}
bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_bridge_mode_fixup, "DRM_MTK_HDMI");
void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n",
adjusted_mode->name, adjusted_mode->hdisplay);
dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d",
adjusted_mode->hsync_start, adjusted_mode->hsync_end,
adjusted_mode->htotal);
dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n",
adjusted_mode->hskew, adjusted_mode->vdisplay);
dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d",
adjusted_mode->vsync_start, adjusted_mode->vsync_end,
adjusted_mode->vtotal);
dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n",
adjusted_mode->vscan, adjusted_mode->flags);
drm_mode_copy(&hdmi->mode, adjusted_mode);
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_bridge_mode_set, "DRM_MTK_HDMI");
static void mtk_hdmi_put_device(void *_dev)
{
struct device *dev = _dev;
put_device(dev);
}
static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struct device_node *np)
{
struct platform_device *cec_pdev;
struct device_node *cec_np;
int ret;
/* The CEC module handles HDMI hotplug detection */
cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec");
if (!cec_np)
return dev_err_probe(dev, -EOPNOTSUPP, "Failed to find CEC node\n");
cec_pdev = of_find_device_by_node(cec_np);
if (!cec_pdev) {
dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", cec_np);
of_node_put(cec_np);
return -EPROBE_DEFER;
}
of_node_put(cec_np);
ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &cec_pdev->dev);
if (ret)
return ret;
/*
* The mediatek,syscon-hdmi property contains a phandle link to the
* MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG
* registers it contains.
*/
hdmi->sys_regmap = syscon_regmap_lookup_by_phandle_args(np, "mediatek,syscon-hdmi",
1, &hdmi->sys_offset);
if (IS_ERR(hdmi->sys_regmap))
return dev_err_probe(dev, PTR_ERR(hdmi->sys_regmap),
"Failed to get system configuration registers\n");
hdmi->cec_dev = &cec_pdev->dev;
return 0;
}
static void mtk_hdmi_put_adapter(void *_adap)
{
struct i2c_adapter *adap = _adap;
i2c_put_adapter(adap);
}
static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev,
const char * const *clk_names, size_t num_clocks)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *remote, *i2c_np;
int ret;
ret = mtk_hdmi_get_all_clk(hdmi, np, clk_names, num_clocks);
if (ret)
return dev_err_probe(dev, ret, "Failed to get clocks\n");
hdmi->irq = platform_get_irq(pdev, 0);
if (hdmi->irq < 0)
return hdmi->irq;
hdmi->regs = device_node_to_regmap(dev->of_node);
if (IS_ERR(hdmi->regs))
return PTR_ERR(hdmi->regs);
remote = of_graph_get_remote_node(np, 1, 0);
if (!remote)
return -EINVAL;
if (!of_device_is_compatible(remote, "hdmi-connector")) {
hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
if (!hdmi->bridge.next_bridge) {
dev_err(dev, "Waiting for external bridge\n");
of_node_put(remote);
return -EPROBE_DEFER;
}
}
i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
of_node_put(remote);
if (!i2c_np)
return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n");
hdmi->ddc_adpt = of_get_i2c_adapter_by_node(i2c_np);
of_node_put(i2c_np);
if (!hdmi->ddc_adpt)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n");
ret = devm_add_action_or_reset(dev, mtk_hdmi_put_adapter, hdmi->ddc_adpt);
if (ret)
return ret;
ret = mtk_hdmi_get_cec_dev(hdmi, dev, np);
if (ret == -EOPNOTSUPP)
dev_info(dev, "CEC support unavailable: node not found\n");
else if (ret)
return ret;
return 0;
}
static void mtk_hdmi_unregister_audio_driver(void *data)
{
platform_device_unregister(data);
}
static int mtk_hdmi_register_audio_driver(struct device *dev)
{
struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
struct hdmi_audio_param *aud_param = &hdmi->aud_param;
struct hdmi_codec_pdata codec_data = {
.ops = hdmi->conf->ver_conf->codec_ops,
.max_i2s_channels = 2,
.i2s = 1,
.data = hdmi,
.no_capture_mute = 1,
};
int ret;
aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
aud_param->aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
hdmi->audio_pdev = platform_device_register_data(dev,
HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&codec_data,
sizeof(codec_data));
if (IS_ERR(hdmi->audio_pdev))
return PTR_ERR(hdmi->audio_pdev);
ret = devm_add_action_or_reset(dev, mtk_hdmi_unregister_audio_driver,
hdmi->audio_pdev);
if (ret)
return ret;
return 0;
}
struct mtk_hdmi *mtk_hdmi_common_probe(struct platform_device *pdev)
{
const struct mtk_hdmi_ver_conf *ver_conf;
const struct mtk_hdmi_conf *hdmi_conf;
struct device *dev = &pdev->dev;
struct mtk_hdmi *hdmi;
int ret;
hdmi_conf = of_device_get_match_data(dev);
if (!hdmi_conf)
return ERR_PTR(-ENODEV);
ver_conf = hdmi_conf->ver_conf;
hdmi = devm_drm_bridge_alloc(dev, struct mtk_hdmi, bridge,
ver_conf->bridge_funcs);
if (IS_ERR(hdmi))
return hdmi;
hdmi->dev = dev;
hdmi->conf = hdmi_conf;
hdmi->clk = devm_kcalloc(dev, ver_conf->num_clocks, sizeof(*hdmi->clk), GFP_KERNEL);
if (!hdmi->clk)
return ERR_PTR(-ENOMEM);
ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev, ver_conf->mtk_hdmi_clock_names,
ver_conf->num_clocks);
if (ret)
return ERR_PTR(ret);
hdmi->phy = devm_phy_get(dev, "hdmi");
if (IS_ERR(hdmi->phy))
return dev_err_cast_probe(dev, hdmi->phy, "Failed to get HDMI PHY\n");
mutex_init(&hdmi->update_plugged_status_lock);
platform_set_drvdata(pdev, hdmi);
ret = mtk_hdmi_register_audio_driver(dev);
if (ret)
return dev_err_ptr_probe(dev, ret, "Cannot register HDMI Audio driver\n");
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_HPD;
/* Only v2 support OP_HDMI now and it we know that it also support SPD */
if (ver_conf->bridge_funcs->hdmi_write_avi_infoframe &&
ver_conf->bridge_funcs->hdmi_clear_avi_infoframe)
hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.ddc = hdmi->ddc_adpt;
hdmi->bridge.vendor = "MediaTek";
hdmi->bridge.product = "On-Chip HDMI";
hdmi->bridge.interlace_allowed = ver_conf->interlace_allowed;
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
if (ret)
return dev_err_ptr_probe(dev, ret, "Failed to add bridge\n");
return hdmi;
}
EXPORT_SYMBOL_NS_GPL(mtk_hdmi_common_probe, "DRM_MTK_HDMI");
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_DESCRIPTION("MediaTek HDMI Common Library");
MODULE_LICENSE("GPL");
@@ -0,0 +1,197 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
*/
#ifndef _MTK_HDMI_COMMON_H
#define _MTK_HDMI_COMMON_H
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_print.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/mfd/syscon.h>
#include <linux/mutex.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <sound/hdmi-codec.h>
enum hdmi_aud_input_type {
HDMI_AUD_INPUT_I2S = 0,
HDMI_AUD_INPUT_SPDIF,
};
enum hdmi_aud_i2s_fmt {
HDMI_I2S_MODE_RJT_24BIT = 0,
HDMI_I2S_MODE_RJT_16BIT,
HDMI_I2S_MODE_LJT_24BIT,
HDMI_I2S_MODE_LJT_16BIT,
HDMI_I2S_MODE_I2S_24BIT,
HDMI_I2S_MODE_I2S_16BIT
};
enum hdmi_aud_mclk {
HDMI_AUD_MCLK_128FS,
HDMI_AUD_MCLK_192FS,
HDMI_AUD_MCLK_256FS,
HDMI_AUD_MCLK_384FS,
HDMI_AUD_MCLK_512FS,
HDMI_AUD_MCLK_768FS,
HDMI_AUD_MCLK_1152FS,
};
enum hdmi_aud_channel_type {
HDMI_AUD_CHAN_TYPE_1_0 = 0,
HDMI_AUD_CHAN_TYPE_1_1,
HDMI_AUD_CHAN_TYPE_2_0,
HDMI_AUD_CHAN_TYPE_2_1,
HDMI_AUD_CHAN_TYPE_3_0,
HDMI_AUD_CHAN_TYPE_3_1,
HDMI_AUD_CHAN_TYPE_4_0,
HDMI_AUD_CHAN_TYPE_4_1,
HDMI_AUD_CHAN_TYPE_5_0,
HDMI_AUD_CHAN_TYPE_5_1,
HDMI_AUD_CHAN_TYPE_6_0,
HDMI_AUD_CHAN_TYPE_6_1,
HDMI_AUD_CHAN_TYPE_7_0,
HDMI_AUD_CHAN_TYPE_7_1,
HDMI_AUD_CHAN_TYPE_3_0_LRS,
HDMI_AUD_CHAN_TYPE_3_1_LRS,
HDMI_AUD_CHAN_TYPE_4_0_CLRS,
HDMI_AUD_CHAN_TYPE_4_1_CLRS,
HDMI_AUD_CHAN_TYPE_6_1_CS,
HDMI_AUD_CHAN_TYPE_6_1_CH,
HDMI_AUD_CHAN_TYPE_6_1_OH,
HDMI_AUD_CHAN_TYPE_6_1_CHR,
HDMI_AUD_CHAN_TYPE_7_1_LH_RH,
HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR,
HDMI_AUD_CHAN_TYPE_7_1_LC_RC,
HDMI_AUD_CHAN_TYPE_7_1_LW_RW,
HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD,
HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS,
HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS,
HDMI_AUD_CHAN_TYPE_7_1_CS_CH,
HDMI_AUD_CHAN_TYPE_7_1_CS_OH,
HDMI_AUD_CHAN_TYPE_7_1_CS_CHR,
HDMI_AUD_CHAN_TYPE_7_1_CH_OH,
HDMI_AUD_CHAN_TYPE_7_1_CH_CHR,
HDMI_AUD_CHAN_TYPE_7_1_OH_CHR,
HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR,
HDMI_AUD_CHAN_TYPE_6_0_CS,
HDMI_AUD_CHAN_TYPE_6_0_CH,
HDMI_AUD_CHAN_TYPE_6_0_OH,
HDMI_AUD_CHAN_TYPE_6_0_CHR,
HDMI_AUD_CHAN_TYPE_7_0_LH_RH,
HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR,
HDMI_AUD_CHAN_TYPE_7_0_LC_RC,
HDMI_AUD_CHAN_TYPE_7_0_LW_RW,
HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD,
HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS,
HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS,
HDMI_AUD_CHAN_TYPE_7_0_CS_CH,
HDMI_AUD_CHAN_TYPE_7_0_CS_OH,
HDMI_AUD_CHAN_TYPE_7_0_CS_CHR,
HDMI_AUD_CHAN_TYPE_7_0_CH_OH,
HDMI_AUD_CHAN_TYPE_7_0_CH_CHR,
HDMI_AUD_CHAN_TYPE_7_0_OH_CHR,
HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR,
HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS,
HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF
};
enum hdmi_aud_channel_swap_type {
HDMI_AUD_SWAP_LR,
HDMI_AUD_SWAP_LFE_CC,
HDMI_AUD_SWAP_LSRS,
HDMI_AUD_SWAP_RLS_RRS,
HDMI_AUD_SWAP_LR_STATUS,
};
struct hdmi_audio_param {
enum hdmi_audio_coding_type aud_codec;
enum hdmi_audio_sample_size aud_sample_size;
enum hdmi_aud_input_type aud_input_type;
enum hdmi_aud_i2s_fmt aud_i2s_fmt;
enum hdmi_aud_mclk aud_mclk;
enum hdmi_aud_channel_type aud_input_chan_type;
struct hdmi_codec_params codec_params;
};
enum hdmi_hpd_state {
HDMI_PLUG_OUT = 0,
HDMI_PLUG_IN_AND_SINK_POWER_ON,
HDMI_PLUG_IN_ONLY,
};
struct mtk_hdmi_ver_conf {
const struct drm_bridge_funcs *bridge_funcs;
const struct hdmi_codec_ops *codec_ops;
const char * const *mtk_hdmi_clock_names;
int num_clocks;
bool interlace_allowed;
};
struct mtk_hdmi_conf {
const struct mtk_hdmi_ver_conf *ver_conf;
bool tz_disabled;
bool cea_modes_only;
unsigned long max_mode_clock;
u32 reg_hdmi_tx_cfg;
};
struct mtk_hdmi {
struct drm_bridge bridge;
struct drm_connector *curr_conn;/* current connector (only valid when 'enabled') */
struct device *dev;
const struct mtk_hdmi_conf *conf;
struct phy *phy;
struct device *cec_dev;
struct i2c_adapter *ddc_adpt;
struct clk **clk;
struct drm_display_mode mode;
bool dvi_mode;
struct regmap *sys_regmap;
unsigned int sys_offset;
struct regmap *regs;
struct platform_device *audio_pdev;
struct hdmi_audio_param aud_param;
bool audio_enable;
bool powered;
bool enabled;
int irq;
enum hdmi_hpd_state hpd;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
struct mutex update_plugged_status_lock;
};
static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b)
{
return container_of(b, struct mtk_hdmi, bridge);
}
int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len);
void mtk_hdmi_audio_set_plugged_cb(struct mtk_hdmi *hdmi, hdmi_codec_plugged_cb fn,
struct device *codec_dev);
int mtk_hdmi_audio_params(struct mtk_hdmi *hdmi, struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params);
void mtk_hdmi_get_ncts(unsigned int sample_rate, unsigned int clock,
unsigned int *n, unsigned int *cts);
bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
struct mtk_hdmi *mtk_hdmi_common_probe(struct platform_device *pdev);
#endif /* _MTK_HDMI_COMMON_H */
@@ -0,0 +1,343 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Jie Qiu <jie.qiu@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include "mtk_drm_drv.h"
#define SIF1_CLOK (288)
#define DDC_DDCMCTL0 (0x0)
#define DDCM_ODRAIN BIT(31)
#define DDCM_CLK_DIV_OFFSET (16)
#define DDCM_CLK_DIV_MASK (0xfff << 16)
#define DDCM_CS_STATUS BIT(4)
#define DDCM_SCL_STATE BIT(3)
#define DDCM_SDA_STATE BIT(2)
#define DDCM_SM0EN BIT(1)
#define DDCM_SCL_STRECH BIT(0)
#define DDC_DDCMCTL1 (0x4)
#define DDCM_ACK_OFFSET (16)
#define DDCM_ACK_MASK (0xff << 16)
#define DDCM_PGLEN_OFFSET (8)
#define DDCM_PGLEN_MASK (0x7 << 8)
#define DDCM_SIF_MODE_OFFSET (4)
#define DDCM_SIF_MODE_MASK (0x7 << 4)
#define DDCM_START (0x1)
#define DDCM_WRITE_DATA (0x2)
#define DDCM_STOP (0x3)
#define DDCM_READ_DATA_NO_ACK (0x4)
#define DDCM_READ_DATA_ACK (0x5)
#define DDCM_TRI BIT(0)
#define DDC_DDCMD0 (0x8)
#define DDCM_DATA3 (0xff << 24)
#define DDCM_DATA2 (0xff << 16)
#define DDCM_DATA1 (0xff << 8)
#define DDCM_DATA0 (0xff << 0)
#define DDC_DDCMD1 (0xc)
#define DDCM_DATA7 (0xff << 24)
#define DDCM_DATA6 (0xff << 16)
#define DDCM_DATA5 (0xff << 8)
#define DDCM_DATA4 (0xff << 0)
struct mtk_hdmi_ddc {
struct i2c_adapter adap;
struct clk *clk;
void __iomem *regs;
};
static inline void sif_set_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset,
unsigned int val)
{
writel(readl(ddc->regs + offset) | val, ddc->regs + offset);
}
static inline void sif_clr_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset,
unsigned int val)
{
writel(readl(ddc->regs + offset) & ~val, ddc->regs + offset);
}
static inline bool sif_bit_is_set(struct mtk_hdmi_ddc *ddc, unsigned int offset,
unsigned int val)
{
return (readl(ddc->regs + offset) & val) == val;
}
static inline void sif_write_mask(struct mtk_hdmi_ddc *ddc, unsigned int offset,
unsigned int mask, unsigned int shift,
unsigned int val)
{
unsigned int tmp;
tmp = readl(ddc->regs + offset);
tmp &= ~mask;
tmp |= (val << shift) & mask;
writel(tmp, ddc->regs + offset);
}
static inline unsigned int sif_read_mask(struct mtk_hdmi_ddc *ddc,
unsigned int offset, unsigned int mask,
unsigned int shift)
{
return (readl(ddc->regs + offset) & mask) >> shift;
}
static void ddcm_trigger_mode(struct mtk_hdmi_ddc *ddc, int mode)
{
u32 val;
sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_SIF_MODE_MASK,
DDCM_SIF_MODE_OFFSET, mode);
sif_set_bit(ddc, DDC_DDCMCTL1, DDCM_TRI);
readl_poll_timeout(ddc->regs + DDC_DDCMCTL1, val,
(val & DDCM_TRI) != DDCM_TRI, 4, 20000);
}
static int mtk_hdmi_ddc_read_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg)
{
struct device *dev = ddc->adap.dev.parent;
u32 remain_count, ack_count, ack_final, read_count, temp_count;
u32 index = 0;
u32 ack;
int i;
ddcm_trigger_mode(ddc, DDCM_START);
sif_write_mask(ddc, DDC_DDCMD0, 0xff, 0, (msg->addr << 1) | 0x01);
sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
0x00);
ddcm_trigger_mode(ddc, DDCM_WRITE_DATA);
ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
dev_dbg(dev, "ack = 0x%x\n", ack);
if (ack != 0x01) {
dev_err(dev, "i2c ack err!\n");
return -ENXIO;
}
remain_count = msg->len;
ack_count = (msg->len - 1) / 8;
ack_final = 0;
while (remain_count > 0) {
if (ack_count > 0) {
read_count = 8;
ack_final = 0;
ack_count--;
} else {
read_count = remain_count;
ack_final = 1;
}
sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK,
DDCM_PGLEN_OFFSET, read_count - 1);
ddcm_trigger_mode(ddc, (ack_final == 1) ?
DDCM_READ_DATA_NO_ACK :
DDCM_READ_DATA_ACK);
ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK,
DDCM_ACK_OFFSET);
temp_count = 0;
while (((ack & (1 << temp_count)) != 0) && (temp_count < 8))
temp_count++;
if (((ack_final == 1) && (temp_count != (read_count - 1))) ||
((ack_final == 0) && (temp_count != read_count))) {
dev_err(dev, "Address NACK! ACK(0x%x)\n", ack);
break;
}
for (i = read_count; i >= 1; i--) {
int shift;
int offset;
if (i > 4) {
offset = DDC_DDCMD1;
shift = (i - 5) * 8;
} else {
offset = DDC_DDCMD0;
shift = (i - 1) * 8;
}
msg->buf[index + i - 1] = sif_read_mask(ddc, offset,
0xff << shift,
shift);
}
remain_count -= read_count;
index += read_count;
}
return 0;
}
static int mtk_hdmi_ddc_write_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg)
{
struct device *dev = ddc->adap.dev.parent;
u32 ack;
ddcm_trigger_mode(ddc, DDCM_START);
sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA0, 0, msg->addr << 1);
sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA1, 8, msg->buf[0]);
sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
0x1);
ddcm_trigger_mode(ddc, DDCM_WRITE_DATA);
ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
dev_dbg(dev, "ack = %d\n", ack);
if (ack != 0x03) {
dev_err(dev, "i2c ack err!\n");
return -EIO;
}
return 0;
}
static int mtk_hdmi_ddc_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
struct mtk_hdmi_ddc *ddc = adapter->algo_data;
struct device *dev = adapter->dev.parent;
int ret;
int i;
if (!ddc) {
dev_err(dev, "invalid arguments\n");
return -EINVAL;
}
sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SCL_STRECH);
sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SM0EN);
sif_clr_bit(ddc, DDC_DDCMCTL0, DDCM_ODRAIN);
if (sif_bit_is_set(ddc, DDC_DDCMCTL1, DDCM_TRI)) {
dev_err(dev, "ddc line is busy!\n");
return -EBUSY;
}
sif_write_mask(ddc, DDC_DDCMCTL0, DDCM_CLK_DIV_MASK,
DDCM_CLK_DIV_OFFSET, SIF1_CLOK);
for (i = 0; i < num; i++) {
struct i2c_msg *msg = &msgs[i];
dev_dbg(dev, "i2c msg, adr:0x%x, flags:%d, len :0x%x\n",
msg->addr, msg->flags, msg->len);
if (msg->flags & I2C_M_RD)
ret = mtk_hdmi_ddc_read_msg(ddc, msg);
else
ret = mtk_hdmi_ddc_write_msg(ddc, msg);
if (ret < 0)
goto xfer_end;
}
ddcm_trigger_mode(ddc, DDCM_STOP);
return i;
xfer_end:
ddcm_trigger_mode(ddc, DDCM_STOP);
dev_err(dev, "ddc failed!\n");
return ret;
}
static u32 mtk_hdmi_ddc_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mtk_hdmi_ddc_algorithm = {
.master_xfer = mtk_hdmi_ddc_xfer,
.functionality = mtk_hdmi_ddc_func,
};
static int mtk_hdmi_ddc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_hdmi_ddc *ddc;
struct resource *mem;
int ret;
ddc = devm_kzalloc(dev, sizeof(struct mtk_hdmi_ddc), GFP_KERNEL);
if (!ddc)
return -ENOMEM;
ddc->clk = devm_clk_get(dev, "ddc-i2c");
if (IS_ERR(ddc->clk))
return dev_err_probe(dev, PTR_ERR(ddc->clk),
"get ddc_clk failed\n");
ddc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(ddc->regs))
return PTR_ERR(ddc->regs);
ret = clk_prepare_enable(ddc->clk);
if (ret)
return dev_err_probe(dev, ret, "enable ddc clk failed!\n");
strscpy(ddc->adap.name, "mediatek-hdmi-ddc", sizeof(ddc->adap.name));
ddc->adap.owner = THIS_MODULE;
ddc->adap.algo = &mtk_hdmi_ddc_algorithm;
ddc->adap.retries = 3;
ddc->adap.dev.of_node = dev->of_node;
ddc->adap.algo_data = ddc;
ddc->adap.dev.parent = &pdev->dev;
ret = i2c_add_adapter(&ddc->adap);
if (ret < 0) {
clk_disable_unprepare(ddc->clk);
return dev_err_probe(dev, ret, "failed to add bus to i2c core\n");
}
platform_set_drvdata(pdev, ddc);
dev_dbg(dev, "ddc->adap: %p\n", &ddc->adap);
dev_dbg(dev, "ddc->clk: %p\n", ddc->clk);
dev_dbg(dev, "physical adr: %pa, end: %pa\n", &mem->start,
&mem->end);
return 0;
}
static void mtk_hdmi_ddc_remove(struct platform_device *pdev)
{
struct mtk_hdmi_ddc *ddc = platform_get_drvdata(pdev);
i2c_del_adapter(&ddc->adap);
clk_disable_unprepare(ddc->clk);
}
static const struct of_device_id mtk_hdmi_ddc_match[] = {
{ .compatible = "mediatek,mt8173-hdmi-ddc", },
{},
};
MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match);
static struct platform_driver mtk_hdmi_ddc_driver = {
.probe = mtk_hdmi_ddc_probe,
.remove = mtk_hdmi_ddc_remove,
.driver = {
.name = "mediatek-hdmi-ddc",
.of_match_table = mtk_hdmi_ddc_match,
},
};
module_platform_driver(mtk_hdmi_ddc_driver);
MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
MODULE_DESCRIPTION("MediaTek HDMI DDC Driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,404 @@
// SPDX-License-Identifier: GPL-2.0
/*
* MediaTek HDMI v2 Display Data Channel Driver
*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2021 BayLibre, SAS
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <drm/drm_edid.h>
#include "mtk_hdmi_common.h"
#include "mtk_hdmi_regs_v2.h"
#define DDC2_DLY_CNT 572 /* BIM=208M/(v*4) = 90Khz */
#define DDC2_DLY_CNT_EDID 832 /* BIM=208M/(v*4) = 62.5Khz */
#define SI2C_ADDR_READ 0xf4
#define SCDC_I2C_SLAVE_ADDRESS 0x54
struct mtk_hdmi_ddc {
struct device *dev;
struct regmap *regs;
struct clk *clk;
struct i2c_adapter adap;
};
static int mtk_ddc_check_and_rise_low_bus(struct mtk_hdmi_ddc *ddc)
{
u32 val;
regmap_read(ddc->regs, HDCP2X_DDCM_STATUS, &val);
if (val & DDC_I2C_BUS_LOW) {
regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLOCK_SCL));
usleep_range(250, 300);
}
if (val & DDC_I2C_NO_ACK) {
u32 ddc_ctrl, hpd_ddc_ctrl, hpd_ddc_status;
regmap_read(ddc->regs, DDC_CTRL, &ddc_ctrl);
regmap_read(ddc->regs, HPD_DDC_CTRL, &hpd_ddc_ctrl);
regmap_read(ddc->regs, HPD_DDC_STATUS, &hpd_ddc_status);
}
if (val & DDC_I2C_NO_ACK)
return -EIO;
return 0;
}
static int mtk_ddcm_write_hdmi(struct mtk_hdmi_ddc *ddc, u16 addr_id,
u16 offset_id, u16 data_cnt, u8 *wr_data)
{
u32 val;
int ret, i;
/* Don't allow transfer with a size over than the transfer fifo size
* (16 byte)
*/
if (data_cnt > 16) {
dev_err(ddc->dev, "Invalid DDCM write request\n");
return -EINVAL;
}
/* If down, rise bus for write operation */
mtk_ddc_check_and_rise_low_bus(ddc);
regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT));
/* In case there is no payload data, just do a single write for the
* address only
*/
if (wr_data) {
/* Fill transfer fifo with payload data */
for (i = 0; i < data_cnt; i++) {
regmap_write(ddc->regs, SI2C_CTRL,
FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
FIELD_PREP(SI2C_WDATA, wr_data[i]) |
SI2C_WR);
}
}
regmap_write(ddc->regs, DDC_CTRL,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) |
FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : data_cnt) |
FIELD_PREP(DDC_CTRL_OFFSET, offset_id) |
FIELD_PREP(DDC_CTRL_ADDR, addr_id));
usleep_range(1000, 1250);
ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
!(val & DDC_I2C_IN_PROG), 500, 1000);
if (ret) {
dev_err(ddc->dev, "DDC I2C write timeout\n");
/* Abort transfer if it is still in progress */
regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ABORT_XFER));
return ret;
}
/* The I2C bus might be down after WR operation: rise it again */
ret = mtk_ddc_check_and_rise_low_bus(ddc);
if (ret) {
dev_err(ddc->dev, "Error during write operation: No ACK\n");
return ret;
}
return 0;
}
static int mtk_ddcm_read_hdmi(struct mtk_hdmi_ddc *ddc, u16 uc_dev,
u8 addr, u8 *puc_value, u16 data_cnt)
{
u16 dly_cnt, i, uc_idx;
u32 rem, temp_length, uc_read_count, val;
u64 loop_counter;
int ret;
mtk_ddc_check_and_rise_low_bus(ddc);
regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLEAR_FIFO));
if (data_cnt >= 16) {
temp_length = 16;
loop_counter = data_cnt;
rem = do_div(loop_counter, temp_length);
if (rem)
loop_counter++;
} else {
temp_length = data_cnt;
loop_counter = 1;
}
if (uc_dev >= DDC_ADDR)
dly_cnt = DDC2_DLY_CNT_EDID;
else
dly_cnt = DDC2_DLY_CNT;
regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
FIELD_PREP(HPD_DDC_DELAY_CNT, dly_cnt));
for (i = 0; i < loop_counter; i++) {
rem = data_cnt % 16;
if (i > 0 && i == (loop_counter - 1) && rem)
temp_length = rem;
/* 0x51 - 0x53: Flow control */
if (uc_dev > DDC_ADDR && uc_dev <= 0x53) {
regmap_update_bits(ddc->regs, SCDC_CTRL, SCDC_DDC_SEGMENT,
FIELD_PREP(SCDC_DDC_SEGMENT, uc_dev - DDC_ADDR));
regmap_write(ddc->regs, DDC_CTRL,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ENH_READ_NOACK) |
FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
FIELD_PREP(DDC_CTRL_OFFSET, addr + i * temp_length) |
FIELD_PREP(DDC_CTRL_ADDR, DDC_ADDR));
} else {
u16 offset;
if (addr != 0x43)
offset = i * 16;
else
offset = 0;
regmap_write(ddc->regs, DDC_CTRL,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_READ_NOACK) |
FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
FIELD_PREP(DDC_CTRL_OFFSET, addr + offset) |
FIELD_PREP(DDC_CTRL_ADDR, uc_dev));
}
usleep_range(5000, 5500);
ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
!(val & DDC_I2C_IN_PROG), 1000,
500 * (temp_length + 5));
if (ret) {
dev_err(ddc->dev, "Timeout waiting for DDC I2C\n");
/* Abort transfer if it is still in progress */
regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ABORT_XFER));
return ret;
}
ret = mtk_ddc_check_and_rise_low_bus(ddc);
if (ret) {
dev_err(ddc->dev, "Error during read operation: No ACK\n");
return ret;
}
for (uc_idx = 0; uc_idx < temp_length; uc_idx++) {
unsigned int read_idx = i * 16 + uc_idx;
regmap_write(ddc->regs, SI2C_CTRL,
FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
SI2C_RD);
regmap_read(ddc->regs, HPD_DDC_STATUS, &val);
puc_value[read_idx] = FIELD_GET(DDC_DATA_OUT, val);
regmap_write(ddc->regs, SI2C_CTRL,
FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
SI2C_CONFIRM_READ);
/*
* If HDMI IP gets reset during EDID read, DDC read
* operation will fail and its delay counter will be
* reset to 400.
*/
regmap_read(ddc->regs, HPD_DDC_CTRL, &val);
if (FIELD_GET(HPD_DDC_DELAY_CNT, val) < DDC2_DLY_CNT)
return 0;
uc_read_count = read_idx + 1;
}
}
if (uc_read_count > U8_MAX)
dev_warn(ddc->dev, "Invalid read data count %u\n", uc_read_count);
return uc_read_count;
}
static int mtk_hdmi_fg_ddc_data_read(struct mtk_hdmi_ddc *ddc, u16 b_dev,
u8 data_addr, u16 data_cnt, u8 *pr_data)
{
int read_data_cnt;
u16 req_data_cnt;
if (!data_cnt) {
dev_err(ddc->dev, "Invalid DDCM read request\n");
return -EINVAL;
}
req_data_cnt = U8_MAX - data_addr + 1;
if (req_data_cnt > data_cnt)
req_data_cnt = data_cnt;
regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
read_data_cnt = mtk_ddcm_read_hdmi(ddc, b_dev, data_addr, pr_data, req_data_cnt);
if (read_data_cnt < 0)
return read_data_cnt;
else if (read_data_cnt != req_data_cnt)
return -EINVAL;
return 0;
}
static int mtk_hdmi_ddc_fg_data_write(struct mtk_hdmi_ddc *ddc, u16 b_dev,
u8 data_addr, u16 data_cnt, u8 *pr_data)
{
regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
return mtk_ddcm_write_hdmi(ddc, b_dev, data_addr, data_cnt, pr_data);
}
static int mtk_hdmi_ddc_v2_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
struct mtk_hdmi_ddc *ddc;
u8 offset = 0;
int i, ret;
ddc = adapter->algo_data;
for (i = 0; i < num; i++) {
struct i2c_msg *msg = &msgs[i];
if (!msg->buf) {
dev_err(ddc->dev, "No message buffer\n");
return -EINVAL;
}
if (msg->flags & I2C_M_RD) {
/*
* The underlying DDC hardware always issues a write request
* that assigns the read offset as part of the read operation,
* therefore, use the `offset` value assigned in the previous
* write request from drm_edid
*/
ret = mtk_hdmi_fg_ddc_data_read(ddc, msg->addr, offset,
msg->len, &msg->buf[0]);
if (ret)
return ret;
} else {
/*
* The HW needs the data offset, found in buf[0], in the
* DDC_CTRL register, and each byte of data, starting at
* buf[1], goes in the SI2C_WDATA register.
*/
ret = mtk_hdmi_ddc_fg_data_write(ddc, msg->addr, msg->buf[0],
msg->len - 1, &msg->buf[1]);
if (ret)
return ret;
/*
* Store the offset value requested by drm_edid or by
* scdc to use in subsequent read requests.
*/
if ((msg->addr == DDC_ADDR || msg->addr == SCDC_I2C_SLAVE_ADDRESS) &&
msg->len == 1) {
offset = msg->buf[0];
}
}
}
return i;
}
static u32 mtk_hdmi_ddc_v2_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mtk_hdmi_ddc_v2_algorithm = {
.master_xfer = mtk_hdmi_ddc_v2_xfer,
.functionality = mtk_hdmi_ddc_v2_func,
};
static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_hdmi_ddc *ddc;
int ret;
ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
if (!ddc)
return -ENOMEM;
ddc->dev = dev;
ddc->regs = device_node_to_regmap(dev->parent->of_node);
if (IS_ERR_OR_NULL(ddc->regs))
return dev_err_probe(dev,
IS_ERR(ddc->regs) ? PTR_ERR(ddc->regs) : -EINVAL,
"Cannot get regmap\n");
ddc->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(ddc->clk))
return dev_err_probe(dev, PTR_ERR(ddc->clk), "Cannot get DDC clock\n");
strscpy(ddc->adap.name, "mediatek-hdmi-ddc-v2", sizeof(ddc->adap.name));
ddc->adap.owner = THIS_MODULE;
ddc->adap.algo = &mtk_hdmi_ddc_v2_algorithm;
ddc->adap.retries = 3;
ddc->adap.dev.of_node = dev->of_node;
ddc->adap.algo_data = ddc;
ddc->adap.dev.parent = &pdev->dev;
ret = devm_pm_runtime_enable(&pdev->dev);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Cannot enable Runtime PM\n");
pm_runtime_get_sync(dev);
ret = devm_i2c_add_adapter(dev, &ddc->adap);
if (ret < 0)
return dev_err_probe(dev, ret, "Cannot add DDC I2C adapter\n");
platform_set_drvdata(pdev, ddc);
return 0;
}
static const struct of_device_id mtk_hdmi_ddc_v2_match[] = {
{ .compatible = "mediatek,mt8195-hdmi-ddc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_v2_match);
static struct platform_driver mtk_hdmi_ddc_v2_driver = {
.probe = mtk_hdmi_ddc_v2_probe,
.driver = {
.name = "mediatek-hdmi-ddc-v2",
.of_match_table = mtk_hdmi_ddc_v2_match,
},
};
module_platform_driver(mtk_hdmi_ddc_v2_driver);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Can Zeng <can.zeng@mediatek.com>");
MODULE_DESCRIPTION("MediaTek HDMIv2 DDC Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,230 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Jie Qiu <jie.qiu@mediatek.com>
*/
#ifndef _MTK_HDMI_REGS_H
#define _MTK_HDMI_REGS_H
#define GRL_INT_MASK 0x18
#define GRL_IFM_PORT 0x188
#define GRL_CH_SWAP 0x198
#define LR_SWAP BIT(0)
#define LFE_CC_SWAP BIT(1)
#define LSRS_SWAP BIT(2)
#define RLS_RRS_SWAP BIT(3)
#define LR_STATUS_SWAP BIT(4)
#define GRL_I2S_C_STA0 0x140
#define GRL_I2S_C_STA1 0x144
#define GRL_I2S_C_STA2 0x148
#define GRL_I2S_C_STA3 0x14C
#define GRL_I2S_C_STA4 0x150
#define GRL_I2S_UV 0x154
#define I2S_UV_V BIT(0)
#define I2S_UV_U BIT(1)
#define I2S_UV_CH_EN_MASK 0x3c
#define I2S_UV_CH_EN(x) BIT((x) + 2)
#define I2S_UV_TMDS_DEBUG BIT(6)
#define I2S_UV_NORMAL_INFO_INV BIT(7)
#define GRL_ACP_ISRC_CTRL 0x158
#define VS_EN BIT(0)
#define ACP_EN BIT(1)
#define ISRC1_EN BIT(2)
#define ISRC2_EN BIT(3)
#define GAMUT_EN BIT(4)
#define GRL_CTS_CTRL 0x160
#define CTS_CTRL_SOFT BIT(0)
#define GRL_INT 0x14
#define INT_MDI BIT(0)
#define INT_HDCP BIT(1)
#define INT_FIFO_O BIT(2)
#define INT_FIFO_U BIT(3)
#define INT_IFM_ERR BIT(4)
#define INT_INF_DONE BIT(5)
#define INT_NCTS_DONE BIT(6)
#define INT_CTRL_PKT_DONE BIT(7)
#define GRL_INT_MASK 0x18
#define GRL_CTRL 0x1C
#define CTRL_GEN_EN BIT(2)
#define CTRL_SPD_EN BIT(3)
#define CTRL_MPEG_EN BIT(4)
#define CTRL_AUDIO_EN BIT(5)
#define CTRL_AVI_EN BIT(6)
#define CTRL_AVMUTE BIT(7)
#define GRL_STATUS 0x20
#define STATUS_HTPLG BIT(0)
#define STATUS_PORD BIT(1)
#define GRL_DIVN 0x170
#define NCTS_WRI_ANYTIME BIT(6)
#define GRL_AUDIO_CFG 0x17C
#define AUDIO_ZERO BIT(0)
#define HIGH_BIT_RATE BIT(1)
#define SACD_DST BIT(2)
#define DST_NORMAL_DOUBLE BIT(3)
#define DSD_INV BIT(4)
#define LR_INV BIT(5)
#define LR_MIX BIT(6)
#define DSD_SEL BIT(7)
#define GRL_NCTS 0x184
#define GRL_CH_SW0 0x18C
#define GRL_CH_SW1 0x190
#define GRL_CH_SW2 0x194
#define CH_SWITCH(from, to) ((from) << ((to) * 3))
#define GRL_INFOFRM_VER 0x19C
#define GRL_INFOFRM_TYPE 0x1A0
#define GRL_INFOFRM_LNG 0x1A4
#define GRL_MIX_CTRL 0x1B4
#define MIX_CTRL_SRC_EN BIT(0)
#define BYPASS_VOLUME BIT(1)
#define MIX_CTRL_FLAT BIT(7)
#define GRL_AOUT_CFG 0x1C4
#define AOUT_BNUM_SEL_MASK 0x03
#define AOUT_24BIT 0x00
#define AOUT_20BIT 0x02
#define AOUT_16BIT 0x03
#define AOUT_FIFO_ADAP_CTRL BIT(6)
#define AOUT_BURST_PREAMBLE_EN BIT(7)
#define HIGH_BIT_RATE_PACKET_ALIGN (AOUT_BURST_PREAMBLE_EN | \
AOUT_FIFO_ADAP_CTRL)
#define GRL_SHIFT_L1 0x1C0
#define GRL_SHIFT_R2 0x1B0
#define AUDIO_PACKET_OFF BIT(6)
#define GRL_CFG0 0x24
#define CFG0_I2S_MODE_MASK 0x3
#define CFG0_I2S_MODE_RTJ 0x1
#define CFG0_I2S_MODE_LTJ 0x0
#define CFG0_I2S_MODE_I2S 0x2
#define CFG0_W_LENGTH_MASK 0x30
#define CFG0_W_LENGTH_24BIT 0x00
#define CFG0_W_LENGTH_16BIT 0x10
#define GRL_CFG1 0x28
#define CFG1_EDG_SEL BIT(0)
#define CFG1_SPDIF BIT(1)
#define CFG1_DVI BIT(2)
#define CFG1_HDCP_DEBUG BIT(3)
#define GRL_CFG2 0x2c
#define CFG2_MHL_DE_SEL BIT(3)
#define CFG2_MHL_FAKE_DE_SEL BIT(4)
#define CFG2_MHL_DATA_REMAP BIT(5)
#define CFG2_NOTICE_EN BIT(6)
#define CFG2_ACLK_INV BIT(7)
#define GRL_CFG3 0x30
#define CFG3_AES_KEY_INDEX_MASK 0x3f
#define CFG3_CONTROL_PACKET_DELAY BIT(6)
#define CFG3_KSV_LOAD_START BIT(7)
#define GRL_CFG4 0x34
#define CFG4_AES_KEY_LOAD BIT(4)
#define CFG4_AV_UNMUTE_EN BIT(5)
#define CFG4_AV_UNMUTE_SET BIT(6)
#define CFG4_MHL_MODE BIT(7)
#define GRL_CFG5 0x38
#define CFG5_CD_RATIO_MASK 0x8F
#define CFG5_FS128 (0x1 << 4)
#define CFG5_FS256 (0x2 << 4)
#define CFG5_FS384 (0x3 << 4)
#define CFG5_FS512 (0x4 << 4)
#define CFG5_FS768 (0x6 << 4)
#define DUMMY_304 0x304
#define CHMO_SEL (0x3 << 2)
#define CHM1_SEL (0x3 << 4)
#define CHM2_SEL (0x3 << 6)
#define AUDIO_I2S_NCTS_SEL BIT(1)
#define AUDIO_I2S_NCTS_SEL_64 (1 << 1)
#define AUDIO_I2S_NCTS_SEL_128 (0 << 1)
#define NEW_GCP_CTRL BIT(0)
#define NEW_GCP_CTRL_MERGE BIT(0)
#define GRL_L_STATUS_0 0x200
#define GRL_L_STATUS_1 0x204
#define GRL_L_STATUS_2 0x208
#define GRL_L_STATUS_3 0x20c
#define GRL_L_STATUS_4 0x210
#define GRL_L_STATUS_5 0x214
#define GRL_L_STATUS_6 0x218
#define GRL_L_STATUS_7 0x21c
#define GRL_L_STATUS_8 0x220
#define GRL_L_STATUS_9 0x224
#define GRL_L_STATUS_10 0x228
#define GRL_L_STATUS_11 0x22c
#define GRL_L_STATUS_12 0x230
#define GRL_L_STATUS_13 0x234
#define GRL_L_STATUS_14 0x238
#define GRL_L_STATUS_15 0x23c
#define GRL_L_STATUS_16 0x240
#define GRL_L_STATUS_17 0x244
#define GRL_L_STATUS_18 0x248
#define GRL_L_STATUS_19 0x24c
#define GRL_L_STATUS_20 0x250
#define GRL_L_STATUS_21 0x254
#define GRL_L_STATUS_22 0x258
#define GRL_L_STATUS_23 0x25c
#define GRL_R_STATUS_0 0x260
#define GRL_R_STATUS_1 0x264
#define GRL_R_STATUS_2 0x268
#define GRL_R_STATUS_3 0x26c
#define GRL_R_STATUS_4 0x270
#define GRL_R_STATUS_5 0x274
#define GRL_R_STATUS_6 0x278
#define GRL_R_STATUS_7 0x27c
#define GRL_R_STATUS_8 0x280
#define GRL_R_STATUS_9 0x284
#define GRL_R_STATUS_10 0x288
#define GRL_R_STATUS_11 0x28c
#define GRL_R_STATUS_12 0x290
#define GRL_R_STATUS_13 0x294
#define GRL_R_STATUS_14 0x298
#define GRL_R_STATUS_15 0x29c
#define GRL_R_STATUS_16 0x2a0
#define GRL_R_STATUS_17 0x2a4
#define GRL_R_STATUS_18 0x2a8
#define GRL_R_STATUS_19 0x2ac
#define GRL_R_STATUS_20 0x2b0
#define GRL_R_STATUS_21 0x2b4
#define GRL_R_STATUS_22 0x2b8
#define GRL_R_STATUS_23 0x2bc
#define GRL_ABIST_CTRL0 0x2D4
#define GRL_ABIST_CTRL1 0x2D8
#define ABIST_EN BIT(7)
#define ABIST_DATA_FMT (0x7 << 0)
#define VIDEO_CFG_0 0x380
#define VIDEO_CFG_1 0x384
#define VIDEO_CFG_2 0x388
#define VIDEO_CFG_3 0x38c
#define VIDEO_CFG_4 0x390
#define VIDEO_SOURCE_SEL BIT(7)
#define NORMAL_PATH (1 << 7)
#define GEN_RGB (0 << 7)
#define HDMI_SYS_CFG1C 0x000
#define HDMI_ON BIT(0)
#define HDMI_RST BIT(1)
#define ANLG_ON BIT(2)
#define CFG10_DVI BIT(3)
#define HDMI_TST BIT(3)
#define SYS_KEYMASK1 (0xff << 8)
#define SYS_KEYMASK2 (0xff << 16)
#define AUD_OUTSYNC_EN BIT(24)
#define AUD_OUTSYNC_PRE_EN BIT(25)
#define I2CM_ON BIT(26)
#define E2PROM_TYPE_8BIT BIT(27)
#define MCM_E2PROM_ON BIT(28)
#define EXT_E2PROM_ON BIT(29)
#define HTPLG_PIN_SEL_OFF BIT(30)
#define AES_EFUSE_ENABLE BIT(31)
#define HDMI_SYS_CFG20 0x004
#define DEEP_COLOR_MODE_MASK (3 << 1)
#define COLOR_8BIT_MODE (0 << 1)
#define COLOR_10BIT_MODE (1 << 1)
#define COLOR_12BIT_MODE (2 << 1)
#define COLOR_16BIT_MODE (3 << 1)
#define DEEP_COLOR_EN BIT(0)
#define HDMI_AUDIO_TEST_SEL BIT(8)
#define HDMI2P0_EN BIT(11)
#define HDMI_OUT_FIFO_EN BIT(16)
#define HDMI_OUT_FIFO_CLK_INV BIT(17)
#define MHL_MODE_ON BIT(28)
#define MHL_PP_MODE BIT(29)
#define MHL_SYNC_AUTO_EN BIT(30)
#define HDMI_PCLK_FREE_RUN BIT(31)
#define MTK_SIP_SET_AUTHORIZED_SECURE_REG 0x82000001
#endif
@@ -0,0 +1,263 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2021 BayLibre, SAS
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#ifndef _MTK_HDMI_REGS_H
#define _MTK_HDMI_REGS_H
/* HDMI_TOP Config */
#define TOP_CFG00 0x000
#define HDMI2_ON BIT(2)
#define HDMI_MODE_HDMI BIT(3)
#define SCR_ON BIT(4)
#define TMDS_PACK_MODE GENMASK(9, 8)
#define TMDS_PACK_MODE_8BPP 0
#define TMDS_PACK_MODE_10BPP 1
#define TMDS_PACK_MODE_12BPP 2
#define TMDS_PACK_MODE_16BPP 3
#define DEEPCOLOR_PKT_EN BIT(12)
#define HDMI_ABIST_VIDEO_FORMAT GENMASK(21, 16)
#define HDMI_ABIST_ENABLE BIT(31)
#define TOP_CFG01 0x004
#define CP_SET_MUTE_EN BIT(0)
#define CP_CLR_MUTE_EN BIT(1)
#define NULL_PKT_EN BIT(2)
#define NULL_PKT_VSYNC_HIGH_EN BIT(3)
/* HDMI_TOP Audio: Channel Mapping */
#define TOP_AUD_MAP 0x00c
#define SD0_MAP GENMASK(2, 0)
#define SD1_MAP GENMASK(6, 4)
#define SD2_MAP GENMASK(10, 8)
#define SD3_MAP GENMASK(14, 12)
#define SD4_MAP GENMASK(18, 16)
#define SD5_MAP GENMASK(22, 20)
#define SD6_MAP GENMASK(26, 24)
#define SD7_MAP GENMASK(30, 28)
/* Auxiliary Video Information (AVI) Infoframe */
#define TOP_AVI_HEADER 0x024
#define TOP_AVI_PKT00 0x028
#define TOP_AVI_PKT01 0x02C
#define TOP_AVI_PKT02 0x030
#define TOP_AVI_PKT03 0x034
#define TOP_AVI_PKT04 0x038
#define TOP_AVI_PKT05 0x03C
/* Audio Interface Infoframe */
#define TOP_AIF_HEADER 0x040
#define TOP_AIF_PKT00 0x044
#define TOP_AIF_PKT01 0x048
#define TOP_AIF_PKT02 0x04c
#define TOP_AIF_PKT03 0x050
/* Audio SPDIF Infoframe */
#define TOP_SPDIF_HEADER 0x054
#define TOP_SPDIF_PKT00 0x058
#define TOP_SPDIF_PKT01 0x05c
#define TOP_SPDIF_PKT02 0x060
#define TOP_SPDIF_PKT03 0x064
#define TOP_SPDIF_PKT04 0x068
#define TOP_SPDIF_PKT05 0x06c
#define TOP_SPDIF_PKT06 0x070
#define TOP_SPDIF_PKT07 0x074
/* Infoframes Configuration */
#define TOP_INFO_EN 0x01c
#define AVI_EN BIT(0)
#define SPD_EN BIT(1)
#define AUD_EN BIT(2)
#define CP_EN BIT(5)
#define VSIF_EN BIT(11)
#define AVI_EN_WR BIT(16)
#define SPD_EN_WR BIT(17)
#define AUD_EN_WR BIT(18)
#define CP_EN_WR BIT(21)
#define VSIF_EN_WR BIT(27)
#define TOP_INFO_RPT 0x020
#define AVI_RPT_EN BIT(0)
#define SPD_RPT_EN BIT(1)
#define AUD_RPT_EN BIT(2)
#define CP_RPT_EN BIT(5)
#define VSIF_RPT_EN BIT(11)
/* Vendor Specific Infoframe */
#define TOP_VSIF_HEADER 0x174
#define TOP_VSIF_PKT00 0x178
#define TOP_VSIF_PKT01 0x17c
#define TOP_VSIF_PKT02 0x180
#define TOP_VSIF_PKT03 0x184
#define TOP_VSIF_PKT04 0x188
#define TOP_VSIF_PKT05 0x18c
#define TOP_VSIF_PKT06 0x190
#define TOP_VSIF_PKT07 0x194
/* HDMI_TOP Misc */
#define TOP_MISC_CTLR 0x1a4
#define DEEP_COLOR_ADD BIT(4)
/* Hardware interrupts */
#define TOP_INT_STA00 0x1a8
#define TOP_INT_ENABLE00 0x1b0
#define HTPLG_R_INT BIT(0)
#define HTPLG_F_INT BIT(1)
#define PORD_R_INT BIT(2)
#define PORD_F_INT BIT(3)
#define HDMI_VSYNC_INT BIT(4)
#define HDMI_AUDIO_INT BIT(5)
#define HDCP2X_RX_REAUTH_REQ_DDCM_INT BIT(25)
#define TOP_INT_ENABLE01 0x1b4
#define TOP_INT_CLR00 0x1b8
#define TOP_INT_CLR01 0x1bc
/* Video Mute */
#define TOP_VMUTE_CFG1 0x1c8
#define REG_VMUTE_EN BIT(16)
/* HDMI Audio IP */
#define AIP_CTRL 0x400
#define CTS_SW_SEL BIT(0)
#define CTS_REQ_EN BIT(1)
#define MCLK_EN BIT(2)
#define NO_MCLK_CTSGEN_SEL BIT(3)
#define AUD_IN_EN BIT(8)
#define AUD_SEL_OWRT BIT(9)
#define SPDIF_EN BIT(13)
#define HBRA_ON BIT(14)
#define DSD_EN BIT(15)
#define I2S_EN GENMASK(19, 16)
#define HBR_FROM_SPDIF BIT(20)
#define CTS_CAL_N4 BIT(23)
#define SPDIF_INTERNAL_MODULE BIT(24)
#define AIP_N_VAL 0x404
#define AIP_CTS_SVAL 0x408
#define AIP_SPDIF_CTRL 0x40c
#define WR_1UI_LOCK BIT(0)
#define FS_OVERRIDE_WRITE BIT(1)
#define WR_2UI_LOCK BIT(2)
#define MAX_1UI_WRITE GENMASK(15, 8)
#define MAX_2UI_SPDIF_WRITE GENMASK(23, 16)
#define MAX_2UI_I2S_HI_WRITE GENMASK(23, 20)
#define MAX_2UI_I2S_LFE_CC_SWAP BIT(1)
#define MAX_2UI_I2S_LO_WRITE GENMASK(19, 16)
#define AUD_ERR_THRESH GENMASK(29, 24)
#define I2S2DSD_EN BIT(30)
#define AIP_I2S_CTRL 0x410
#define FIFO0_MAP GENMASK(1, 0)
#define FIFO1_MAP GENMASK(3, 2)
#define FIFO2_MAP GENMASK(5, 4)
#define FIFO3_MAP GENMASK(7, 6)
#define I2S_1ST_BIT_NOSHIFT BIT(8)
#define I2S_DATA_DIR_LSB BIT(9)
#define JUSTIFY_RIGHT BIT(10)
#define WS_HIGH BIT(11)
#define VBIT_COMPRESSED BIT(12)
#define CBIT_ORDER_SAME BIT(13)
#define SCK_EDGE_RISE BIT(14)
#define AIP_I2S_CHST0 0x414
#define AIP_I2S_CHST1 0x418
#define AIP_TXCTRL 0x424
#define RST4AUDIO BIT(0)
#define RST4AUDIO_FIFO BIT(1)
#define RST4AUDIO_ACR BIT(2)
#define AUD_LAYOUT_1 BIT(4)
#define AUD_MUTE_FIFO_EN BIT(5)
#define AUD_PACKET_DROP BIT(6)
#define DSD_MUTE_EN BIT(7)
#define AIP_TPI_CTRL 0x428
#define TPI_AUDIO_LOOKUP_EN BIT(2)
/* Video downsampling configuration */
#define VID_DOWNSAMPLE_CONFIG 0x8d0
#define C444_C422_CONFIG_ENABLE BIT(0)
#define C422_C420_CONFIG_ENABLE BIT(4)
#define C422_C420_CONFIG_BYPASS BIT(5)
#define C422_C420_CONFIG_OUT_CB_OR_CR BIT(6)
#define VID_OUT_FORMAT 0x8fc
#define OUTPUT_FORMAT_DEMUX_420_ENABLE BIT(10)
/* HDCP registers */
#define HDCP_TOP_CTRL 0xc00
#define HDCP2X_CTRL_0 0xc20
#define HDCP2X_EN BIT(0)
#define HDCP2X_ENCRYPT_EN BIT(7)
#define HDCP2X_HPD_OVR BIT(10)
#define HDCP2X_HPD_SW BIT(11)
#define HDCP2X_POL_CTRL 0xc54
#define HDCP2X_DIS_POLL_EN BIT(16)
#define HDCP1X_CTRL 0xcd0
#define HDCP1X_ENC_EN BIT(6)
/* HDMI DDC registers */
#define HPD_DDC_CTRL 0xc08
#define HPD_DDC_DELAY_CNT GENMASK(31, 16)
#define HPD_DDC_HPD_DBNC_EN BIT(2)
#define HPD_DDC_PORD_DBNC_EN BIT(3)
#define DDC_CTRL 0xc10
#define DDC_CTRL_ADDR GENMASK(7, 1)
#define DDC_CTRL_OFFSET GENMASK(15, 8)
#define DDC_CTRL_DIN_CNT GENMASK(25, 16)
#define DDC_CTRL_CMD GENMASK(31, 28)
#define SCDC_CTRL 0xc18
#define SCDC_DDC_SEGMENT GENMASK(15, 8)
#define HPD_DDC_STATUS 0xc60
#define HPD_STATE GENMASK(1, 0)
#define HPD_STATE_CONNECTED 2
#define HPD_PIN_STA BIT(4)
#define PORD_PIN_STA BIT(5)
#define DDC_I2C_IN_PROG BIT(13)
#define DDC_DATA_OUT GENMASK(23, 16)
#define SI2C_CTRL 0xcac
#define SI2C_WR BIT(0)
#define SI2C_RD BIT(1)
#define SI2C_CONFIRM_READ BIT(2)
#define SI2C_WDATA GENMASK(15, 8)
#define SI2C_ADDR GENMASK(23, 16)
/* HDCP DDC registers */
#define HDCP2X_DDCM_STATUS 0xc68
#define DDC_I2C_NO_ACK BIT(10)
#define DDC_I2C_BUS_LOW BIT(11)
/* HDMI TX registers */
#define HDMITX_CONFIG_MT8188 0xea0
#define HDMITX_CONFIG_MT8195 0x900
#define HDMI_YUV420_MODE BIT(10)
#define HDMITX_SW_HPD BIT(29)
#define HDMITX_SW_RSTB BIT(31)
/**
* enum mtk_hdmi_ddc_v2_cmds - DDC_CMD register commands
* @DDC_CMD_READ_NOACK: Current address read with no ACK on last byte
* @DDC_CMD_READ: Current address read with ACK on last byte
* @DDC_CMD_SEQ_READ_NOACK: Sequential read with no ACK on last byte
* @DDC_CMD_SEQ_READ: Sequential read with ACK on last byte
* @DDC_CMD_ENH_READ_NOACK: Enhanced read with no ACK on last byte
* @DDC_CMD_ENH_READ: Enhanced read with ACK on last byte
* @DDC_CMD_SEQ_WRITE_NOACK: Sequential write ignoring ACK on last byte
* @DDC_CMD_SEQ_WRITE: Sequential write requiring ACK on last byte
* @DDC_CMD_RSVD: Reserved for future use
* @DDC_CMD_CLEAR_FIFO: Clear DDC I2C FIFO
* @DDC_CMD_CLOCK_SCL: Start clocking DDC I2C SCL
* @DDC_CMD_ABORT_XFER: Abort DDC I2C transaction
*/
enum mtk_hdmi_ddc_v2_cmds {
DDC_CMD_READ_NOACK = 0x0,
DDC_CMD_READ,
DDC_CMD_SEQ_READ_NOACK,
DDC_CMD_SEQ_READ,
DDC_CMD_ENH_READ_NOACK,
DDC_CMD_ENH_READ,
DDC_CMD_SEQ_WRITE_NOACK,
DDC_CMD_SEQ_WRITE = 0x07,
DDC_CMD_CLEAR_FIFO = 0x09,
DDC_CMD_CLOCK_SCL = 0x0a,
DDC_CMD_ABORT_XFER = 0x0f
};
#endif /* _MTK_HDMI_REGS_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,346 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <drm/drm_fourcc.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_disp_drv.h"
#include "mtk_drm_drv.h"
#include "mtk_mdp_rdma.h"
#define MDP_RDMA_EN 0x000
#define FLD_ROT_ENABLE BIT(0)
#define MDP_RDMA_RESET 0x008
#define MDP_RDMA_CON 0x020
#define FLD_OUTPUT_10B BIT(5)
#define FLD_SIMPLE_MODE BIT(4)
#define MDP_RDMA_GMCIF_CON 0x028
#define FLD_COMMAND_DIV BIT(0)
#define FLD_EXT_PREULTRA_EN BIT(3)
#define FLD_RD_REQ_TYPE GENMASK(7, 4)
#define VAL_RD_REQ_TYPE_BURST_8_ACCESS 7
#define FLD_ULTRA_EN GENMASK(13, 12)
#define VAL_ULTRA_EN_ENABLE 1
#define FLD_PRE_ULTRA_EN GENMASK(17, 16)
#define VAL_PRE_ULTRA_EN_ENABLE 1
#define FLD_EXT_ULTRA_EN BIT(18)
#define MDP_RDMA_SRC_CON 0x030
#define FLD_OUTPUT_ARGB BIT(25)
#define FLD_BIT_NUMBER GENMASK(19, 18)
#define FLD_SWAP BIT(14)
#define FLD_UNIFORM_CONFIG BIT(17)
#define RDMA_INPUT_10BIT BIT(18)
#define FLD_SRC_FORMAT GENMASK(3, 0)
#define MDP_RDMA_COMP_CON 0x038
#define FLD_AFBC_EN BIT(22)
#define FLD_AFBC_YUV_TRANSFORM BIT(21)
#define FLD_UFBDC_EN BIT(12)
#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE 0x060
#define FLD_MF_BKGD_WB GENMASK(22, 0)
#define MDP_RDMA_MF_SRC_SIZE 0x070
#define FLD_MF_SRC_H GENMASK(30, 16)
#define FLD_MF_SRC_W GENMASK(14, 0)
#define MDP_RDMA_MF_CLIP_SIZE 0x078
#define FLD_MF_CLIP_H GENMASK(30, 16)
#define FLD_MF_CLIP_W GENMASK(14, 0)
#define MDP_RDMA_SRC_OFFSET_0 0x118
#define FLD_SRC_OFFSET_0 GENMASK(31, 0)
#define MDP_RDMA_TRANSFORM_0 0x200
#define FLD_INT_MATRIX_SEL GENMASK(27, 23)
#define FLD_TRANS_EN BIT(16)
#define MDP_RDMA_SRC_BASE_0 0xf00
#define FLD_SRC_BASE_0 GENMASK(31, 0)
#define RDMA_CSC_FULL709_TO_RGB 5
#define RDMA_CSC_BT601_TO_RGB 6
static const u32 formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
};
enum rdma_format {
RDMA_INPUT_FORMAT_RGB565 = 0,
RDMA_INPUT_FORMAT_RGB888 = 1,
RDMA_INPUT_FORMAT_RGBA8888 = 2,
RDMA_INPUT_FORMAT_ARGB8888 = 3,
RDMA_INPUT_FORMAT_UYVY = 4,
RDMA_INPUT_FORMAT_YUY2 = 5,
RDMA_INPUT_FORMAT_Y8 = 7,
RDMA_INPUT_FORMAT_YV12 = 8,
RDMA_INPUT_FORMAT_UYVY_3PL = 9,
RDMA_INPUT_FORMAT_NV12 = 12,
RDMA_INPUT_FORMAT_UYVY_2PL = 13,
RDMA_INPUT_FORMAT_Y410 = 14
};
struct mtk_mdp_rdma {
void __iomem *regs;
struct clk *clk;
struct cmdq_client_reg cmdq_reg;
};
static unsigned int rdma_fmt_convert(unsigned int fmt)
{
switch (fmt) {
default:
case DRM_FORMAT_RGB565:
return RDMA_INPUT_FORMAT_RGB565;
case DRM_FORMAT_BGR565:
return RDMA_INPUT_FORMAT_RGB565 | FLD_SWAP;
case DRM_FORMAT_RGB888:
return RDMA_INPUT_FORMAT_RGB888;
case DRM_FORMAT_BGR888:
return RDMA_INPUT_FORMAT_RGB888 | FLD_SWAP;
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_RGBA8888:
return RDMA_INPUT_FORMAT_ARGB8888;
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_BGRA8888:
return RDMA_INPUT_FORMAT_ARGB8888 | FLD_SWAP;
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
return RDMA_INPUT_FORMAT_RGBA8888;
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR8888:
return RDMA_INPUT_FORMAT_RGBA8888 | FLD_SWAP;
case DRM_FORMAT_ABGR2101010:
return RDMA_INPUT_FORMAT_RGBA8888 | FLD_SWAP | RDMA_INPUT_10BIT;
case DRM_FORMAT_ARGB2101010:
return RDMA_INPUT_FORMAT_RGBA8888 | RDMA_INPUT_10BIT;
case DRM_FORMAT_RGBA1010102:
return RDMA_INPUT_FORMAT_ARGB8888 | FLD_SWAP | RDMA_INPUT_10BIT;
case DRM_FORMAT_BGRA1010102:
return RDMA_INPUT_FORMAT_ARGB8888 | RDMA_INPUT_10BIT;
case DRM_FORMAT_UYVY:
return RDMA_INPUT_FORMAT_UYVY;
case DRM_FORMAT_YUYV:
return RDMA_INPUT_FORMAT_YUY2;
}
}
static unsigned int rdma_color_convert(unsigned int color_encoding)
{
switch (color_encoding) {
default:
case DRM_COLOR_YCBCR_BT709:
return RDMA_CSC_FULL709_TO_RGB;
case DRM_COLOR_YCBCR_BT601:
return RDMA_CSC_BT601_TO_RGB;
}
}
static void mtk_mdp_rdma_fifo_config(struct device *dev, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_mdp_rdma *priv = dev_get_drvdata(dev);
mtk_ddp_write_mask(cmdq_pkt, FLD_EXT_ULTRA_EN | VAL_PRE_ULTRA_EN_ENABLE << 16 |
VAL_ULTRA_EN_ENABLE << 12 | VAL_RD_REQ_TYPE_BURST_8_ACCESS << 4 |
FLD_EXT_PREULTRA_EN | FLD_COMMAND_DIV, &priv->cmdq_reg,
priv->regs, MDP_RDMA_GMCIF_CON, FLD_EXT_ULTRA_EN |
FLD_PRE_ULTRA_EN | FLD_ULTRA_EN | FLD_RD_REQ_TYPE |
FLD_EXT_PREULTRA_EN | FLD_COMMAND_DIV);
}
void mtk_mdp_rdma_start(struct device *dev, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_mdp_rdma *priv = dev_get_drvdata(dev);
mtk_ddp_write_mask(cmdq_pkt, FLD_ROT_ENABLE, &priv->cmdq_reg,
priv->regs, MDP_RDMA_EN, FLD_ROT_ENABLE);
}
void mtk_mdp_rdma_stop(struct device *dev, struct cmdq_pkt *cmdq_pkt)
{
struct mtk_mdp_rdma *priv = dev_get_drvdata(dev);
mtk_ddp_write_mask(cmdq_pkt, 0, &priv->cmdq_reg,
priv->regs, MDP_RDMA_EN, FLD_ROT_ENABLE);
mtk_ddp_write(cmdq_pkt, 1, &priv->cmdq_reg, priv->regs, MDP_RDMA_RESET);
mtk_ddp_write(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs, MDP_RDMA_RESET);
}
void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg,
struct cmdq_pkt *cmdq_pkt)
{
struct mtk_mdp_rdma *priv = dev_get_drvdata(dev);
const struct drm_format_info *fmt_info = drm_format_info(cfg->fmt);
bool csc_enable = fmt_info->is_yuv ? true : false;
unsigned int src_pitch_y = cfg->pitch;
unsigned int offset_y = 0;
mtk_mdp_rdma_fifo_config(dev, cmdq_pkt);
mtk_ddp_write_mask(cmdq_pkt, FLD_UNIFORM_CONFIG, &priv->cmdq_reg, priv->regs,
MDP_RDMA_SRC_CON, FLD_UNIFORM_CONFIG);
mtk_ddp_write_mask(cmdq_pkt, rdma_fmt_convert(cfg->fmt), &priv->cmdq_reg, priv->regs,
MDP_RDMA_SRC_CON, FLD_SWAP | FLD_SRC_FORMAT | FLD_BIT_NUMBER);
if (!csc_enable && fmt_info->has_alpha)
mtk_ddp_write_mask(cmdq_pkt, FLD_OUTPUT_ARGB, &priv->cmdq_reg,
priv->regs, MDP_RDMA_SRC_CON, FLD_OUTPUT_ARGB);
else
mtk_ddp_write_mask(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs,
MDP_RDMA_SRC_CON, FLD_OUTPUT_ARGB);
mtk_ddp_write_mask(cmdq_pkt, cfg->addr0, &priv->cmdq_reg, priv->regs,
MDP_RDMA_SRC_BASE_0, FLD_SRC_BASE_0);
mtk_ddp_write_mask(cmdq_pkt, src_pitch_y, &priv->cmdq_reg, priv->regs,
MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, FLD_MF_BKGD_WB);
mtk_ddp_write_mask(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs, MDP_RDMA_COMP_CON,
FLD_AFBC_YUV_TRANSFORM | FLD_UFBDC_EN | FLD_AFBC_EN);
mtk_ddp_write_mask(cmdq_pkt, FLD_OUTPUT_10B, &priv->cmdq_reg, priv->regs,
MDP_RDMA_CON, FLD_OUTPUT_10B);
mtk_ddp_write_mask(cmdq_pkt, FLD_SIMPLE_MODE, &priv->cmdq_reg, priv->regs,
MDP_RDMA_CON, FLD_SIMPLE_MODE);
if (csc_enable)
mtk_ddp_write_mask(cmdq_pkt, rdma_color_convert(cfg->color_encoding) << 23,
&priv->cmdq_reg, priv->regs, MDP_RDMA_TRANSFORM_0,
FLD_INT_MATRIX_SEL);
mtk_ddp_write_mask(cmdq_pkt, csc_enable << 16, &priv->cmdq_reg, priv->regs,
MDP_RDMA_TRANSFORM_0, FLD_TRANS_EN);
offset_y = cfg->x_left * fmt_info->cpp[0] + cfg->y_top * src_pitch_y;
mtk_ddp_write_mask(cmdq_pkt, offset_y, &priv->cmdq_reg, priv->regs,
MDP_RDMA_SRC_OFFSET_0, FLD_SRC_OFFSET_0);
mtk_ddp_write_mask(cmdq_pkt, cfg->width, &priv->cmdq_reg, priv->regs,
MDP_RDMA_MF_SRC_SIZE, FLD_MF_SRC_W);
mtk_ddp_write_mask(cmdq_pkt, cfg->height << 16, &priv->cmdq_reg, priv->regs,
MDP_RDMA_MF_SRC_SIZE, FLD_MF_SRC_H);
mtk_ddp_write_mask(cmdq_pkt, cfg->width, &priv->cmdq_reg, priv->regs,
MDP_RDMA_MF_CLIP_SIZE, FLD_MF_CLIP_W);
mtk_ddp_write_mask(cmdq_pkt, cfg->height << 16, &priv->cmdq_reg, priv->regs,
MDP_RDMA_MF_CLIP_SIZE, FLD_MF_CLIP_H);
}
const u32 *mtk_mdp_rdma_get_formats(struct device *dev)
{
return formats;
}
size_t mtk_mdp_rdma_get_num_formats(struct device *dev)
{
return ARRAY_SIZE(formats);
}
int mtk_mdp_rdma_power_on(struct device *dev)
{
int ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "Failed to power on: %d\n", ret);
return ret;
}
return 0;
}
void mtk_mdp_rdma_power_off(struct device *dev)
{
pm_runtime_put(dev);
}
int mtk_mdp_rdma_clk_enable(struct device *dev)
{
struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev);
return clk_prepare_enable(rdma->clk);
}
void mtk_mdp_rdma_clk_disable(struct device *dev)
{
struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev);
clk_disable_unprepare(rdma->clk);
}
static int mtk_mdp_rdma_bind(struct device *dev, struct device *master,
void *data)
{
return 0;
}
static void mtk_mdp_rdma_unbind(struct device *dev, struct device *master,
void *data)
{
}
static const struct component_ops mtk_mdp_rdma_component_ops = {
.bind = mtk_mdp_rdma_bind,
.unbind = mtk_mdp_rdma_unbind,
};
static int mtk_mdp_rdma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_mdp_rdma *priv;
int ret = 0;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs))
return dev_err_probe(dev, PTR_ERR(priv->regs),
"failed to ioremap rdma\n");
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get rdma clk\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");
#endif
platform_set_drvdata(pdev, priv);
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_mdp_rdma_component_ops);
if (ret != 0) {
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
return 0;
}
static void mtk_mdp_rdma_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_mdp_rdma_component_ops);
pm_runtime_disable(&pdev->dev);
}
static const struct of_device_id mtk_mdp_rdma_driver_dt_match[] = {
{ .compatible = "mediatek,mt8195-vdo1-rdma", },
{},
};
MODULE_DEVICE_TABLE(of, mtk_mdp_rdma_driver_dt_match);
struct platform_driver mtk_mdp_rdma_driver = {
.probe = mtk_mdp_rdma_probe,
.remove = mtk_mdp_rdma_remove,
.driver = {
.name = "mediatek-mdp-rdma",
.of_match_table = mtk_mdp_rdma_driver_dt_match,
},
};
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifndef __MTK_MDP_RDMA_H__
#define __MTK_MDP_RDMA_H__
struct mtk_mdp_rdma_cfg {
unsigned int pitch;
unsigned int addr0;
unsigned int width;
unsigned int height;
unsigned int x_left;
unsigned int y_top;
int fmt;
int color_encoding;
};
#endif // __MTK_MDP_RDMA_H__
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_disp_drv.h"
#define PADDING_CONTROL_REG 0x00
#define PADDING_BYPASS BIT(0)
#define PADDING_ENABLE BIT(1)
#define PADDING_PIC_SIZE_REG 0x04
#define PADDING_H_REG 0x08 /* horizontal */
#define PADDING_V_REG 0x0c /* vertical */
#define PADDING_COLOR_REG 0x10
/**
* struct mtk_padding - Basic information of the Padding
* @clk: Clock of the module
* @reg: Virtual address of the Padding for CPU to access
* @cmdq_reg: CMDQ setting of the Padding
*
* Every Padding should have different clock source, register base, and
* CMDQ settings, we stored these differences all together.
*/
struct mtk_padding {
struct clk *clk;
void __iomem *reg;
struct cmdq_client_reg cmdq_reg;
};
int mtk_padding_clk_enable(struct device *dev)
{
struct mtk_padding *padding = dev_get_drvdata(dev);
return clk_prepare_enable(padding->clk);
}
void mtk_padding_clk_disable(struct device *dev)
{
struct mtk_padding *padding = dev_get_drvdata(dev);
clk_disable_unprepare(padding->clk);
}
void mtk_padding_start(struct device *dev)
{
struct mtk_padding *padding = dev_get_drvdata(dev);
writel(PADDING_ENABLE | PADDING_BYPASS,
padding->reg + PADDING_CONTROL_REG);
/*
* Notice that even the padding is in bypass mode,
* all the settings must be cleared to 0 or
* undefined behaviors could happen
*/
writel(0, padding->reg + PADDING_PIC_SIZE_REG);
writel(0, padding->reg + PADDING_H_REG);
writel(0, padding->reg + PADDING_V_REG);
writel(0, padding->reg + PADDING_COLOR_REG);
}
void mtk_padding_stop(struct device *dev)
{
struct mtk_padding *padding = dev_get_drvdata(dev);
writel(0, padding->reg + PADDING_CONTROL_REG);
}
static int mtk_padding_bind(struct device *dev, struct device *master, void *data)
{
return 0;
}
static void mtk_padding_unbind(struct device *dev, struct device *master, void *data)
{
}
static const struct component_ops mtk_padding_component_ops = {
.bind = mtk_padding_bind,
.unbind = mtk_padding_unbind,
};
static int mtk_padding_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_padding *priv;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get clk\n");
priv->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(priv->reg))
return dev_err_probe(dev, PTR_ERR(priv->reg),
"failed to do ioremap\n");
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
if (ret)
return dev_err_probe(dev, ret, "failed to get gce client reg\n");
#endif
platform_set_drvdata(pdev, priv);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
ret = component_add(dev, &mtk_padding_component_ops);
if (ret) {
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "failed to add component\n");
}
return 0;
}
static void mtk_padding_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_padding_component_ops);
}
static const struct of_device_id mtk_padding_driver_dt_match[] = {
{ .compatible = "mediatek,mt8188-disp-padding" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_padding_driver_dt_match);
struct platform_driver mtk_padding_driver = {
.probe = mtk_padding_probe,
.remove = mtk_padding_remove,
.driver = {
.name = "mediatek-disp-padding",
.of_match_table = mtk_padding_driver_dt_match,
},
};
@@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 MediaTek Inc.
* Author: CK Hu <ck.hu@mediatek.com>
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_print.h>
#include <linux/align.h>
#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
#include "mtk_drm_drv.h"
#include "mtk_plane.h"
static const u64 modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
};
static void mtk_plane_reset(struct drm_plane *plane)
{
struct mtk_plane_state *state;
if (plane->state) {
__drm_atomic_helper_plane_destroy_state(plane->state);
state = to_mtk_plane_state(plane->state);
memset(state, 0, sizeof(*state));
} else {
state = kzalloc_obj(*state);
if (!state)
return;
}
__drm_atomic_helper_plane_reset(plane, &state->base);
state->base.plane = plane;
state->pending.format = DRM_FORMAT_RGB565;
state->pending.modifier = DRM_FORMAT_MOD_LINEAR;
}
static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
{
struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
struct mtk_plane_state *state;
state = kmalloc_obj(*state);
if (!state)
return NULL;
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
WARN_ON(state->base.plane != plane);
state->pending = old_state->pending;
return &state->base;
}
static bool mtk_plane_format_mod_supported(struct drm_plane *plane,
uint32_t format,
uint64_t modifier)
{
return modifier == DRM_FORMAT_MOD_LINEAR;
}
static void mtk_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
__drm_atomic_helper_plane_destroy_state(state);
kfree(to_mtk_plane_state(state));
}
static int mtk_plane_atomic_async_check(struct drm_plane *plane,
struct drm_atomic_commit *state, bool flip)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct drm_crtc_state *crtc_state;
int ret;
if (plane != new_plane_state->crtc->cursor)
return -EINVAL;
if (!plane->state)
return -EINVAL;
if (!plane->state->fb)
return -EINVAL;
ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,
to_mtk_plane_state(new_plane_state));
if (ret)
return ret;
crtc_state = drm_atomic_get_new_crtc_state(state,
new_plane_state->crtc);
return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
true, true);
}
static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
struct mtk_plane_state *mtk_plane_state)
{
struct drm_framebuffer *fb = new_state->fb;
struct drm_gem_dma_object *dma_obj;
struct drm_gem_object *gem;
unsigned int pitch, format;
u64 modifier;
dma_addr_t addr;
dma_addr_t hdr_addr = 0;
unsigned int hdr_pitch = 0;
int offset;
gem = fb->obj[0];
dma_obj = to_drm_gem_dma_obj(gem);
addr = dma_obj->dma_addr;
pitch = fb->pitches[0];
format = fb->format->format;
modifier = fb->modifier;
if (modifier == DRM_FORMAT_MOD_LINEAR) {
/*
* Using dma_addr_t variable to calculate with multiplier of different types,
* for example: addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];
* may cause coverity issue with unintentional overflow.
*/
offset = (new_state->src.x1 >> 16) * fb->format->cpp[0];
addr += offset;
offset = (new_state->src.y1 >> 16) * pitch;
addr += offset;
} else {
int width_in_blocks = ALIGN(fb->width, AFBC_DATA_BLOCK_WIDTH)
/ AFBC_DATA_BLOCK_WIDTH;
int height_in_blocks = ALIGN(fb->height, AFBC_DATA_BLOCK_HEIGHT)
/ AFBC_DATA_BLOCK_HEIGHT;
int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;
int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;
int hdr_size, hdr_offset;
hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;
pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *
AFBC_DATA_BLOCK_HEIGHT * fb->format->cpp[0];
hdr_size = ALIGN(hdr_pitch * height_in_blocks, AFBC_HEADER_ALIGNMENT);
hdr_offset = hdr_pitch * y_offset_in_blocks +
AFBC_HEADER_BLOCK_SIZE * x_offset_in_blocks;
/*
* Using dma_addr_t variable to calculate with multiplier of different types,
* for example: addr += hdr_pitch * y_offset_in_blocks;
* may cause coverity issue with unintentional overflow.
*/
hdr_addr = addr + hdr_offset;
/* The data plane is offset by 1 additional block. */
offset = pitch * y_offset_in_blocks +
AFBC_DATA_BLOCK_WIDTH * AFBC_DATA_BLOCK_HEIGHT *
fb->format->cpp[0] * (x_offset_in_blocks + 1);
/*
* Using dma_addr_t variable to calculate with multiplier of different types,
* for example: addr += pitch * y_offset_in_blocks;
* may cause coverity issue with unintentional overflow.
*/
addr = addr + hdr_size + offset;
}
mtk_plane_state->pending.enable = true;
mtk_plane_state->pending.pitch = pitch;
mtk_plane_state->pending.hdr_pitch = hdr_pitch;
mtk_plane_state->pending.format = format;
mtk_plane_state->pending.modifier = modifier;
mtk_plane_state->pending.addr = addr;
mtk_plane_state->pending.hdr_addr = hdr_addr;
mtk_plane_state->pending.x = new_state->dst.x1;
mtk_plane_state->pending.y = new_state->dst.y1;
mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);
mtk_plane_state->pending.height = drm_rect_height(&new_state->dst);
mtk_plane_state->pending.rotation = new_state->rotation;
mtk_plane_state->pending.color_encoding = new_state->color_encoding;
}
static void mtk_plane_atomic_async_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct mtk_plane_state *new_plane_state = to_mtk_plane_state(plane->state);
plane->state->crtc_x = new_state->crtc_x;
plane->state->crtc_y = new_state->crtc_y;
plane->state->crtc_h = new_state->crtc_h;
plane->state->crtc_w = new_state->crtc_w;
plane->state->src_x = new_state->src_x;
plane->state->src_y = new_state->src_y;
plane->state->src_h = new_state->src_h;
plane->state->src_w = new_state->src_w;
plane->state->dst.x1 = new_state->dst.x1;
plane->state->dst.y1 = new_state->dst.y1;
mtk_plane_update_new_state(new_state, new_plane_state);
swap(plane->state->fb, new_state->fb);
wmb(); /* Make sure the above parameters are set before update */
new_plane_state->pending.async_dirty = true;
mtk_crtc_async_update(new_state->crtc, plane, state);
}
static const struct drm_plane_funcs mtk_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = mtk_plane_reset,
.atomic_duplicate_state = mtk_plane_duplicate_state,
.atomic_destroy_state = mtk_plane_destroy_state,
.format_mod_supported = mtk_plane_format_mod_supported,
};
static int mtk_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct drm_framebuffer *fb = new_plane_state->fb;
struct drm_crtc_state *crtc_state;
int ret;
if (!fb)
return 0;
if (WARN_ON(!new_plane_state->crtc))
return 0;
ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,
to_mtk_plane_state(new_plane_state));
if (ret)
return ret;
crtc_state = drm_atomic_get_crtc_state(state,
new_plane_state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
return drm_atomic_helper_check_plane_state(new_plane_state,
crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
true, true);
}
static void mtk_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
plane);
mtk_plane_state->pending.enable = false;
wmb(); /* Make sure the above parameter is set before update */
mtk_plane_state->pending.dirty = true;
if (old_state && old_state->crtc)
mtk_crtc_plane_disable(old_state->crtc, plane);
}
static void mtk_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);
if (!new_state->crtc || WARN_ON(!new_state->fb))
return;
if (!new_state->visible) {
mtk_plane_atomic_disable(plane, state);
return;
}
mtk_plane_update_new_state(new_state, mtk_plane_state);
wmb(); /* Make sure the above parameters are set before update */
mtk_plane_state->pending.dirty = true;
}
static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
.atomic_check = mtk_plane_atomic_check,
.atomic_update = mtk_plane_atomic_update,
.atomic_disable = mtk_plane_atomic_disable,
.atomic_async_update = mtk_plane_atomic_async_update,
.atomic_async_check = mtk_plane_atomic_async_check,
};
int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
unsigned long possible_crtcs, enum drm_plane_type type,
unsigned int supported_rotations, const u32 blend_modes,
const u32 *formats, size_t num_formats,
bool supports_afbc, unsigned int plane_idx)
{
int err;
if (!formats || !num_formats) {
DRM_ERROR("no formats for plane\n");
return -EINVAL;
}
err = drm_universal_plane_init(dev, plane, possible_crtcs,
&mtk_plane_funcs, formats,
num_formats,
supports_afbc ? modifiers : NULL,
type, NULL);
if (err) {
DRM_ERROR("failed to initialize plane\n");
return err;
}
/*
* The hardware does not support repositioning planes by muxing: their
* Z-position is infact fixed and the only way to change the actual
* order is to swap the contents of the entire register set of one
* overlay with another, which may be more expensive than desired.
*
* With no repositioning, the caller of this function guarantees that
* the plane_idx is correct. This means that, for example, the PRIMARY
* plane fed to this function will always have plane_idx zero.
*/
err = drm_plane_create_zpos_immutable_property(plane, plane_idx);
if (err) {
DRM_ERROR("Failed to create zpos property for plane %u\n", plane_idx);
return err;
}
if (supported_rotations) {
err = drm_plane_create_rotation_property(plane,
DRM_MODE_ROTATE_0,
supported_rotations);
if (err)
DRM_INFO("Create rotation property failed\n");
}
err = drm_plane_create_alpha_property(plane);
if (err)
DRM_ERROR("failed to create property: alpha\n");
if (blend_modes) {
err = drm_plane_create_blend_mode_property(plane, blend_modes);
if (err)
DRM_ERROR("failed to create property: blend_mode\n");
}
drm_plane_helper_add(plane, &mtk_plane_helper_funcs);
return 0;
}
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015 MediaTek Inc.
* Author: CK Hu <ck.hu@mediatek.com>
*/
#ifndef _MTK_PLANE_H_
#define _MTK_PLANE_H_
#include <drm/drm_crtc.h>
#include <linux/types.h>
#define AFBC_DATA_BLOCK_WIDTH 32
#define AFBC_DATA_BLOCK_HEIGHT 8
#define AFBC_HEADER_BLOCK_SIZE 16
#define AFBC_HEADER_ALIGNMENT 1024
struct mtk_plane_pending_state {
bool config;
bool enable;
dma_addr_t addr;
dma_addr_t hdr_addr;
unsigned int pitch;
unsigned int hdr_pitch;
unsigned int format;
unsigned long long modifier;
unsigned int x;
unsigned int y;
unsigned int width;
unsigned int height;
unsigned int rotation;
bool dirty;
bool async_dirty;
bool async_config;
enum drm_color_encoding color_encoding;
};
struct mtk_plane_state {
struct drm_plane_state base;
struct mtk_plane_pending_state pending;
};
static inline struct mtk_plane_state *
to_mtk_plane_state(struct drm_plane_state *state)
{
return container_of(state, struct mtk_plane_state, base);
}
int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
unsigned long possible_crtcs, enum drm_plane_type type,
unsigned int supported_rotations, const u32 blend_modes,
const u32 *formats, size_t num_formats,
bool supports_afbc, unsigned int plane_idx);
#endif