drm/tidss: Add OLDI bridge support

The AM62x and AM62Px SoCs feature 2 OLDI TXes each, which makes it
possible to connect them in dual-link or cloned single-link OLDI display
modes. The current OLDI support in tidss_dispc.c can only support for
a single OLDI TX, connected to a VP and doesn't really support
configuration of OLDIs in the other modes. The current OLDI support in
tidss_dispc.c also works on the principle that the OLDI output can only
be served by one, and only one, DSS video-port. This isn't the case in
the AM62Px SoC, where there are 2 DSS controllers present that share the
OLDI TXes.

Having their own devicetree and their own bridge entity will help
support the various display modes and sharing possiblilities of the OLDI
hardware.

For all these reasons, add support for the OLDI TXes as DRM bridges.

Signed-off-by: Aradhya Bhatia <a-bhatia1@ti.com>
Signed-off-by: Aradhya Bhatia <aradhya.bhatia@linux.dev>
Tested-by: Michael Walle <mwalle@kernel.org> # on am67a
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Link: https://lore.kernel.org/r/20250528122544.817829-5-aradhya.bhatia@linux.dev
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
This commit is contained in:
Aradhya Bhatia
2025-06-26 12:37:24 +03:00
committed by Tomi Valkeinen
parent d18bf71253
commit 7246e09299
8 changed files with 699 additions and 2 deletions
+2 -1
View File
@@ -7,6 +7,7 @@ tidss-y := tidss_crtc.o \
tidss_irq.o \
tidss_plane.o \
tidss_scale_coefs.o \
tidss_dispc.o
tidss_dispc.o \
tidss_oldi.o
obj-$(CONFIG_DRM_TIDSS) += tidss.o
+23 -1
View File
@@ -566,6 +566,29 @@ static u32 dispc_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg)
return ioread32(base + reg);
}
int tidss_configure_oldi(struct tidss_device *tidss, u32 hw_videoport,
u32 oldi_cfg)
{
u32 count = 0;
u32 oldi_reset_bit = BIT(5 + hw_videoport);
dispc_vp_write(tidss->dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, oldi_cfg);
while (!(oldi_reset_bit & dispc_read(tidss->dispc, DSS_SYSSTATUS)) &&
count < 10000)
count++;
if (!(oldi_reset_bit & dispc_read(tidss->dispc, DSS_SYSSTATUS)))
return -ETIMEDOUT;
return 0;
}
void tidss_disable_oldi(struct tidss_device *tidss, u32 hw_videoport)
{
dispc_vp_write(tidss->dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0);
}
/*
* TRM gives bitfields as start:end, where start is the higher bit
* number. For example 7:0
@@ -1418,7 +1441,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
* Calculate the percentage difference between the requested pixel clock rate
* and the effective rate resulting from calculating the clock divider value.
*/
static
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
{
int r = rate / 100, rr = real_rate / 100;
+5
View File
@@ -101,6 +101,11 @@ extern const struct dispc_features dispc_am62l_feats;
extern const struct dispc_features dispc_am65x_feats;
extern const struct dispc_features dispc_j721e_feats;
int tidss_configure_oldi(struct tidss_device *tidss, u32 hw_videoport,
u32 oldi_cfg);
void tidss_disable_oldi(struct tidss_device *tidss, u32 hw_videoport);
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate);
void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask);
dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc);
+14
View File
@@ -226,6 +226,20 @@ enum dispc_common_regs {
#define DISPC_VP_DSS_DMA_THREADSIZE 0x170 /* J721E */
#define DISPC_VP_DSS_DMA_THREADSIZE_STATUS 0x174 /* J721E */
/* OLDI Config Bits (DISPC_VP_DSS_OLDI_CFG) */
#define OLDI_ENABLE BIT(0)
#define OLDI_MAP (BIT(1) | BIT(2) | BIT(3))
#define OLDI_SRC BIT(4)
#define OLDI_CLONE_MODE BIT(5)
#define OLDI_MASTERSLAVE BIT(6)
#define OLDI_DEPOL BIT(7)
#define OLDI_MSB BIT(8)
#define OLDI_LBEN BIT(9)
#define OLDI_LBDATA BIT(10)
#define OLDI_DUALMODESYNC BIT(11)
#define OLDI_SOFTRST BIT(12)
#define OLDI_TPATCFG BIT(13)
/* LVDS Format values for OLDI_MAP field in DISPC_VP_OLDI_CFG register */
enum oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 };
+9
View File
@@ -24,6 +24,7 @@
#include "tidss_drv.h"
#include "tidss_kms.h"
#include "tidss_irq.h"
#include "tidss_oldi.h"
/* Power management */
@@ -147,6 +148,10 @@ static int tidss_probe(struct platform_device *pdev)
return ret;
}
ret = tidss_oldi_init(tidss);
if (ret)
return dev_err_probe(dev, ret, "failed to init OLDI\n");
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 1000);
@@ -203,6 +208,8 @@ err_runtime_suspend:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
tidss_oldi_deinit(tidss);
return ret;
}
@@ -227,6 +234,8 @@ static void tidss_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
tidss_oldi_deinit(tidss);
/* devm allocated dispc goes away with the dev so mark it NULL */
dispc_remove(tidss);
+5
View File
@@ -11,8 +11,10 @@
#define TIDSS_MAX_PORTS 4
#define TIDSS_MAX_PLANES 4
#define TIDSS_MAX_OLDI_TXES 2
typedef u32 dispc_irq_t;
struct tidss_oldi;
struct tidss_device {
struct drm_device ddev; /* DRM device for DSS */
@@ -27,6 +29,9 @@ struct tidss_device {
unsigned int num_planes;
struct drm_plane *planes[TIDSS_MAX_PLANES];
unsigned int num_oldis;
struct tidss_oldi *oldis[TIDSS_MAX_OLDI_TXES];
unsigned int irq;
/* protects the irq masks field and irqenable/irqstatus registers */
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 - Texas Instruments Incorporated
*
* Aradhya Bhatia <a-bhatia1@ti.com>
*/
#ifndef __TIDSS_OLDI_H__
#define __TIDSS_OLDI_H__
#include "tidss_drv.h"
struct tidss_oldi;
/* OLDI PORTS */
#define OLDI_INPUT_PORT 0
#define OLDI_OUTPUT_PORT 1
/* Control MMR Registers */
/* Register offsets */
#define OLDI_PD_CTRL 0x100
#define OLDI_LB_CTRL 0x104
/* Power control bits */
#define OLDI_PWRDOWN_TX(n) BIT(n)
/* LVDS Bandgap reference Enable/Disable */
#define OLDI_PWRDN_BG BIT(8)
enum tidss_oldi_link_type {
OLDI_MODE_UNSUPPORTED,
OLDI_MODE_SINGLE_LINK,
OLDI_MODE_CLONE_SINGLE_LINK,
OLDI_MODE_SECONDARY_CLONE_SINGLE_LINK,
OLDI_MODE_DUAL_LINK,
OLDI_MODE_SECONDARY_DUAL_LINK,
};
int tidss_oldi_init(struct tidss_device *tidss);
void tidss_oldi_deinit(struct tidss_device *tidss);
#endif /* __TIDSS_OLDI_H__ */