Mediatek DRM Next for Linux 6.9

1. Add display driver for MT8188 VDOSYS1
 2. DSI driver cleanups
 3. Filter modes according to hardware capability
 4. Fix a null pointer crash in mtk_drm_crtc_finish_page_flip
 -----BEGIN PGP SIGNATURE-----
 
 iQJMBAABCgA2FiEEACwLKSDmq+9RDv5P4cpzo8lZTiQFAmXgrhsYHGNodW5rdWFu
 Zy5odUBrZXJuZWwub3JnAAoJEOHKc6PJWU4kiR0P/1AqLdIf5HOpSRJSI45iiGc1
 Y7LKpCshf7OQG5WlIAK417Fv6Rq0oLNrUZe0KLtIzRtTazdhJOyi8wdsRaGdpqop
 abPkHhBpEIX37raEXeZjjYtU06QPfK1do512nrXK30SdDqb33jiNEvbzaLQamMrQ
 +gUASajPf+RqRKuuUZIDOGQA1/tWS8sVWTxcriwBhFu5QPY+kfImF0E1ivsfOIiC
 l6LEGhXD1mMfwQ3Q6O80/Yeqxng4V6KOkTHvfNGvvCQVIvC5IxW0yrve2UvM56Hb
 rgZ0j4ztk/tnjNUHIUOq8Pb+9Hg7LrrLRcF8bMbN/l7Agna9FqW2LiTA5KVoAWjE
 ISqcuZ1y3aVNDMN0l+g3vKHW8qMxIHqb8+rCxh7nRxGjTRkkDkHruMi11p8F/2vG
 7pL9x6sdgXG+Ea482UubpscjeymZiZzoJL+PpbqoRJUUknfXEbAHfyedH5YIi1QK
 bJk/1Ri/08hsfesXdKuH5Yk0JTb8eZKByt/CUHcLMBlmXtscTTgPiCY4rLsubr7a
 JLT2T04VqsD5JUbwn0PRVz2/ckjF1vNP+BgGUxy5tWuv5ecJGNU76L5gXgzPh3ke
 k0R2W9UUJlZodDBX6rGYkJJrYhdjQ9GE2TG6lkjyyTT7NdEY8ja53GVQ9289HPde
 Y1syVXkG7JeLq+07tRiM
 =gaet
 -----END PGP SIGNATURE-----

Merge tag 'mediatek-drm-next-6.9' of https://git.kernel.org/pub/scm/linux/kernel/git/chunkuang.hu/linux into drm-next

Mediatek DRM Next for Linux 6.9

1. Add display driver for MT8188 VDOSYS1
2. DSI driver cleanups
3. Filter modes according to hardware capability
4. Fix a null pointer crash in mtk_drm_crtc_finish_page_flip

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Chun-Kuang Hu <chunkuang.hu@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240229162143.28957-1-chunkuang.hu@kernel.org
This commit is contained in:
Dave Airlie 2024-03-01 19:14:24 +10:00
commit 017da39e9c
8 changed files with 281 additions and 193 deletions

View file

@ -73,6 +73,8 @@ void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int
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);
@ -131,6 +133,8 @@ unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev);
struct device *mtk_ovl_adaptor_dma_dev_get(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);

View file

@ -222,6 +222,71 @@ void mtk_merge_clk_disable(struct device *dev)
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)
{

View file

@ -30,6 +30,7 @@ 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,
};
@ -47,6 +48,14 @@ enum mtk_ovl_adaptor_comp_id {
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
};
@ -67,6 +76,7 @@ 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 = {
@ -79,6 +89,14 @@ static const struct mtk_ddp_comp_funcs ethdr = {
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 = {
@ -102,6 +120,14 @@ static const struct ovl_adaptor_comp_match comp_matches[OVL_ADAPTOR_ID_MAX] = {
[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,
@ -317,6 +343,22 @@ void mtk_ovl_adaptor_clk_disable(struct device *dev)
}
}
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;
@ -437,6 +479,7 @@ static int ovl_adaptor_comp_get_id(struct device *dev, struct device_node *node,
}
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 },

View file

@ -95,11 +95,13 @@ static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
struct drm_crtc *crtc = &mtk_crtc->base;
unsigned long flags;
spin_lock_irqsave(&crtc->dev->event_lock, flags);
drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
drm_crtc_vblank_put(crtc);
mtk_crtc->event = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
if (mtk_crtc->event) {
spin_lock_irqsave(&crtc->dev->event_lock, flags);
drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
drm_crtc_vblank_put(crtc);
mtk_crtc->event = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
}
static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
@ -213,6 +215,22 @@ static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
kfree(to_mtk_crtc_state(state));
}
static enum drm_mode_status
mtk_drm_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
enum drm_mode_status status = MODE_OK;
int i;
for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
status = mtk_ddp_comp_mode_valid(mtk_crtc->ddp_comp[i], mode);
if (status != MODE_OK)
break;
}
return status;
}
static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -831,6 +849,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
.mode_fixup = mtk_drm_crtc_mode_fixup,
.mode_set_nofb = mtk_drm_crtc_mode_set_nofb,
.mode_valid = mtk_drm_crtc_mode_valid,
.atomic_begin = mtk_drm_crtc_atomic_begin,
.atomic_flush = mtk_drm_crtc_atomic_flush,
.atomic_enable = mtk_drm_crtc_atomic_enable,

View file

@ -418,6 +418,7 @@ static const struct mtk_ddp_comp_funcs ddp_ovl_adaptor = {
.remove = mtk_ovl_adaptor_remove_comp,
.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] = {

View file

@ -12,6 +12,8 @@
#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;
@ -85,6 +87,7 @@ struct mtk_ddp_comp_funcs {
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 {
@ -126,6 +129,15 @@ static inline void mtk_ddp_comp_clk_disable(struct mtk_ddp_comp *comp)
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,

View file

@ -293,7 +293,7 @@ static const struct mtk_mmsys_driver_data mt8188_vdosys0_driver_data = {
.main_len = ARRAY_SIZE(mt8188_mtk_ddp_main),
.conn_routes = mt8188_mtk_ddp_main_routes,
.num_conn_routes = ARRAY_SIZE(mt8188_mtk_ddp_main_routes),
.mmsys_dev_num = 1,
.mmsys_dev_num = 2,
};
static const struct mtk_mmsys_driver_data mt8192_mmsys_driver_data = {
@ -334,6 +334,8 @@ static const struct of_device_id mtk_drm_of_ids[] = {
.data = &mt8186_mmsys_driver_data},
{ .compatible = "mediatek,mt8188-vdosys0",
.data = &mt8188_vdosys0_driver_data},
{ .compatible = "mediatek,mt8188-vdosys1",
.data = &mt8195_vdosys1_driver_data},
{ .compatible = "mediatek,mt8192-mmsys",
.data = &mt8192_mmsys_driver_data},
{ .compatible = "mediatek,mt8195-mmsys",

View file

@ -3,6 +3,7 @@
* Copyright (c) 2015 MediaTek Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/iopoll.h>
@ -12,6 +13,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/units.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
@ -58,28 +60,31 @@
#define DSI_TXRX_CTRL 0x18
#define VC_NUM BIT(1)
#define LANE_NUM (0xf << 2)
#define LANE_NUM GENMASK(5, 2)
#define DIS_EOT BIT(6)
#define NULL_EN BIT(7)
#define TE_FREERUN BIT(8)
#define EXT_TE_EN BIT(9)
#define EXT_TE_EDGE BIT(10)
#define MAX_RTN_SIZE (0xf << 12)
#define MAX_RTN_SIZE GENMASK(15, 12)
#define HSTX_CKLP_EN BIT(16)
#define DSI_PSCTRL 0x1c
#define DSI_PS_WC 0x3fff
#define DSI_PS_SEL (3 << 16)
#define PACKED_PS_16BIT_RGB565 (0 << 16)
#define LOOSELY_PS_18BIT_RGB666 (1 << 16)
#define PACKED_PS_18BIT_RGB666 (2 << 16)
#define PACKED_PS_24BIT_RGB888 (3 << 16)
#define DSI_PS_WC GENMASK(13, 0)
#define DSI_PS_SEL GENMASK(17, 16)
#define PACKED_PS_16BIT_RGB565 0
#define PACKED_PS_18BIT_RGB666 1
#define LOOSELY_PS_24BIT_RGB666 2
#define PACKED_PS_24BIT_RGB888 3
#define DSI_VSA_NL 0x20
#define DSI_VBP_NL 0x24
#define DSI_VFP_NL 0x28
#define DSI_VACT_NL 0x2C
#define VACT_NL GENMASK(14, 0)
#define DSI_SIZE_CON 0x38
#define DSI_HEIGHT GENMASK(30, 16)
#define DSI_WIDTH GENMASK(14, 0)
#define DSI_HSA_WC 0x50
#define DSI_HBP_WC 0x54
#define DSI_HFP_WC 0x58
@ -109,26 +114,27 @@
#define LD0_WAKEUP_EN BIT(2)
#define DSI_PHY_TIMECON0 0x110
#define LPX (0xff << 0)
#define HS_PREP (0xff << 8)
#define HS_ZERO (0xff << 16)
#define HS_TRAIL (0xff << 24)
#define LPX GENMASK(7, 0)
#define HS_PREP GENMASK(15, 8)
#define HS_ZERO GENMASK(23, 16)
#define HS_TRAIL GENMASK(31, 24)
#define DSI_PHY_TIMECON1 0x114
#define TA_GO (0xff << 0)
#define TA_SURE (0xff << 8)
#define TA_GET (0xff << 16)
#define DA_HS_EXIT (0xff << 24)
#define TA_GO GENMASK(7, 0)
#define TA_SURE GENMASK(15, 8)
#define TA_GET GENMASK(23, 16)
#define DA_HS_EXIT GENMASK(31, 24)
#define DSI_PHY_TIMECON2 0x118
#define CONT_DET (0xff << 0)
#define CLK_ZERO (0xff << 16)
#define CLK_TRAIL (0xff << 24)
#define CONT_DET GENMASK(7, 0)
#define DA_HS_SYNC GENMASK(15, 8)
#define CLK_ZERO GENMASK(23, 16)
#define CLK_TRAIL GENMASK(31, 24)
#define DSI_PHY_TIMECON3 0x11c
#define CLK_HS_PREP (0xff << 0)
#define CLK_HS_POST (0xff << 8)
#define CLK_HS_EXIT (0xff << 16)
#define CLK_HS_PREP GENMASK(7, 0)
#define CLK_HS_POST GENMASK(15, 8)
#define CLK_HS_EXIT GENMASK(23, 16)
#define DSI_VM_CMD_CON 0x130
#define VM_CMD_EN BIT(0)
@ -138,13 +144,14 @@
#define FORCE_COMMIT BIT(0)
#define BYPASS_SHADOW BIT(1)
#define CONFIG (0xff << 0)
/* CMDQ related bits */
#define CONFIG GENMASK(7, 0)
#define SHORT_PACKET 0
#define LONG_PACKET 2
#define BTA BIT(2)
#define DATA_ID (0xff << 8)
#define DATA_0 (0xff << 16)
#define DATA_1 (0xff << 24)
#define DATA_ID GENMASK(15, 8)
#define DATA_0 GENMASK(23, 16)
#define DATA_1 GENMASK(31, 24)
#define NS_TO_CYCLE(n, c) ((n) / (c) + (((n) % (c)) ? 1 : 0))
@ -232,7 +239,7 @@ static void mtk_dsi_mask(struct mtk_dsi *dsi, u32 offset, u32 mask, u32 data)
static void mtk_dsi_phy_timconfig(struct mtk_dsi *dsi)
{
u32 timcon0, timcon1, timcon2, timcon3;
u32 data_rate_mhz = DIV_ROUND_UP(dsi->data_rate, 1000000);
u32 data_rate_mhz = DIV_ROUND_UP(dsi->data_rate, HZ_PER_MHZ);
struct mtk_phy_timing *timing = &dsi->phy_timing;
timing->lpx = (60 * data_rate_mhz / (8 * 1000)) + 1;
@ -252,14 +259,23 @@ static void mtk_dsi_phy_timconfig(struct mtk_dsi *dsi)
timing->clk_hs_zero = timing->clk_hs_trail * 4;
timing->clk_hs_exit = 2 * timing->clk_hs_trail;
timcon0 = timing->lpx | timing->da_hs_prepare << 8 |
timing->da_hs_zero << 16 | timing->da_hs_trail << 24;
timcon1 = timing->ta_go | timing->ta_sure << 8 |
timing->ta_get << 16 | timing->da_hs_exit << 24;
timcon2 = 1 << 8 | timing->clk_hs_zero << 16 |
timing->clk_hs_trail << 24;
timcon3 = timing->clk_hs_prepare | timing->clk_hs_post << 8 |
timing->clk_hs_exit << 16;
timcon0 = FIELD_PREP(LPX, timing->lpx) |
FIELD_PREP(HS_PREP, timing->da_hs_prepare) |
FIELD_PREP(HS_ZERO, timing->da_hs_zero) |
FIELD_PREP(HS_TRAIL, timing->da_hs_trail);
timcon1 = FIELD_PREP(TA_GO, timing->ta_go) |
FIELD_PREP(TA_SURE, timing->ta_sure) |
FIELD_PREP(TA_GET, timing->ta_get) |
FIELD_PREP(DA_HS_EXIT, timing->da_hs_exit);
timcon2 = FIELD_PREP(DA_HS_SYNC, 1) |
FIELD_PREP(CLK_ZERO, timing->clk_hs_zero) |
FIELD_PREP(CLK_TRAIL, timing->clk_hs_trail);
timcon3 = FIELD_PREP(CLK_HS_PREP, timing->clk_hs_prepare) |
FIELD_PREP(CLK_HS_POST, timing->clk_hs_post) |
FIELD_PREP(CLK_HS_EXIT, timing->clk_hs_exit);
writel(timcon0, dsi->regs + DSI_PHY_TIMECON0);
writel(timcon1, dsi->regs + DSI_PHY_TIMECON1);
@ -350,101 +366,63 @@ static void mtk_dsi_set_vm_cmd(struct mtk_dsi *dsi)
mtk_dsi_mask(dsi, DSI_VM_CMD_CON, TS_VFP_EN, TS_VFP_EN);
}
static void mtk_dsi_ps_control_vact(struct mtk_dsi *dsi)
static void mtk_dsi_rxtx_control(struct mtk_dsi *dsi)
{
struct videomode *vm = &dsi->vm;
u32 dsi_buf_bpp, ps_wc;
u32 ps_bpp_mode;
u32 regval, tmp_reg = 0;
u8 i;
/* Number of DSI lanes (max 4 lanes), each bit enables one DSI lane. */
for (i = 0; i < dsi->lanes; i++)
tmp_reg |= BIT(i);
regval = FIELD_PREP(LANE_NUM, tmp_reg);
if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
regval |= HSTX_CKLP_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)
regval |= DIS_EOT;
writel(regval, dsi->regs + DSI_TXRX_CTRL);
}
static void mtk_dsi_ps_control(struct mtk_dsi *dsi, bool config_vact)
{
u32 dsi_buf_bpp, ps_val, ps_wc, vact_nl;
if (dsi->format == MIPI_DSI_FMT_RGB565)
dsi_buf_bpp = 2;
else
dsi_buf_bpp = 3;
ps_wc = vm->hactive * dsi_buf_bpp;
ps_bpp_mode = ps_wc;
/* Word count */
ps_wc = FIELD_PREP(DSI_PS_WC, dsi->vm.hactive * dsi_buf_bpp);
ps_val = ps_wc;
/* Pixel Stream type */
switch (dsi->format) {
default:
fallthrough;
case MIPI_DSI_FMT_RGB888:
ps_bpp_mode |= PACKED_PS_24BIT_RGB888;
ps_val |= FIELD_PREP(DSI_PS_SEL, PACKED_PS_24BIT_RGB888);
break;
case MIPI_DSI_FMT_RGB666:
ps_bpp_mode |= PACKED_PS_18BIT_RGB666;
ps_val |= FIELD_PREP(DSI_PS_SEL, LOOSELY_PS_24BIT_RGB666);
break;
case MIPI_DSI_FMT_RGB666_PACKED:
ps_bpp_mode |= LOOSELY_PS_18BIT_RGB666;
ps_val |= FIELD_PREP(DSI_PS_SEL, PACKED_PS_18BIT_RGB666);
break;
case MIPI_DSI_FMT_RGB565:
ps_bpp_mode |= PACKED_PS_16BIT_RGB565;
ps_val |= FIELD_PREP(DSI_PS_SEL, PACKED_PS_16BIT_RGB565);
break;
}
writel(vm->vactive, dsi->regs + DSI_VACT_NL);
writel(ps_bpp_mode, dsi->regs + DSI_PSCTRL);
writel(ps_wc, dsi->regs + DSI_HSTX_CKL_WC);
}
static void mtk_dsi_rxtx_control(struct mtk_dsi *dsi)
{
u32 tmp_reg;
switch (dsi->lanes) {
case 1:
tmp_reg = 1 << 2;
break;
case 2:
tmp_reg = 3 << 2;
break;
case 3:
tmp_reg = 7 << 2;
break;
case 4:
tmp_reg = 0xf << 2;
break;
default:
tmp_reg = 0xf << 2;
break;
if (config_vact) {
vact_nl = FIELD_PREP(VACT_NL, dsi->vm.vactive);
writel(vact_nl, dsi->regs + DSI_VACT_NL);
writel(ps_wc, dsi->regs + DSI_HSTX_CKL_WC);
}
if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
tmp_reg |= HSTX_CKLP_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)
tmp_reg |= DIS_EOT;
writel(tmp_reg, dsi->regs + DSI_TXRX_CTRL);
}
static void mtk_dsi_ps_control(struct mtk_dsi *dsi)
{
u32 dsi_tmp_buf_bpp;
u32 tmp_reg;
switch (dsi->format) {
case MIPI_DSI_FMT_RGB888:
tmp_reg = PACKED_PS_24BIT_RGB888;
dsi_tmp_buf_bpp = 3;
break;
case MIPI_DSI_FMT_RGB666:
tmp_reg = LOOSELY_PS_18BIT_RGB666;
dsi_tmp_buf_bpp = 3;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
tmp_reg = PACKED_PS_18BIT_RGB666;
dsi_tmp_buf_bpp = 3;
break;
case MIPI_DSI_FMT_RGB565:
tmp_reg = PACKED_PS_16BIT_RGB565;
dsi_tmp_buf_bpp = 2;
break;
default:
tmp_reg = PACKED_PS_24BIT_RGB888;
dsi_tmp_buf_bpp = 3;
break;
}
tmp_reg += dsi->vm.hactive * dsi_tmp_buf_bpp & DSI_PS_WC;
writel(tmp_reg, dsi->regs + DSI_PSCTRL);
writel(ps_val, dsi->regs + DSI_PSCTRL);
}
static void mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi)
@ -471,7 +449,8 @@ static void mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi)
writel(vm->vactive, dsi->regs + DSI_VACT_NL);
if (dsi->driver_data->has_size_ctl)
writel(vm->vactive << 16 | vm->hactive,
writel(FIELD_PREP(DSI_HEIGHT, vm->vactive) |
FIELD_PREP(DSI_WIDTH, vm->hactive),
dsi->regs + DSI_SIZE_CON);
horizontal_sync_active_byte = (vm->hsync_len * dsi_tmp_buf_bpp - 10);
@ -520,7 +499,7 @@ static void mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi)
writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC);
writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC);
mtk_dsi_ps_control(dsi);
mtk_dsi_ps_control(dsi, false);
}
static void mtk_dsi_start(struct mtk_dsi *dsi)
@ -619,19 +598,12 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
if (++dsi->refcount != 1)
return 0;
switch (dsi->format) {
case MIPI_DSI_FMT_RGB565:
bit_per_pixel = 16;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
bit_per_pixel = 18;
break;
case MIPI_DSI_FMT_RGB666:
case MIPI_DSI_FMT_RGB888:
default:
bit_per_pixel = 24;
break;
ret = mipi_dsi_pixel_format_to_bpp(dsi->format);
if (ret < 0) {
dev_err(dev, "Unknown MIPI DSI format %d\n", dsi->format);
return ret;
}
bit_per_pixel = ret;
dsi->data_rate = DIV_ROUND_UP_ULL(dsi->vm.pixelclock * bit_per_pixel,
dsi->lanes);
@ -665,7 +637,7 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
mtk_dsi_reset_engine(dsi);
mtk_dsi_phy_timconfig(dsi);
mtk_dsi_ps_control_vact(dsi);
mtk_dsi_ps_control(dsi, true);
mtk_dsi_set_vm_cmd(dsi);
mtk_dsi_config_vdo_timing(dsi);
mtk_dsi_set_interrupt_enable(dsi);
@ -814,12 +786,11 @@ mtk_dsi_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
struct mtk_dsi *dsi = bridge_to_dsi(bridge);
u32 bpp;
int bpp;
if (dsi->format == MIPI_DSI_FMT_RGB565)
bpp = 16;
else
bpp = 24;
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
if (bpp < 0)
return MODE_ERROR;
if (mode->clock * bpp / dsi->lanes > 1500000)
return MODE_CLOCK_HIGH;
@ -1135,67 +1106,47 @@ static int mtk_dsi_probe(struct platform_device *pdev)
if (!dsi)
return -ENOMEM;
dsi->host.ops = &mtk_dsi_ops;
dsi->host.dev = dev;
ret = mipi_dsi_host_register(&dsi->host);
if (ret < 0) {
dev_err(dev, "failed to register DSI host: %d\n", ret);
return ret;
}
dsi->driver_data = of_device_get_match_data(dev);
dsi->engine_clk = devm_clk_get(dev, "engine");
if (IS_ERR(dsi->engine_clk)) {
ret = PTR_ERR(dsi->engine_clk);
if (IS_ERR(dsi->engine_clk))
return dev_err_probe(dev, PTR_ERR(dsi->engine_clk),
"Failed to get engine clock\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get engine clock: %d\n", ret);
goto err_unregister_host;
}
dsi->digital_clk = devm_clk_get(dev, "digital");
if (IS_ERR(dsi->digital_clk)) {
ret = PTR_ERR(dsi->digital_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get digital clock: %d\n", ret);
goto err_unregister_host;
}
if (IS_ERR(dsi->digital_clk))
return dev_err_probe(dev, PTR_ERR(dsi->digital_clk),
"Failed to get digital clock\n");
dsi->hs_clk = devm_clk_get(dev, "hs");
if (IS_ERR(dsi->hs_clk)) {
ret = PTR_ERR(dsi->hs_clk);
dev_err(dev, "Failed to get hs clock: %d\n", ret);
goto err_unregister_host;
}
if (IS_ERR(dsi->hs_clk))
return dev_err_probe(dev, PTR_ERR(dsi->hs_clk), "Failed to get hs clock\n");
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->regs = devm_ioremap_resource(dev, regs);
if (IS_ERR(dsi->regs)) {
ret = PTR_ERR(dsi->regs);
dev_err(dev, "Failed to ioremap memory: %d\n", ret);
goto err_unregister_host;
}
if (IS_ERR(dsi->regs))
return dev_err_probe(dev, PTR_ERR(dsi->regs), "Failed to ioremap memory\n");
dsi->phy = devm_phy_get(dev, "dphy");
if (IS_ERR(dsi->phy)) {
ret = PTR_ERR(dsi->phy);
dev_err(dev, "Failed to get MIPI-DPHY: %d\n", ret);
goto err_unregister_host;
}
if (IS_ERR(dsi->phy))
return dev_err_probe(dev, PTR_ERR(dsi->phy), "Failed to get MIPI-DPHY\n");
irq_num = platform_get_irq(pdev, 0);
if (irq_num < 0) {
ret = irq_num;
goto err_unregister_host;
}
if (irq_num < 0)
return irq_num;
dsi->host.ops = &mtk_dsi_ops;
dsi->host.dev = dev;
ret = mipi_dsi_host_register(&dsi->host);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register DSI host\n");
ret = devm_request_irq(&pdev->dev, irq_num, mtk_dsi_irq,
IRQF_TRIGGER_NONE, dev_name(&pdev->dev), dsi);
if (ret) {
dev_err(&pdev->dev, "failed to request mediatek dsi irq\n");
goto err_unregister_host;
mipi_dsi_host_unregister(&dsi->host);
return dev_err_probe(&pdev->dev, ret, "Failed to request DSI irq\n");
}
init_waitqueue_head(&dsi->irq_wait_queue);
@ -1207,10 +1158,6 @@ static int mtk_dsi_probe(struct platform_device *pdev)
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
return 0;
err_unregister_host:
mipi_dsi_host_unregister(&dsi->host);
return ret;
}
static void mtk_dsi_remove(struct platform_device *pdev)
@ -1249,17 +1196,12 @@ static const struct mtk_dsi_driver_data mt8188_dsi_driver_data = {
};
static const struct of_device_id mtk_dsi_of_match[] = {
{ .compatible = "mediatek,mt2701-dsi",
.data = &mt2701_dsi_driver_data },
{ .compatible = "mediatek,mt8173-dsi",
.data = &mt8173_dsi_driver_data },
{ .compatible = "mediatek,mt8183-dsi",
.data = &mt8183_dsi_driver_data },
{ .compatible = "mediatek,mt8186-dsi",
.data = &mt8186_dsi_driver_data },
{ .compatible = "mediatek,mt8188-dsi",
.data = &mt8188_dsi_driver_data },
{ },
{ .compatible = "mediatek,mt2701-dsi", .data = &mt2701_dsi_driver_data },
{ .compatible = "mediatek,mt8173-dsi", .data = &mt8173_dsi_driver_data },
{ .compatible = "mediatek,mt8183-dsi", .data = &mt8183_dsi_driver_data },
{ .compatible = "mediatek,mt8186-dsi", .data = &mt8186_dsi_driver_data },
{ .compatible = "mediatek,mt8188-dsi", .data = &mt8188_dsi_driver_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_dsi_of_match);