Received: by 2002:a05:7412:a9a2:b0:e2:908c:2ebd with SMTP id o34csp2430106rdh; Sun, 29 Oct 2023 16:19:33 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEx6OnuBzL+uhUqZjbXQf+j+hQxRppGW+09mVp6Ivwbj7y+17V2Vp8Lb/rwetXTrSDgcTXB X-Received: by 2002:a05:6808:1412:b0:3af:6595:e53 with SMTP id w18-20020a056808141200b003af65950e53mr11978098oiv.13.1698621572854; Sun, 29 Oct 2023 16:19:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698621572; cv=none; d=google.com; s=arc-20160816; b=frl/0kk/6lM4C2PrBNhwz8z4rRH3biNjZS5VmKlr6TzyxCLeAvSITt/C47OlEfY4Mu NK1CnFxieJWKiybwk1xrS+z4dc4h5+jo47KiRlej4dlhXynRUMBPlcJn4v7B2S/mshZu BrcYdsSlmCqvWOwzbQhm33UxmKMUzYcDaSv92ZvTlmnrjfEWwhRlolNFXDbYEmr4bnZs bI8tv8Z3FVuUoqe99Nr1zsQLXvdGgSCpEg2oNavPMy2qxcLkM1xi75urbeyrC4Hb7OOa d+M53S9nKlY3S/TJ8Y7q6HoOB4CWJ64N7VOkuq5GAW2+cFRtEu+7nR6j3eaTA0lDHe/t o+OA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=+DH4rv/4NY/FIjo3AzTUCDCldF7TtP+gmNlRvM2z38c=; fh=gs5Ca2Pm2HjjtfjDl5SRKddX4w6nlfz+7vN7wN7EBUI=; b=kVDEBXT51tFDX58P7P9Srh+UT6RtxyXDnlqZrMRdh5CUtdJwAsFm0sWwO+QAxLXi1L cGT2NnPmzKE6d5JIfv0EGEVlf18zwRrhmHpIuLSlT+C1IrHskWrhBnaGdQDu7omzpRoh z6alVZTu8/nY8enJSbB3LTq/tkcz7CGsHT83eHm4CZ/5C4p/PYcQtWGZ/XeZuSqYG6pI 46hrRhxI3Ho2NAveh3KL0uTBndswT6oSnLSMvPdY/kY0csq0Tj1MGcTcYo/dRmnQRwaG g2T3sLT1vTLqkn9+xizeNVM048iol4kGtIfr5XH8uThKLE7wKdpn5E+YZI6mIDu51Bjs af2A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=E8InmPS3; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from pete.vger.email (pete.vger.email. [2620:137:e000::3:6]) by mx.google.com with ESMTPS id e6-20020a056a001a8600b006b7e321c0aesi4161852pfv.40.2023.10.29.16.19.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 16:19:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) client-ip=2620:137:e000::3:6; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=E8InmPS3; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 8EF838088566; Sun, 29 Oct 2023 16:19:26 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230519AbjJ2XTE (ORCPT + 99 others); Sun, 29 Oct 2023 19:19:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41242 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231932AbjJ2XSe (ORCPT ); Sun, 29 Oct 2023 19:18:34 -0400 Received: from mail-yw1-x1133.google.com (mail-yw1-x1133.google.com [IPv6:2607:f8b0:4864:20::1133]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8C04C3E523 for ; Sun, 29 Oct 2023 16:10:21 -0700 (PDT) Received: by mail-yw1-x1133.google.com with SMTP id 00721157ae682-5a7afd45199so34841837b3.0 for ; Sun, 29 Oct 2023 16:10:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1698621020; x=1699225820; darn=vger.kernel.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=+DH4rv/4NY/FIjo3AzTUCDCldF7TtP+gmNlRvM2z38c=; b=E8InmPS3BNDzg7zy6BJ5+mp7DxAPZU3b7Kwa3aLfV8tODQlt75Lb3WheWheM3GBqaX jPcff7Grd86iFjePZ796uHTSLxqIOeobXAYoCOlcYotiAHVKTBSjavV18lzSH7ysQJ4n AGEoIDTxAkRf9YqKFtRc99j+tDCXFvvXqAvb8eXqFVYoVxnnjSTrAv8+kfyk1wU/7yKZ xppq0/5LDChODFgB3WSJEzwkOqSWVk+3r4kW5KtoaH+/hym1WYFwv2yQXClQCqiJvg04 yHUV5hH+Rl5DCBLTBM+Ha01DNg/jyNfxDObIMZiZv+L2c/QZOLvh0e650+zq/3geZzAJ ECvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698621020; x=1699225820; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=+DH4rv/4NY/FIjo3AzTUCDCldF7TtP+gmNlRvM2z38c=; b=NUyqHOVGBqmXDmW7QjjHF8xO4HoRdTQimBU8jRm6ee/owMwjP/tQaCSsXdV22q3bdh 0zqaX0H/njb3fU3D/7HFV/3TNbhl0BuzS3d3a2SBcX1yFhzPR1QkG0VsfrD7MPFs3d/P n8cbDY3QlOa47Pfv6f0APS2mSaX9XMyFs1+KEg/w46JIXCLtlmel8p0AW25LJSKPxEsv 5YxbajHwASfMVFuEpJG9sBjWRs3els6c1BEVyuVdo8Ve0plhgUQXIMFC8S5/CxHZQ0AC xdZLgSDKazEtx/ZoqzuX8Ac98utepnma4F3xmpTbxAJ+oYDcDBkP+ixW+1APP8kT20rl EimA== X-Gm-Message-State: AOJu0YxzBU8nxLVtQSrQKTiRO5YqMfHaowjoTwIRWSM2OMzygDCmzmdE YPqwoKktmVJibUXekDlyK0SKlVnW/kDMiLuM9gvQhw== X-Received: by 2002:a25:d02:0:b0:d9d:8fb:d29f with SMTP id 2-20020a250d02000000b00d9d08fbd29fmr7060505ybn.45.1698621020163; Sun, 29 Oct 2023 16:10:20 -0700 (PDT) MIME-Version: 1.0 References: <20231029194607.379459-1-suijingfeng@loongson.cn> <20231029194607.379459-4-suijingfeng@loongson.cn> In-Reply-To: <20231029194607.379459-4-suijingfeng@loongson.cn> From: Dmitry Baryshkov Date: Mon, 30 Oct 2023 01:10:08 +0200 Message-ID: Subject: Re: [PATCH 3/8] drm/loongson: Allow attach drm bridge driver by calling lsdc_output_init() To: Sui Jingfeng Cc: Maxime Ripard , Thomas Zimmermann , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Sun, 29 Oct 2023 16:19:26 -0700 (PDT) On Sun, 29 Oct 2023 at 21:46, Sui Jingfeng wrote: > > Move the sharable subroutine into lsdc_output.c and refactor. > > Signed-off-by: Sui Jingfeng > --- > drivers/gpu/drm/loongson/Makefile | 1 + > drivers/gpu/drm/loongson/lsdc_drv.h | 17 - > drivers/gpu/drm/loongson/lsdc_output.c | 640 +++++++++++++++++++++++++ > drivers/gpu/drm/loongson/lsdc_output.h | 52 +- This diffstat doesn't match patch description. > 4 files changed, 691 insertions(+), 19 deletions(-) > create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c > > diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile > index 1459d19b2c90..393709e686aa 100644 > --- a/drivers/gpu/drm/loongson/Makefile > +++ b/drivers/gpu/drm/loongson/Makefile > @@ -9,6 +9,7 @@ loongson-y := \ > lsdc_gfxpll.o \ > lsdc_i2c.o \ > lsdc_irq.o \ > + lsdc_output.o \ > lsdc_output_7a1000.o \ > lsdc_output_7a2000.o \ > lsdc_plane.o \ > diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h > index 335953c988d1..46ba9b88a30d 100644 > --- a/drivers/gpu/drm/loongson/lsdc_drv.h > +++ b/drivers/gpu/drm/loongson/lsdc_drv.h > @@ -175,23 +175,6 @@ struct lsdc_cursor { > struct lsdc_device *ldev; > }; > > -struct lsdc_output { > - struct drm_encoder encoder; > - struct drm_connector connector; > -}; > - > -static inline struct lsdc_output * > -connector_to_lsdc_output(struct drm_connector *connector) > -{ > - return container_of(connector, struct lsdc_output, connector); > -} > - > -static inline struct lsdc_output * > -encoder_to_lsdc_output(struct drm_encoder *encoder) > -{ > - return container_of(encoder, struct lsdc_output, encoder); > -} > - > struct lsdc_display_pipe { > struct lsdc_crtc crtc; > struct lsdc_primary primary; > diff --git a/drivers/gpu/drm/loongson/lsdc_output.c b/drivers/gpu/drm/loongson/lsdc_output.c > new file mode 100644 > index 000000000000..8262c3f91ebe > --- /dev/null > +++ b/drivers/gpu/drm/loongson/lsdc_output.c > @@ -0,0 +1,640 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include "lsdc_drv.h" > +#include "lsdc_output.h" > + > +/* This file contain shared subroutines for the output part */ > + > +/* Usable for generic DVO, VGA and buitl-in HDMI connector */ > + > +static int lsdc_connector_get_modes(struct drm_connector *connector) > +{ > + unsigned int num = 0; > + struct edid *edid; > + > + if (connector->ddc) { > + edid = drm_get_edid(connector, connector->ddc); > + if (edid) { > + drm_connector_update_edid_property(connector, edid); > + num = drm_add_edid_modes(connector, edid); > + kfree(edid); > + } > + > + return num; drm_connector_helper_get_modes_from_ddc() > + } > + > + num = drm_add_modes_noedid(connector, 1920, 1200); > + > + drm_set_preferred_mode(connector, 1024, 768); > + > + return num; > +} > + > +static struct drm_encoder * > +lsdc_connector_get_best_encoder(struct drm_connector *connector, > + struct drm_atomic_state *state) > +{ > + struct lsdc_output *output = connector_to_lsdc_output(connector); > + > + return &output->encoder; Do you really need it? Does drm_connector_get_single_encoder() work for your case? > +} > + > +const struct drm_connector_helper_funcs lsdc_connector_helper_funcs = { > + .atomic_best_encoder = lsdc_connector_get_best_encoder, > + .get_modes = lsdc_connector_get_modes, > +}; > + > +static enum drm_connector_status > +lsdc_connector_detect(struct drm_connector *connector, bool force) > +{ > + struct i2c_adapter *ddc = connector->ddc; > + > + if (ddc) { > + if (drm_probe_ddc(ddc)) > + return connector_status_connected; > + > + return connector_status_disconnected; > + } > + > + return connector_status_unknown; > +} > + > +const struct drm_connector_funcs lsdc_connector_funcs = { > + .detect = lsdc_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state > +}; > + > +/* debugfs */ > + > +#define LSDC_HDMI_REG(i, reg) { \ > + .name = __stringify_1(LSDC_HDMI##i##_##reg##_REG), \ > + .offset = LSDC_HDMI##i##_##reg##_REG, \ > +} > + > +static int lsdc_hdmi_regs_show(struct seq_file *m, void *data) > +{ > + struct drm_info_node *node = (struct drm_info_node *)m->private; > + struct drm_device *ddev = node->minor->dev; > + struct lsdc_device *ldev = to_lsdc(ddev); > + const struct lsdc_reg32 *preg; > + > + preg = (const struct lsdc_reg32 *)node->info_ent->data; > + > + while (preg->name) { > + u32 offset = preg->offset; > + > + seq_printf(m, "%s (0x%04x): 0x%08x\n", > + preg->name, offset, lsdc_rreg32(ldev, offset)); > + ++preg; > + } > + > + return 0; > +} > + > +/* LSDC built-in HDMI encoder, connected with display pipe 0 */ > + > +static const struct lsdc_reg32 lsdc_hdmi_regs_pipe0[] = { > + LSDC_HDMI_REG(0, ZONE), > + LSDC_HDMI_REG(0, INTF_CTRL), > + LSDC_HDMI_REG(0, PHY_CTRL), > + LSDC_HDMI_REG(0, PHY_PLL), > + LSDC_HDMI_REG(0, AVI_INFO_CRTL), > + LSDC_HDMI_REG(0, PHY_CAL), > + LSDC_HDMI_REG(0, AUDIO_PLL_LO), > + LSDC_HDMI_REG(0, AUDIO_PLL_HI), > + {NULL, 0}, /* MUST be {NULL, 0} terminated */ > +}; > + > +static const struct drm_info_list lsdc_pipe0_hdmi_debugfs_files[] = { > + { "regs", lsdc_hdmi_regs_show, 0, (void *)lsdc_hdmi_regs_pipe0 }, > +}; > + > +static enum drm_connector_status > +lsdc_pipe0_hdmi_connector_detect(struct drm_connector *connector, bool force) > +{ > + struct lsdc_device *ldev = to_lsdc(connector->dev); > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG); > + > + if (val & HDMI0_HPD_FLAG) > + return connector_status_connected; > + > + return connector_status_disconnected; > +} > + > +static void lsdc_pipe0_hdmi_late_register(struct drm_connector *connector, > + struct dentry *root) > +{ > + struct drm_device *ddev = connector->dev; > + struct drm_minor *minor = ddev->primary; > + > + drm_debugfs_create_files(lsdc_pipe0_hdmi_debugfs_files, > + ARRAY_SIZE(lsdc_pipe0_hdmi_debugfs_files), > + root, minor); > +} > + > +const struct drm_connector_funcs lsdc_pipe0_hdmi_connector_funcs = { > + .detect = lsdc_pipe0_hdmi_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > + .debugfs_init = lsdc_pipe0_hdmi_late_register, > +}; > + > +/* LSDC built-in HDMI connector, connected with display pipe 1 */ > + > +static enum drm_connector_status > +lsdc_pipe1_hdmi_connector_detect(struct drm_connector *connector, bool force) > +{ > + struct lsdc_device *ldev = to_lsdc(connector->dev); > + u32 val; > + > + val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG); > + > + if (val & HDMI1_HPD_FLAG) > + return connector_status_connected; > + > + return connector_status_disconnected; > +} > + > +static const struct lsdc_reg32 lsdc_pipe1_hdmi_encoder_regs[] = { > + LSDC_HDMI_REG(1, ZONE), > + LSDC_HDMI_REG(1, INTF_CTRL), > + LSDC_HDMI_REG(1, PHY_CTRL), > + LSDC_HDMI_REG(1, PHY_PLL), > + LSDC_HDMI_REG(1, AVI_INFO_CRTL), > + LSDC_HDMI_REG(1, PHY_CAL), > + LSDC_HDMI_REG(1, AUDIO_PLL_LO), > + LSDC_HDMI_REG(1, AUDIO_PLL_HI), > + {NULL, 0}, /* MUST be {NULL, 0} terminated */ > +}; > + > +static const struct drm_info_list lsdc_pipe1_hdmi_debugfs_files[] = { > + { "regs", lsdc_hdmi_regs_show, 0, (void *)lsdc_pipe1_hdmi_encoder_regs }, > +}; > + > +static void lsdc_pipe1_hdmi_late_register(struct drm_connector *connector, > + struct dentry *root) > +{ > + struct drm_device *ddev = connector->dev; > + struct drm_minor *minor = ddev->primary; > + > + drm_debugfs_create_files(lsdc_pipe1_hdmi_debugfs_files, > + ARRAY_SIZE(lsdc_pipe1_hdmi_debugfs_files), > + root, minor); > +} > + > +const struct drm_connector_funcs lsdc_pipe1_hdmi_connector_funcs = { > + .detect = lsdc_pipe1_hdmi_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > + .debugfs_init = lsdc_pipe1_hdmi_late_register, > +}; > + > +/* > + * Fout = M * in_khz > + * > + * M = (4 * LF) / (IDF * ODF) > + * > + * IDF: Input Division Factor > + * ODF: Output Division Factor > + * LF: Loop Factor > + * M: Required Mult > + * > + * +--------------------------------------------------------+ > + * | in_khz | M | IDF | LF | ODF | Fout(Mhz) | > + * |-------------------+----+-----+----+-----+--------------| > + * | 170000 ~ 340000 | 10 | 16 | 40 | 1 | 1700 ~ 3400 | > + * | 85000 ~ 170000 | 10 | 8 | 40 | 2 | 850 ~ 1700 | > + * | 42500 ~ 85000 | 10 | 4 | 40 | 4 | 425 ~ 850 | > + * | 21250 ~ 42500 | 10 | 2 | 40 | 8 | 212.5 ~ 425 | > + * | 20000 ~ 21250 | 10 | 1 | 40 | 16 | 200 ~ 212.5 | > + * +--------------------------------------------------------+ > + */ > +static void lsdc_hdmi_phy_pll_config(struct drm_device *ddev, > + int in_khz, > + unsigned int pipe) > +{ > + struct lsdc_device *ldev = to_lsdc(ddev); > + int count = 0; > + u32 val; > + > + /* Firstly, disable phy pll */ > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, pipe, 0x0); > + > + /* > + * Most of time, loongson HDMI require M = 10 > + * for example, 10 = (4 * 40) / (8 * 2) > + * here, write "1" to the ODF will get "2" > + */ > + > + if (in_khz >= 170000) > + val = (16 << HDMI_PLL_IDF_SHIFT) | > + (40 << HDMI_PLL_LF_SHIFT) | > + (0 << HDMI_PLL_ODF_SHIFT); > + else if (in_khz >= 85000) > + val = (8 << HDMI_PLL_IDF_SHIFT) | > + (40 << HDMI_PLL_LF_SHIFT) | > + (1 << HDMI_PLL_ODF_SHIFT); > + else if (in_khz >= 42500) > + val = (4 << HDMI_PLL_IDF_SHIFT) | > + (40 << HDMI_PLL_LF_SHIFT) | > + (2 << HDMI_PLL_ODF_SHIFT); > + else if (in_khz >= 21250) > + val = (2 << HDMI_PLL_IDF_SHIFT) | > + (40 << HDMI_PLL_LF_SHIFT) | > + (3 << HDMI_PLL_ODF_SHIFT); > + else > + val = (1 << HDMI_PLL_IDF_SHIFT) | > + (40 << HDMI_PLL_LF_SHIFT) | > + (4 << HDMI_PLL_ODF_SHIFT); > + > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, pipe, val); > + > + val |= HDMI_PLL_ENABLE; > + > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, pipe, val); > + > + udelay(2); > + > + drm_dbg(ddev, "Input frequency of HDMI-%u: %d kHz\n", pipe, in_khz); > + > + /* Wait hdmi phy pll lock */ > + do { > + val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, pipe); > + > + if (val & HDMI_PLL_LOCKED) { > + drm_dbg(ddev, "Setting HDMI-%u PLL take %d cycles\n", > + pipe, count); > + break; > + } > + ++count; > + } while (count < 1000); > + > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CAL_REG, pipe, 0x0f000ff0); > + > + if (count >= 1000) > + drm_err(ddev, "Setting HDMI-%u PLL failed\n", pipe); > +} > + > +static int lsdc_hdmi_phy_set_avi_infoframe(struct drm_encoder *encoder, > + struct drm_connector *connector, > + struct drm_display_mode *mode, > + unsigned int index) > +{ > + struct drm_device *ddev = encoder->dev; > + struct lsdc_device *ldev = to_lsdc(ddev); > + struct hdmi_avi_infoframe infoframe; > + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; > + unsigned char *ptr = &buffer[HDMI_INFOFRAME_HEADER_SIZE]; > + unsigned int content0, content1, content2, content3; > + int err; > + > + err = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, > + connector, > + mode); > + if (err < 0) { > + drm_err(ddev, "failed to setup AVI infoframe: %d\n", err); > + return err; > + } > + > + /* Fixed infoframe configuration not linked to the mode */ > + infoframe.colorspace = HDMI_COLORSPACE_RGB; > + infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; > + infoframe.colorimetry = HDMI_COLORIMETRY_NONE; > + > + err = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer)); > + if (err < 0) { > + drm_err(ddev, "failed to pack AVI infoframe: %d\n", err); > + return err; > + } > + > + content0 = *(unsigned int *)ptr; > + content1 = *(ptr + 4); > + content2 = *(unsigned int *)(ptr + 5); > + content3 = *(unsigned int *)(ptr + 9); > + > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT0, index, content0); > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT1, index, content1); > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT2, index, content2); > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT3, index, content3); > + > + lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_INFO_CRTL_REG, index, > + AVI_PKT_ENABLE | AVI_PKT_UPDATE); > + > + drm_dbg(ddev, "Update HDMI-%u avi infoframe\n", index); > + > + return 0; > +} > + > +/* Built-in HDMI encoder funcs on display pipe 0 */ > + > +static void lsdc_pipe0_hdmi_encoder_reset(struct drm_encoder *encoder) > +{ > + struct drm_device *ddev = encoder->dev; > + struct lsdc_device *ldev = to_lsdc(ddev); > + u32 val; > + > + val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN; > + lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, val); > + > + /* Using built-in GPIO emulated I2C instead of the hardware I2C */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI0_INTF_CTRL_REG, HW_I2C_EN); > + > + /* Help the HDMI phy get out of reset state */ > + lsdc_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, HDMI_PHY_RESET_N); > + > + drm_dbg(ddev, "%s reset\n", encoder->name); > + > + mdelay(20); > +} > + > +const struct drm_encoder_funcs lsdc_pipe0_hdmi_encoder_funcs = { > + .reset = lsdc_pipe0_hdmi_encoder_reset, > + .destroy = drm_encoder_cleanup, > +}; > + > +/* Built-in HDMI encoder funcs on display pipe 1 */ All pipe 1 code looks like a pipe0, just the registers were changed. Could you please refactor that to use a single instance of all functions and pass pipe id through the data struct? Then you can use macros to determine whether to use pipe 0 or pipe 1 register. > + > +static void lsdc_pipe1_hdmi_encoder_reset(struct drm_encoder *encoder) > +{ > + struct drm_device *ddev = encoder->dev; > + struct lsdc_device *ldev = to_lsdc(ddev); > + u32 val; > + > + val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN; > + lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, val); > + > + /* Using built-in GPIO emulated I2C instead of the hardware I2C */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI1_INTF_CTRL_REG, HW_I2C_EN); > + > + /* Help the HDMI phy get out of reset state */ > + lsdc_wreg32(ldev, LSDC_HDMI1_PHY_CTRL_REG, HDMI_PHY_RESET_N); > + > + drm_dbg(ddev, "%s reset\n", encoder->name); > + > + mdelay(20); > +} > + > +const struct drm_encoder_funcs lsdc_pipe1_hdmi_encoder_funcs = { > + .reset = lsdc_pipe1_hdmi_encoder_reset, > + .destroy = drm_encoder_cleanup, > +}; > + > +/* Built-in DVO encoder helper funcs */ > + > +static void lsdc_dvo_atomic_disable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > +} > + > +static void lsdc_dvo_atomic_enable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > +} > + > +static void lsdc_dvo_atomic_modeset(struct drm_encoder *encoder, > + struct drm_crtc_state *crtc_state, > + struct drm_connector_state *conn_state) > +{ > +} You do not need these empty callbacks, do you? > + > +const struct drm_encoder_helper_funcs lsdc_encoder_helper_funcs = { > + .atomic_disable = lsdc_dvo_atomic_disable, > + .atomic_enable = lsdc_dvo_atomic_enable, > + .atomic_mode_set = lsdc_dvo_atomic_modeset, > +}; > + > +/* Built-in HDMI encoder helper funcs on display pipe 0 */ > + > +static void lsdc_pipe0_hdmi_atomic_disable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > + struct lsdc_device *ldev = to_lsdc(encoder->dev); > + > + /* Disable the HDMI PHY */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI0_PHY_CTRL_REG, HDMI_PHY_EN); > + > + /* Disable the HDMI interface */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI0_INTF_CTRL_REG, HDMI_INTERFACE_EN); > +} > + > +static void lsdc_pipe0_hdmi_atomic_enable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > + struct lsdc_device *ldev = to_lsdc(encoder->dev); > + u32 val; > + > + /* datasheet say it should larger than 48 */ > + val = 64 << HDMI_H_ZONE_IDLE_SHIFT | 64 << HDMI_V_ZONE_IDLE_SHIFT; > + lsdc_wreg32(ldev, LSDC_HDMI0_ZONE_REG, val); > + > + val = HDMI_PHY_TERM_STATUS | > + HDMI_PHY_TERM_DET_EN | > + HDMI_PHY_TERM_H_EN | > + HDMI_PHY_TERM_L_EN | > + HDMI_PHY_RESET_N | > + HDMI_PHY_EN; > + lsdc_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, val); > + > + udelay(2); > + > + val = HDMI_CTL_PERIOD_MODE | > + HDMI_AUDIO_EN | > + HDMI_PACKET_EN | > + HDMI_INTERFACE_EN | > + (8 << HDMI_VIDEO_PREAMBLE_SHIFT); > + lsdc_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, val); > +} > + > +static void lsdc_pipe0_hdmi_atomic_modeset(struct drm_encoder *encoder, > + struct drm_crtc_state *crtc_state, > + struct drm_connector_state *conn_state) > +{ > + struct lsdc_output *output = encoder_to_lsdc_output(encoder); > + struct drm_device *ddev = encoder->dev; > + struct drm_display_mode *mode = &crtc_state->mode; > + > + lsdc_hdmi_phy_pll_config(ddev, mode->clock, 0); > + > + lsdc_hdmi_phy_set_avi_infoframe(encoder, &output->connector, mode, 0); > + > + drm_dbg(ddev, "%s modeset finished\n", encoder->name); > +} > + > +const struct drm_encoder_helper_funcs lsdc_pipe0_hdmi_encoder_helper_funcs = { > + .atomic_disable = lsdc_pipe0_hdmi_atomic_disable, > + .atomic_enable = lsdc_pipe0_hdmi_atomic_enable, > + .atomic_mode_set = lsdc_pipe0_hdmi_atomic_modeset, > +}; > + > +/* Built-in HDMI encoder helper funcs on display pipe 1 */ > + > +static void lsdc_pipe1_hdmi_atomic_disable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > + struct lsdc_device *ldev = to_lsdc(encoder->dev); > + > + /* Disable the HDMI PHY */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI1_PHY_CTRL_REG, HDMI_PHY_EN); > + > + /* Disable the HDMI interface */ > + lsdc_ureg32_clr(ldev, LSDC_HDMI1_INTF_CTRL_REG, HDMI_INTERFACE_EN); > +} > + > +static void lsdc_pipe1_hdmi_atomic_enable(struct drm_encoder *encoder, > + struct drm_atomic_state *state) > +{ > + struct lsdc_device *ldev = to_lsdc(encoder->dev); > + u32 val; > + > + /* datasheet say it should larger than 48 */ > + val = 64 << HDMI_H_ZONE_IDLE_SHIFT | 64 << HDMI_V_ZONE_IDLE_SHIFT; > + lsdc_wreg32(ldev, LSDC_HDMI1_ZONE_REG, val); > + > + val = HDMI_PHY_TERM_STATUS | > + HDMI_PHY_TERM_DET_EN | > + HDMI_PHY_TERM_H_EN | > + HDMI_PHY_TERM_L_EN | > + HDMI_PHY_RESET_N | > + HDMI_PHY_EN; > + lsdc_wreg32(ldev, LSDC_HDMI1_PHY_CTRL_REG, val); > + > + udelay(2); > + > + val = HDMI_CTL_PERIOD_MODE | > + HDMI_AUDIO_EN | > + HDMI_PACKET_EN | > + HDMI_INTERFACE_EN | > + (8 << HDMI_VIDEO_PREAMBLE_SHIFT); > + lsdc_wreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG, val); > +} > + > +static void lsdc_pipe1_hdmi_atomic_modeset(struct drm_encoder *encoder, > + struct drm_crtc_state *crtc_state, > + struct drm_connector_state *conn_state) > +{ > + struct lsdc_output *output = encoder_to_lsdc_output(encoder); > + struct drm_device *ddev = encoder->dev; > + struct drm_display_mode *mode = &crtc_state->mode; > + > + lsdc_hdmi_phy_pll_config(ddev, mode->clock, 1); > + > + lsdc_hdmi_phy_set_avi_infoframe(encoder, &output->connector, mode, 1); > + > + drm_dbg(ddev, "%s modeset finished\n", encoder->name); > +} > + > +const struct drm_encoder_helper_funcs lsdc_pipe1_hdmi_encoder_helper_funcs = { > + .atomic_disable = lsdc_pipe1_hdmi_atomic_disable, > + .atomic_enable = lsdc_pipe1_hdmi_atomic_enable, > + .atomic_mode_set = lsdc_pipe1_hdmi_atomic_modeset, > +}; > + > +int lsdc_encoder_init(struct drm_device *ddev, > + struct lsdc_output *output, > + unsigned int pipe) > +{ > + const struct lsdc_output_desc *descp = output->descp; > + struct drm_encoder *encoder = &output->encoder; > + int ret; > + > + ret = drm_encoder_init(ddev, > + encoder, > + descp->encoder_funcs, > + descp->encoder_type, > + descp->name); > + if (ret) > + return ret; > + > + encoder->possible_crtcs = BIT(pipe); > + > + drm_encoder_helper_add(encoder, descp->encoder_helper_funcs); > + > + return 0; > +} > + > +int lsdc_connector_init(struct drm_device *ddev, > + struct lsdc_output *output, > + struct i2c_adapter *ddc, > + unsigned int pipe) > +{ > + const struct lsdc_output_desc *descp = output->descp; > + struct drm_connector *connector = &output->connector; > + int ret; > + > + ret = drm_connector_init_with_ddc(ddev, > + connector, > + descp->connector_funcs, > + descp->connector_type, > + ddc); > + if (ret) > + return ret; > + > + drm_connector_helper_add(connector, descp->connector_helper_funcs); > + > + drm_connector_attach_encoder(connector, &output->encoder); > + > + connector->polled = DRM_CONNECTOR_POLL_CONNECT | > + DRM_CONNECTOR_POLL_DISCONNECT; > + > + connector->interlace_allowed = 0; > + connector->doublescan_allowed = 0; > + > + drm_info(ddev, "DisplayPipe-%u has %s\n", pipe, descp->name); > + > + return 0; > +} > + > +/* > + * A common, sharable subroutine for the initialization of output part. > + * If there is external non-transparent display bridge chip on the display > + * pipe, we will attach it. Otherwise, the output is simple, we will just > + * initial a connector for it. > + */ > +int lsdc_output_init(struct drm_device *ddev, > + struct lsdc_display_pipe *dispipe, > + struct i2c_adapter *ddc, > + unsigned int pipe) > +{ > + struct lsdc_output *output = &dispipe->output; > + int ret; > + > + ret = lsdc_encoder_init(ddev, output, pipe); > + if (ret) > + return ret; > + > + if (output->bridge) { > + ret = drm_bridge_attach(&output->encoder, output->bridge, > + NULL, 0); Since you are creating the connector manually, you should pass DRM_BRIDGE_ATTACH_NO_CONNECTOR here. > + if (ret) { > + drm_err(ddev, "Attach display bridge failed\n"); > + ret = lsdc_connector_init(ddev, output, ddc, pipe); > + } > + } else { > + ret = lsdc_connector_init(ddev, output, ddc, pipe); > + } > + > + return ret; > +} > diff --git a/drivers/gpu/drm/loongson/lsdc_output.h b/drivers/gpu/drm/loongson/lsdc_output.h > index 097789051a1d..a37a72687bdf 100644 > --- a/drivers/gpu/drm/loongson/lsdc_output.h > +++ b/drivers/gpu/drm/loongson/lsdc_output.h > @@ -6,16 +6,64 @@ > #ifndef __LSDC_OUTPUT_H__ > #define __LSDC_OUTPUT_H__ > > -#include "lsdc_drv.h" > +#include > +#include > +#include > + > +struct lsdc_output_desc { > + u32 pipe; > + u32 encoder_type; > + u32 connector_type; > + const struct drm_encoder_funcs *encoder_funcs; > + const struct drm_encoder_helper_funcs *encoder_helper_funcs; > + const struct drm_connector_funcs *connector_funcs; > + const struct drm_connector_helper_funcs *connector_helper_funcs; > + const char name[32]; > +}; > + > +struct lsdc_output { > + struct drm_encoder encoder; > + struct drm_connector connector; > + struct drm_bridge *bridge; > + const struct lsdc_output_desc *descp; > +}; > + > +static inline struct lsdc_output * > +connector_to_lsdc_output(struct drm_connector *connector) > +{ > + return container_of(connector, struct lsdc_output, connector); > +} > + > +static inline struct lsdc_output * > +encoder_to_lsdc_output(struct drm_encoder *encoder) > +{ > + return container_of(encoder, struct lsdc_output, encoder); > +} > + > +extern const struct drm_connector_funcs lsdc_connector_funcs; > +extern const struct drm_connector_funcs lsdc_pipe0_hdmi_connector_funcs; > +extern const struct drm_connector_funcs lsdc_pipe1_hdmi_connector_funcs; > +extern const struct drm_connector_helper_funcs lsdc_connector_helper_funcs; > + > +extern const struct drm_encoder_funcs lsdc_pipe0_hdmi_encoder_funcs; > +extern const struct drm_encoder_funcs lsdc_pipe1_hdmi_encoder_funcs; > +extern const struct drm_encoder_helper_funcs lsdc_encoder_helper_funcs; > +extern const struct drm_encoder_helper_funcs lsdc_pipe0_hdmi_encoder_helper_funcs; > +extern const struct drm_encoder_helper_funcs lsdc_pipe1_hdmi_encoder_helper_funcs; > > int ls7a1000_output_init(struct drm_device *ddev, > struct lsdc_display_pipe *dispipe, > struct i2c_adapter *ddc, > unsigned int index); > > -int ls7a2000_output_init(struct drm_device *ldev, > +int ls7a2000_output_init(struct drm_device *ddev, > struct lsdc_display_pipe *dispipe, > struct i2c_adapter *ddc, > unsigned int index); > > +int lsdc_output_init(struct drm_device *ddev, > + struct lsdc_display_pipe *dispipe, > + struct i2c_adapter *ddc, > + unsigned int pipe); > + > #endif > -- > 2.34.1 > -- With best wishes Dmitry