[PATCH v7 4/6] drm/tidss: Add support to configure OLDI mode for am625-dss
Aradhya Bhatia
a-bhatia1 at ti.com
Mon Feb 6 17:38:08 UTC 2023
On 06-Feb-23 19:12, Tomi Valkeinen wrote:
> On 05/02/2023 15:42, Aradhya Bhatia wrote:
>> Hi Tomi,
>>
>> On 03-Feb-23 20:42, Tomi Valkeinen wrote:
>>> On 25/01/2023 13:35, Aradhya Bhatia wrote:
>>>> The newer version of DSS (AM625-DSS) has 2 OLDI TXes at its disposal.
>>>> These can be configured to support the following modes:
>>>>
>>>> 1. OLDI_SINGLE_LINK_SINGLE_MODE
>>>> Single Output over OLDI 0.
>>>> +------+ +---------+ +-------+
>>>> | | | | | |
>>>> | CRTC +------->+ ENCODER +----->| PANEL |
>>>> | | | | | |
>>>> +------+ +---------+ +-------+
>>>>
>>>> 2. OLDI_SINGLE_LINK_CLONE_MODE
>>>> Duplicate Output over OLDI 0 and 1.
>>>> +------+ +---------+ +-------+
>>>> | | | | | |
>>>> | CRTC +---+--->| ENCODER +----->| PANEL |
>>>> | | | | | | |
>>>> +------+ | +---------+ +-------+
>>>> |
>>>> | +---------+ +-------+
>>>> | | | | |
>>>> +--->| ENCODER +----->| PANEL |
>>>> | | | |
>>>> +---------+ +-------+
>>>>
>>>> 3. OLDI_DUAL_LINK_MODE
>>>> Combined Output over OLDI 0 and 1.
>>>> +------+ +---------+ +-------+
>>>> | | | +----->| |
>>>> | CRTC +------->+ ENCODER | | PANEL |
>>>> | | | +----->| |
>>>> +------+ +---------+ +-------+
>>>>
>>>> Following the above pathways for different modes, 2 encoder/panel-bridge
>>>> pipes get created for clone mode, and 1 pipe in cases of single link and
>>>> dual link mode.
>>>>
>>>> Add support for confguring the OLDI modes using OF and LVDS DRM helper
>>>> functions.
>>>>
>>>> Signed-off-by: Aradhya Bhatia <a-bhatia1 at ti.com>
>>>> ---
>>>> drivers/gpu/drm/tidss/tidss_dispc.c | 24 ++-
>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 12 ++
>>>> drivers/gpu/drm/tidss/tidss_drv.h | 3 +
>>>> drivers/gpu/drm/tidss/tidss_encoder.c | 4 +-
>>>> drivers/gpu/drm/tidss/tidss_encoder.h | 3 +-
>>>> drivers/gpu/drm/tidss/tidss_kms.c | 221 ++++++++++++++++++++++++--
>>>> 6 files changed, 245 insertions(+), 22 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> index b55ccbcaa67f..37a73e309330 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> @@ -88,6 +88,8 @@ const struct dispc_features dispc_k2g_feats = {
>>>> .subrev = DISPC_K2G,
>>>> + .has_oldi = false,
>>>> +
>>>> .common = "common",
>>>> .common_regs = tidss_k2g_common_regs,
>>>> @@ -166,6 +168,8 @@ const struct dispc_features dispc_am625_feats = {
>>>> .subrev = DISPC_AM625,
>>>> + .has_oldi = true,
>>>> +
>>>> .common = "common",
>>>> .common_regs = tidss_am65x_common_regs,
>>>> @@ -218,6 +222,8 @@ const struct dispc_features dispc_am65x_feats = {
>>>> .subrev = DISPC_AM65X,
>>>> + .has_oldi = true,
>>>> +
>>>> .common = "common",
>>>> .common_regs = tidss_am65x_common_regs,
>>>> @@ -309,6 +315,8 @@ const struct dispc_features dispc_j721e_feats = {
>>>> .subrev = DISPC_J721E,
>>>> + .has_oldi = false,
>>>> +
>>>> .common = "common_m",
>>>> .common_regs = tidss_j721e_common_regs,
>>>> @@ -361,6 +369,8 @@ struct dispc_device {
>>>> struct dss_vp_data vp_data[TIDSS_MAX_VPS];
>>>> + enum dispc_oldi_modes oldi_mode;
>>>> +
>>>> u32 *fourccs;
>>>> u32 num_fourccs;
>>>> @@ -1963,6 +1973,12 @@ const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len)
>>>> return dispc->fourccs;
>>>> }
>>>> +void dispc_set_oldi_mode(struct dispc_device *dispc,
>>>> + enum dispc_oldi_modes oldi_mode)
>>>> +{
>>>> + dispc->oldi_mode = oldi_mode;
>>>> +}
>>>> +
>>>> static s32 pixinc(int pixels, u8 ps)
>>>> {
>>>> if (pixels == 1)
>>>> @@ -2647,7 +2663,7 @@ int dispc_runtime_resume(struct dispc_device *dispc)
>>>> REG_GET(dispc, DSS_SYSSTATUS, 2, 2),
>>>> REG_GET(dispc, DSS_SYSSTATUS, 3, 3));
>>>> - if (dispc->feat->subrev == DISPC_AM65X)
>>>> + if (dispc->feat->has_oldi)
>>>> dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n",
>>>> REG_GET(dispc, DSS_SYSSTATUS, 5, 5),
>>>> REG_GET(dispc, DSS_SYSSTATUS, 6, 6),
>>>> @@ -2688,7 +2704,7 @@ static int dispc_iomap_resource(struct platform_device *pdev, const char *name,
>>>> return 0;
>>>> }
>>>> -static int dispc_init_am65x_oldi_io_ctrl(struct device *dev,
>>>> +static int dispc_init_am6xx_oldi_io_ctrl(struct device *dev,
>>>> struct dispc_device *dispc)
>>>> {
>>>> dispc->oldi_io_ctrl =
>>>> @@ -2827,8 +2843,8 @@ int dispc_init(struct tidss_device *tidss)
>>>> dispc->vp_data[i].gamma_table = gamma_table;
>>>> }
>>>> - if (feat->subrev == DISPC_AM65X) {
>>>> - r = dispc_init_am65x_oldi_io_ctrl(dev, dispc);
>>>> + if (feat->has_oldi) {
>>>> + r = dispc_init_am6xx_oldi_io_ctrl(dev, dispc);
>>>> if (r)
>>>> return r;
>>>> }
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
>>>> index 971f2856f015..880bc7de68b3 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>>>> @@ -64,6 +64,15 @@ enum dispc_dss_subrevision {
>>>> DISPC_J721E,
>>>> };
>>>> +enum dispc_oldi_modes {
>>>> + OLDI_MODE_SINGLE_LINK, /* Single output over OLDI 0. */
>>>> + OLDI_MODE_CLONE_SINGLE_LINK, /* Cloned output over OLDI 0 and 1. */
>>>> + OLDI_MODE_DUAL_LINK, /* Combined output over OLDI 0 and 1. */
>>>> + OLDI_MODE_OFF, /* OLDI TXes not connected in OF. */
>>>> + OLDI_MODE_UNSUPPORTED, /* Unsupported OLDI configuration in OF. */
>>>> + OLDI_MODE_UNAVAILABLE, /* OLDI TXes not available in SoC. */
>>>> +};
>>>> +
>>>> struct dispc_features {
>>>> int min_pclk_khz;
>>>> int max_pclk_khz[DISPC_PORT_MAX_BUS_TYPE];
>>>> @@ -72,6 +81,8 @@ struct dispc_features {
>>>> enum dispc_dss_subrevision subrev;
>>>> + bool has_oldi;
>>>> +
>>>> const char *common;
>>>> const u16 *common_regs;
>>>> u32 num_vps;
>>>> @@ -131,6 +142,7 @@ int dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane,
>>>> u32 hw_videoport);
>>>> int dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable);
>>>> const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len);
>>>> +void dispc_set_oldi_mode(struct dispc_device *dispc, enum dispc_oldi_modes oldi_mode);
>>>> int dispc_init(struct tidss_device *tidss);
>>>> void dispc_remove(struct tidss_device *tidss);
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
>>>> index 0ce7ee5ccd5b..58892f065c16 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_drv.h
>>>> +++ b/drivers/gpu/drm/tidss/tidss_drv.h
>>>> @@ -13,6 +13,9 @@
>>>> #define TIDSS_MAX_PLANES 4
>>>> #define TIDSS_MAX_OUTPUT_PORTS 4
>>>> +/* For AM625-DSS with 2 OLDI TXes */
>>>> +#define TIDSS_MAX_BRIDGES_PER_PIPE 2
>>>> +
>>>> typedef u32 dispc_irq_t;
>>>> struct tidss_device {
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c
>>>> index 0d4865e9c03d..bd2a7358d7b0 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_encoder.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_encoder.c
>>>> @@ -70,7 +70,8 @@ static const struct drm_encoder_funcs encoder_funcs = {
>>>> };
>>>> struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
>>>> - u32 encoder_type, u32 possible_crtcs)
>>>> + u32 encoder_type, u32 possible_crtcs,
>>>> + u32 possible_clones)
>>>> {
>>>> struct drm_encoder *enc;
>>>> int ret;
>>>> @@ -80,6 +81,7 @@ struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
>>>> return ERR_PTR(-ENOMEM);
>>>> enc->possible_crtcs = possible_crtcs;
>>>> + enc->possible_clones = possible_clones;
>>>> ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs,
>>>> encoder_type, NULL);
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h
>>>> index ace877c0e0fd..01c62ba3ef16 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_encoder.h
>>>> +++ b/drivers/gpu/drm/tidss/tidss_encoder.h
>>>> @@ -12,6 +12,7 @@
>>>> struct tidss_device;
>>>> struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
>>>> - u32 encoder_type, u32 possible_crtcs);
>>>> + u32 encoder_type, u32 possible_crtcs,
>>>> + u32 possible_clones);
>>>> #endif
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
>>>> index d449131935d2..8322ee6310bf 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_kms.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_kms.c
>>>> @@ -13,6 +13,7 @@
>>>> #include <drm/drm_of.h>
>>>> #include <drm/drm_panel.h>
>>>> #include <drm/drm_vblank.h>
>>>> +#include <linux/of.h>
>>>> #include "tidss_crtc.h"
>>>> #include "tidss_dispc.h"
>>>> @@ -104,26 +105,129 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
>>>> .atomic_commit = drm_atomic_helper_commit,
>>>> };
>>>> +static enum dispc_oldi_modes tidss_get_oldi_mode(struct tidss_device *tidss)
>>>> +{
>>>> + int pixel_order;
>>>> + enum dispc_oldi_modes oldi_mode;
>>>> + struct device_node *oldi0_port, *oldi1_port;
>>>> +
>>>> + /*
>>>> + * For am625-dss, the OLDI ports are expected at port reg = 0 and 2,
>>>> + * and for am65x-dss, the OLDI port is expected only at port reg = 0.
>>>> + */
>>>> + const u32 portnum_oldi0 = 0, portnum_oldi1 = 2;
>>>> +
>>>> + oldi0_port = of_graph_get_port_by_id(tidss->dev->of_node, portnum_oldi0);
>>>> + oldi1_port = of_graph_get_port_by_id(tidss->dev->of_node, portnum_oldi1);
>>>> +
>>>> + if (!(oldi0_port || oldi1_port)) {
>>>> + /* Keep OLDI TXes OFF if neither OLDI port is present. */
>>>> + oldi_mode = OLDI_MODE_OFF;
>>>> + } else if (oldi0_port && !oldi1_port) {
>>>> + /*
>>>> + * OLDI0 port found, but not OLDI1 port. Setting single
>>>> + * link output mode.
>>>> + */
>>>> + oldi_mode = OLDI_MODE_SINGLE_LINK;
>>>> + } else if (!oldi0_port && oldi1_port) {
>>>> + /*
>>>> + * The 2nd OLDI TX cannot be operated alone. This use case is
>>>> + * not supported in the HW. Since the pins for OLDIs 0 and 1 are
>>>> + * separate, one could theoretically set a clone mode over OLDIs
>>>> + * 0 and 1 and just simply not use the OLDI 0. This is a hacky
>>>> + * way to enable only OLDI TX 1 and hence is not officially
>>>> + * supported.
>>>> + */
>>>> + dev_warn(tidss->dev,
>>>> + "Single Mode over OLDI 1 is not supported in HW.\n");
>>>> + oldi_mode = OLDI_MODE_UNSUPPORTED;
>>>> + } else {
>>>> + /*
>>>> + * OLDI Ports found for both the OLDI TXes. The DSS is to be
>>>> + * configured in either Dual Link or Clone Mode.
>>>> + */
>>>> + pixel_order = drm_of_lvds_get_dual_link_pixel_order(oldi0_port,
>>>> + oldi1_port);
>>>> + switch (pixel_order) {
>>>> + case -EINVAL:
>>>> + /*
>>>> + * The dual link properties were not found in at least
>>>> + * one of the sink nodes. Since 2 OLDI ports are present
>>>> + * in the DT, it can be safely assumed that the required
>>>> + * configuration is Clone Mode.
>>>> + */
>>>> + oldi_mode = OLDI_MODE_CLONE_SINGLE_LINK;
>>>> + break;
>>>> +
>>>> + case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
>>>> + /*
>>>> + * Note that the OLDI TX 0 transmits the odd set of
>>>> + * pixels while the OLDI TX 1 transmits the even set.
>>>> + * This is a fixed configuration in the HW and an cannot
>>>> + * be change via SW.
>>>> + */
>>>> + dev_warn(tidss->dev,
>>>> + "EVEN-ODD Dual-Link Mode is not supported in HW.\n");
>>>> + oldi_mode = OLDI_MODE_UNSUPPORTED;
>>>> + break;
>>>> +
>>>> + case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
>>>> + oldi_mode = OLDI_MODE_DUAL_LINK;
>>>> + break;
>>>> +
>>>> + default:
>>>> + oldi_mode = OLDI_MODE_UNSUPPORTED;
>>>> + break;
>>>> + }
>>>> + }
>>>> +
>>>> + of_node_put(oldi0_port);
>>>> + of_node_put(oldi1_port);
>>>> +
>>>> + return oldi_mode;
>>>> +}
>>>> +
>>>> static int tidss_dispc_modeset_init(struct tidss_device *tidss)
>>>> {
>>>> struct device *dev = tidss->dev;
>>>> unsigned int fourccs_len;
>>>> const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len);
>>>> - unsigned int i;
>>>> + unsigned int i, j;
>>>> struct pipe {
>>>> u32 hw_videoport;
>>>> - struct drm_bridge *bridge;
>>>> + struct drm_bridge *bridge[TIDSS_MAX_BRIDGES_PER_PIPE];
>>>> u32 enc_type;
>>>> + u32 num_bridges;
>>>> };
>>>> const struct dispc_features *feat = tidss->feat;
>>>> u32 output_ports = feat->num_output_ports;
>>>> u32 max_planes = feat->num_planes;
>>>> - struct pipe pipes[TIDSS_MAX_VPS];
>>>> + struct pipe pipes[TIDSS_MAX_VPS] = {0};
>>>> +
>>>> u32 num_pipes = 0;
>>>> u32 crtc_mask;
>>>> + enum dispc_oldi_modes oldi_mode = OLDI_MODE_UNAVAILABLE;
>>>> + u32 num_oldi = 0;
>>>> + u32 num_encoders = 0;
>>>> + u32 oldi_pipe_index = 0;
>>>> +
>>>> + if (feat->has_oldi) {
>>>> + oldi_mode = tidss_get_oldi_mode(tidss);
>>>> +
>>>> + if ((oldi_mode == OLDI_MODE_DUAL_LINK ||
>>>> + oldi_mode == OLDI_MODE_CLONE_SINGLE_LINK) &&
>>>> + feat->subrev == DISPC_AM65X) {
>>>> + dev_warn(tidss->dev,
>>>> + "am65x-dss does not support this OLDI mode.\n");
>>>> +
>>>> + oldi_mode = OLDI_MODE_UNSUPPORTED;
>>>> + }
>>>
>>> Shouldn't OLDI_MODE_UNSUPPORTED be handled as an error? It means the DT
>>> is faulty, doesn't it? Maybe it could even be renamed to
>>> OLDI_MODE_ERROR. Or tidss_get_oldi_mode() could return a negative error
>>> code.
>>>
>>
>> The idea was to let the framework continue configuring the 2nd videoport
>> for DPI, even if the OLDI DT is wrong. But I have come across more
>> examples recently where that is not the case. DT error for one pipe has
>> resulted in returning of an error code.
>>
>> Will make the change.
>
> My opinion is that the DT has to be correct. If it isn't, just fail and
> exit as soon as possible. There shouldn't be any reasons for the drivers
> to be trying to cope with a broken DT.
Understood! Will error out in those cases!
>
>>>> +
>>>> + dispc_set_oldi_mode(tidss->dispc, oldi_mode);
>>>> + }
>>>
>>> Would it be better to move the above dispc_set_oldi_mode() to be outside
>>> the if block? Then oldi mode would be set to OLDI_MODE_UNAVAILABLE on
>>> SoCs that don't have OLDI.
>>
>> Ahh, yes! Will make the change.
>>
>>>
>>> tidss_get_oldi_mode and dispc_set_oldi_mode sound like opposites, but
>>> they're totally different things. Maybe tidss_get_oldi_mode should
>>> rather be something about parsing oldi dt properties or such.
>>
>> Okay! Is 'tidss_parse_oldi_properties' acceptable? This is just
>> something I came up with now. I can think of more if this is not good.
>
> Sounds fine.
>
Okay!
Regards
Aradhya
More information about the dri-devel
mailing list