850 lines
23 KiB
C
850 lines
23 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (c) 2019 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <drm/drmP.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/component.h>
|
||
|
#include <linux/of_device.h>
|
||
|
#include <linux/of_address.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/soc/mediatek/mtk-cmdq.h>
|
||
|
#include <drm/drm_atomic_helper.h>
|
||
|
#include <drm/drm_crtc_helper.h>
|
||
|
|
||
|
#include "mtk_drm_crtc.h"
|
||
|
#include "mtk_drm_ddp_comp.h"
|
||
|
#include "mtk_dump.h"
|
||
|
#include "mtk_drm_mmp.h"
|
||
|
#include "mtk_drm_gem.h"
|
||
|
#include "mtk_drm_fb.h"
|
||
|
#include "mt-plat/sync_write.h"
|
||
|
#include "mtk_dp.h"
|
||
|
|
||
|
#define DP_EN 0x0000
|
||
|
#define DP_CONTROLLER_EN BIT(0)
|
||
|
#define CON_FLD_DP_EN REG_FLD_MSB_LSB(0, 0)
|
||
|
#define DP_RST 0x0004
|
||
|
#define CON_FLD_DP_RST REG_FLD_MSB_LSB(0, 0)
|
||
|
#define CON_FLD_DP_RST_SEL REG_FLD_MSB_LSB(16, 16)
|
||
|
#define DP_INTEN 0x0008
|
||
|
#define INT_TARGET_LINE_EN BIT(3)
|
||
|
#define INT_UNDERFLOW_EN BIT(2)
|
||
|
#define INT_VDE_EN BIT(1)
|
||
|
#define INT_VSYNC_EN BIT(0)
|
||
|
#define DP_INTSTA 0x000C
|
||
|
#define INTSTA_TARGET_LINE BIT(3)
|
||
|
#define INTSTA_UNDERFLOW BIT(2)
|
||
|
#define INTSTA_VDE BIT(1)
|
||
|
#define INTSTA_VSYNC BIT(0)
|
||
|
#define DP_CON 0x0010
|
||
|
#define CON_FLD_DP_BG_EN REG_FLD_MSB_LSB(0, 0)
|
||
|
#define CON_FLD_DP_INTL_EN REG_FLD_MSB_LSB(2, 2)
|
||
|
#define DP_OUTPUT_SETTING 0x0014
|
||
|
#define RB_SWAP BIT(0)
|
||
|
#define DP_SIZE 0x0018
|
||
|
#define DP_TGEN_HWIDTH 0x0020
|
||
|
#define DP_TGEN_HPORCH 0x0024
|
||
|
#define DP_TGEN_VWIDTH 0x0028
|
||
|
#define DP_TGEN_VPORCH 0x002C
|
||
|
#define DP_BG_HCNTL 0x0030
|
||
|
#define DP_BG_VCNTL 0x0034
|
||
|
#define DP_BG_COLOR 0x0038
|
||
|
#define DP_FIFO_CTL 0x003C
|
||
|
#define DP_STATUS 0x0040
|
||
|
#define DP_BUSY BIT(24)
|
||
|
#define DP_DCM 0x004C
|
||
|
#define DP_DUMMY 0x0050
|
||
|
#define DP_TGEN_VWIDTH_LEVEN 0x0068
|
||
|
#define DP_TGEN_VPORCH_LEVEN 0x006C
|
||
|
#define DP_TGEN_VWIDTH_RODD 0x0070
|
||
|
#define DP_TGEN_VPORCH_RODD 0x0074
|
||
|
#define DP_TGEN_VWIDTH_REVEN 0x0078
|
||
|
#define DP_TGEN_VPORCH_REVEN 0x007C
|
||
|
#define DP_MUTEX_VSYNC_SETTING 0x00E0
|
||
|
#define DP_SHEUDO_REG_UPDATE 0x00E4
|
||
|
#define DP_INTERNAL_DCM_DIS 0x00E8
|
||
|
#define DP_TARGET_LINE 0x00F0
|
||
|
#define DP_CHKSUM_EN 0x0100
|
||
|
#define DP_CHKSUM0 0x0104
|
||
|
#define DP_CHKSUM1 0x0108
|
||
|
#define DP_CHKSUM2 0x010C
|
||
|
#define DP_CHKSUM3 0x0110
|
||
|
#define DP_CHKSUM4 0x0114
|
||
|
#define DP_CHKSUM5 0x0118
|
||
|
#define DP_CHKSUM6 0x011C
|
||
|
#define DP_CHKSUM7 0x0120
|
||
|
#define DP_PATTERN_CTRL0 0x0F00
|
||
|
#define DP_PATTERN_CTRL1 0x0F04
|
||
|
|
||
|
static const struct of_device_id mtk_dp_intf_driver_dt_match[];
|
||
|
/**
|
||
|
* struct mtk_dp_intf - DP_INTF driver structure
|
||
|
* @ddp_comp - structure containing type enum and hardware resources
|
||
|
*/
|
||
|
struct mtk_dp_intf {
|
||
|
struct mtk_ddp_comp ddp_comp;
|
||
|
struct device *dev;
|
||
|
struct mtk_dp_intf_driver_data *driver_data;
|
||
|
struct drm_encoder encoder;
|
||
|
struct drm_connector conn;
|
||
|
struct drm_bridge *bridge;
|
||
|
void __iomem *regs;
|
||
|
struct clk *hf_fmm_ck;
|
||
|
struct clk *hf_fdp_ck;
|
||
|
struct clk *pclk;
|
||
|
struct clk *pclk_src[5];
|
||
|
int irq;
|
||
|
struct drm_display_mode mode;
|
||
|
int enable;
|
||
|
int res;
|
||
|
};
|
||
|
|
||
|
struct mtk_dp_intf_driver_data {
|
||
|
const u32 reg_cmdq_ofs;
|
||
|
s32 (*poll_for_idle)(struct mtk_dp_intf *dp_intf,
|
||
|
struct cmdq_pkt *handle);
|
||
|
irqreturn_t (*irq_handler)(int irq, void *dev_id);
|
||
|
};
|
||
|
|
||
|
#define DISP_REG_SET(handle, reg32, val) \
|
||
|
do { \
|
||
|
if (handle == NULL) { \
|
||
|
mt_reg_sync_writel(val, (unsigned long *)(reg32));\
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
#if 0
|
||
|
#define DISP_REG_SET_FIELD(handle, field, reg32, val) \
|
||
|
do { \
|
||
|
if (handle == NULL) { \
|
||
|
unsigned int regval; \
|
||
|
regval = __raw_readl((unsigned long *)(reg32)); \
|
||
|
regval = (regval & ~REG_FLD_MASK(field)) | \
|
||
|
(REG_FLD_VAL((field), (val))); \
|
||
|
mt_reg_sync_writel(regval, (reg32)); \
|
||
|
} \
|
||
|
} while (0)
|
||
|
#else
|
||
|
#define DISP_REG_SET_FIELD(handle, field, reg32, val) \
|
||
|
do { \
|
||
|
if (handle == NULL) { \
|
||
|
unsigned int regval; \
|
||
|
regval = readl((unsigned long *)(reg32)); \
|
||
|
regval = (regval & ~REG_FLD_MASK(field)) | \
|
||
|
(REG_FLD_VAL((field), (val))); \
|
||
|
writel(regval, (reg32)); \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static void __iomem *clk_apmixed_base;
|
||
|
static int irq_intsa;
|
||
|
static int irq_vdesa;
|
||
|
static int irq_underflowsa;
|
||
|
static int irq_tl;
|
||
|
static struct mtk_dp_intf *g_dp_intf;
|
||
|
|
||
|
|
||
|
static inline struct mtk_dp_intf *comp_to_dp_intf(struct mtk_ddp_comp *comp)
|
||
|
{
|
||
|
return container_of(comp, struct mtk_dp_intf, ddp_comp);
|
||
|
}
|
||
|
|
||
|
static inline struct mtk_dp_intf *encoder_to_dp_intf(struct drm_encoder *e)
|
||
|
{
|
||
|
return container_of(e, struct mtk_dp_intf, encoder);
|
||
|
}
|
||
|
|
||
|
static inline struct mtk_dp_intf *connector_to_dp_intf(struct drm_connector *c)
|
||
|
{
|
||
|
return container_of(c, struct mtk_dp_intf, conn);
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_mask(struct mtk_dp_intf *dp_intf, u32 offset,
|
||
|
u32 mask, u32 data)
|
||
|
{
|
||
|
u32 temp = readl(dp_intf->regs + offset);
|
||
|
|
||
|
writel((temp & ~mask) | (data & mask), dp_intf->regs + offset);
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_destroy_conn_enc(struct mtk_dp_intf *dp_intf)
|
||
|
{
|
||
|
drm_encoder_cleanup(&dp_intf->encoder);
|
||
|
/* Skip connector cleanup if creation was delegated to the bridge */
|
||
|
if (dp_intf->conn.dev)
|
||
|
drm_connector_cleanup(&dp_intf->conn);
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_start(struct mtk_ddp_comp *comp,
|
||
|
struct cmdq_pkt *handle)
|
||
|
{
|
||
|
void __iomem *baddr = comp->regs;
|
||
|
struct mtk_dp_intf *dp_intf = comp_to_dp_intf(comp);
|
||
|
|
||
|
irq_intsa = 0;
|
||
|
irq_vdesa = 0;
|
||
|
irq_underflowsa = 0;
|
||
|
irq_tl = 0;
|
||
|
|
||
|
mtk_dp_intf_mask(dp_intf, DP_INTSTA, 0xf, 0);
|
||
|
|
||
|
mtk_ddp_write_mask(comp, 1,
|
||
|
DP_RST, CON_FLD_DP_RST, handle);
|
||
|
mtk_ddp_write_mask(comp, 0,
|
||
|
DP_RST, CON_FLD_DP_RST, handle);
|
||
|
|
||
|
#if 0
|
||
|
mtk_ddp_write_mask(comp,
|
||
|
(INT_UNDERFLOW_EN |
|
||
|
INT_VDE_EN | INT_VSYNC_EN),
|
||
|
DP_INTEN,
|
||
|
(INT_UNDERFLOW_EN |
|
||
|
INT_VDE_EN | INT_VSYNC_EN), handle);
|
||
|
#else
|
||
|
mtk_ddp_write_mask(comp,
|
||
|
INT_VSYNC_EN,
|
||
|
DP_INTEN,
|
||
|
(INT_UNDERFLOW_EN |
|
||
|
INT_VDE_EN | INT_VSYNC_EN), handle);
|
||
|
|
||
|
#endif
|
||
|
mtk_ddp_write_mask(comp, DP_CONTROLLER_EN,
|
||
|
DP_EN, DP_CONTROLLER_EN, handle);
|
||
|
dp_intf->enable = 1;
|
||
|
|
||
|
DDPMSG("%s, dp_intf_start:0x%x!\n",
|
||
|
mtk_dump_comp_str(comp), readl(baddr + DP_EN));
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_stop(struct mtk_ddp_comp *comp, struct cmdq_pkt *handle)
|
||
|
{
|
||
|
mtk_ddp_write_mask(comp, 0x0, DP_EN, DP_CONTROLLER_EN, handle);
|
||
|
|
||
|
//mtk_dp_video_trigger(video_mute<<16 | 0);
|
||
|
irq_intsa = 0;
|
||
|
irq_vdesa = 0;
|
||
|
irq_underflowsa = 0;
|
||
|
irq_tl = 0;
|
||
|
|
||
|
DDPMSG("%s, stop\n", mtk_dump_comp_str(comp));
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_prepare(struct mtk_ddp_comp *comp)
|
||
|
{
|
||
|
struct mtk_dp_intf *dp_intf = NULL;
|
||
|
int ret;
|
||
|
|
||
|
DDPFUNC();
|
||
|
mtk_dp_poweron();
|
||
|
|
||
|
dp_intf = comp_to_dp_intf(comp);
|
||
|
|
||
|
/* Enable dp intf clk */
|
||
|
if (dp_intf != NULL) {
|
||
|
ret = clk_prepare_enable(dp_intf->hf_fmm_ck);
|
||
|
if (ret < 0)
|
||
|
DDPPR_ERR("%s Failed to enable hf_fmm_ck clock: %d\n",
|
||
|
__func__, ret);
|
||
|
ret = clk_prepare_enable(dp_intf->hf_fdp_ck);
|
||
|
if (ret < 0)
|
||
|
DDPPR_ERR("%s Failed to enable hf_fdp_ck clock: %d\n",
|
||
|
__func__, ret);
|
||
|
//ret = clk_prepare_enable(dp_intf->pclk);
|
||
|
if (ret < 0)
|
||
|
DDPPR_ERR("%s Failed to enable pclk clock: %d\n",
|
||
|
__func__, ret);
|
||
|
DDPMSG("%s:succesed eanble dp_intf clock\n", __func__);
|
||
|
} else
|
||
|
DDPPR_ERR("Failed to enable dp_intf clock\n");
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_unprepare(struct mtk_ddp_comp *comp)
|
||
|
{
|
||
|
struct mtk_dp_intf *dp_intf = NULL;
|
||
|
|
||
|
DDPFUNC();
|
||
|
mtk_dp_poweroff();
|
||
|
|
||
|
dp_intf = comp_to_dp_intf(comp);
|
||
|
|
||
|
/* disable dp intf clk */
|
||
|
if (dp_intf != NULL) {
|
||
|
clk_disable_unprepare(dp_intf->hf_fmm_ck);
|
||
|
clk_disable_unprepare(dp_intf->hf_fdp_ck);
|
||
|
clk_disable_unprepare(dp_intf->pclk);
|
||
|
DDPMSG("%s:succesed disable dp_intf clock\n", __func__);
|
||
|
} else
|
||
|
DDPPR_ERR("Failed to disable dp_intf clock\n");
|
||
|
}
|
||
|
|
||
|
enum TVDPLL_CLK {
|
||
|
TCK_26M = 0,
|
||
|
TVDPLL_D2 = 1,
|
||
|
TVDPLL_D4 = 2,
|
||
|
TVDPLL_D8 = 3,
|
||
|
TVDPLL_D16 = 4,
|
||
|
};
|
||
|
|
||
|
void mtk_dp_inf_video_clock(struct mtk_dp_intf *dp_intf)
|
||
|
{
|
||
|
unsigned int clksrc = TVDPLL_D2;
|
||
|
unsigned int con1 = 0;
|
||
|
int ret = 0;
|
||
|
struct device_node *node;
|
||
|
|
||
|
switch (dp_intf->res) {
|
||
|
case SINK_640_480: /* pix clk: 6.3M, dpi clk: 6.3*4M */
|
||
|
clksrc = TVDPLL_D16;
|
||
|
con1 = 0x840F81F8;
|
||
|
break;
|
||
|
case SINK_1280_720: /* pix clk: 18.5625M, dpi clk: 18.5625*4M */
|
||
|
clksrc = TVDPLL_D8; // 18.585*4M
|
||
|
con1 = 0x8416DFB4;
|
||
|
break;
|
||
|
case SINK_1920_1080: /* pix clk: 37.125M, dpi clk: 37.125*4M */
|
||
|
clksrc = TVDPLL_D16;
|
||
|
con1 = 0x8216D89D;
|
||
|
break;
|
||
|
case SINK_1080_2460: /* pix clk: 43.5275M, dpi clk: 43.5275*4M */
|
||
|
clksrc = TVDPLL_D16;
|
||
|
con1 = 0x821AC941;
|
||
|
break;
|
||
|
case SINK_1920_1200: /* pix clk: 43.5275M, dpi clk: 43.5275*4M */
|
||
|
clksrc = TVDPLL_D16;
|
||
|
con1 = 0x8217B645;
|
||
|
break;
|
||
|
case SINK_3840_2160_30: /* pix clk: 74.25M, dpi clk: 74.25*4M */
|
||
|
clksrc = TVDPLL_D8;
|
||
|
con1 = 0x8216D89D;
|
||
|
break;
|
||
|
case SINK_3840_2160: /* pix clk: 74.25M, dpi clk: 74.25*4M */
|
||
|
//clksrc = TVDPLL_D8;
|
||
|
//con1 = 0x8216D89D;
|
||
|
clksrc = TVDPLL_D4;
|
||
|
con1 = 0x83109D89; //htotal = 1600
|
||
|
con1 = 0x830F93B1; //htotal = 1500
|
||
|
break;
|
||
|
default:
|
||
|
pr_info("%s error res %d!\n", __func__, dp_intf->res);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (clk_apmixed_base == NULL) {
|
||
|
node = of_find_compatible_node(NULL, NULL, "mediatek,apmixed");
|
||
|
if (!node)
|
||
|
pr_info("dp_intf [CLK_APMIXED] find node failed\n");
|
||
|
clk_apmixed_base = of_iomap(node, 0);
|
||
|
if (clk_apmixed_base == NULL) {
|
||
|
pr_info("dp_intf [CLK_APMIXED] io map failed\n");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pr_info("clk_apmixed_base clk_apmixed_base 0x%lx!!!,res %d\n",
|
||
|
clk_apmixed_base, dp_intf->res);
|
||
|
ret = clk_prepare_enable(dp_intf->pclk);
|
||
|
ret = clk_set_parent(dp_intf->pclk, dp_intf->pclk_src[clksrc]);
|
||
|
|
||
|
DISP_REG_SET(NULL, clk_apmixed_base + 0x384, con1);
|
||
|
|
||
|
/*enable TVDPLL */
|
||
|
DISP_REG_SET_FIELD(NULL, REG_FLD_MSB_LSB(0, 0),
|
||
|
clk_apmixed_base + 0x380, 1);
|
||
|
|
||
|
pr_info("%s set pclk2 and src %d\n", __func__, clksrc);
|
||
|
|
||
|
}
|
||
|
|
||
|
void mhal_DPTx_VideoClock(bool enable, int resolution)
|
||
|
{
|
||
|
if (enable) {
|
||
|
g_dp_intf->res = resolution;
|
||
|
mtk_dp_inf_video_clock(g_dp_intf);
|
||
|
} else
|
||
|
clk_disable_unprepare(g_dp_intf->pclk);
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_config(struct mtk_ddp_comp *comp,
|
||
|
struct mtk_ddp_config *cfg,
|
||
|
struct cmdq_pkt *handle)
|
||
|
{
|
||
|
/*u32 reg_val;*/
|
||
|
struct mtk_dp_intf *dp_intf = comp_to_dp_intf(comp);
|
||
|
unsigned int hsize, vsize;
|
||
|
unsigned int hpw;
|
||
|
unsigned int hfp, hbp;
|
||
|
unsigned int vpw;
|
||
|
unsigned int vfp, vbp;
|
||
|
unsigned int bg_left, bg_right;
|
||
|
unsigned int bg_top, bg_bot;
|
||
|
|
||
|
pr_info("%s w %d, h, %d, fps %d!\n",
|
||
|
__func__, cfg->w, cfg->h, cfg->vrefresh);
|
||
|
|
||
|
hsize = cfg->w;
|
||
|
vsize = cfg->h;
|
||
|
if ((cfg->w == 640) && (cfg->h == 480)) {
|
||
|
dp_intf->res = SINK_640_480;
|
||
|
hpw = 24;
|
||
|
hfp = 4;
|
||
|
hbp = 12;
|
||
|
vpw = 2;
|
||
|
vfp = 10;
|
||
|
vbp = 33;
|
||
|
} else if ((cfg->w == 1280) && (cfg->h == 720)
|
||
|
&& (cfg->vrefresh == 60)) {
|
||
|
dp_intf->res = SINK_1280_720;
|
||
|
hpw = 10;
|
||
|
hfp = 28;
|
||
|
hbp = 55;
|
||
|
vpw = 5;
|
||
|
vfp = 5;
|
||
|
vbp = 20;
|
||
|
} else if ((cfg->w == 1920) && (cfg->h == 1080)
|
||
|
&& (cfg->vrefresh == 60)) {
|
||
|
dp_intf->res = SINK_1920_1080;
|
||
|
hpw = 11;
|
||
|
hfp = 22;
|
||
|
hbp = 37;
|
||
|
vpw = 5;
|
||
|
vfp = 4;
|
||
|
vbp = 36;
|
||
|
} else if ((cfg->w == 1080) && (cfg->h == 2460)
|
||
|
&& (cfg->vrefresh == 60)) {
|
||
|
dp_intf->res = SINK_1080_2460;
|
||
|
hpw = 8;
|
||
|
hfp = 8; //30/4
|
||
|
hbp = 7; //30/4
|
||
|
vpw = 2;
|
||
|
vfp = 9;
|
||
|
vbp = 5;
|
||
|
} else if ((cfg->w == 1920) && (cfg->h == 1200)
|
||
|
&& (cfg->vrefresh == 60)) {
|
||
|
dp_intf->res = SINK_1920_1200;
|
||
|
hpw = 8;
|
||
|
hfp = 12;
|
||
|
hbp = 20;
|
||
|
vpw = 6;
|
||
|
vfp = 3;
|
||
|
vbp = 26;
|
||
|
} else if ((cfg->w == 3840) && (cfg->h == 2160)
|
||
|
&& (cfg->vrefresh == 30)) {
|
||
|
dp_intf->res = SINK_3840_2160_30;
|
||
|
hpw = 22;
|
||
|
hfp = 44;
|
||
|
hbp = 74;
|
||
|
vpw = 10;
|
||
|
vfp = 8;
|
||
|
vbp = 72;
|
||
|
} else if ((cfg->w == 3840) && (cfg->h == 2160)
|
||
|
&& (cfg->vrefresh == 60)) {
|
||
|
dp_intf->res = SINK_3840_2160;
|
||
|
hpw = 10; //22;
|
||
|
hfp = 25; //134;
|
||
|
hbp = 20; //74;
|
||
|
vpw = 10;
|
||
|
vfp = 8;
|
||
|
vbp = 72;
|
||
|
hsize /= 3;/* with dsc on*/
|
||
|
mtk_ddp_write_mask(comp, RB_SWAP,
|
||
|
DP_OUTPUT_SETTING, RB_SWAP, handle);
|
||
|
} else
|
||
|
pr_info("%s error, w %d, h, %d, fps %d!\n",
|
||
|
__func__, cfg->w, cfg->h, cfg->vrefresh);
|
||
|
|
||
|
|
||
|
mtk_dp_inf_video_clock(dp_intf);
|
||
|
|
||
|
mtk_ddp_write_relaxed(comp, vsize << 16 | hsize,
|
||
|
DP_SIZE, handle);
|
||
|
|
||
|
mtk_ddp_write_relaxed(comp, hpw,
|
||
|
DP_TGEN_HWIDTH, handle);
|
||
|
mtk_ddp_write_relaxed(comp, hfp << 16 | hbp,
|
||
|
DP_TGEN_HPORCH, handle);
|
||
|
|
||
|
mtk_ddp_write_relaxed(comp, vpw,
|
||
|
DP_TGEN_VWIDTH, handle);
|
||
|
mtk_ddp_write_relaxed(comp, vfp << 16 | vbp,
|
||
|
DP_TGEN_VPORCH, handle);
|
||
|
|
||
|
bg_left = 0x0;
|
||
|
bg_right = 0x0;
|
||
|
mtk_ddp_write_relaxed(comp, bg_left << 16 | bg_right,
|
||
|
DP_BG_HCNTL, handle);
|
||
|
|
||
|
bg_top = 0x0;
|
||
|
bg_bot = 0x0;
|
||
|
mtk_ddp_write_relaxed(comp, bg_top << 16 | bg_bot,
|
||
|
DP_BG_VCNTL, handle);
|
||
|
#if 0
|
||
|
mtk_ddp_write_mask(comp, DSC_UFOE_SEL,
|
||
|
DISP_REG_DSC_CON, DSC_UFOE_SEL, handle);
|
||
|
mtk_ddp_write_relaxed(comp,
|
||
|
(slice_group_width - 1) << 16 | slice_width,
|
||
|
DISP_REG_DSC_SLICE_W, handle);
|
||
|
mtk_ddp_write(comp, 0x20000c03, DISP_REG_DSC_PPS6, handle);
|
||
|
#endif
|
||
|
DDPMSG("%s config done\n",
|
||
|
mtk_dump_comp_str(comp));
|
||
|
|
||
|
dp_intf->enable = true;
|
||
|
}
|
||
|
|
||
|
int mtk_dp_intf_dump(struct mtk_ddp_comp *comp)
|
||
|
{
|
||
|
void __iomem *baddr = comp->regs;
|
||
|
|
||
|
DDPDUMP("== %s REGS ==\n", mtk_dump_comp_str(comp));
|
||
|
DDPDUMP("(0x0000) DP_EN =0x%x\n",
|
||
|
readl(baddr + DP_EN));
|
||
|
DDPDUMP("(0x0004) DP_RST =0x%x\n",
|
||
|
readl(baddr + DP_RST));
|
||
|
DDPDUMP("(0x0008) DP_INTEN =0x%x\n",
|
||
|
readl(baddr + DP_INTEN));
|
||
|
DDPDUMP("(0x000C) DP_INTSTA =0x%x\n",
|
||
|
readl(baddr + DP_INTSTA));
|
||
|
DDPDUMP("(0x0010) DP_CON =0x%x\n",
|
||
|
readl(baddr + DP_CON));
|
||
|
DDPDUMP("(0x0014) DP_OUTPUT_SETTING =0x%x\n",
|
||
|
readl(baddr + DP_OUTPUT_SETTING));
|
||
|
DDPDUMP("(0x0018) DP_SIZE =0x%x\n",
|
||
|
readl(baddr + DP_SIZE));
|
||
|
DDPDUMP("(0x0020) DP_TGEN_HWIDTH =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_HWIDTH));
|
||
|
DDPDUMP("(0x0024) DP_TGEN_HPORCH =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_HPORCH));
|
||
|
DDPDUMP("(0x0028) DP_TGEN_VWIDTH =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VWIDTH));
|
||
|
DDPDUMP("(0x002C) DP_TGEN_VPORCH =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VPORCH));
|
||
|
DDPDUMP("(0x0030) DP_BG_HCNTL =0x%x\n",
|
||
|
readl(baddr + DP_BG_HCNTL));
|
||
|
DDPDUMP("(0x0034) DP_BG_VCNTL =0x%x\n",
|
||
|
readl(baddr + DP_BG_VCNTL));
|
||
|
DDPDUMP("(0x0038) DP_BG_COLOR =0x%x\n",
|
||
|
readl(baddr + DP_BG_COLOR));
|
||
|
DDPDUMP("(0x003C) DP_FIFO_CTL =0x%x\n",
|
||
|
readl(baddr + DP_FIFO_CTL));
|
||
|
DDPDUMP("(0x0040) DP_STATUS =0x%x\n",
|
||
|
readl(baddr + DP_STATUS));
|
||
|
DDPDUMP("(0x004C) DP_DCM =0x%x\n",
|
||
|
readl(baddr + DP_DCM));
|
||
|
DDPDUMP("(0x0050) DP_DUMMY =0x%x\n",
|
||
|
readl(baddr + DP_DUMMY));
|
||
|
DDPDUMP("(0x0068) DP_TGEN_VWIDTH_LEVEN =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VWIDTH_LEVEN));
|
||
|
DDPDUMP("(0x006C) DP_TGEN_VPORCH_LEVEN =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VPORCH_LEVEN));
|
||
|
DDPDUMP("(0x0070) DP_TGEN_VWIDTH_RODD =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VWIDTH_RODD));
|
||
|
DDPDUMP("(0x0074) DP_TGEN_VPORCH_RODD =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VPORCH_RODD));
|
||
|
DDPDUMP("(0x0078) DP_TGEN_VWIDTH_REVEN =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VWIDTH_REVEN));
|
||
|
DDPDUMP("(0x007C) DP_TGEN_VPORCH_REVEN =0x%x\n",
|
||
|
readl(baddr + DP_TGEN_VPORCH_REVEN));
|
||
|
DDPDUMP("(0x00E0) DP_MUTEX_VSYNC_SETTING=0x%x\n",
|
||
|
readl(baddr + DP_MUTEX_VSYNC_SETTING));
|
||
|
DDPDUMP("(0x00E4) DP_SHEUDO_REG_UPDATE =0x%x\n",
|
||
|
readl(baddr + DP_SHEUDO_REG_UPDATE));
|
||
|
DDPDUMP("(0x00E8) DP_INTERNAL_DCM_DIS =0x%x\n",
|
||
|
readl(baddr + DP_INTERNAL_DCM_DIS));
|
||
|
DDPDUMP("(0x00F0) DP_TARGET_LINE =0x%x\n",
|
||
|
readl(baddr + DP_TARGET_LINE));
|
||
|
DDPDUMP("(0x0100) DP_CHKSUM_EN =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM_EN));
|
||
|
DDPDUMP("(0x0104) DP_CHKSUM0 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM0));
|
||
|
DDPDUMP("(0x0108) DP_CHKSUM1 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM1));
|
||
|
DDPDUMP("(0x010C) DP_CHKSUM2 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM2));
|
||
|
DDPDUMP("(0x0110) DP_CHKSUM3 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM3));
|
||
|
DDPDUMP("(0x0114) DP_CHKSUM4 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM4));
|
||
|
DDPDUMP("(0x0118) DP_CHKSUM5 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM5));
|
||
|
DDPDUMP("(0x011C) DP_CHKSUM6 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM6));
|
||
|
DDPDUMP("(0x0120) DP_CHKSUM7 =0x%x\n",
|
||
|
readl(baddr + DP_CHKSUM7));
|
||
|
DDPDUMP("(0x0F00) DP_PATTERN_CTRL0 =0x%x\n",
|
||
|
readl(baddr + DP_PATTERN_CTRL0));
|
||
|
DDPDUMP("(0x0F04) DP_PATTERN_CTRL1 =0x%x\n",
|
||
|
readl(baddr + DP_PATTERN_CTRL1));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int mtk_dp_intf_analysis(struct mtk_ddp_comp *comp)
|
||
|
{
|
||
|
void __iomem *baddr = comp->regs;
|
||
|
|
||
|
DDPDUMP("== %s ANALYSIS ==\n", mtk_dump_comp_str(comp));
|
||
|
DDPDUMP("en=%d, rst_sel=%d, rst=%d, bg_en=%d, intl_en=%d\n",
|
||
|
DISP_REG_GET_FIELD(CON_FLD_DP_EN,
|
||
|
baddr + DP_EN),
|
||
|
DISP_REG_GET_FIELD(CON_FLD_DP_RST_SEL,
|
||
|
baddr + DP_RST),
|
||
|
DISP_REG_GET_FIELD(CON_FLD_DP_RST,
|
||
|
baddr + DP_RST),
|
||
|
DISP_REG_GET_FIELD(CON_FLD_DP_BG_EN,
|
||
|
baddr + DP_CON),
|
||
|
DISP_REG_GET_FIELD(CON_FLD_DP_INTL_EN,
|
||
|
baddr + DP_CON));
|
||
|
DDPDUMP("== End %s ANALYSIS ==\n", mtk_dump_comp_str(comp));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct mtk_ddp_comp_funcs mtk_dp_intf_funcs = {
|
||
|
.config = mtk_dp_intf_config,
|
||
|
.start = mtk_dp_intf_start,
|
||
|
.stop = mtk_dp_intf_stop,
|
||
|
.prepare = mtk_dp_intf_prepare,
|
||
|
.unprepare = mtk_dp_intf_unprepare,
|
||
|
};
|
||
|
|
||
|
static int mtk_dp_intf_bind(struct device *dev, struct device *master,
|
||
|
void *data)
|
||
|
{
|
||
|
struct mtk_dp_intf *dp_intf = dev_get_drvdata(dev);
|
||
|
struct drm_device *drm_dev = data;
|
||
|
int ret;
|
||
|
|
||
|
DDPINFO("%s\n", __func__);
|
||
|
ret = mtk_ddp_comp_register(drm_dev, &dp_intf->ddp_comp);
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "Failed to register component %s: %d\n",
|
||
|
dev->of_node->full_name, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
DDPINFO("%s-\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void mtk_dp_intf_unbind(struct device *dev, struct device *master,
|
||
|
void *data)
|
||
|
{
|
||
|
struct mtk_dp_intf *dp_intf = dev_get_drvdata(dev);
|
||
|
struct drm_device *drm_dev = data;
|
||
|
|
||
|
mtk_dp_intf_destroy_conn_enc(dp_intf);
|
||
|
mtk_ddp_comp_unregister(drm_dev, &dp_intf->ddp_comp);
|
||
|
}
|
||
|
|
||
|
static const struct component_ops mtk_dp_intf_component_ops = {
|
||
|
.bind = mtk_dp_intf_bind,
|
||
|
.unbind = mtk_dp_intf_unbind,
|
||
|
};
|
||
|
|
||
|
static int mtk_dp_intf_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device *dev = &pdev->dev;
|
||
|
struct mtk_dp_intf *dp_intf;
|
||
|
enum mtk_ddp_comp_id comp_id;
|
||
|
const struct of_device_id *of_id;
|
||
|
struct resource *mem;
|
||
|
int ret;
|
||
|
struct device_node *node;
|
||
|
|
||
|
DDPMSG("%s+\n", __func__);
|
||
|
dp_intf = devm_kzalloc(dev, sizeof(*dp_intf), GFP_KERNEL);
|
||
|
if (!dp_intf)
|
||
|
return -ENOMEM;
|
||
|
dp_intf->dev = dev;
|
||
|
|
||
|
of_id = of_match_device(mtk_dp_intf_driver_dt_match, &pdev->dev);
|
||
|
dp_intf->driver_data = (struct mtk_dp_intf_driver_data *)of_id->data;
|
||
|
DDPMSG("%s:%d\n", __func__, __LINE__);
|
||
|
|
||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||
|
dp_intf->regs = devm_ioremap_resource(dev, mem);
|
||
|
if (IS_ERR(dp_intf->regs)) {
|
||
|
ret = PTR_ERR(dp_intf->regs);
|
||
|
dev_err(dev, "Failed to ioremap mem resource: %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Get dp intf clk
|
||
|
* Input pixel clock(hf_fmm_ck) frequency needs to be > hf_fdp_ck * 4
|
||
|
* Otherwise FIFO will underflow
|
||
|
*/
|
||
|
dp_intf->hf_fmm_ck = devm_clk_get(dev, "hf_fmm_ck");
|
||
|
if (IS_ERR(dp_intf->hf_fmm_ck)) {
|
||
|
ret = PTR_ERR(dp_intf->hf_fmm_ck);
|
||
|
dev_err(dev, "Failed to get hf_fmm_ck clock: %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
dp_intf->hf_fdp_ck = devm_clk_get(dev, "hf_fdp_ck");
|
||
|
if (IS_ERR(dp_intf->hf_fdp_ck)) {
|
||
|
ret = PTR_ERR(dp_intf->hf_fdp_ck);
|
||
|
dev_err(dev, "Failed to get hf_fdp_ck clock: %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
dp_intf->pclk = devm_clk_get(dev, "MUX_DP");
|
||
|
///dp_intf->pclk_src[0] = devm_clk_get(dev, "TVDPLL_D2");
|
||
|
dp_intf->pclk_src[1] = devm_clk_get(dev, "TVDPLL_D2");
|
||
|
dp_intf->pclk_src[2] = devm_clk_get(dev, "TVDPLL_D4");
|
||
|
dp_intf->pclk_src[3] = devm_clk_get(dev, "TVDPLL_D8");
|
||
|
dp_intf->pclk_src[4] = devm_clk_get(dev, "TVDPLL_D16");
|
||
|
|
||
|
if (clk_apmixed_base == NULL) {
|
||
|
node = of_find_compatible_node(NULL, NULL, "mediatek,apmixed");
|
||
|
if (!node)
|
||
|
pr_info("[CLK_APMIXED] find node failed\n");
|
||
|
clk_apmixed_base = of_iomap(node, 0);
|
||
|
if (clk_apmixed_base == NULL)
|
||
|
pr_info("[CLK_APMIXED] io map failed\n");
|
||
|
|
||
|
pr_info("clk_apmixed_base clk_apmixed_base 0x%lx!!!\n",
|
||
|
clk_apmixed_base);
|
||
|
}
|
||
|
|
||
|
if (IS_ERR(dp_intf->pclk)
|
||
|
|| IS_ERR(dp_intf->pclk_src[0])
|
||
|
|| IS_ERR(dp_intf->pclk_src[1])
|
||
|
|| IS_ERR(dp_intf->pclk_src[2])
|
||
|
|| IS_ERR(dp_intf->pclk_src[3]))
|
||
|
dev_err(dev, "Failed to get pclk andr src clock !!!\n");
|
||
|
|
||
|
comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DP_INTF);
|
||
|
if ((int)comp_id < 0) {
|
||
|
dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
|
||
|
return comp_id;
|
||
|
}
|
||
|
|
||
|
DDPMSG("%s:%d\n", __func__, __LINE__);
|
||
|
ret = mtk_ddp_comp_init(dev, dev->of_node, &dp_intf->ddp_comp, comp_id,
|
||
|
&mtk_dp_intf_funcs);
|
||
|
if (ret) {
|
||
|
dev_err(dev, "Failed to initialize component: %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
DDPMSG("%s:%d\n", __func__, __LINE__);
|
||
|
/* Get dp intf irq num and request irq */
|
||
|
dp_intf->irq = platform_get_irq(pdev, 0);
|
||
|
dp_intf->res = SINK_MAX;
|
||
|
if (dp_intf->irq <= 0) {
|
||
|
dev_err(dev, "Failed to get irq: %d\n", dp_intf->irq);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
irq_set_status_flags(dp_intf->irq, IRQ_TYPE_LEVEL_HIGH);
|
||
|
ret = devm_request_irq(
|
||
|
&pdev->dev, dp_intf->irq, dp_intf->driver_data->irq_handler,
|
||
|
IRQF_TRIGGER_NONE | IRQF_SHARED, dev_name(&pdev->dev), dp_intf);
|
||
|
if (ret) {
|
||
|
dev_err(&pdev->dev, "failed to request mediatek dp intf irq\n");
|
||
|
ret = -EPROBE_DEFER;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
DDPMSG("%s:%d\n", __func__, __LINE__);
|
||
|
|
||
|
platform_set_drvdata(pdev, dp_intf);
|
||
|
pm_runtime_enable(dev);
|
||
|
|
||
|
ret = component_add(dev, &mtk_dp_intf_component_ops);
|
||
|
if (ret != 0) {
|
||
|
dev_err(dev, "Failed to add component: %d\n", ret);
|
||
|
pm_runtime_disable(dev);
|
||
|
}
|
||
|
|
||
|
g_dp_intf = dp_intf;
|
||
|
DDPMSG("%s-\n", __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int mtk_dp_intf_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
component_del(&pdev->dev, &mtk_dp_intf_component_ops);
|
||
|
|
||
|
pm_runtime_disable(&pdev->dev);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static s32 mtk_dp_intf_poll_for_idle(struct mtk_dp_intf *dp_intf,
|
||
|
struct cmdq_pkt *handle)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t mtk_dp_intf_irq_status(int irq, void *dev_id)
|
||
|
{
|
||
|
struct mtk_dp_intf *dp_intf = dev_id;
|
||
|
u32 status = 0;
|
||
|
struct mtk_drm_crtc *mtk_crtc;
|
||
|
|
||
|
status = readl(dp_intf->regs + DP_INTSTA);
|
||
|
|
||
|
DRM_MMP_MARK(dp_intf0, status, 0);
|
||
|
|
||
|
status &= 0xf;
|
||
|
if (status) {
|
||
|
mtk_dp_intf_mask(dp_intf, DP_INTSTA, status, 0);
|
||
|
if (status & INTSTA_VSYNC) {
|
||
|
mtk_crtc = dp_intf->ddp_comp.mtk_crtc;
|
||
|
mtk_crtc_vblank_irq(&mtk_crtc->base);
|
||
|
irq_intsa++;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (status & INTSTA_VDE)
|
||
|
irq_vdesa++;
|
||
|
|
||
|
if (status & INTSTA_UNDERFLOW)
|
||
|
irq_underflowsa++;
|
||
|
|
||
|
if (status & INTSTA_TARGET_LINE)
|
||
|
irq_tl++;
|
||
|
}
|
||
|
|
||
|
if (irq_intsa == 3)
|
||
|
mtk_dp_video_trigger(video_unmute<<16 | dp_intf->res);
|
||
|
|
||
|
if (((irq_intsa+1)%200 == 0)
|
||
|
|| ((irq_vdesa+1)%200 == 0)
|
||
|
|| ((irq_underflowsa+1)%200 == 0)
|
||
|
|| ((irq_tl+1)%200 == 0))
|
||
|
pr_info("dp_intf irq %d - %d - %d - %d! 0x%x\n", irq_intsa,
|
||
|
irq_vdesa, irq_underflowsa, irq_tl, status);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
|
||
|
static const struct mtk_dp_intf_driver_data mt6885_dp_intf_driver_data = {
|
||
|
.reg_cmdq_ofs = 0x200,
|
||
|
.poll_for_idle = mtk_dp_intf_poll_for_idle,
|
||
|
.irq_handler = mtk_dp_intf_irq_status,
|
||
|
};
|
||
|
|
||
|
static const struct of_device_id mtk_dp_intf_driver_dt_match[] = {
|
||
|
{ .compatible = "mediatek,mt6885-dp-intf",
|
||
|
.data = &mt6885_dp_intf_driver_data},
|
||
|
{},
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, mtk_dp_intf_driver_dt_match);
|
||
|
|
||
|
struct platform_driver mtk_dp_intf_driver = {
|
||
|
.probe = mtk_dp_intf_probe,
|
||
|
.remove = mtk_dp_intf_remove,
|
||
|
.driver = {
|
||
|
.name = "mediatek-dp-intf",
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = mtk_dp_intf_driver_dt_match,
|
||
|
},
|
||
|
};
|