From patchwork Sat Nov 19 17:30:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aradhya Bhatia X-Patchwork-Id: 626931 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6998BC43217 for ; Sat, 19 Nov 2022 17:30:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234463AbiKSRaq (ORCPT ); Sat, 19 Nov 2022 12:30:46 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43942 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234295AbiKSRan (ORCPT ); Sat, 19 Nov 2022 12:30:43 -0500 Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E6FA7167EE; Sat, 19 Nov 2022 09:30:41 -0800 (PST) Received: from lelv0266.itg.ti.com ([10.180.67.225]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id 2AJHUONv044524; Sat, 19 Nov 2022 11:30:24 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1668879024; bh=+KrMvro8oqOpCG+tSTiaUcBA/1wpa56Pv1IckB8sCIg=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=aOwS+bheorhn67spkKAYk9AHre9Soz6E4DSA23ndzycEB2d0n7YCZ1tRZzPRWxm1x 1Xle/alAm8iaLq1KKqBQK8cAzoAFui6FHlI6SDyiBLssjghZ313PWatT0Ylk3RQERT kKCkz7f3PVvQSsOiFVGje0L02pOF+EN7yf30k1yM= Received: from DFLE113.ent.ti.com (dfle113.ent.ti.com [10.64.6.34]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 2AJHUOSg030806 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Sat, 19 Nov 2022 11:30:24 -0600 Received: from DFLE101.ent.ti.com (10.64.6.22) by DFLE113.ent.ti.com (10.64.6.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.16; Sat, 19 Nov 2022 11:30:24 -0600 Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE101.ent.ti.com (10.64.6.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.16 via Frontend Transport; Sat, 19 Nov 2022 11:30:24 -0600 Received: from localhost (ileaxei01-snat2.itg.ti.com [10.180.69.6]) by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 2AJHUNnQ058855; Sat, 19 Nov 2022 11:30:24 -0600 From: Aradhya Bhatia To: Tomi Valkeinen , Jyri Sarha , Rob Herring , David Airlie , Daniel Vetter , Krzysztof Kozlowski CC: DRI Development List , Devicetree List , Linux Kernel List , Nishanth Menon , Vignesh Raghavendra , Rahul T R , Devarsh Thakkar , Jayesh Choudhary , Aradhya Bhatia Subject: [PATCH v6 2/5] drm/tidss: Add support for AM625 DSS Date: Sat, 19 Nov 2022 23:00:16 +0530 Message-ID: <20221119173019.15643-3-a-bhatia1@ti.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221119173019.15643-1-a-bhatia1@ti.com> References: <20221119173019.15643-1-a-bhatia1@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Add support for the DSS controller on TI's new AM625 SoC in the tidss driver. The first video port (VP0) in am625-dss can output OLDI signals through 2 OLDI TXes. A 3rd output port has been added with "DISPC_VP_OLDI" bus type. DSS controllers before AM625 had a 1 to 1 coupling between Videoports and Output Ports. Since this stands no longer to be true for AM625 DSS, this couppling has been separated with the introduction of output port based variables. This will help address the addition of the 2nd OLDI TX over VP0 as the 3rd output port. Signed-off-by: Aradhya Bhatia --- drivers/gpu/drm/tidss/tidss_dispc.c | 80 ++++++++++++++++++++++++++--- drivers/gpu/drm/tidss/tidss_dispc.h | 15 ++++-- drivers/gpu/drm/tidss/tidss_drv.c | 1 + drivers/gpu/drm/tidss/tidss_drv.h | 5 +- drivers/gpu/drm/tidss/tidss_irq.h | 2 +- drivers/gpu/drm/tidss/tidss_kms.c | 2 +- 6 files changed, 90 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index ad93acc9abd2..dbc6a5b617ca 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -93,10 +93,13 @@ const struct dispc_features dispc_k2g_feats = { .common_regs = tidss_k2g_common_regs, .num_vps = 1, + .num_output_ports = 1, + .oldi_supported = false, .vp_name = { "vp1" }, .ovr_name = { "ovr1" }, .vpclk_name = { "vp1" }, .vp_bus_type = { DISPC_VP_DPI }, + .output_port_bus_type = { DISPC_VP_DPI }, .vp_feat = { .color = { .has_ctm = true, @@ -168,10 +171,13 @@ const struct dispc_features dispc_am65x_feats = { .common_regs = tidss_am65x_common_regs, .num_vps = 2, + .num_output_ports = 2, + .oldi_supported = true, .vp_name = { "vp1", "vp2" }, .ovr_name = { "ovr1", "ovr2" }, .vpclk_name = { "vp1", "vp2" }, .vp_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, + .output_port_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, .vp_feat = { .color = { .has_ctm = true, @@ -257,12 +263,16 @@ const struct dispc_features dispc_j721e_feats = { .common_regs = tidss_j721e_common_regs, .num_vps = 4, + .num_output_ports = 4, + .oldi_supported = false, .vp_name = { "vp1", "vp2", "vp3", "vp4" }, .ovr_name = { "ovr1", "ovr2", "ovr3", "ovr4" }, .vpclk_name = { "vp1", "vp2", "vp3", "vp4" }, /* Currently hard coded VP routing (see dispc_initial_config()) */ .vp_bus_type = { DISPC_VP_INTERNAL, DISPC_VP_DPI, DISPC_VP_INTERNAL, DISPC_VP_DPI, }, + .output_port_bus_type = { DISPC_VP_INTERNAL, DISPC_VP_DPI, + DISPC_VP_INTERNAL, DISPC_VP_DPI, }, .vp_feat = { .color = { .has_ctm = true, .gamma_size = 1024, @@ -275,6 +285,59 @@ const struct dispc_features dispc_j721e_feats = { .vid_order = { 1, 3, 0, 2 }, }; +const struct dispc_features dispc_am625_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 165000, + [DISPC_VP_OLDI] = 165000, + }, + + .scaling = { + .in_width_max_5tap_rgb = 1280, + .in_width_max_3tap_rgb = 2560, + .in_width_max_5tap_yuv = 2560, + .in_width_max_3tap_yuv = 4096, + .upscale_limit = 16, + .downscale_limit_5tap = 4, + .downscale_limit_3tap = 2, + /* + * The max supported pixel inc value is 255. The value + * of pixel inc is calculated like this: 1+(xinc-1)*bpp. + * The maximum bpp of all formats supported by the HW + * is 8. So the maximum supported xinc value is 32, + * because 1+(32-1)*8 < 255 < 1+(33-1)*4. + */ + .xinc_max = 32, + }, + + .subrev = DISPC_AM625, + + .common = "common", + .common_regs = tidss_am65x_common_regs, + + .num_vps = 2, + /* note: the 3rd port is not representative of a 3rd pipeline */ + .num_output_ports = 3, + .oldi_supported = true, + .vp_name = { "vp1", "vp2" }, + .ovr_name = { "ovr1", "ovr2" }, + .vpclk_name = { "vp1", "vp2" }, + .vp_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, + .output_port_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI, DISPC_VP_OLDI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_planes = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ + .vid_name = { "vid", "vidl1" }, + .vid_lite = { false, true, }, + .vid_order = { 1, 0 }, +}; + static const u16 *dispc_common_regmap; struct dss_vp_data { @@ -287,12 +350,12 @@ struct dispc_device { void __iomem *base_common; void __iomem *base_vid[TIDSS_MAX_PLANES]; - void __iomem *base_ovr[TIDSS_MAX_PORTS]; - void __iomem *base_vp[TIDSS_MAX_PORTS]; + void __iomem *base_ovr[TIDSS_MAX_VPS]; + void __iomem *base_vp[TIDSS_MAX_VPS]; struct regmap *oldi_io_ctrl; - struct clk *vp_clk[TIDSS_MAX_PORTS]; + struct clk *vp_clk[TIDSS_MAX_VPS]; const struct dispc_features *feat; @@ -300,7 +363,7 @@ struct dispc_device { bool is_enabled; - struct dss_vp_data vp_data[TIDSS_MAX_PORTS]; + struct dss_vp_data vp_data[TIDSS_MAX_VPS]; u32 *fourccs; u32 num_fourccs; @@ -778,6 +841,7 @@ dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) return dispc_k2g_read_and_clear_irqstatus(dispc); case DISPC_AM65X: case DISPC_J721E: + case DISPC_AM625: return dispc_k3_read_and_clear_irqstatus(dispc); default: WARN_ON(1); @@ -793,6 +857,7 @@ void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) break; case DISPC_AM65X: case DISPC_J721E: + case DISPC_AM625: dispc_k3_set_irqenable(dispc, mask); break; default: @@ -1116,7 +1181,7 @@ enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc, const struct drm_display_mode *mode) { u32 hsw, hfp, hbp, vsw, vfp, vbp; - enum dispc_vp_bus_type bus_type; + enum dispc_bus_type bus_type; int max_pclk; bus_type = dispc->feat->vp_bus_type[hw_videoport]; @@ -1282,6 +1347,7 @@ void dispc_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, x, y, layer); break; case DISPC_AM65X: + case DISPC_AM625: dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, x, y, layer); break; @@ -2205,6 +2271,7 @@ static void dispc_plane_init(struct dispc_device *dispc) break; case DISPC_AM65X: case DISPC_J721E: + case DISPC_AM625: dispc_k3_plane_init(dispc); break; default: @@ -2310,6 +2377,7 @@ static void dispc_vp_write_gamma_table(struct dispc_device *dispc, dispc_k2g_vp_write_gamma_table(dispc, hw_videoport); break; case DISPC_AM65X: + case DISPC_AM625: dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); break; case DISPC_J721E: @@ -2583,7 +2651,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->oldi_supported) dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n", REG_GET(dispc, DSS_SYSSTATUS, 5, 5), REG_GET(dispc, DSS_SYSSTATUS, 6, 6), diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index e49432f0abf5..51db500590ee 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -50,7 +50,7 @@ struct dispc_errata { bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */ }; -enum dispc_vp_bus_type { +enum dispc_bus_type { DISPC_VP_DPI, /* DPI output */ DISPC_VP_OLDI, /* OLDI (LVDS) output */ DISPC_VP_INTERNAL, /* SoC internal routing */ @@ -61,6 +61,7 @@ enum dispc_dss_subrevision { DISPC_K2G, DISPC_AM65X, DISPC_J721E, + DISPC_AM625, }; struct dispc_features { @@ -74,10 +75,13 @@ struct dispc_features { const char *common; const u16 *common_regs; u32 num_vps; - const char *vp_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ - const char *ovr_name[TIDSS_MAX_PORTS]; /* Should match dt reg names */ - const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */ - const enum dispc_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS]; + u32 num_output_ports; + bool oldi_supported; + const char *vp_name[TIDSS_MAX_VPS]; /* Should match dt reg names */ + const char *ovr_name[TIDSS_MAX_VPS]; /* Should match dt reg names */ + const char *vpclk_name[TIDSS_MAX_VPS]; /* Should match dt clk names */ + const enum dispc_bus_type vp_bus_type[TIDSS_MAX_VPS]; + const enum dispc_bus_type output_port_bus_type[TIDSS_MAX_OUTPUT_PORTS]; struct tidss_vp_feat vp_feat; u32 num_planes; const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */ @@ -88,6 +92,7 @@ struct dispc_features { extern const struct dispc_features dispc_k2g_feats; extern const struct dispc_features dispc_am65x_feats; extern const struct dispc_features dispc_j721e_feats; +extern const struct dispc_features dispc_am625_feats; void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask); dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc); diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 15cd9b91b7e2..c5e119828823 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -235,6 +235,7 @@ static const struct of_device_id tidss_of_table[] = { { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, + { .compatible = "ti,am625-dss", .data = &dispc_am625_feats, }, { } }; diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h index d7f27b0b0315..0ce7ee5ccd5b 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.h +++ b/drivers/gpu/drm/tidss/tidss_drv.h @@ -9,8 +9,9 @@ #include -#define TIDSS_MAX_PORTS 4 +#define TIDSS_MAX_VPS 4 #define TIDSS_MAX_PLANES 4 +#define TIDSS_MAX_OUTPUT_PORTS 4 typedef u32 dispc_irq_t; @@ -22,7 +23,7 @@ struct tidss_device { struct dispc_device *dispc; unsigned int num_crtcs; - struct drm_crtc *crtcs[TIDSS_MAX_PORTS]; + struct drm_crtc *crtcs[TIDSS_MAX_VPS]; unsigned int num_planes; struct drm_plane *planes[TIDSS_MAX_PLANES]; diff --git a/drivers/gpu/drm/tidss/tidss_irq.h b/drivers/gpu/drm/tidss/tidss_irq.h index b512614d5863..a753f5e3ce15 100644 --- a/drivers/gpu/drm/tidss/tidss_irq.h +++ b/drivers/gpu/drm/tidss/tidss_irq.h @@ -35,7 +35,7 @@ #define DSS_IRQ_VP_BIT_N(ch, bit) (4 + 4 * (ch) + (bit)) #define DSS_IRQ_PLANE_BIT_N(plane, bit) \ - (DSS_IRQ_VP_BIT_N(TIDSS_MAX_PORTS, 0) + 1 * (plane) + (bit)) + (DSS_IRQ_VP_BIT_N(TIDSS_MAX_VPS, 0) + 1 * (plane) + (bit)) #define DSS_IRQ_VP_BIT(ch, bit) BIT(DSS_IRQ_VP_BIT_N((ch), (bit))) #define DSS_IRQ_PLANE_BIT(plane, bit) \ diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index afb2879980c6..fc9c4eefd31d 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -123,7 +123,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) u32 max_vps = feat->num_vps; u32 max_planes = feat->num_planes; - struct pipe pipes[TIDSS_MAX_PORTS]; + struct pipe pipes[TIDSS_MAX_VPS]; u32 num_pipes = 0; u32 crtc_mask; From patchwork Sat Nov 19 17:30:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aradhya Bhatia X-Patchwork-Id: 626929 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D85D0C4332F for ; Sat, 19 Nov 2022 17:31:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234959AbiKSRbP (ORCPT ); Sat, 19 Nov 2022 12:31:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43930 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234789AbiKSRbG (ORCPT ); Sat, 19 Nov 2022 12:31:06 -0500 Received: from fllv0015.ext.ti.com (fllv0015.ext.ti.com [198.47.19.141]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4ED015FC6; Sat, 19 Nov 2022 09:30:50 -0800 (PST) Received: from fllv0034.itg.ti.com ([10.64.40.246]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 2AJHUQFi061510; Sat, 19 Nov 2022 11:30:26 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1668879026; bh=MmQDSndL+oPTUzZotxLcg5udeYBFarHX7zG4rAsSAEU=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=AOQy6Yo2TTwHxhBF7VBg8+J8Nxq+/RkD81WF9VyKShGjZnl7cueCjL1Ulms6JFxWi l7EJIQWB3/QUWeEowafV51aF/SJx5e/mNbD2uuqVrJrzx/xxbg2UWSUiTD6h3jkf3V 5tWLWCupk648LT8qzDLLlxRZ53tGammE089UKqL4= Received: from DFLE109.ent.ti.com (dfle109.ent.ti.com [10.64.6.30]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 2AJHUQ7u014117 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Sat, 19 Nov 2022 11:30:26 -0600 Received: from DFLE107.ent.ti.com (10.64.6.28) by DFLE109.ent.ti.com (10.64.6.30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.16; Sat, 19 Nov 2022 11:30:25 -0600 Received: from lelv0327.itg.ti.com (10.180.67.183) by DFLE107.ent.ti.com (10.64.6.28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.16 via Frontend Transport; Sat, 19 Nov 2022 11:30:25 -0600 Received: from localhost (ileaxei01-snat2.itg.ti.com [10.180.69.6]) by lelv0327.itg.ti.com (8.15.2/8.15.2) with ESMTP id 2AJHUPIZ055680; Sat, 19 Nov 2022 11:30:25 -0600 From: Aradhya Bhatia To: Tomi Valkeinen , Jyri Sarha , Rob Herring , David Airlie , Daniel Vetter , Krzysztof Kozlowski CC: DRI Development List , Devicetree List , Linux Kernel List , Nishanth Menon , Vignesh Raghavendra , Rahul T R , Devarsh Thakkar , Jayesh Choudhary , Aradhya Bhatia Subject: [PATCH v6 3/5] drm/tidss: Add support to configure OLDI mode for am625-dss. Date: Sat, 19 Nov 2022 23:00:17 +0530 Message-ID: <20221119173019.15643-4-a-bhatia1@ti.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221119173019.15643-1-a-bhatia1@ti.com> References: <20221119173019.15643-1-a-bhatia1@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org 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 --- drivers/gpu/drm/tidss/tidss_dispc.c | 12 ++ drivers/gpu/drm/tidss/tidss_dispc.h | 9 ++ 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 | 188 +++++++++++++++++++++++--- 6 files changed, 198 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index dbc6a5b617ca..472226a83251 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -365,6 +365,8 @@ struct dispc_device { struct dss_vp_data vp_data[TIDSS_MAX_VPS]; + enum dispc_oldi_modes oldi_mode; + u32 *fourccs; u32 num_fourccs; @@ -1967,6 +1969,16 @@ const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len) return dispc->fourccs; } +int dispc_set_oldi_mode(struct dispc_device *dispc, + enum dispc_oldi_modes oldi_mode) +{ + WARN_ON(!dispc); + + dispc->oldi_mode = oldi_mode; + + return 0; +} + static s32 pixinc(int pixels, u8 ps) { if (pixels == 1) diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 51db500590ee..e76a7599b544 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -64,6 +64,14 @@ enum dispc_dss_subrevision { DISPC_AM625, }; +enum dispc_oldi_modes { + OLDI_MODE_OFF, /* OLDI turned off / tied off in IP. */ + OLDI_SINGLE_LINK_SINGLE_MODE, /* Single Output over OLDI 0. */ + OLDI_SINGLE_LINK_CLONE_MODE, /* Duplicate Output over OLDI 0 and 1. */ + OLDI_DUAL_LINK_MODE, /* Combined Output over OLDI 0 and 1. */ + OLDI_MODE_UNSUPPORTED, /* Unsupported OLDI Mode */ +}; + struct dispc_features { int min_pclk_khz; int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE]; @@ -133,6 +141,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); +int 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 e278a9c89476..141383ec4045 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 fc9c4eefd31d..8ae321f02197 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -106,30 +106,115 @@ 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 device_node *oldi0_port, + struct device_node *oldi1_port) +{ + int pixel_order; + + if (!(oldi0_port || oldi1_port)) { + /* Keep OLDI TXes off if neither OLDI port is present. */ + return OLDI_MODE_OFF; + } else if (oldi0_port && !oldi1_port) { + /* + * OLDI0 port found, but not OLDI1 port. Setting single + * link, single mode output. + */ + return OLDI_SINGLE_LINK_SINGLE_MODE; + } 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. + */ + return OLDI_MODE_UNSUPPORTED; + } + + /* + * 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. + */ + return OLDI_SINGLE_LINK_CLONE_MODE; + + 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 IP and an cannot be change vis SW. These + * properties have been used to merely identify if a Dual Link + * configuration is required. Swapping this property in the panel + * port DT nodes will not make any difference. + */ + pr_warn("EVEN-ODD config for dual-link sinks is not supported in HW. Switching to ODD-EVEN.\n"); + fallthrough; + + case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: + return OLDI_DUAL_LINK_MODE; + + default: + return OLDI_MODE_OFF; + } +} + 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 max_vps = feat->num_vps; + 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; + u32 portnum_oldi0 = 0, portnum_oldi1 = 2; + enum dispc_oldi_modes oldi_mode = OLDI_MODE_OFF; + u32 num_oldi = 0; + u32 oldi_pipe_index = 0; + u32 num_encoders = 0; + + if (feat->oldi_supported) { + struct device_node *oldi0_port, *oldi1_port; + + oldi0_port = of_graph_get_port_by_id(dev->of_node, + portnum_oldi0); + oldi1_port = of_graph_get_port_by_id(dev->of_node, + portnum_oldi1); + + oldi_mode = tidss_get_oldi_mode(oldi0_port, oldi1_port); + + of_node_put(oldi0_port); + of_node_put(oldi1_port); + + dispc_set_oldi_mode(tidss->dispc, oldi_mode); + } /* first find all the connected panels & bridges */ - for (i = 0; i < max_vps; i++) { + for (i = 0; i < output_ports; i++) { struct drm_panel *panel; struct drm_bridge *bridge; u32 enc_type = DRM_MODE_ENCODER_NONE; @@ -145,16 +230,24 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) return ret; } + if (feat->output_port_bus_type[i] == DISPC_VP_OLDI && + oldi_mode == OLDI_MODE_UNSUPPORTED) { + dev_err(dev, + "Single Mode over OLDI 1 is not supported in HW. Keeping OLDI off.\n"); + continue; + } + if (panel) { u32 conn_type; dev_dbg(dev, "Setting up panel for port %d\n", i); - switch (feat->vp_bus_type[i]) { + switch (feat->output_port_bus_type[i]) { case DISPC_VP_OLDI: enc_type = DRM_MODE_ENCODER_LVDS; conn_type = DRM_MODE_CONNECTOR_LVDS; break; + case DISPC_VP_DPI: enc_type = DRM_MODE_ENCODER_DPI; conn_type = DRM_MODE_CONNECTOR_DPI; @@ -172,6 +265,17 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) return -EINVAL; } + /* + * If the 2nd OLDI port is discovered connected to a panel + * which is not to be connected in the Clone Mode then a + * bridge is not required because the detected port is the + * 2nd port for the previously connected panel. + */ + if (feat->output_port_bus_type[i] == DISPC_VP_OLDI && + oldi_mode != OLDI_SINGLE_LINK_CLONE_MODE && + num_oldi) + break; + bridge = devm_drm_panel_bridge_add(dev, panel); if (IS_ERR(bridge)) { dev_err(dev, @@ -181,10 +285,47 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) } } - pipes[num_pipes].hw_videoport = i; - pipes[num_pipes].bridge = bridge; - pipes[num_pipes].enc_type = enc_type; - num_pipes++; + if (feat->output_port_bus_type[i] == DISPC_VP_OLDI) { + if (++num_oldi == 1) { + /* Setting up pipe parameters when 1st OLDI port is detected */ + + pipes[num_pipes].hw_videoport = i; + pipes[num_pipes].enc_type = enc_type; + + /* + * Saving the pipe index in case its required for + * 2nd OLDI Port. + */ + oldi_pipe_index = num_pipes; + + /* + * No additional pipe is required for the 2nd OLDI + * port, because the 2nd Encoder -> Bridge connection + * is the part of the first OLDI Port pipe. + * + * num_pipes will only be incremented when the first + * OLDI port is discovered. + */ + num_pipes++; + } + + /* + * Bridge is required to be added only if the detected + * port is the first OLDI port or a subsequent port in + * Clone Mode. + */ + if (oldi_mode == OLDI_SINGLE_LINK_CLONE_MODE || + num_oldi == 1) { + pipes[oldi_pipe_index].bridge[num_oldi - 1] = bridge; + pipes[oldi_pipe_index].num_bridges++; + } + } else { + pipes[num_pipes].hw_videoport = i; + pipes[num_pipes].bridge[0] = bridge; + pipes[num_pipes].num_bridges++; + pipes[num_pipes].enc_type = enc_type; + num_pipes++; + } } /* all planes can be on any crtc */ @@ -196,6 +337,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) struct tidss_plane *tplane; struct tidss_crtc *tcrtc; struct drm_encoder *enc; + u32 possible_clones = 0; u32 hw_plane_id = feat->vid_order[tidss->num_planes]; int ret; @@ -218,16 +360,24 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc; - enc = tidss_encoder_create(tidss, pipes[i].enc_type, - 1 << tcrtc->crtc.index); - if (IS_ERR(enc)) { - dev_err(tidss->dev, "encoder create failed\n"); - return PTR_ERR(enc); - } + for (j = 0; j < pipes[i].num_bridges; j++) { + if (pipes[i].num_bridges > 1) + possible_clones = (((1 << pipes[i].num_bridges) - 1) + << num_encoders); + + enc = tidss_encoder_create(tidss, pipes[i].enc_type, + 1 << tcrtc->crtc.index, + possible_clones); + if (IS_ERR(enc)) { + dev_err(tidss->dev, "encoder create failed\n"); + return PTR_ERR(enc); + } - ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0); - if (ret) - return ret; + ret = drm_bridge_attach(enc, pipes[i].bridge[j], NULL, 0); + if (ret) + return ret; + } + num_encoders += pipes[i].num_bridges; } /* create overlay planes of the leftover planes */