From patchwork Sun Oct 29 19:46:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159410 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831919vqb; Sun, 29 Oct 2023 12:47:22 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFKRLZwmSqTlup62jXcaiir6HbgVFwhPyqA86NymBW6UGOBbEVOqCM7yUcTJDzN8inHcr/B X-Received: by 2002:a05:6358:51d8:b0:168:dc4a:c96f with SMTP id 24-20020a05635851d800b00168dc4ac96fmr7003225rwl.16.1698608842169; Sun, 29 Oct 2023 12:47:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608842; cv=none; d=google.com; s=arc-20160816; b=If+SrsmgZAeI5InEvaKecE4I6D+j+G3CdO0MdlO3uE/GGjPk/RrYWRJiXaGht5N4TZ 26Lja7pcgraS3gOJfX74zVROSGdq+17usPB8fLNMZRoMl9rR8/KVXf8jgnbgPeg1i0yj Fo3uJL+c9Kf2Xg3Ppv+jqTxH51xNJLk62wv8yJ16B175t5I23HCVwJ2XdQPV7yrPyBpO t8d4dqGgkQhoHFnWaVpAjzcS55LtGlNCR15y4xkhOFHkdRN7/SGHhTxfbsQ0e3y3B+4d IEoThv8oNGdZpRMsxQx+DViZt6m6V+LaV8zxM6CX2EhoGMD5T9T9WOyF9x24Zgg1zox5 ZjXg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=bTFv0DLkGqWitqyUiHxoAAmRNSUI7IryMd9xcdNu1QE=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=e9XeoJlh7m6sc3id8uHdX2afJ/x9Tg2IDToag7be4zErGAcXm6ZEEFemDH1xg+FtJd TV77q91lEoPuO2V26rqNtsnt9ERwldXo5PfOr14c9DspD/iWoAtenpQNCE3CAmt12Dr2 ETDkN172AHUDVYDJL5pQi2UJP0A1EDE+z1aRf2lpasC4jq/nf8mNiWH6L/Fd/qOPlGmX j2Qv0TO2r6AOa0I4xmhytlNtfG/JayAEteDoNJ4yJeZwlzTsK9yF8UPFJD+LUY5qN8OP /c4JLviU1CwVE/sDy4hTEm7RylD1s11U2Daa8zpOgZ4QZrPRGw55WD6Ld/eh1MTd7k4q dJWg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from groat.vger.email (groat.vger.email. [2620:137:e000::3:5]) by mx.google.com with ESMTPS id 6-20020a17090a004600b00262e3c1c1efsi3962206pjb.79.2023.10.29.12.47.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:22 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) client-ip=2620:137:e000::3:5; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by groat.vger.email (Postfix) with ESMTP id EBA21808722D; Sun, 29 Oct 2023 12:47:11 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at groat.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230386AbjJ2TqS (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54290 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229512AbjJ2TqQ (ORCPT ); Sun, 29 Oct 2023 15:46:16 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 8ADC79B for ; Sun, 29 Oct 2023 12:46:12 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8AxV_GCtj5lsps1AA--.39947S3; Mon, 30 Oct 2023 03:46:10 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S3; Mon, 30 Oct 2023 03:46:06 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/8] drm/loongson: Introduce a minimal support for Loongson VBIOS Date: Mon, 30 Oct 2023 03:46:00 +0800 Message-Id: <20231029194607.379459-2-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S3 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj9fXoW3urWDWF1rGw1UtF1kWr1kXrc_yoW8Wr4kCo WxZan8Zw4Fgr4UZayvgr1Fqr1xXFyFqF1av3s3Zr4DuFWY9F4UJFW3Xry3W34FyF13Xr43 A34rKw4xGr47Jrs5l-sFpf9Il3svdjkaLaAFLSUrUUUU1b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUY67kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUXVWUAwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26r4j6ryUM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26r4j6F4UM28EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v2 6r4j6r4UJwAaw2AFwI0_Jrv_JF1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_JF0_ Jw1lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km07C267AKxVWU XVWUAwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVWUCVW8JwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Jr0_Gr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0x vEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Jr0_GrUvcSsGvfC2Kfnx nUUI43ZEXa7IU8q2NtUUUUU== X-Spam-Status: No, score=-0.8 required=5.0 tests=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 groat.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 (groat.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:47:12 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120465216555542 X-GMAIL-MSGID: 1781120465216555542 Because some boards are equipped with non-transparent display bridges, which need the VBIOS to provided parameters. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 3 +- drivers/gpu/drm/loongson/loongson_device.c | 4 + drivers/gpu/drm/loongson/loongson_vbios.c | 420 +++++++++++++++++++++ drivers/gpu/drm/loongson/loongson_vbios.h | 59 +++ drivers/gpu/drm/loongson/lsdc_drv.c | 4 + drivers/gpu/drm/loongson/lsdc_drv.h | 8 + 6 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.c create mode 100644 drivers/gpu/drm/loongson/loongson_vbios.h diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 91e72bd900c1..bef00b2c5569 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -17,6 +17,7 @@ loongson-y := \ lsdc_ttm.o loongson-y += loongson_device.o \ - loongson_module.o + loongson_module.o \ + loongson_vbios.o obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 9986c8a2a255..64096ad5466e 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -7,6 +7,8 @@ #include "lsdc_drv.h" +extern struct loongson_vbios __loongson_vbios; + static const struct lsdc_kms_funcs ls7a1000_kms_funcs = { .create_i2c = lsdc_create_i2c_chan, .irq_handler = ls7a1000_dc_irq_handler, @@ -53,6 +55,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx = { .reg_size = 8, }, }, + .vbios = &__loongson_vbios, .chip_id = CHIP_LS7A1000, .model = "LS7A1000 bridge chipset", }; @@ -85,6 +88,7 @@ static const struct loongson_gfx_desc ls7a2000_gfx = { .reg_size = 8, }, }, + .vbios = &__loongson_vbios, .chip_id = CHIP_LS7A2000, .model = "LS7A2000 bridge chipset", }; diff --git a/drivers/gpu/drm/loongson/loongson_vbios.c b/drivers/gpu/drm/loongson/loongson_vbios.c new file mode 100644 index 000000000000..dc304018779e --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_vbios.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include +#include + +#include "loongson_vbios.h" +#include "lsdc_drv.h" + +#define LOONGSON_VBIOS_HEADER_STR "Loongson-VBIOS" +/* Legacy VBIOS is stored at offset 0 */ +#define LOONGSON_VBIOS_LEGACY_OFFSET 0 +/* The size of legacy VBIOS is 1 KiB */ +#define LOONGSON_VBIOS_LEGACY_SIZE 0x000400 + +/* Data Control Block of Newer version of the VBIOS started at here */ +#define LOONGSON_VBIOS_DCB_OFFSET 0x006000 +/* The last 1 MiB of the VRAM contains the raw VBIOS data */ +#define LOONGSON_VBIOS_BLOCK_OFFSET 0x100000 +/* Only 256KB of the 1 MiB are used for now */ +#define LOONGSON_VBIOS_BLOCK_SIZE 0x040000 + +struct loongson_vbios __loongson_vbios; + +/* + * vbios data control block is a kind of metadata, which is used to index + * real hardware device data block. + */ +struct loongson_vbios_dcb { + u16 type; /* what is it */ + u8 version; /* version of it, useless */ + u8 id; /* index (usually same with the display pipe) of the hardware */ + u32 offset; /* the offset of the real data */ + u32 size; /* the size of the real data */ + u64 ext0; /* for extension purpose */ + u64 ext1; /* extra space reserved for future use */ +} __packed; + +/* + * Loongson-VBIOS Data Block Layout + * + * + * _____________________ 0x00000 + * |_____________________| + * | | [0x0000, 0x0400) : legacy vbios storage + * | Not Used Yet | + * | | + * |---------------------|<------- 0x6000 + * +----| DCB 0 | + * | |---------------------| + * | | DCB 1 | + * | |---------------------| Format of Data Control Blocks + * | | One by one, packed | +------------+ + * | |---------------------| | u16 type | + * | | DCB N |----+ | | + * | |---------------------| | +------------+ + * | | . | | | u8 version | + * | | . | | | u8 index | + * | | . | | +------------+ + * | |---------------------| | | | + * | | DCB end | | | u32 offset | + * | |---------------------| | +------- | + * | | | | | | | + * | |_____________________| | | +------------+ + * | |_____________________| | | | | + * | | | | | | u32 size | + * +--->| vbios header info | | | | -------+ + * |_____________________| | | | | | + * | . | | | +------------+ | + * | . | | | | useless | | + * | . | | | | members | | + * |_____________________| | | +------------+ | + * | | | | | + * | encoders info |<---+ | | + * |_____________________| | | + * | | ___| | + * |_____________________|____/ | + * | | | + * | Something | | + * |_____________________|_________________ | + * | | | | + * | EDID | |<--------------+ + * |_____________________|_____________|___ + * | | + * | | Contents of those device specific data + * | GPU specific info | block are implement-defined and version + * | | dependent :0 + * |_____________________| + * / . / + * / . / + * / . / + * |_____________________| 0x040000 + * + */ + +enum loongson_vbios_dcb_type { + LV_DCB_HEADER = 0, + LV_DCB_CRTC = 1, + LV_DCB_ENCODER = 2, + LV_DCB_CONNECTOR = 3, + LV_DCB_I2C = 4, + LV_DCB_PWM = 5, + LV_DCB_GPIO = 6, + LV_DCB_BACKLIGHT = 7, + LV_DCB_FAN = 8, + LV_DCB_IRQ = 9, + LV_DCB_ENCODER_CFG = 10, + LV_DCB_ENCODER_RES = 11, + LV_DCB_GPU = 12, + LV_DCB_UNKNOWN = 13, + LV_DCB_END = 0xffff, +}; + +struct loongson_vbios_header { + char header[16]; + u32 version_major; + u32 version_minor; + char information[20]; + u32 num_crtc; + u32 crtc_offset; + u32 num_connector; + u32 connector_offset; + u32 num_encoder; + u32 encoder_offset; +} __packed; + +struct loongson_vbios_encoder { + u32 feature; + u32 i2c_id; + u32 connector_id; + u32 type; + u32 config_method; + u32 chip_id; + u8 chip_addr; +} __packed; + +struct loongson_vbios_connector { + u32 feature; + u32 i2c_id; + u8 edid[256]; + u32 type; + u32 hotplug_method; + u32 edid_method; + u32 hpd_int_gpio; + u32 gpio_place; +} __packed; + +/* + * A list node which contains the information about the device specific data + * block, the device here refer to the property or topology of hardware + * configuration, such as external display bridges, HDP GPIO, connectors etc. + */ +struct loongson_vbios_node { + struct list_head head; + + /* @type: the type of the data. For search */ + u32 type; + /* @id: the index(display pipe) of the data belong to. For search */ + u32 id; + /* + * @data: point to the device specific data block, such as external + * encoders name and it's i2c device address, hpd gpio resource etc. + */ + const void *data; + /* + * The size of the data. + */ + u32 size; +}; + +/* + * The returned pointer is actually point to &__loongson_vbios, but this + * function is only intended to provide READ-ONLY access. As our vbios is + * only be able to pass(provide) parameters, it is not executable and outside + * should not modify it. + */ +const struct loongson_vbios *to_loongson_vbios(struct drm_device *ddev) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp); + + return gfx->vbios; +} + +static bool loongson_vbios_is_valid(const struct loongson_vbios *vbios) +{ + char header[32]; + + memcpy(header, vbios->raw_data, sizeof(header)); + + if (strcmp(header, LOONGSON_VBIOS_HEADER_STR)) + return false; + + return true; +} + +/* + * The VBIOS blob is stored at the last 1 MiB of the VRAM, no SPI flush or + * EEPROM is needed. Platform BIOS is responsible for store this VBIOS blob + * data at right position on per boot time. + */ +static int loongson_vbios_construct(struct drm_device *ddev, + struct loongson_vbios *vbios) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + u64 vram_end = ldev->vram_base + ldev->vram_size; + u64 vbios_start = vram_end - LOONGSON_VBIOS_BLOCK_OFFSET; + void __iomem *ptr; + + vbios->raw_data = kzalloc(LOONGSON_VBIOS_BLOCK_SIZE, GFP_KERNEL); + if (!vbios->raw_data) + return -ENOMEM; + + ptr = ioremap(vbios_start, LOONGSON_VBIOS_BLOCK_SIZE); + if (!ptr) { + drm_err(ddev, "Map VBIOS region failed\n"); + return -ENOMEM; + } + + memcpy_fromio(vbios->raw_data, ptr, LOONGSON_VBIOS_BLOCK_SIZE); + + iounmap(ptr); + + INIT_LIST_HEAD(&vbios->list); + vbios->ddev = ddev; + + return 0; +} + +static void loongson_vbios_destruct(struct drm_device *ddev, void *data) +{ + struct loongson_vbios *vbios = (struct loongson_vbios *)data; + struct loongson_vbios_node *node; + struct loongson_vbios_node *tmp; + + list_for_each_entry_safe(node, tmp, &vbios->list, head) { + list_del(&node->head); + kfree(node); + } + + kfree(vbios->raw_data); + vbios->raw_data = NULL; +} + +static void loongson_vbios_print_dcb(struct drm_device *ddev, + struct loongson_vbios_dcb *dcb) +{ + drm_info(ddev, "type: %u, Offset: %u, Size: %u, version: %u, ID: %u\n", + dcb->type, dcb->offset, dcb->size, dcb->version, dcb->id); +} + +/* + * Process the data control block, establish a list for later searching. + * returns the number of data control block. Generally, loongson vbios + * has only 10 DCB or so. + */ +static int loongson_vbios_process_dcb(struct loongson_vbios *vbios, + bool verbose) +{ + struct drm_device *ddev = vbios->ddev; + void *base = vbios->raw_data; + int count = 0; + struct loongson_vbios_dcb *dcb; + + dcb = (struct loongson_vbios_dcb *)(base + LOONGSON_VBIOS_DCB_OFFSET); + + while (dcb->type != LV_DCB_END) { + struct loongson_vbios_node *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->type = dcb->type; + node->id = dcb->id; + node->data = base + dcb->offset; + node->size = dcb->size; + + list_add_tail(&node->head, &vbios->list); + + if (verbose) + loongson_vbios_print_dcb(ddev, dcb); + + ++dcb; + + if (++count > 1024) { + drm_err(ddev, "Unlikely, DCB is too much\n"); + break; + } + } + + return count; +} + +static const struct loongson_vbios_node * +loongson_vbios_get_node(struct drm_device *ddev, u32 type, u32 id) +{ + const struct loongson_vbios *vbios = to_loongson_vbios(ddev); + struct loongson_vbios_node *np; + + if (!vbios) + return NULL; + + list_for_each_entry(np, &vbios->list, head) { + if (np->type == type && np->id == id) + return np; + } + + return NULL; +} + +bool loongson_vbios_query_encoder_info(struct drm_device *ddev, + u32 pipe, + u32 *type, + enum loongson_vbios_encoder_name *name, + u8 *i2c_addr) +{ + const struct loongson_vbios_encoder *vencoder; + const struct loongson_vbios_node *np; + + np = loongson_vbios_get_node(ddev, LV_DCB_ENCODER, pipe); + if (!np) + return false; + + if (np->size != sizeof(*vencoder)) + WARN_ON(1); + + vencoder = (const struct loongson_vbios_encoder *)np->data; + + if (type) + *type = vencoder->type; + + if (name) + *name = vencoder->chip_id; + + /* i2c address, as a slave device */ + if (i2c_addr) + *i2c_addr = vencoder->chip_addr; + + return true; +} + +bool loongson_vbios_query_connector_info(struct drm_device *ddev, + u32 pipe, + u32 *connector_type, + u32 *hpd_method, + u32 *int_gpio, + u8 *edid_blob) +{ + const struct loongson_vbios_connector *vconnector; + const struct loongson_vbios_node *np; + + np = loongson_vbios_get_node(ddev, LV_DCB_CONNECTOR, pipe); + if (!np) + return false; + + if (np->size != sizeof(*vconnector)) + WARN_ON(1); + + vconnector = (const struct loongson_vbios_connector *)np->data; + + if (connector_type) + *connector_type = vconnector->type; + + if (edid_blob) + memcpy(edid_blob, vconnector->edid, 256); + + if (int_gpio) + *int_gpio = vconnector->hpd_int_gpio; + + return true; +} + +static void loongson_vbios_acquire_version(struct drm_device *ddev, + struct loongson_vbios *vbios) +{ + struct loongson_vbios_header *vh; + + vh = (struct loongson_vbios_header *)vbios->raw_data; + + vbios->version_major = vh->version_major; + vbios->version_minor = vh->version_minor; + + drm_info(ddev, "Loongson VBIOS version: %u.%u\n", + vh->version_major, vh->version_minor); +} + +int loongson_vbios_init(struct drm_device *ddev) +{ + struct loongson_vbios *vbios = &__loongson_vbios; + int ret; + int num; + + ret = loongson_vbios_construct(ddev, vbios); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(ddev, loongson_vbios_destruct, vbios); + if (ret) + return ret; + + if (!loongson_vbios_is_valid(vbios)) { + drm_err(ddev, "Loongson VBIOS: header is invalid\n"); + return -EINVAL; + } + + loongson_vbios_acquire_version(ddev, vbios); + + num = loongson_vbios_process_dcb(vbios, false); + if (num <= 0) { + drm_err(ddev, "Loongson VBIOS: Process DCB failed\n"); + return -EINVAL; + } + + drm_info(ddev, "Loongson VBIOS: has %d DCBs\n", num); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/loongson_vbios.h b/drivers/gpu/drm/loongson/loongson_vbios.h new file mode 100644 index 000000000000..66fb43b3609e --- /dev/null +++ b/drivers/gpu/drm/loongson/loongson_vbios.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __LOONGSON_VBIOS_H__ +#define __LOONGSON_VBIOS_H__ + +#include + +struct loongson_vbios { + struct list_head list; + void *raw_data; + struct drm_device *ddev; + u32 version_major; + u32 version_minor; +}; + +enum loongson_vbios_encoder_name { + ENCODER_CHIP_UNKNOWN = 0x00, + ENCODER_CHIP_INTERNAL_VGA = 0x01, + ENCODER_CHIP_INTERNAL_HDMI = 0x02, + ENCODER_CHIP_CH7055 = 0x10, + ENCODER_CHIP_ADV7125 = 0x11, + ENCODER_CHIP_TFP410 = 0x20, + ENCODER_CHIP_IT66121 = 0x30, + ENCODER_CHIP_SIL9022 = 0x31, + ENCODER_CHIP_LT8618 = 0x32, + ENCODER_CHIP_MS7210 = 0x33, + ENCODER_CHIP_NCS8805 = 0x40, + ENCODER_CHIP_LT9721 = 0x42, + ENCODER_CHIP_LT6711 = 0x43, + ENCODER_CHIP_LT8619 = 0x50, +}; + +enum loongson_vbios_hotplug_method { + LV_HPD_DISABLED = 0, + LV_HPD_POLLING = 1, + LV_HPD_IRQ = 2, +}; + +const struct loongson_vbios *to_loongson_vbios(struct drm_device *ddev); + +bool loongson_vbios_query_encoder_info(struct drm_device *ddev, + u32 pipe, + u32 *type, + enum loongson_vbios_encoder_name *name, + u8 *i2c_addr); + +bool loongson_vbios_query_connector_info(struct drm_device *ddev, + u32 pipe, + u32 *connector_type, + u32 *hpd_method, + u32 *int_gpio, + u8 *edid_blob); + +int loongson_vbios_init(struct drm_device *ddev); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index 89ccc0c43169..aebb200fa567 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -213,6 +213,10 @@ lsdc_create_device(struct pci_dev *pdev, return ERR_PTR(ret); } + ret = loongson_vbios_init(ddev); + if (ret) + drm_info(ddev, "No VBIOS support\n"); + ret = drm_aperture_remove_conflicting_framebuffers(ldev->vram_base, ldev->vram_size, driver); diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h index fbf2d760ef27..335953c988d1 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.h +++ b/drivers/gpu/drm/loongson/lsdc_drv.h @@ -16,6 +16,7 @@ #include #include +#include "loongson_vbios.h" #include "lsdc_i2c.h" #include "lsdc_irq.h" #include "lsdc_gfxpll.h" @@ -85,6 +86,13 @@ struct loongson_gfx_desc { u32 reg_size; } pixpll[LSDC_NUM_CRTC]; + /* + * @vbios: Provide information about the output configuration, + * and provide information about dynamic features which cannot + * be detected(determined) with the chip_id. + */ + const struct loongson_vbios *vbios; + enum loongson_chip_id chip_id; char model[64]; }; From patchwork Sun Oct 29 19:46:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159407 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831880vqb; Sun, 29 Oct 2023 12:47:14 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE391KPPXLVE9CroUyUydu7ZEXxTPoYhjwmrfuGNhYiAu1ehwHpXrsrKzJH3qvZIlwN0+K1 X-Received: by 2002:a17:90b:f83:b0:27d:2108:af18 with SMTP id ft3-20020a17090b0f8300b0027d2108af18mr7399022pjb.25.1698608833967; Sun, 29 Oct 2023 12:47:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608833; cv=none; d=google.com; s=arc-20160816; b=L+434yrKPbz6kBR6KvU3yy6flH3IOSEvUHUVj5LSx2aSS7GXnzfYMZwM9C54sdFppd GvvKHKKrQtcPPEul6LMa/cJo2FkkeoBkZJ0bMeVkdW8S0IAnlkf8aQjNpLWPvuZ+ScL5 3OOnKy7ggo2W4D/58L+HhCxcfBLGAuoeimNDYxJwssa7OREezgggOOrECXMTM/JYHzij EUcHaLfHi8HtzzxRRqTiiWFawNvFxFUhkMTPsgXZZE5hbjZ+wTw85WTx/HW9NUD/f/Cw nbpGRsU143xfWeXvoD5zMrVBOcSUULekiUACHNOc1/H95/KxhDLYhBxnthdkhLtEslQ5 Livw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=0mi0nPEMBZK9zz8J7huptgByFCvy/fG81DfigSIcg5M=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=iqXaM3ifnzd0xx5O8trr/Xivc3lFf51koQo1sJ+0YJD/7v5pRZqKx31/Gel1onbvlS 0iEgLHjtkt1g7tKIQsTjSIOnxS/wq17iFSTJgz1to31zDr0mmiToZOF0nZmf5GQS6EI/ 3P+HAbwPvSoBGxQcIVda1aaRvd1Mj4hfdQXRG4/1FLJdttgRadNxtXd1JbtJJDiBtmbv sIPDAJbl/2IUmaCtHeDheYlGA6/22yYWa3JLbfNalGD5Vm2Du0ENx6f1aQnHhOX68cX/ RCTp3TQgZ86gofcsDhemKoA8TvFnzZdC3LP7mlugfF4tMRP547DNTy7rs+EXWa/2lpc3 5GoQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from snail.vger.email (snail.vger.email. [23.128.96.37]) by mx.google.com with ESMTPS id i16-20020a17090adc1000b00273f1cd73b2si2773147pjv.75.2023.10.29.12.47.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) client-ip=23.128.96.37; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 2E32C805A797; Sun, 29 Oct 2023 12:46:54 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230473AbjJ2Tqj (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34564 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230384AbjJ2TqS (ORCPT ); Sun, 29 Oct 2023 15:46:18 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6247EBC for ; Sun, 29 Oct 2023 12:46:13 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8DxxPCDtj5ltps1AA--.40384S3; Mon, 30 Oct 2023 03:46:11 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S4; Mon, 30 Oct 2023 03:46:10 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/8] drm/loongson: Introduce a drm bridge driver for it66121 HDMI transmitter Date: Mon, 30 Oct 2023 03:46:01 +0800 Message-Id: <20231029194607.379459-3-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S4 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj9fXoWftFyDGr1fZw4kXw13GFWrXrc_yoW5ZrW7Go WvvFnxtw15XryxZws7Wa12gF1DZ3WDtrWFvr4Fq3ykuayUKwn8Ja17JwnrZr1aqwn7tr13 Jr1ktrs5Gr43Cr18l-sFpf9Il3svdjkaLaAFLSUrUUUU1b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYD7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUXVWUAwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26r4j6ryUM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26r4j6F4UM28EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v2 6r4j6r4UJwAaw2AFwI0_Jrv_JF1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Jw0_ WrylYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km07C267AKxVWU XVWUAwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVW8JVW5JwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0x vEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVj vjDU0xZFpf9x07jz2NtUUUUU= X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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 (snail.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:46:54 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120456435523694 X-GMAIL-MSGID: 1781120456435523694 The IT66121 is a DVO to HDMI converter, LS3A5000+LS7A1000 ML5A_MB use this chip to support HDMI output. Thus add a drm bridge based driver for it. This patch is developed with drivers/gpu/drm/bridge/ite-it66121.c as base. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Kconfig | 1 + drivers/gpu/drm/loongson/Makefile | 2 + drivers/gpu/drm/loongson/ite_it66121.c | 749 ++++++++++++++++++++ drivers/gpu/drm/loongson/ite_it66121.h | 19 + drivers/gpu/drm/loongson/ite_it66121_regs.h | 268 +++++++ 5 files changed, 1039 insertions(+) create mode 100644 drivers/gpu/drm/loongson/ite_it66121.c create mode 100644 drivers/gpu/drm/loongson/ite_it66121.h create mode 100644 drivers/gpu/drm/loongson/ite_it66121_regs.h diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig index df6946d505fa..a96f5699099e 100644 --- a/drivers/gpu/drm/loongson/Kconfig +++ b/drivers/gpu/drm/loongson/Kconfig @@ -7,6 +7,7 @@ config DRM_LOONGSON select DRM_TTM select I2C select I2C_ALGOBIT + select REGMAP_I2C help This is a DRM driver for Loongson Graphics, it may including LS7A2000, LS7A1000, LS2K2000 and LS2K1000 etc. Loongson LS7A diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index bef00b2c5569..1459d19b2c90 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -20,4 +20,6 @@ loongson-y += loongson_device.o \ loongson_module.o \ loongson_vbios.o +loongson-y += ite_it66121.o + obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/ite_it66121.c b/drivers/gpu/drm/loongson/ite_it66121.c new file mode 100644 index 000000000000..7b085575f864 --- /dev/null +++ b/drivers/gpu/drm/loongson/ite_it66121.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Phong LE + * Copyright (C) 2018-2019, Artem Mygaiev + * Copyright (C) 2017, Fresco Logic, Incorporated. + * + * IT66121 HDMI transmitter driver + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ite_it66121.h" +#include "ite_it66121_regs.h" + +#define IT66121_CHIP_NAME "IT66121" + +struct it66121_bridge { + struct drm_bridge bridge; + struct drm_connector connector; + struct regmap *regmap; + struct i2c_client *client; + /* Protects fields below and device registers */ + struct mutex lock; + u16 vendor_id; + u16 device_id; + u32 revision; +}; + +static inline struct it66121_bridge * +bridge_to_it66121(struct drm_bridge *bridge) +{ + return container_of(bridge, struct it66121_bridge, bridge); +} + +static inline struct it66121_bridge * +connector_to_it66121(struct drm_connector *connector) +{ + return container_of(connector, struct it66121_bridge, connector); +} + +static const struct regmap_range_cfg it66121_regmap_banks[] = { + { + .name = IT66121_CHIP_NAME, + .range_min = 0x00, + .range_max = 0x1FF, + .selector_reg = IT66121_CLK_BANK_REG, + .selector_mask = 0x1, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, +}; + +static const struct regmap_config it66121_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + .max_register = 0x1FF, + .ranges = it66121_regmap_banks, + .num_ranges = ARRAY_SIZE(it66121_regmap_banks), +}; + +static inline int it66121_preamble_ddc(struct it66121_bridge *itb) +{ + return regmap_write(itb->regmap, IT66121_MASTER_SEL_REG, + IT66121_MASTER_SEL_HOST); +} + +static inline int it66121_fire_afe(struct it66121_bridge *itb) +{ + return regmap_write(itb->regmap, IT66121_AFE_DRV_REG, 0); +} + +static int it66121_configure_input(struct it66121_bridge *itb) +{ + int ret; + + ret = regmap_write(itb->regmap, IT66121_INPUT_MODE_REG, + IT66121_INPUT_MODE_RGB888); + if (ret) + return ret; + + return regmap_write(itb->regmap, IT66121_INPUT_CSC_REG, + IT66121_INPUT_CSC_NO_CONV); +} + +/* + * it66121_configure_afe() - Configure the analog front end + * @ctx: it66121_ctx object + * @mode: mode to configure + * + * RETURNS: + * zero if success, a negative error code otherwise. + */ +static int it66121_configure_afe(struct it66121_bridge *itb, + const struct drm_display_mode *mode) +{ + int ret; + + ret = regmap_write(itb->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_RST); + if (ret) + return ret; + + if (mode->clock > IT66121_AFE_CLK_HIGH) { + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_GAINBIT | + IT66121_AFE_XP_ENO, + IT66121_AFE_XP_GAINBIT); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_GAINBIT | + IT66121_AFE_IP_ER0, + IT66121_AFE_IP_GAINBIT); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, 0x80); + if (ret) + return ret; + } else { + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_GAINBIT | + IT66121_AFE_XP_ENO, + IT66121_AFE_XP_ENO); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_GAINBIT | + IT66121_AFE_IP_ER0, + IT66121_AFE_IP_ER0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, + IT66121_AFE_IP_EC1); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, + IT66121_AFE_XP_EC1_LOWCLK); + if (ret) + return ret; + } + + /* Clear reset flags */ + ret = regmap_write_bits(itb->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_REF | IT66121_SW_RST_VID, 0); + if (ret) + return ret; + + return it66121_fire_afe(itb); +} + +static inline int it66121_wait_ddc_ready(struct it66121_bridge *itb) +{ + u32 error = IT66121_DDC_STATUS_NOACK | + IT66121_DDC_STATUS_WAIT_BUS | + IT66121_DDC_STATUS_ARBI_LOSE; + u32 done = IT66121_DDC_STATUS_TX_DONE; + int ret, val; + + ret = regmap_read_poll_timeout(itb->regmap, IT66121_DDC_STATUS_REG, + val, val & (error | done), + IT66121_EDID_SLEEP_US, + IT66121_EDID_TIMEOUT_US); + if (ret) + return ret; + + if (val & error) + return -EAGAIN; + + return 0; +} + +static int it66121_abort_ddc_ops(struct it66121_bridge *itb) +{ + unsigned int swreset, cpdesire; + int ret; + + ret = regmap_read(itb->regmap, IT66121_SW_RST_REG, &swreset); + if (ret) + return ret; + + ret = regmap_read(itb->regmap, IT66121_HDCP_REG, &cpdesire); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_HDCP_REG, + cpdesire & (~IT66121_HDCP_CPDESIRED & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_SW_RST_REG, + (swreset | IT66121_SW_RST_HDCP)); + if (ret) + return ret; + + ret = it66121_preamble_ddc(itb); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_ABORT); + if (ret) + return ret; + + return it66121_wait_ddc_ready(itb); +} + +static int it66121_get_edid_block(void *context, + u8 *buf, + unsigned int block, + size_t len) +{ + struct it66121_bridge *itb = (struct it66121_bridge *)context; + int remain = len; + int offset = 0; + int ret, cnt; + + offset = (block % 2) * len; + block = block / 2; + + while (remain > 0) { + cnt = (remain > IT66121_EDID_FIFO_SIZE) ? + IT66121_EDID_FIFO_SIZE : remain; + + ret = regmap_write(itb->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_FIFO_CLR); + if (ret) + return ret; + + ret = it66121_wait_ddc_ready(itb); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_DDC_OFFSET_REG, offset); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_DDC_BYTE_REG, cnt); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_DDC_SEGMENT_REG, block); + if (ret) + return ret; + + ret = regmap_write(itb->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_EDID_READ); + if (ret) + return ret; + + offset += cnt; + remain -= cnt; + + ret = it66121_wait_ddc_ready(itb); + if (ret) { + it66121_abort_ddc_ops(itb); + return ret; + } + + ret = regmap_noinc_read(itb->regmap, IT66121_DDC_RD_FIFO_REG, + buf, cnt); + if (ret) + return ret; + + buf += cnt; + } + + return 0; +} + +static bool it66121_is_hpd_detect(struct it66121_bridge *itb) +{ + int val; + + if (regmap_read(itb->regmap, IT66121_SYS_STATUS_REG, &val)) + return false; + + return val & IT66121_SYS_STATUS_HPDETECT; +} + +static int it66121_connector_get_modes(struct drm_connector *connector) +{ + struct it66121_bridge *itb = connector_to_it66121(connector); + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + int num_modes = 0; + struct edid *edid; + int ret; + + edid = drm_bridge_get_edid(&itb->bridge, connector); + if (!edid) { + drm_err(connector->dev, "Failed to read EDID\n"); + goto failed; + } + + if (drm_connector_update_edid_property(connector, edid)) { + drm_err(connector->dev, "Failed to update EDID\n"); + goto failed; + } + + ret = drm_display_info_set_bus_formats(&connector->display_info, + &bus_format, 1); + if (ret) + goto failed; + + num_modes = drm_add_edid_modes(connector, edid); + +failed: + return num_modes; +} + +static int it66121_connector_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct it66121_bridge *itb = connector_to_it66121(connector); + + return it66121_is_hpd_detect(itb) ? connector_status_connected + : connector_status_disconnected; +} + +static struct drm_connector_helper_funcs it66121_connector_helper_funcs = { + .get_modes = it66121_connector_get_modes, + .detect_ctx = it66121_connector_detect_ctx, +}; + +static const struct drm_connector_funcs it66121_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int it66121_bridge_connector_init(struct drm_bridge *bridge) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + struct drm_connector *connector = &itb->connector; + int ret; + + if (bridge->ops & DRM_BRIDGE_OP_HPD) { + connector->polled = DRM_CONNECTOR_POLL_HPD; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + } + + ret = drm_connector_init(bridge->dev, + connector, + &it66121_connector_funcs, + bridge->type); + if (ret) + return ret; + + drm_connector_helper_add(connector, &it66121_connector_helper_funcs); + + drm_connector_attach_encoder(connector, bridge->encoder); + + return 0; +} + +static int it66121_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + int ret; + + ret = it66121_bridge_connector_init(bridge); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_RCLK, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_INT_REG, + IT66121_INT_TX_CLK_OFF, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_PWD, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_PWDI | IT66121_AFE_XP_PWDPLL, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_PWDPLL, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_RST, 0); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_RESETB, IT66121_AFE_XP_RESETB); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_RESETB, IT66121_AFE_IP_RESETB); + if (ret) + return ret; + + ret = regmap_write_bits(itb->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_REF, + IT66121_SW_RST_REF); + if (ret) + return ret; + + drm_info(bridge->dev, + "IT66121 attached, Vendor ID: 0x%x, Device ID: 0x%x\n", + itb->vendor_id, itb->device_id); + + /* Per programming manual, sleep here for bridge to settle */ + msleep(50); + + return 0; +} + +static void it66121_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *state) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + struct regmap *regmap = itb->regmap; + int ret; + + ret = regmap_clear_bits(regmap, IT66121_AVMUTE_REG, IT66121_AVMUTE_BIT); + if (ret) + drm_err(bridge->dev, "Enable it66121 bridge failed"); + + regmap_write(regmap, IT66121_PKT_GEN_CTRL_REG, + IT66121_PKT_GEN_CTRL_ON | IT66121_PKT_GEN_CTRL_RPT); +} + +static void it66121_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + struct regmap *regmap = itb->regmap; + int ret; + + ret = regmap_set_bits(regmap, IT66121_AVMUTE_REG, IT66121_AVMUTE_BIT); + if (ret) + drm_err(bridge->dev, "Disable it66121 bridge failed"); + + regmap_write(regmap, IT66121_PKT_GEN_CTRL_REG, + IT66121_PKT_GEN_CTRL_ON | IT66121_PKT_GEN_CTRL_RPT); +} + +static void it66121_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adj_mode) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + struct hdmi_avi_infoframe avi_infoframe; + u8 av_buf[HDMI_INFOFRAME_SIZE(AVI)]; + int ret; + + mutex_lock(&itb->lock); + + hdmi_avi_infoframe_init(&avi_infoframe); + + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_infoframe, + &itb->connector, + adj_mode); + if (ret) { + drm_err(bridge->dev, "Failed to setup AVI infoframe\n"); + goto unlock; + } + + ret = hdmi_avi_infoframe_pack(&avi_infoframe, av_buf, sizeof(av_buf)); + if (ret < 0) { + drm_err(bridge->dev, "Failed to pack infoframe\n"); + goto unlock; + } + + /* Write new AVI infoframe packet */ + ret = regmap_bulk_write(itb->regmap, IT66121_AVIINFO_DB1_REG, + &av_buf[HDMI_INFOFRAME_HEADER_SIZE], + HDMI_AVI_INFOFRAME_SIZE); + if (ret) + goto unlock; + + if (regmap_write(itb->regmap, IT66121_AVIINFO_CSUM_REG, av_buf[3])) + goto unlock; + + /* Enable AVI infoframe */ + if (regmap_write(itb->regmap, IT66121_AVI_INFO_PKT_REG, + IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT)) + goto unlock; + + /* Set TX mode to HDMI */ + if (regmap_write(itb->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) + goto unlock; + + if (regmap_write_bits(itb->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, + IT66121_CLK_BANK_PWROFF_TXCLK)) + goto unlock; + + if (it66121_configure_input(itb)) + goto unlock; + + if (it66121_configure_afe(itb, adj_mode)) + goto unlock; + + if (regmap_write_bits(itb->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, 0)) + goto unlock; + +unlock: + mutex_unlock(&itb->lock); +} + +static enum drm_mode_status +it66121_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + if (mode->clock > 148500) + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + struct edid *edid; + int ret; + + mutex_lock(&itb->lock); + ret = it66121_preamble_ddc(itb); + if (ret) { + edid = NULL; + goto unlock; + } + + ret = regmap_write(itb->regmap, IT66121_DDC_HEADER_REG, + IT66121_DDC_HEADER_EDID); + if (ret) { + edid = NULL; + goto unlock; + } + + edid = drm_do_get_edid(connector, it66121_get_edid_block, itb); + +unlock: + mutex_unlock(&itb->lock); + + return edid; +} + +static void it66121_bridge_detach(struct drm_bridge *bridge) +{ + struct it66121_bridge *itb = bridge_to_it66121(bridge); + + mutex_destroy(&itb->lock); + + i2c_unregister_device(itb->client); + + drm_bridge_remove(bridge); +} + +static const struct drm_bridge_funcs it66121_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .attach = it66121_bridge_attach, + .detach = it66121_bridge_detach, + .atomic_enable = it66121_bridge_enable, + .atomic_disable = it66121_bridge_disable, + .mode_set = it66121_bridge_mode_set, + .mode_valid = it66121_bridge_mode_valid, + .get_edid = it66121_bridge_get_edid, +}; + +static void it66121_bridge_get_version(struct it66121_bridge *itb) +{ + u32 vendor_ids[2] = { 0 }; + u32 device_ids[2] = { 0 }; + + regmap_read(itb->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); + regmap_read(itb->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); + regmap_read(itb->regmap, IT66121_DEVICE_ID0_REG, &device_ids[0]); + regmap_read(itb->regmap, IT66121_DEVICE_ID1_REG, &device_ids[1]); + + /* Revision is shared with DEVICE_ID1 */ + itb->revision = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); + device_ids[1] &= IT66121_DEVICE_ID1_MASK; + + itb->vendor_id = vendor_ids[1] << 8 | vendor_ids[0]; + itb->device_id = device_ids[1] << 8 | device_ids[0]; +} + +static void it66121_bridge_init_base(struct it66121_bridge *itb, bool hpd) +{ + struct drm_bridge *bridge = &itb->bridge; + + bridge->funcs = &it66121_bridge_funcs; + bridge->type = DRM_MODE_CONNECTOR_HDMIA; + bridge->ops = DRM_BRIDGE_OP_EDID; + + if (hpd) + bridge->ops |= DRM_BRIDGE_OP_HPD; + + drm_bridge_add(bridge); +} + +/* + * The device address is 0x98 if PCADR pin is pulled low, 0x98 >> 1 = 0x4c + * The device address is 0x9A if PCADR pin is pulled high, 0x9A >> 1 = 0x4d + */ +static bool it66121_probe_slave(struct drm_device *ddev, + struct i2c_adapter *adapter, + u8 *addr) +{ + struct i2c_msg msg = { + .len = 0, + }; + int num = 3; + int count; + int i; + + /* Try slave address 0x4c */ + msg.addr = 0x4c; + count = 0; + for (i = 0; i < num; i++) { + count += i2c_transfer(adapter, &msg, 1); + udelay(9); + } + + if (count == num) { + *addr = 0x4c; + return true; + } + + /* Try slave address 0x4d */ + msg.addr = 0x4d; + count = 0; + for (i = 0; i < num; i++) { + count += i2c_transfer(adapter, &msg, 1); + udelay(9); + } + + if (count == num) { + *addr = 0x4d; + return true; + } + + drm_err(ddev, "No reliable slave i2c device found\n"); + + /* + * If no reliable slave i2c device found, we would like drop the + * support. + */ + return false; +} + +struct drm_bridge *it66121_bridge_create(struct drm_device *ddev, + struct i2c_adapter *i2c, + u8 addr, + bool enable_hpd, + u32 int_gpio, + unsigned int pipe) +{ + struct i2c_board_info it66121_board_info = { + .type = IT66121_CHIP_NAME, + }; + struct it66121_bridge *itb; + struct i2c_client *client; + u8 addr_probed; + + if (!it66121_probe_slave(ddev, i2c, &addr_probed)) + return NULL; + + if (addr != addr_probed) { + drm_warn(ddev, "device address(0x%x) is not correct\n", addr); + addr = addr_probed; + } + + it66121_board_info.addr = addr; + + itb = devm_kzalloc(ddev->dev, sizeof(*itb), GFP_KERNEL); + if (!itb) + return NULL; + + client = i2c_new_client_device(i2c, &it66121_board_info); + if (IS_ERR(client)) + return NULL; + + drm_info(ddev, "i2c client %s@0x%02x created\n", + it66121_board_info.type, it66121_board_info.addr); + + itb->client = client; + + i2c_set_clientdata(client, itb); + + mutex_init(&itb->lock); + + itb->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); + if (IS_ERR(itb->regmap)) { + drm_err(ddev, "Failed to map registers\n"); + return NULL; + } + + it66121_bridge_get_version(itb); + + it66121_bridge_init_base(itb, enable_hpd); + + return &itb->bridge; +} diff --git a/drivers/gpu/drm/loongson/ite_it66121.h b/drivers/gpu/drm/loongson/ite_it66121.h new file mode 100644 index 000000000000..c3e26cce1b02 --- /dev/null +++ b/drivers/gpu/drm/loongson/ite_it66121.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __ITE_IT66121_H__ +#define __ITE_IT66121_H__ + +#include +#include + +struct drm_bridge *it66121_bridge_create(struct drm_device *ddev, + struct i2c_adapter *i2c, + u8 addr, + bool enable_hpd, + u32 int_gpio, + unsigned int pipe); + +#endif diff --git a/drivers/gpu/drm/loongson/ite_it66121_regs.h b/drivers/gpu/drm/loongson/ite_it66121_regs.h new file mode 100644 index 000000000000..57118a4501c1 --- /dev/null +++ b/drivers/gpu/drm/loongson/ite_it66121_regs.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Phong LE + * Copyright (C) 2018-2019, Artem Mygaiev + * Copyright (C) 2017, Fresco Logic, Incorporated. + */ + +#ifndef __ITE_IT66121_h__ +#define __ITE_IT66121_h__ + +#define IT66121_VENDOR_ID0_REG 0x00 +#define IT66121_VENDOR_ID1_REG 0x01 +#define IT66121_DEVICE_ID0_REG 0x02 +#define IT66121_DEVICE_ID1_REG 0x03 + +#define IT66121_REVISION_MASK GENMASK(7, 4) +#define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) + +#define IT66121_MASTER_SEL_REG 0x10 +#define IT66121_MASTER_SEL_HOST BIT(0) + +#define IT66121_AFE_DRV_REG 0x61 +#define IT66121_AFE_DRV_RST BIT(4) +#define IT66121_AFE_DRV_PWD BIT(5) + +#define IT66121_INPUT_MODE_REG 0x70 +#define IT66121_INPUT_MODE_RGB888 (0 << 6) +#define IT66121_INPUT_MODE_YUV422 BIT(6) +#define IT66121_INPUT_MODE_YUV444 (2 << 6) +#define IT66121_INPUT_MODE_CCIR656 BIT(4) +#define IT66121_INPUT_MODE_SYNCEMB BIT(3) +#define IT66121_INPUT_MODE_DDR BIT(2) + +#define IT66121_INPUT_CSC_REG 0x72 +#define IT66121_INPUT_CSC_ENDITHER BIT(7) +#define IT66121_INPUT_CSC_ENUDFILTER BIT(6) +#define IT66121_INPUT_CSC_DNFREE_GO BIT(5) +#define IT66121_INPUT_CSC_RGB_TO_YUV 0x02 +#define IT66121_INPUT_CSC_YUV_TO_RGB 0x03 +#define IT66121_INPUT_CSC_NO_CONV 0x00 + +#define IT66121_AFE_XP_REG 0x62 +#define IT66121_AFE_XP_GAINBIT BIT(7) +#define IT66121_AFE_XP_PWDPLL BIT(6) +#define IT66121_AFE_XP_ENI BIT(5) +#define IT66121_AFE_XP_ENO BIT(4) +#define IT66121_AFE_XP_RESETB BIT(3) +#define IT66121_AFE_XP_PWDI BIT(2) +#define IT6610_AFE_XP_BYPASS BIT(0) + +#define IT66121_AFE_IP_REG 0x64 +#define IT66121_AFE_IP_GAINBIT BIT(7) +#define IT66121_AFE_IP_PWDPLL BIT(6) +#define IT66121_AFE_IP_CKSEL_05 (0 << 4) +#define IT66121_AFE_IP_CKSEL_1 BIT(4) +#define IT66121_AFE_IP_CKSEL_2 (2 << 4) +#define IT66121_AFE_IP_CKSEL_2OR4 (3 << 4) +#define IT66121_AFE_IP_ER0 BIT(3) +#define IT66121_AFE_IP_RESETB BIT(2) +#define IT66121_AFE_IP_ENC BIT(1) +#define IT66121_AFE_IP_EC1 BIT(0) + +#define IT66121_AFE_XP_EC1_REG 0x68 +#define IT66121_AFE_XP_EC1_LOWCLK BIT(4) + +#define IT66121_SW_RST_REG 0x04 +#define IT66121_SW_RST_REF BIT(5) +#define IT66121_SW_RST_AREF BIT(4) +#define IT66121_SW_RST_VID BIT(3) +#define IT66121_SW_RST_AUD BIT(2) +#define IT66121_SW_RST_HDCP BIT(0) + +#define IT66121_DDC_COMMAND_REG 0x15 +#define IT66121_DDC_COMMAND_BURST_READ 0x0 +#define IT66121_DDC_COMMAND_EDID_READ 0x3 +#define IT66121_DDC_COMMAND_FIFO_CLR 0x9 +#define IT66121_DDC_COMMAND_SCL_PULSE 0xA +#define IT66121_DDC_COMMAND_ABORT 0xF + +#define IT66121_HDCP_REG 0x20 +#define IT66121_HDCP_CPDESIRED BIT(0) +#define IT66121_HDCP_EN1P1FEAT BIT(1) + +#define IT66121_INT_STATUS1_REG 0x06 +#define IT66121_INT_STATUS1_AUD_OVF BIT(7) +#define IT66121_INT_STATUS1_DDC_NOACK BIT(5) +#define IT66121_INT_STATUS1_DDC_FIFOERR BIT(4) +#define IT66121_INT_STATUS1_DDC_BUSHANG BIT(2) +#define IT66121_INT_STATUS1_RX_SENS_STATUS BIT(1) +#define IT66121_INT_STATUS1_HPD_STATUS BIT(0) + +#define IT66121_DDC_HEADER_REG 0x11 +#define IT66121_DDC_HEADER_HDCP 0x74 +#define IT66121_DDC_HEADER_EDID 0xA0 + +#define IT66121_DDC_OFFSET_REG 0x12 +#define IT66121_DDC_BYTE_REG 0x13 +#define IT66121_DDC_SEGMENT_REG 0x14 +#define IT66121_DDC_RD_FIFO_REG 0x17 + +#define IT66121_CLK_BANK_REG 0x0F +#define IT66121_CLK_BANK_PWROFF_RCLK BIT(6) +#define IT66121_CLK_BANK_PWROFF_ACLK BIT(5) +#define IT66121_CLK_BANK_PWROFF_TXCLK BIT(4) +#define IT66121_CLK_BANK_PWROFF_CRCLK BIT(3) +#define IT66121_CLK_BANK_0 0 +#define IT66121_CLK_BANK_1 1 + +#define IT66121_INT_REG 0x05 +#define IT66121_INT_ACTIVE_HIGH BIT(7) +#define IT66121_INT_OPEN_DRAIN BIT(6) +#define IT66121_INT_TX_CLK_OFF BIT(0) + +#define IT66121_INT_MASK1_REG 0x09 +#define IT66121_INT_MASK1_AUD_OVF BIT(7) +#define IT66121_INT_MASK1_DDC_NOACK BIT(5) +#define IT66121_INT_MASK1_DDC_FIFOERR BIT(4) +#define IT66121_INT_MASK1_DDC_BUSHANG BIT(2) +#define IT66121_INT_MASK1_RX_SENS BIT(1) +#define IT66121_INT_MASK1_HPD BIT(0) + +#define IT66121_INT_CLR1_REG 0x0C +#define IT66121_INT_CLR1_PKTACP BIT(7) +#define IT66121_INT_CLR1_PKTNULL BIT(6) +#define IT66121_INT_CLR1_PKTGEN BIT(5) +#define IT66121_INT_CLR1_KSVLISTCHK BIT(4) +#define IT66121_INT_CLR1_AUTHDONE BIT(3) +#define IT66121_INT_CLR1_AUTHFAIL BIT(2) +#define IT66121_INT_CLR1_RX_SENS BIT(1) +#define IT66121_INT_CLR1_HPD BIT(0) + +#define IT66121_AVMUTE_REG 0xC1 +#define IT66121_AVMUTE_BIT BIT(0) +#define IT66121_AVMUTE_BLUESCR BIT(1) + +#define IT66121_PKT_CTS_CTRL_REG 0xC5 +#define IT66121_PKT_CTS_CTRL_SEL BIT(1) + +#define IT66121_PKT_GEN_CTRL_REG 0xC6 +#define IT66121_PKT_GEN_CTRL_ON BIT(0) +#define IT66121_PKT_GEN_CTRL_RPT BIT(1) + +#define IT66121_AVIINFO_DB1_REG 0x158 +#define IT66121_AVIINFO_DB2_REG 0x159 +#define IT66121_AVIINFO_DB3_REG 0x15A +#define IT66121_AVIINFO_DB4_REG 0x15B +#define IT66121_AVIINFO_DB5_REG 0x15C +#define IT66121_AVIINFO_CSUM_REG 0x15D +#define IT66121_AVIINFO_DB6_REG 0x15E +#define IT66121_AVIINFO_DB7_REG 0x15F +#define IT66121_AVIINFO_DB8_REG 0x160 +#define IT66121_AVIINFO_DB9_REG 0x161 +#define IT66121_AVIINFO_DB10_REG 0x162 +#define IT66121_AVIINFO_DB11_REG 0x163 +#define IT66121_AVIINFO_DB12_REG 0x164 +#define IT66121_AVIINFO_DB13_REG 0x165 + +#define IT66121_AVI_INFO_PKT_REG 0xCD +#define IT66121_AVI_INFO_PKT_ON BIT(0) +#define IT66121_AVI_INFO_PKT_RPT BIT(1) + +#define IT66121_HDMI_MODE_REG 0xC0 +#define IT66121_HDMI_MODE_HDMI BIT(0) + +#define IT66121_SYS_STATUS_REG 0x0E +#define IT66121_SYS_STATUS_ACTIVE_IRQ BIT(7) +#define IT66121_SYS_STATUS_HPDETECT BIT(6) +#define IT66121_SYS_STATUS_SENDECTECT BIT(5) +#define IT66121_SYS_STATUS_VID_STABLE BIT(4) +#define IT66121_SYS_STATUS_AUD_CTS_CLR BIT(1) +#define IT66121_SYS_STATUS_CLEAR_IRQ BIT(0) + +#define IT66121_DDC_STATUS_REG 0x16 +#define IT66121_DDC_STATUS_TX_DONE BIT(7) +#define IT66121_DDC_STATUS_ACTIVE BIT(6) +#define IT66121_DDC_STATUS_NOACK BIT(5) +#define IT66121_DDC_STATUS_WAIT_BUS BIT(4) +#define IT66121_DDC_STATUS_ARBI_LOSE BIT(3) +#define IT66121_DDC_STATUS_FIFO_FULL BIT(2) +#define IT66121_DDC_STATUS_FIFO_EMPTY BIT(1) +#define IT66121_DDC_STATUS_FIFO_VALID BIT(0) + +#define IT66121_EDID_SLEEP_US 20000 +#define IT66121_EDID_TIMEOUT_US 200000 +#define IT66121_EDID_FIFO_SIZE 32 + +#define IT66121_CLK_CTRL0_REG 0x58 +#define IT66121_CLK_CTRL0_AUTO_OVER_SAMPLING BIT(4) +#define IT66121_CLK_CTRL0_EXT_MCLK_MASK GENMASK(3, 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_128FS (0 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_256FS BIT(2) +#define IT66121_CLK_CTRL0_EXT_MCLK_512FS (2 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_1024FS (3 << 2) +#define IT66121_CLK_CTRL0_AUTO_IPCLK BIT(0) +#define IT66121_CLK_STATUS1_REG 0x5E +#define IT66121_CLK_STATUS2_REG 0x5F + +#define IT66121_AUD_CTRL0_REG 0xE0 +#define IT66121_AUD_SWL (3 << 6) +#define IT66121_AUD_16BIT (0 << 6) +#define IT66121_AUD_18BIT BIT(6) +#define IT66121_AUD_20BIT (2 << 6) +#define IT66121_AUD_24BIT (3 << 6) +#define IT66121_AUD_SPDIFTC BIT(5) +#define IT66121_AUD_SPDIF BIT(4) +#define IT66121_AUD_I2S (0 << 4) +#define IT66121_AUD_EN_I2S3 BIT(3) +#define IT66121_AUD_EN_I2S2 BIT(2) +#define IT66121_AUD_EN_I2S1 BIT(1) +#define IT66121_AUD_EN_I2S0 BIT(0) +#define IT66121_AUD_CTRL0_AUD_SEL BIT(4) + +#define IT66121_AUD_CTRL1_REG 0xE1 +#define IT66121_AUD_FIFOMAP_REG 0xE2 +#define IT66121_AUD_CTRL3_REG 0xE3 +#define IT66121_AUD_SRCVALID_FLAT_REG 0xE4 +#define IT66121_AUD_FLAT_SRC0 BIT(4) +#define IT66121_AUD_FLAT_SRC1 BIT(5) +#define IT66121_AUD_FLAT_SRC2 BIT(6) +#define IT66121_AUD_FLAT_SRC3 BIT(7) +#define IT66121_AUD_HDAUDIO_REG 0xE5 + +#define IT66121_AUD_PKT_CTS0_REG 0x130 +#define IT66121_AUD_PKT_CTS1_REG 0x131 +#define IT66121_AUD_PKT_CTS2_REG 0x132 +#define IT66121_AUD_PKT_N0_REG 0x133 +#define IT66121_AUD_PKT_N1_REG 0x134 +#define IT66121_AUD_PKT_N2_REG 0x135 + +#define IT66121_AUD_CHST_MODE_REG 0x191 +#define IT66121_AUD_CHST_CAT_REG 0x192 +#define IT66121_AUD_CHST_SRCNUM_REG 0x193 +#define IT66121_AUD_CHST_CHTNUM_REG 0x194 +#define IT66121_AUD_CHST_CA_FS_REG 0x198 +#define IT66121_AUD_CHST_OFS_WL_REG 0x199 + +#define IT66121_AUD_PKT_CTS_CNT0_REG 0x1A0 +#define IT66121_AUD_PKT_CTS_CNT1_REG 0x1A1 +#define IT66121_AUD_PKT_CTS_CNT2_REG 0x1A2 + +#define IT66121_AUD_FS_22P05K 0x4 +#define IT66121_AUD_FS_44P1K 0x0 +#define IT66121_AUD_FS_88P2K 0x8 +#define IT66121_AUD_FS_176P4K 0xC +#define IT66121_AUD_FS_24K 0x6 +#define IT66121_AUD_FS_48K 0x2 +#define IT66121_AUD_FS_96K 0xA +#define IT66121_AUD_FS_192K 0xE +#define IT66121_AUD_FS_768K 0x9 +#define IT66121_AUD_FS_32K 0x3 +#define IT66121_AUD_FS_OTHER 0x1 + +#define IT66121_AUD_SWL_21BIT 0xD +#define IT66121_AUD_SWL_24BIT 0xB +#define IT66121_AUD_SWL_23BIT 0x9 +#define IT66121_AUD_SWL_22BIT 0x5 +#define IT66121_AUD_SWL_20BIT 0x3 +#define IT66121_AUD_SWL_17BIT 0xC +#define IT66121_AUD_SWL_19BIT 0x8 +#define IT66121_AUD_SWL_18BIT 0x4 +#define IT66121_AUD_SWL_16BIT 0x2 +#define IT66121_AUD_SWL_NOT_INDICATED 0x0 + +#define IT66121_AFE_CLK_HIGH 80000 /* Khz */ + +#endif From patchwork Sun Oct 29 19:46:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159411 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831960vqb; Sun, 29 Oct 2023 12:47:31 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGoD+0hZX9QJ7TAz22o1N4UpT6WrVogChQXUs64oaGWOXQ9HvrSdggUTYcGYgadHuHGin0/ X-Received: by 2002:a05:6870:c69c:b0:1ef:c112:4b36 with SMTP id cv28-20020a056870c69c00b001efc1124b36mr3760164oab.31.1698608850596; Sun, 29 Oct 2023 12:47:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608850; cv=none; d=google.com; s=arc-20160816; b=K6GVV9tQpDnKFNnnxJCO9aCzA263a1meVO+g7oUuW8YHJ9t7xyTEzHy1oqszSBamDQ MXc3cAiV56r4qLR8jKACmBVqz9rO330Sl5KqZDelJcX+oi0Dhb3jXo2rMHDlJe+zu+rk ovcLKZ02lmidqUxHoqPYd0gc5VBPLI7FmjYP7eketQ2/+Y2dp3mHIIgd3eMwgWLpndmN 3VCBvwec9FhzV/J6er0H8dTPTm4o0pUGD7RuEeNAGVGU40wUUP8EWs0j3xsEngle4o4X BHMXQiDaQx7QzJj6LtcGd6EIg/EGlCCLK30ICpqaH+5TxNkox+3lLO3Hf5JIKVrBBdJ7 jq9Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=BXzIFHkkc5bsNe9Qh2Ov33c7nNAwcqP0M0oDbZJBcnE=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=NcsZu1BpgAKGAyco9v1w79FLsIesX8y9KtPX/SdDpv79Ei/MpxDF3YIrDugta9rdUq uZ4bgyeNp9/9UCODaqSRhghykiWrAryLJCSHWzacg+6zJG2jE1AUIpuxiPWleGSfjooE yTarX+U2pU20Dkyfa/IIgIVnpC46VboDTyxeE2g0wBKOOO7yzaNIx5tV1H/OceIGKroh OL5v8xkONko0Lo8THfbUYihk4/ReZPCRpkxRf7CgPDxnLezU4HlDS9InghFSvG22wFlz Xkong/Oui58sEE+jnVClUgcdZjqQ0PsNh65uImOJIofwY10jnhG4E1VehdX9JZu1c2as JH+Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.34 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from howler.vger.email (howler.vger.email. [23.128.96.34]) by mx.google.com with ESMTPS id i184-20020a6387c1000000b005b9607abd4asi2376088pge.73.2023.10.29.12.47.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:30 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.34 as permitted sender) client-ip=23.128.96.34; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.34 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by howler.vger.email (Postfix) with ESMTP id E14AD8065CFB; Sun, 29 Oct 2023 12:47:19 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at howler.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230379AbjJ2Tqa (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34576 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230382AbjJ2TqS (ORCPT ); Sun, 29 Oct 2023 15:46:18 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 83068BD for ; Sun, 29 Oct 2023 12:46:13 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8CxruuEtj5lups1AA--.36329S3; Mon, 30 Oct 2023 03:46:12 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S5; Mon, 30 Oct 2023 03:46:11 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/8] drm/loongson: Allow attach drm bridge driver by calling lsdc_output_init() Date: Mon, 30 Oct 2023 03:46:02 +0800 Message-Id: <20231029194607.379459-4-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S5 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj9fXoWfuF4rZFyxAr4ftw1fKFyUJwc_yoW8KFy8to WxXwnavw18KryIqrZ5KF18KFWDZa1Fq3W3Gwn5GFZrua98Cw4Yq34xG34UKFWSqr1Sgr4U Z3Wqq3s7XFsrZF4kl-sFpf9Il3svdjkaLaAFLSUrUUUU1b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYD7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUAVWUZwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26r4j6ryUM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26r4j6F4UM28EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v2 6r4j6r4UJwAaw2AFwI0_Jrv_JF1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Jw0_ WrylYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km07C267AKxVWU XVWUAwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVW8JVW5JwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0x vEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVj vjDU0xZFpf9x07jrPEfUUUUU= X-Spam-Status: No, score=-0.8 required=5.0 tests=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 howler.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 (howler.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:47:20 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120474379152282 X-GMAIL-MSGID: 1781120474379152282 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 +- 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; + } + + 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; +} + +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 */ + +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) +{ +} + +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); + 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 From patchwork Sun Oct 29 19:46:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159413 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831981vqb; Sun, 29 Oct 2023 12:47:34 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGseC5+S0McRX9VRBFnGQtoKw5zRq3K8zsY8jOCnMqtraWGlcJbhXMqPVq6UIQP5wTOFlJC X-Received: by 2002:a05:6a20:1608:b0:17d:faed:603c with SMTP id l8-20020a056a20160800b0017dfaed603cmr8988664pzj.11.1698608853881; Sun, 29 Oct 2023 12:47:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608853; cv=none; d=google.com; s=arc-20160816; b=X/vUyaHrmIS/fmG5wkvW5sSP2RwA//177HZ90v92zhTtGHhY6NkaUAzg5LJiKv6yQ5 a/MEomaTs9SmB57ehdXbos9FyauOk3TmqHBkJ9VmOc71brNseyl7Y02pKRf3/saPxrWV fbxvcm4LCDpQofTuUeVoxVUMq5w4KhD3AyaUNY9c6yxKXvpI85nusdgSj74j+d38UY4a IQKJHbXbDUwvoBNaV8AK7/YwYZoQSrBTmEg3QFS+SdOxQIvhDoS8FXKyl3yCeLxYmHBA CZLt45HXzM2cZEkPyap0/ynqTYDgG6ii0c7ugk4Dy3fwT9n0tbVXph2Pjl9SMyLk479s 7IjQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=tIYxdFBz02/rYcHc58lH3Syri14Nj3blOBg5Hux4OVo=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=NdVEdcAgewPim6Y5KVdH1v34wYWN4CTbg5g1mIVwmeV5IUvBoq4hzEQinUKAgkUOLe lWnq3jbHT3hIWUAq6GX03wvN8HfYCcjEy8EhzqNU/Odc4u3kJ+IWJIl2sffVQVIFiyaZ kGWN9YkZbDhZUM6/feLAaQ7UOlf0060G5BRaZN3TXIDxnwVfukNTfdCykDXI6BlxizIU DdPxOo3acwTiR2TYBLMVYCpZr/8oE53s7pC79a+dFNiittk6wuYg2hIGxXTsIDQ4FqkK 4dZ9+zi9q2wd3HK2Q9x0wxNHicNQFIxvusb4VI85J9HSsOE7yJYCDys2X5sl62sXB9K3 7MHQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.32 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from agentk.vger.email (agentk.vger.email. [23.128.96.32]) by mx.google.com with ESMTPS id l1-20020a056a00140100b0068e4704fd5bsi3991293pfu.346.2023.10.29.12.47.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:33 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.32 as permitted sender) client-ip=23.128.96.32; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.32 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by agentk.vger.email (Postfix) with ESMTP id D0595805A583; Sun, 29 Oct 2023 12:47:30 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at agentk.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230429AbjJ2Tq0 (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34528 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230344AbjJ2TqR (ORCPT ); Sun, 29 Oct 2023 15:46:17 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 3207EC2 for ; Sun, 29 Oct 2023 12:46:13 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8Dxg_CEtj5lvps1AA--.39559S3; Mon, 30 Oct 2023 03:46:12 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S6; Mon, 30 Oct 2023 03:46:11 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 4/8] drm/loongson: Started to attach display bridge driver for LS7A1000 Date: Mon, 30 Oct 2023 03:46:03 +0800 Message-Id: <20231029194607.379459-5-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S6 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj93XoW3Gry5trW8Ar4fZFy8JF17XFc_yoWxArWUpF s8t393tr48XF1rWr4vyr1DWw15ArWqkFyUtrs7uw1S9as3Krn0qF4xtr1DW3WDXa95ur1U twsFqw43CF18CwcCm3ZEXasCq-sJn29KB7ZKAUJUUUUr529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUU9jb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ Gr0_Gr1UM2kKe7AKxVWUXVWUAwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYI kI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUtVWr XwAv7VC2z280aVAFwI0_Gr0_Cr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI4 8JMxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMxCIbckI1I0E14v26r1Y 6r17MI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7 AF67AKxVWUAVWUtwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r4j6ryUMIIF0xvE 2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcV C2z280aVAFwI0_Gr0_Cr1lIxAIcVC2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2Kfnx nUUI43ZEXa7IU8XyCJUUUUU== X-Spam-Status: No, score=-0.8 required=5.0 tests=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 agentk.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 (agentk.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:47:30 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120477359484179 X-GMAIL-MSGID: 1781120477359484179 Loongson ML5B_MA board using ITE IT66121 HDMI transmitter to support HDMI display output, with the vbios provided the necessary information, we are able to create a minimal drm bridge driver for it. After apply this patch we are able to change mode freely. Tested on LS3A5000+LS7A1000 ML5B_MA board. $ dmesg | grep drm [drm] dc: 264MHz, gmc: 529MHz, gpu: 529MHz [drm] Dedicated vram start: 0xe0030000000, size: 64MiB [drm] Loongson VBIOS version: 0.3 [drm] Loongson VBIOS: has 8 DCBs [drm] VRAM: 4096 pages ready [drm] GTT: 32768 pages ready [drm] lsdc-i2c0(sda pin mask=1, scl pin mask=2) created [drm] lsdc-i2c1(sda pin mask=4, scl pin mask=8) created [drm] DisplayPipe-0 has DVO-0 [drm] device address(0x4d) is not correct [drm] i2c client IT66121@0x4c created [drm] IT66121 attached, Vendor ID: 0x4954, Device ID: 0x612 [drm] Total 2 outputs [drm] registered irq: 40 [drm] Initialized loongson 1.0.0 20220701 for 0000:00:06.1 on minor 0 loongson 0000:00:06.1: [drm] fb0: loongsondrmfb frame buffer device Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 144 +++++++----------- 1 file changed, 55 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c index 6fc8dd1c7d9a..e12f9a0157d0 100644 --- a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c +++ b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c @@ -10,6 +10,7 @@ #include "lsdc_drv.h" #include "lsdc_output.h" +#include "ite_it66121.h" /* * The display controller in the LS7A1000 exports two DVO interfaces, thus * external encoder is required, except connected to the DPI panel directly. @@ -38,68 +39,6 @@ * TODO: Add support for non-transparent encoders */ -static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn) -{ - unsigned int num = 0; - struct edid *edid; - - if (conn->ddc) { - edid = drm_get_edid(conn, conn->ddc); - if (edid) { - drm_connector_update_edid_property(conn, edid); - num = drm_add_edid_modes(conn, edid); - kfree(edid); - } - - return num; - } - - num = drm_add_modes_noedid(conn, 1920, 1200); - - drm_set_preferred_mode(conn, 1024, 768); - - return num; -} - -static struct drm_encoder * -ls7a1000_dpi_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; -} - -static const struct drm_connector_helper_funcs -ls7a1000_dpi_connector_helpers = { - .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder, - .get_modes = ls7a1000_dpi_connector_get_modes, -}; - -static enum drm_connector_status -ls7a1000_dpi_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; -} - -static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = { - .detect = ls7a1000_dpi_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 -}; - static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder) { struct drm_device *ddev = encoder->dev; @@ -139,40 +78,67 @@ static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = { }, }; +/* + * This is a default output description for LS7A1000/LS2K1000, this is always + * true from the hardware perspective. It is just that when there are external + * display bridge connected, this description no longer complete. As it cannot + * describe the topology about the external encoders. + */ +static const struct lsdc_output_desc ls7a1000_output_desc[2] = { + { + .pipe = 0, + .encoder_type = DRM_MODE_ENCODER_DPI, + .connector_type = DRM_MODE_CONNECTOR_DPI, + .encoder_funcs = &ls7a1000_encoder_funcs[0], + .encoder_helper_funcs = &lsdc_encoder_helper_funcs, + .connector_funcs = &lsdc_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "DVO-0", + }, + { + .pipe = 1, + .encoder_type = DRM_MODE_ENCODER_DPI, + .connector_type = DRM_MODE_CONNECTOR_DPI, + .encoder_funcs = &ls7a1000_encoder_funcs[1], + .encoder_helper_funcs = &lsdc_encoder_helper_funcs, + .connector_funcs = &lsdc_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "DVO-1", + }, +}; + int ls7a1000_output_init(struct drm_device *ddev, struct lsdc_display_pipe *dispipe, struct i2c_adapter *ddc, unsigned int index) { struct lsdc_output *output = &dispipe->output; - struct drm_encoder *encoder = &output->encoder; - struct drm_connector *connector = &output->connector; - int ret; - - ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index], - DRM_MODE_ENCODER_TMDS, "encoder-%u", index); - if (ret) - return ret; - - encoder->possible_crtcs = BIT(index); - - ret = drm_connector_init_with_ddc(ddev, connector, - &ls7a1000_dpi_connector_funcs, - DRM_MODE_CONNECTOR_DPI, ddc); - if (ret) - return ret; - - drm_info(ddev, "display pipe-%u has a DVO\n", index); - - drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers); - - drm_connector_attach_encoder(connector, encoder); + enum loongson_vbios_encoder_name encoder_name = 0; + struct drm_bridge *bridge = NULL; + u8 slave_addr; + bool ret; + + output->descp = &ls7a1000_output_desc[index]; + + ret = loongson_vbios_query_encoder_info(ddev, index, NULL, + &encoder_name, &slave_addr); + if (!ret) + goto skip; + + switch (encoder_name) { + case ENCODER_CHIP_IT66121: + bridge = it66121_bridge_create(ddev, ddc, slave_addr, false, + 0, index); + break; + default: + break; + } - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; + if (IS_ERR(bridge)) + goto skip; - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; + output->bridge = bridge; - return 0; +skip: + return lsdc_output_init(ddev, dispipe, ddc, index); } From patchwork Sun Oct 29 19:46:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159409 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831904vqb; Sun, 29 Oct 2023 12:47:18 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFb2DCvN2dtlTffFaRuFtgRGIYZKftfE2eZt/HFUch3JJqmKMsIIoI4xkc9IY7iP1Z25+0O X-Received: by 2002:a05:6602:2b91:b0:79f:d4e6:5175 with SMTP id r17-20020a0566022b9100b0079fd4e65175mr12035469iov.16.1698608838178; Sun, 29 Oct 2023 12:47:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608838; cv=none; d=google.com; s=arc-20160816; b=ekzW5dr5cnmF6xlBvEw7C5x4a6w8NtP9m8nZkBAuXgy3OdqfIADhp9nA+8iDctuJrT j7QV4Co/wN0fy8Q5hz1TYNSQG/S0+a252P8vm/KfTDdrWH7ji6N4UQk/HQA7FR0wUwFP gZOw2eIyepxmbPTiRp7Gj+ztBoeGdah0v7GymNCSIokSupX2QVq4XpKPRm3XNID5N8Eh x67I6xe4NP0IscnVZ1CeAQKrqXqe04EW/tURdDDbjchm4VbKFu18R9Hqy9KnLN2XiZaT xpW7YVWUzn74lG1NUdq6mb7sUdG1gCLaeq163xYtSdaO2t1OdwEFwQCdtCHVvB1gEM78 l4ZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=5MThRd7LNQAD4f0z2huOLK9qBWbyk9tZAHmnM3A77oo=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=XH/nfRbLa9lYo/vFITsA36khzaEgUPdkroBz/ArqN5rxx9+snxOKQV2Sv4MydXoAc6 xCMFaPafXGtU1PkwWsD3QBvwUbZN/NtrInKnxJXfm5K6KGmPUyGyFi1Iarc7c5Ht3saG 23j3VDERXOMo0vzq5K4OY/uk5PjFvl6/x/H4sRngC9TWcsU9jV5/Ye9jtzxMNePO0T65 QEGF4Fvzvh0/wlOWeFPoQU0S9BR0kQ/ArWk4JE8cz4KJv3Xer1UCB4FjNOSolyw11fQx wM2SB/W8wQZs4PHjyNgyL5UbSCxu54LvuH62ZYVybI9sK8/1nMIuzJcu/cHtmp5CZtuQ sk6g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from howler.vger.email (howler.vger.email. [2620:137:e000::3:4]) by mx.google.com with ESMTPS id bc14-20020a656d8e000000b005b96b1dc9d9si1992445pgb.195.2023.10.29.12.47.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:18 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) client-ip=2620:137:e000::3:4; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by howler.vger.email (Postfix) with ESMTP id 43CEE8061282; Sun, 29 Oct 2023 12:47:00 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at howler.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230422AbjJ2TqW (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34542 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230338AbjJ2TqR (ORCPT ); Sun, 29 Oct 2023 15:46:17 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 752D0C4 for ; Sun, 29 Oct 2023 12:46:14 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8CxtPCFtj5lwps1AA--.40172S3; Mon, 30 Oct 2023 03:46:13 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S7; Mon, 30 Oct 2023 03:46:12 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 5/8] drm/loongson: Using vbios for the LS7A2000 output initialization Date: Mon, 30 Oct 2023 03:46:04 +0800 Message-Id: <20231029194607.379459-6-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S7 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj93XoW3Gr4UAw4kGw45JF4DZw1rKrX_yoWxtw4Dpr sxtrZ3Jr1kZF1Fyr1kAr1kX34YyrWvkFySy3s29w1Sy34fJr90qF47tr1UW3WUJa9Y9r12 vrsrXw4ak3WUC3gCm3ZEXasCq-sJn29KB7ZKAUJUUUUr529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUU9jb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r126r13M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ Gr0_Gr1UM2kKe7AKxVWUXVWUAwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYI kI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUtVWr XwAv7VC2z280aVAFwI0_Gr0_Cr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI4 8JMxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMxCIbckI1I0E14v26r1Y 6r17MI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7 AF67AKxVWUAVWUtwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26ryj6F1UMIIF0xvE 2Ix0cI8IcVCY1x0267AKxVW8JVWxJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcV C2z280aVAFwI0_Gr0_Cr1lIxAIcVC2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2Kfnx nUUI43ZEXa7IU8XyCJUUUUU== X-Spam-Status: No, score=-0.8 required=5.0 tests=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 howler.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 (howler.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:47:00 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120460765981782 X-GMAIL-MSGID: 1781120460765981782 For LS7A2000, the built-in VGA encoder is transparent. Connect another external transmitter with this internal VGA encoder is not sane, thus is not allowed. Because there are two internal encoders hardware resource on the first display pipe, call loongson_vbios_query_encoder_info() to know what exatly the output configutaion is. Either VGA or HDMI display output interface, but not both. And formal products should not export three display connector interfaces. As the hardware has two-way I2Cs and two CRTCs. So with this observation, we can untangle more. If there a need to extend(transform) the output interface type, then the internal HDMI phy MUST be enabled and initialized. External transmitters must take the HDMI signal as input, this is the only choices. Such as lt6711(HDMI to eDP), lt8619(HDMI to LVDS) etc. Before apply this patch, ls7a2000_output_init() is simplified function which assumed that there is no external display bridge attached. This naive abstraction no longer suit the needs in the long run. Hence, switch to call the newly implemented lsdc_output_init() function, which allow us model the external encoder as a drm display bridge. The driver of this drm display bridge should reside in the same kernel module with drm/loongson. We will attach it by ourself, and rely on the VBIOS tell us which display pipe has what display bridge connected. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 154 ++++++++++++++---- 1 file changed, 124 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c index ce3dabec887e..bf558b61802b 100644 --- a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c +++ b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c @@ -501,6 +501,126 @@ static const struct drm_encoder_helper_funcs ls7a2000_encoder_helper_funcs = { .atomic_mode_set = ls7a2000_hdmi_atomic_mode_set, }; +/* The built-in tranparent VGA encoder is only available on display pipe 0 */ +static void ls7a2000_pipe0_vga_encoder_reset(struct drm_encoder *encoder) +{ + struct lsdc_device *ldev = to_lsdc(encoder->dev); + u32 val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN; + + lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, val); + + /* + * The firmware set LSDC_HDMIx_CTRL_REG blindly to use hardware I2C, + * which is may not works because of hardware bug. We using built-in + * GPIO emulated I2C instead of the hardware I2C here. + */ + lsdc_ureg32_clr(ldev, LSDC_HDMI0_INTF_CTRL_REG, HW_I2C_EN); + + mdelay(20); +} + +static const struct drm_encoder_funcs ls7a2000_pipe0_vga_encoder_funcs = { + .reset = ls7a2000_pipe0_vga_encoder_reset, + .destroy = drm_encoder_cleanup, +}; + +static const struct lsdc_output_desc ls7a2000_vga_pipe0 = { + .pipe = 0, + .encoder_type = DRM_MODE_ENCODER_DAC, + .connector_type = DRM_MODE_CONNECTOR_VGA, + .encoder_funcs = &ls7a2000_pipe0_vga_encoder_funcs, + .encoder_helper_funcs = &lsdc_pipe0_hdmi_encoder_helper_funcs, + .connector_funcs = &lsdc_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "VGA-0", +}; + +static const struct lsdc_output_desc ls7a2000_hdmi_pipe0 = { + .pipe = 0, + .encoder_type = DRM_MODE_ENCODER_TMDS, + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + .encoder_funcs = &lsdc_pipe0_hdmi_encoder_funcs, + .encoder_helper_funcs = &lsdc_pipe0_hdmi_encoder_helper_funcs, + .connector_funcs = &lsdc_pipe0_hdmi_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "HDMI-0", +}; + +static const struct lsdc_output_desc ls7a2000_hdmi_pipe1 = { + .pipe = 1, + .encoder_type = DRM_MODE_ENCODER_TMDS, + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + .encoder_funcs = &lsdc_pipe1_hdmi_encoder_funcs, + .encoder_helper_funcs = &lsdc_pipe1_hdmi_encoder_helper_funcs, + .connector_funcs = &lsdc_pipe1_hdmi_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "HDMI-1", +}; + +/* + * For LS7A2000, the built-in VGA encoder is transparent. If there are + * external encoder exist, then the internal HDMI encoder MUST be enabled + * and initialized. As the internal HDMI encoder is always connected, so + * only the transmitters which take HDMI signal (such as HDMI to eDP, HDMI + * to LVDS, etc) are usable with. + */ +const struct lsdc_output_desc * +ls7a2000_query_output_configuration(struct drm_device *ddev, unsigned int pipe) +{ + enum loongson_vbios_encoder_name encoder_name = 0; + bool ret; + + ret = loongson_vbios_query_encoder_info(ddev, pipe, NULL, + &encoder_name, NULL); + if (!ret) + goto bailout; + + if (pipe == 0) { + switch (encoder_name) { + case ENCODER_CHIP_INTERNAL_HDMI: + return &ls7a2000_hdmi_pipe0; + + /* + * For LS7A2000, the built-in VGA encoder is transparent. + */ + case ENCODER_CHIP_INTERNAL_VGA: + return &ls7a2000_vga_pipe0; + + /* + * External display bridge exists, the internal HDMI encoder + * MUST be enabled and initialized. Please add a drm bridge + * driver, and attach to this encoder. + */ + default: + return &ls7a2000_hdmi_pipe0; + } + } + + if (pipe == 1) { + switch (encoder_name) { + case ENCODER_CHIP_INTERNAL_HDMI: + return &ls7a2000_hdmi_pipe1; + + /* + * External display bridge exists, the internal HDMI encoder + * MUST be enabled and initialized. Please add a drm bridge + * driver, and attach it to this encoder. + */ + default: + return &ls7a2000_hdmi_pipe1; + } + } + +bailout: + if (pipe == 0) + return &ls7a2000_vga_pipe0; + + if (pipe == 1) + return &ls7a2000_hdmi_pipe1; + + return NULL; +} + /* * For LS7A2000: * @@ -517,36 +637,10 @@ int ls7a2000_output_init(struct drm_device *ddev, unsigned int pipe) { struct lsdc_output *output = &dispipe->output; - struct drm_encoder *encoder = &output->encoder; - struct drm_connector *connector = &output->connector; - int ret; - - ret = drm_encoder_init(ddev, encoder, &ls7a2000_encoder_funcs[pipe], - DRM_MODE_ENCODER_TMDS, "encoder-%u", pipe); - if (ret) - return ret; - - encoder->possible_crtcs = BIT(pipe); - - drm_encoder_helper_add(encoder, &ls7a2000_encoder_helper_funcs); - - ret = drm_connector_init_with_ddc(ddev, connector, - &ls7a2000_hdmi_connector_funcs[pipe], - DRM_MODE_CONNECTOR_HDMIA, ddc); - if (ret) - return ret; - drm_info(ddev, "display pipe-%u has HDMI %s\n", pipe, pipe ? "" : "and/or VGA"); + output->descp = ls7a2000_query_output_configuration(ddev, pipe); + if (!output->descp) + return -EINVAL; - drm_connector_helper_add(connector, &ls7a2000_connector_helpers); - - drm_connector_attach_encoder(connector, encoder); - - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; - - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - return 0; + return lsdc_output_init(ddev, dispipe, ddc, pipe); } From patchwork Sun Oct 29 19:46:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159406 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831870vqb; Sun, 29 Oct 2023 12:47:13 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF2Nr5RfGIPw/gYWrm8ehzJnz/ecC5gha3zVEnK9WwqgG2YhNDM92RKLuCi+hGsQZO5J7Zp X-Received: by 2002:a05:6a21:4843:b0:166:ef18:658e with SMTP id au3-20020a056a21484300b00166ef18658emr8818785pzc.62.1698608833217; Sun, 29 Oct 2023 12:47:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608833; cv=none; d=google.com; s=arc-20160816; b=ee6K4UJ11AM2UGTxy44lILnZNPfQEmEq3iBNxv+ul4qub0XmZAFUlV/Uz2G0wVH4QX aIIIT/A9u0hDxMoJb6+iAhTNAmNK+6Qn9orKx5pMPwX4SI+ao1dkiYooH4gPUxJ6wLXv uSgKSqurqDysLig65Be8LV3De8iKccId719ETAQi9kLx9zwGMb6SleyQ06gxu2mmHXin hPBNoX+vfMhhqGQcDg8hqgWhu2lhlHrwHihs5kAcdBsGCDAr6U/eg8cCDNSDQQKuPKSe 4HFc3G+Y46O6MmOJuS2cAE7KIlQGGHN7QhqID7fs/sI7FJ5110EicyWN/lXi9xCidaV4 Pw3g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=sNZJdEzeiJ8RooCrEofVGr5TfB6znZbcad48Xkyj7PU=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=JEcjDZzbfSBzKrjVmM4Y/Qd6B1KDMM2Mph7iMtBckpS0a8VpC4g3P9wWSCciluzQ1t MUKyO81VNZTD631hNkcxE5C478TL/7rl2XuYtrxt4dgVvtqwgYLR8lbRF/lvi2j4vA+g kKDdSGZxVZx6dX1Ij2OL+lTtD4+TRWF9c9bTDMgjWs6qvbvvsKqPNn9hhZCtxu77IZpe Tb5730mA3mcHf/h7vGiHfR+UeercFMUN057auxzPwO0A8e4d+xsUBQHxYVodLoDVYhFq nd+XK6cHknOhPcY1E+kfw+nwe3gJPpKugv/7rtn19ebXfgdUTO60z2khtSOgljgoJvcN DxQg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from snail.vger.email (snail.vger.email. [2620:137:e000::3:7]) by mx.google.com with ESMTPS id h184-20020a6383c1000000b0056c55eb251csi3958927pge.123.2023.10.29.12.47.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) client-ip=2620:137:e000::3:7; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 73694805A78F; Sun, 29 Oct 2023 12:46:51 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230462AbjJ2Tqg (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34580 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230387AbjJ2TqS (ORCPT ); Sun, 29 Oct 2023 15:46:18 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 25B33C5 for ; Sun, 29 Oct 2023 12:46:14 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8BxXeuFtj5lxps1AA--.34253S3; Mon, 30 Oct 2023 03:46:13 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S8; Mon, 30 Oct 2023 03:46:12 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 6/8] drm/loongson: Clean up the output part of LS7A2000 Date: Mon, 30 Oct 2023 03:46:05 +0800 Message-Id: <20231029194607.379459-7-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S8 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj9fXoWfJry8tr1xJr1kWrW7ZFWfCrX_yoW8Wr1UZo W7Zwna9w10gry7XFs8tF15KFyDZa10q3W3Jw18GFWDuanxGa1jq34xGw15KrWSqF13WF4j y3Wvqwn7XF17uan5l-sFpf9Il3svdjkaLaAFLSUrUUUU1b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYq7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUXVWUAwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26ryj6F1UM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26r4j6F4UM28EF7xvwVC2z280aVAFwI0_Cr0_Gr1UM28EF7xvwVC2z280aVCY1x0267AK xVW8JVW8Jr1ln4kS14v26r1Y6r17M2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6xkI12 xvs2x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v26r1q 6rW5McIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2Ij64 vIr41l42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1l4IxYO2xFxVAFwI0_ Jrv_JF1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1V AY17CE14v26r126r1DMIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Xr0_Ar1lIxAI cVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42 IY6I8E87Iv67AKxVW8JVWxJwCI42IY6I8E87Iv6xkF7I0E14v26r4j6r4UJbIYCTnIWIev Ja73UjIFyTuYvjxU4AhLUUUUU X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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 (snail.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:46:51 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120455898716659 X-GMAIL-MSGID: 1781120455898716659 Since the majority of sharable subroutines have been move to lsdc_output.c, and functional changes are done with previous patch. We finally see the light to cleanup, no functional change. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/lsdc_output_7a2000.c | 469 ------------------ 1 file changed, 469 deletions(-) diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c index bf558b61802b..981ab2045e91 100644 --- a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c +++ b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c @@ -42,465 +42,6 @@ * |______________________| */ -static int ls7a2000_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; - } - - num = drm_add_modes_noedid(connector, 1920, 1200); - - drm_set_preferred_mode(connector, 1024, 768); - - return num; -} - -static struct drm_encoder * -ls7a2000_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; -} - -static const struct drm_connector_helper_funcs ls7a2000_connector_helpers = { - .atomic_best_encoder = ls7a2000_connector_get_best_encoder, - .get_modes = ls7a2000_connector_get_modes, -}; - -/* debugfs */ - -#define LSDC_HDMI_REG(i, reg) { \ - .name = __stringify_1(LSDC_HDMI##i##_##reg##_REG), \ - .offset = LSDC_HDMI##i##_##reg##_REG, \ -} - -static const struct lsdc_reg32 ls7a2000_hdmi0_encoder_regs[] = { - 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 lsdc_reg32 ls7a2000_hdmi1_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 int ls7a2000_hdmi_encoder_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; -} - -static const struct drm_info_list ls7a2000_hdmi0_debugfs_files[] = { - { "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi0_encoder_regs }, -}; - -static const struct drm_info_list ls7a2000_hdmi1_debugfs_files[] = { - { "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi1_encoder_regs }, -}; - -static void ls7a2000_hdmi0_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(ls7a2000_hdmi0_debugfs_files, - ARRAY_SIZE(ls7a2000_hdmi0_debugfs_files), - root, minor); -} - -static void ls7a2000_hdmi1_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(ls7a2000_hdmi1_debugfs_files, - ARRAY_SIZE(ls7a2000_hdmi1_debugfs_files), - root, minor); -} - -/* monitor present detection */ - -static enum drm_connector_status -ls7a2000_hdmi0_vga_connector_detect(struct drm_connector *connector, bool force) -{ - struct drm_device *ddev = connector->dev; - struct lsdc_device *ldev = to_lsdc(ddev); - u32 val; - - val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG); - - if (val & HDMI0_HPD_FLAG) - return connector_status_connected; - - if (connector->ddc) { - if (drm_probe_ddc(connector->ddc)) - return connector_status_connected; - - return connector_status_disconnected; - } - - return connector_status_unknown; -} - -static enum drm_connector_status -ls7a2000_hdmi1_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 drm_connector_funcs ls7a2000_hdmi_connector_funcs[2] = { - { - .detect = ls7a2000_hdmi0_vga_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 = ls7a2000_hdmi0_late_register, - }, - { - .detect = ls7a2000_hdmi1_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 = ls7a2000_hdmi1_late_register, - }, -}; - -/* Even though some board has only one hdmi on display pipe 1, - * We still need hook lsdc_encoder_funcs up on display pipe 0, - * This is because we need its reset() callback get called, to - * set the LSDC_HDMIx_CTRL_REG using software gpio emulated i2c. - * Otherwise, the firmware may set LSDC_HDMIx_CTRL_REG blindly. - */ -static void ls7a2000_hdmi0_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 software gpio emulated i2c */ - val = lsdc_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG); - val &= ~HW_I2C_EN; - lsdc_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, val); - - /* help the hdmi phy to get out of reset state */ - lsdc_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, HDMI_PHY_RESET_N); - - mdelay(20); - - drm_dbg(ddev, "HDMI-0 Reset\n"); -} - -static void ls7a2000_hdmi1_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 software gpio emulated i2c */ - val = lsdc_rreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG); - val &= ~HW_I2C_EN; - lsdc_wreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG, val); - - /* help the hdmi phy to get out of reset state */ - lsdc_wreg32(ldev, LSDC_HDMI1_PHY_CTRL_REG, HDMI_PHY_RESET_N); - - mdelay(20); - - drm_dbg(ddev, "HDMI-1 Reset\n"); -} - -static const struct drm_encoder_funcs ls7a2000_encoder_funcs[2] = { - { - .reset = ls7a2000_hdmi0_encoder_reset, - .destroy = drm_encoder_cleanup, - }, - { - .reset = ls7a2000_hdmi1_encoder_reset, - .destroy = drm_encoder_cleanup, - }, -}; - -static int ls7a2000_hdmi_set_avi_infoframe(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - struct lsdc_output *output = encoder_to_lsdc_output(encoder); - struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); - unsigned int index = dispipe->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, - &output->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; -} - -static void ls7a2000_hdmi_atomic_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) -{ - struct lsdc_output *output = encoder_to_lsdc_output(encoder); - struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); - unsigned int index = dispipe->index; - struct drm_device *ddev = encoder->dev; - struct lsdc_device *ldev = to_lsdc(ddev); - u32 val; - - /* Disable the hdmi phy */ - val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index); - val &= ~HDMI_PHY_EN; - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val); - - /* Disable the hdmi interface */ - val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index); - val &= ~HDMI_INTERFACE_EN; - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val); - - drm_dbg(ddev, "HDMI-%u disabled\n", index); -} - -static void ls7a2000_hdmi_atomic_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) -{ - struct drm_device *ddev = encoder->dev; - struct lsdc_device *ldev = to_lsdc(ddev); - struct lsdc_output *output = encoder_to_lsdc_output(encoder); - struct lsdc_display_pipe *dispipe = output_to_display_pipe(output); - unsigned int index = dispipe->index; - u32 val; - - /* datasheet say it should larger than 48 */ - val = 64 << HDMI_H_ZONE_IDLE_SHIFT | 64 << HDMI_V_ZONE_IDLE_SHIFT; - - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_ZONE_REG, index, 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_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val); - - udelay(2); - - val = HDMI_CTL_PERIOD_MODE | - HDMI_AUDIO_EN | - HDMI_PACKET_EN | - HDMI_INTERFACE_EN | - (8 << HDMI_VIDEO_PREAMBLE_SHIFT); - - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val); - - drm_dbg(ddev, "HDMI-%u enabled\n", index); -} - -/* - * Fout = M * Fin - * - * M = (4 * LF) / (IDF * ODF) - * - * IDF: Input Division Factor - * ODF: Output Division Factor - * LF: Loop Factor - * M: Required Mult - * - * +--------------------------------------------------------+ - * | Fin (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 ls7a2000_hdmi_phy_pll_config(struct lsdc_device *ldev, - int fin, - unsigned int index) -{ - struct drm_device *ddev = &ldev->base; - int count = 0; - u32 val; - - /* Firstly, disable phy pll */ - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, 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 (fin >= 170000) - val = (16 << HDMI_PLL_IDF_SHIFT) | - (40 << HDMI_PLL_LF_SHIFT) | - (0 << HDMI_PLL_ODF_SHIFT); - else if (fin >= 85000) - val = (8 << HDMI_PLL_IDF_SHIFT) | - (40 << HDMI_PLL_LF_SHIFT) | - (1 << HDMI_PLL_ODF_SHIFT); - else if (fin >= 42500) - val = (4 << HDMI_PLL_IDF_SHIFT) | - (40 << HDMI_PLL_LF_SHIFT) | - (2 << HDMI_PLL_ODF_SHIFT); - else if (fin >= 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, index, val); - - val |= HDMI_PLL_ENABLE; - - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, val); - - udelay(2); - - drm_dbg(ddev, "Fin of HDMI-%u: %d kHz\n", index, fin); - - /* Wait hdmi phy pll lock */ - do { - val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index); - - if (val & HDMI_PLL_LOCKED) { - drm_dbg(ddev, "Setting HDMI-%u PLL take %d cycles\n", - index, count); - break; - } - ++count; - } while (count < 1000); - - lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CAL_REG, index, 0x0f000ff0); - - if (count >= 1000) - drm_err(ddev, "Setting HDMI-%u PLL failed\n", index); -} - -static void ls7a2000_hdmi_atomic_mode_set(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 lsdc_display_pipe *dispipe = output_to_display_pipe(output); - unsigned int index = dispipe->index; - struct drm_device *ddev = encoder->dev; - struct lsdc_device *ldev = to_lsdc(ddev); - struct drm_display_mode *mode = &crtc_state->mode; - - ls7a2000_hdmi_phy_pll_config(ldev, mode->clock, index); - - ls7a2000_hdmi_set_avi_infoframe(encoder, mode); - - drm_dbg(ddev, "%s modeset finished\n", encoder->name); -} - -static const struct drm_encoder_helper_funcs ls7a2000_encoder_helper_funcs = { - .atomic_disable = ls7a2000_hdmi_atomic_disable, - .atomic_enable = ls7a2000_hdmi_atomic_enable, - .atomic_mode_set = ls7a2000_hdmi_atomic_mode_set, -}; - /* The built-in tranparent VGA encoder is only available on display pipe 0 */ static void ls7a2000_pipe0_vga_encoder_reset(struct drm_encoder *encoder) { @@ -621,16 +162,6 @@ ls7a2000_query_output_configuration(struct drm_device *ddev, unsigned int pipe) return NULL; } -/* - * For LS7A2000: - * - * 1) Most of board export one vga + hdmi output interface. - * 2) Yet, Some boards export double hdmi output interface. - * 3) Still have boards export three output(2 hdmi + 1 vga). - * - * So let's hook hdmi helper funcs to all display pipe, don't miss. - * writing hdmi register do no harms. - */ int ls7a2000_output_init(struct drm_device *ddev, struct lsdc_display_pipe *dispipe, struct i2c_adapter *ddc, From patchwork Sun Oct 29 19:46:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159412 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831974vqb; Sun, 29 Oct 2023 12:47:32 -0700 (PDT) X-Google-Smtp-Source: AGHT+IExe3wtWBs2TXIOVAvdHpVYP6HzeJf/T/eeAvXhR6gCQahFMUOojPxDVFbZCrXvir4Ui+SB X-Received: by 2002:a05:6358:99a3:b0:168:e922:cba9 with SMTP id j35-20020a05635899a300b00168e922cba9mr8708078rwb.20.1698608852547; Sun, 29 Oct 2023 12:47:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608852; cv=none; d=google.com; s=arc-20160816; b=cFzptGwec7pDrWqYOQPmSYQMg4xf+m/61kT98+LRDhhMPs8HCPRa32ocK6nHm2HNuU djpl/lkFUf/vJ6rRH2NNIGFTuaBN/BMXGvoRj30X2Ob1/WPSGW8KFB0nhduhnY+fAJMX BLV8RlrHwfdHV+87P92RacCpJDIGoGYwraojjMPOUrIxi2NfHd88a/aPtZYayRNCbBnS RIwKj2qE0o5uBYmGj+qtQZGdO3m8HHu/ktuCVMHj00xtqnGQQNOJJ0Wl5kRNZZr2Flc1 Vt4OGhagZLevYD/WAV+F8IezFHa20tIkcSNXPQVFWbup8XoHIydV0+GYGlwjTAV1navI TLtQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=Ewda1mYKvupl0n+GNMnctu0KRGdMdkSUGItASMQtG7E=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=C7v+FlvN8TaQXKrwzAVHGc10fePDIiXlO8svsE0g77bBmVs+6NTkGBUvBWxrxH1PPn eebbIWapZv0mGguYzmAeDqdPfR8Y7gvYYdjdlOEB/Qz4NQdBQKVxshMtbpzVAPcLv6qr D3lxOUgqCE10dysgUNQbBmDv9yxOLLHFALQxJqIPDsVh/xktQGImDSX9RdOAvxofezOC X4tXSClm0HDfVOoE0ksOw0z4r/DQyZboU95bjHY3vTYtf+SiSZoZUh8vA90lUXf81F/u O5YNwtidEq10xrRoQgdpEew03gAJJUI5Yq50veBUdS5zFhsEfZXXKZQ+1ljGiElB3r5k XQkw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from pete.vger.email (pete.vger.email. [23.128.96.36]) by mx.google.com with ESMTPS id w18-20020a17090aea1200b002774c17dd73si3896178pjy.11.2023.10.29.12.47.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) client-ip=23.128.96.36; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.36 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 3920E8065E2D; Sun, 29 Oct 2023 12:47:22 -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 S230420AbjJ2Tqe (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34546 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230380AbjJ2TqR (ORCPT ); Sun, 29 Oct 2023 15:46:17 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 53F00C6 for ; Sun, 29 Oct 2023 12:46:15 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8Cxh+iGtj5lyps1AA--.3638S3; Mon, 30 Oct 2023 03:46:14 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S9; Mon, 30 Oct 2023 03:46:13 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 7/8] drm/loongson: Support to infer DC reversion from CPU's PRID value Date: Mon, 30 Oct 2023 03:46:06 +0800 Message-Id: <20231029194607.379459-8-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S9 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj93XoWxJw4kKw4UCw15AF1ruFWfJFc_yoWrGw4xpr ZxAFySkryDGw12y39xAr18Aa4fAa4fXFZ3uFZ2kw1qgw1UAa4UWFyUCF4YvrZrZryxJry2 v3sakrWUuF1aywcCm3ZEXasCq-sJn29KB7ZKAUJUUUUr529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUU90b4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVWxJVW8Jr1l84ACjcxK6I8E87Iv6xkF7I0E14v2 6r4j6r4UJwAaw2AFwI0_Jrv_JF1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Jw0_ WrylYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km07C267AKxVWU XVWUAwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVW5JVW7JwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0x vEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVj vjDU0xZFpf9x07jz2NtUUUUU= X-Spam-Status: No, score=-0.8 required=5.0 tests=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 12:47:22 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120476625430290 X-GMAIL-MSGID: 1781120476625430290 Due to the fact that the same display IP core has been integrated into different platform, there is a need to differentiate them on the runtime. The DC in LS7A1000/LS2K1000 has the PCI vendor & device ID of 0x0014:0x7A06 The DC in LS7A2000/LS2K2000 has the PCI vendor & device ID of 0x0014:0x7A36 Because the output ports and host platform of the DC IP varies, without a revision information we can't achieve fine-grained controls. The canonical approach to do such a thing is to read reversion register from the PCIe device. But LS2K1000 SoC was taped out at 2017, it is rather old. Our BIOS engineer don't assign a different revision ID to it, probably because of ignorance. LS2K2000 SoC was newly taped on 2023, we strictly force the BIOS engineer assign a different revision ID(0x10) to it. But the problem is that it is too casual, there is no formal convention or public documented rule established. For Loongson LS2K series SoC, the display controller IP is taped togather with the CPU core. For Loongson LS7A series bridge chips, the display controller IP is taped togather with the bridge chips itself. Consider the fact the all Loongson CPU has a unique PRID, this patch choose to infer DC reversion from CPU's PRID value. - LS3A4000/LS3A5000 has 0xC0 as its processor ID. - LS2K2000 has 0xB0 as its processor ID. - LS2K2000LA has 0xA0 as its processor ID. The provided approach has no dependency on DT or ACPI, thus is preferfed. Besides, this approach can be used to acquire more addtional HW features. So the provided method has the potential to bring more benifits. Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/lsdc_drv.h | 2 ++ drivers/gpu/drm/loongson/lsdc_probe.c | 35 +++++++++++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_probe.h | 2 ++ 3 files changed, 39 insertions(+) diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h index 46ba9b88a30d..e1f4a2db2a0a 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.h +++ b/drivers/gpu/drm/loongson/lsdc_drv.h @@ -42,6 +42,8 @@ enum loongson_chip_id { CHIP_LS7A1000 = 0, CHIP_LS7A2000 = 1, + CHIP_LS2K1000 = 2, + CHIP_LS2K2000 = 3, CHIP_LS_LAST, }; diff --git a/drivers/gpu/drm/loongson/lsdc_probe.c b/drivers/gpu/drm/loongson/lsdc_probe.c index 48ba69bb8a98..f49b642d8f65 100644 --- a/drivers/gpu/drm/loongson/lsdc_probe.c +++ b/drivers/gpu/drm/loongson/lsdc_probe.c @@ -54,3 +54,38 @@ unsigned int loongson_cpu_get_prid(u8 *imp, u8 *rev) return prid; } + +enum loongson_chip_id loongson_chip_id_fixup(enum loongson_chip_id chip_id) +{ + u8 impl; + + if (loongson_cpu_get_prid(&impl, NULL)) { + /* + * LS2K2000 only has the LoongArch edition. + */ + if (chip_id == CHIP_LS7A2000) { + if (impl == LOONGARCH_CPU_IMP_LS2K2000) + return CHIP_LS2K2000; + } + + /* + * LS2K1000 has the LoongArch edition(with two LA264 CPU core) + * and the Mips edition(with two mips64r2 CPU core), Only the + * instruction set of the CPU are changed, the peripheral + * devices are basically same. + */ + if (chip_id == CHIP_LS7A1000) { +#if defined(__loongarch__) + if (impl == LOONGARCH_CPU_IMP_LS2K1000) + return CHIP_LS2K1000; +#endif + +#if defined(__mips__) + if (impl == LOONGSON_CPU_MIPS_IMP_LS2K) + return CHIP_LS2K1000; +#endif + } + } + + return chip_id; +} diff --git a/drivers/gpu/drm/loongson/lsdc_probe.h b/drivers/gpu/drm/loongson/lsdc_probe.h index 8bb6de2e3c64..8c630c5c90ce 100644 --- a/drivers/gpu/drm/loongson/lsdc_probe.h +++ b/drivers/gpu/drm/loongson/lsdc_probe.h @@ -9,4 +9,6 @@ /* Helpers for chip detection */ unsigned int loongson_cpu_get_prid(u8 *impl, u8 *rev); +enum loongson_chip_id loongson_chip_id_fixup(enum loongson_chip_id chip_id); + #endif From patchwork Sun Oct 29 19:46:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sui Jingfeng X-Patchwork-Id: 159408 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1831892vqb; Sun, 29 Oct 2023 12:47:15 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGBUzt3jgxhntRzWxpv7TAwqXX5am1hOukBt7h/xr1IiHMysVkWaWDBoidffaRUDx/v+vOf X-Received: by 2002:a05:6808:3a2:b0:3b2:edfb:614d with SMTP id n2-20020a05680803a200b003b2edfb614dmr8970907oie.36.1698608835388; Sun, 29 Oct 2023 12:47:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698608835; cv=none; d=google.com; s=arc-20160816; b=xkxBqlld4K6fUbyGgXsj/5uiioxUNBMjE0ZhSL0dAC0tJnpwfGtW7lxiOtK8UC70Gi ZpNJuXR7m/oOa4kpQ+m2Wm8OsuAi6snCHxB+2K35jo7MjqCuyExTk/p6NmnY7NUn0Qk2 dW1yC2vSBwe3KC5p2ITIR6ko7o9Y3ll+5TA3CeXqPpdCG3f6qfP64mhA/Yk7qMEy2K/Z xa27yX7cPY5ZjrEcgMnYSVVN0KOnOUKpxG49XHA+9MV0Bia51cHAZ5t9ToRbOl6YhV+H CErGnUjD3I4DGqi45w6bmKqWo7CT1gRbJOz6+YLfURuOwuqJ9INYjhT8t3EypOxSCQuq t7ng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=SPO8dPGeLbOTUQ56wG489JEruBwAK9ierDYB7/aN/Z0=; fh=E4IHGZ2HGRuNU3iuhglIJe36ussdVU4dBtmdj2I6SyA=; b=ezCukr/Emq38vhBYAQQNpchu1S4Wr2wDr94mBcIaf9O8PIbpkCzMzN0avmgtnadjxn q6gtVbqiEFBZVCgzhWL0m2XLhWr2chcwgdNLlS/BxMoCarxtE6yeyHM3BGprUjbQPv8B x2R6HbnlMPewm9picB9/hJmAJIaMaTED0plqs2PJhRXKK8vOWxsfbs/ihbrUcQ3jLguh C89fzjzQu5qQAm9YR2MIiU48RZg/kaRhJOAHMXKOjHoTEUXcMCzz5vKqxeWxE0D6Wx0c E5k+P+O9+I9h1PYDPCK2RfnWlwlLi43IPsp0l2kYJj98YB7kb456xNGs3o5W4dxygCsw fnIg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from snail.vger.email (snail.vger.email. [23.128.96.37]) by mx.google.com with ESMTPS id j36-20020a635964000000b00557531eafb0si3944380pgm.559.2023.10.29.12.47.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 12:47:15 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) client-ip=23.128.96.37; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 0533B805A58F; Sun, 29 Oct 2023 12:47:00 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230481AbjJ2Tqm (ORCPT + 30 others); Sun, 29 Oct 2023 15:46:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34590 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230402AbjJ2TqU (ORCPT ); Sun, 29 Oct 2023 15:46:20 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 767ABC4 for ; Sun, 29 Oct 2023 12:46:17 -0700 (PDT) Received: from loongson.cn (unknown [10.20.42.43]) by gateway (Coremail) with SMTP id _____8CxNvGHtj5l0Js1AA--.38906S3; Mon, 30 Oct 2023 03:46:15 +0800 (CST) Received: from openarena.loongson.cn (unknown [10.20.42.43]) by localhost.localdomain (Coremail) with SMTP id AQAAf8AxXNx+tj5lKq02AA--.51878S10; Mon, 30 Oct 2023 03:46:13 +0800 (CST) From: Sui Jingfeng To: Maxime Ripard , Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 8/8] drm/loongson: Add support for the display subsystem in LS2K2000 Date: Mon, 30 Oct 2023 03:46:07 +0800 Message-Id: <20231029194607.379459-9-suijingfeng@loongson.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231029194607.379459-1-suijingfeng@loongson.cn> References: <20231029194607.379459-1-suijingfeng@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8AxXNx+tj5lKq02AA--.51878S10 X-CM-SenderInfo: xvxlyxpqjiv03j6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBj93XoW3KF47KF4xtFy5XFyfWFy5WrX_yoWDWFWxpa 13A3ySgr48tFnI939xtr1UXw1YkFyayFZayFWfGw1rW3srAr18tFnYyF4FqFW7XFy5Jr12 qrn7G3yIk3WUGabCm3ZEXasCq-sJn29KB7ZKAUJUUUUr529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUU90b4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVWxJVW8Jr1l84ACjcxK6I8E87Iv6xkF7I0E14v2 6r4j6r4UJwAaw2AFwI0_Jrv_JF1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqjxCEc2xF0c Ia020Ex4CE44I27wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_Wrv_ ZF1lYx0Ex4A2jsIE14v26r4j6F4UMcvjeVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwI xGrwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwCFI7km07C267AKxVWU XVWUAwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67AF67 kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVW5JVW7JwCI42IY 6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0x vEx4A2jsIE14v26r4j6F4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVj vjDU0xZFpf9x07j6sjUUUUUU= X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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 (snail.vger.email [0.0.0.0]); Sun, 29 Oct 2023 12:47:00 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781120457857512929 X-GMAIL-MSGID: 1781120457857512929 Before apply this patch, drm/loongson is basically works on LS2K2000. Because majority of hardware features of the DC are same with LS7A2000's counterpart. Despite LS2K2000 is a SoC, it don't has a dedicated VRAM. But the firmware will carve out part of system RAM as VRAM, and write the base address and size of this reserved RAM to the PCI Bar 2 of the GPU. So this kind of reserved RAM is nearly same with the dedicated video RAM. In short, the display subsystem in LS2K2000 are nearly compatible with the display subsystem in LS7A2000. But LS2K2000 has only one built-in HDMI encoder, which is connected with the CRTC-0 (display pipe 0). Display pipe 1 exports a generic DVO interface. So there still need a trivial fix. Before apply this patch: $ dmesg | grep 0000:00:06.1 pci 0000:00:06.1: [0014:7a36] type 00 class 0x030000 pci 0000:00:06.1: reg 0x10: [mem 0x51250000-0x5125ffff 64bit] pci 0000:00:06.1: reg 0x18: [mem 0x512b6000-0x512b60ff] pci 0000:00:06.1: BAR 0: assigned [mem 0x51250000-0x5125ffff 64bit] pci 0000:00:06.1: BAR 2: assigned [mem 0x512b7f00-0x512b7fff] pci 0000:00:06.1: vgaarb: setting as boot VGA device loongson 0000:00:06.1: Found LS7A2000 bridge chipset, revision: 16 loongson 0000:00:06.1: [drm] dc: 400MHz, gmc: 800MHz, gpu: 533MHz loongson 0000:00:06.1: [drm] Dedicated vram start: 0x40000000, size: 256MiB loongson 0000:00:06.1: [drm] Loongson VBIOS version: 2.1 loongson 0000:00:06.1: [drm] Loongson VBIOS: has 8 DCBs loongson 0000:00:06.1: [drm] VRAM: 16384 pages ready loongson 0000:00:06.1: [drm] GTT: 32768 pages ready loongson 0000:00:06.1: [drm] lsdc-i2c0(sda pin mask=1, scl pin mask=2) created loongson 0000:00:06.1: [drm] lsdc-i2c1(sda pin mask=4, scl pin mask=8) created loongson 0000:00:06.1: [drm] DisplayPipe-0 has HDMI-0 loongson 0000:00:06.1: [drm] DisplayPipe-1 has HDMI-1 loongson 0000:00:06.1: [drm] Total 2 outputs loongson 0000:00:06.1: [drm] registered irq: 42 [drm] Initialized loongson 1.0.0 20220701 for 0000:00:06.1 on minor 0 loongson 0000:00:06.1: [drm] *ERROR* Setting HDMI-1 PLL failed loongson 0000:00:06.1: [drm] fb0: loongsondrmfb frame buffer device After apply this patch, the error "*ERROR* Setting HDMI-1 PLL failed" got fixed. $ dmesg | grep 0000:00:06.1 pci 0000:00:06.1: [0014:7a36] type 00 class 0x030000 pci 0000:00:06.1: reg 0x10: [mem 0x51250000-0x5125ffff 64bit] pci 0000:00:06.1: reg 0x18: [mem 0x512b6000-0x512b60ff] pci 0000:00:06.1: BAR 0: assigned [mem 0x51250000-0x5125ffff 64bit] pci 0000:00:06.1: BAR 2: assigned [mem 0x512b7f00-0x512b7fff] pci 0000:00:06.1: vgaarb: setting as boot VGA device loongson 0000:00:06.1: Found LS2K2000 SoC, revision: 16 loongson 0000:00:06.1: [drm] dc: 400MHz, gmc: 800MHz, gpu: 533MHz loongson 0000:00:06.1: [drm] Dedicated vram start: 0x40000000, size: 256MiB loongson 0000:00:06.1: [drm] Loongson VBIOS version: 2.1 loongson 0000:00:06.1: [drm] Loongson VBIOS: has 8 DCBs loongson 0000:00:06.1: [drm] VRAM: 16384 pages ready loongson 0000:00:06.1: [drm] GTT: 32768 pages ready loongson 0000:00:06.1: [drm] lsdc-i2c0(sda pin mask=1, scl pin mask=2) created loongson 0000:00:06.1: [drm] lsdc-i2c1(sda pin mask=4, scl pin mask=8) created loongson 0000:00:06.1: [drm] DisplayPipe-0 has HDMI-0 loongson 0000:00:06.1: [drm] DisplayPipe-1 has DVO-1 loongson 0000:00:06.1: [drm] Total 2 outputs loongson 0000:00:06.1: [drm] registered irq: 42 [drm] Initialized loongson 1.0.0 20220701 for 0000:00:06.1 on minor 0 loongson 0000:00:06.1: [drm] fb0: loongsondrmfb frame buffer device Signed-off-by: Sui Jingfeng --- drivers/gpu/drm/loongson/Makefile | 1 + drivers/gpu/drm/loongson/loongson_device.c | 46 ++++++++++ drivers/gpu/drm/loongson/lsdc_output.h | 5 ++ drivers/gpu/drm/loongson/lsdc_output_2k2000.c | 84 +++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 drivers/gpu/drm/loongson/lsdc_output_2k2000.c diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile index 393709e686aa..7d3d82ddd5ff 100644 --- a/drivers/gpu/drm/loongson/Makefile +++ b/drivers/gpu/drm/loongson/Makefile @@ -10,6 +10,7 @@ loongson-y := \ lsdc_i2c.o \ lsdc_irq.o \ lsdc_output.o \ + lsdc_output_2k2000.o \ lsdc_output_7a1000.o \ lsdc_output_7a2000.o \ lsdc_plane.o \ diff --git a/drivers/gpu/drm/loongson/loongson_device.c b/drivers/gpu/drm/loongson/loongson_device.c index 64096ad5466e..33aae403f0b0 100644 --- a/drivers/gpu/drm/loongson/loongson_device.c +++ b/drivers/gpu/drm/loongson/loongson_device.c @@ -6,6 +6,7 @@ #include #include "lsdc_drv.h" +#include "lsdc_probe.h" extern struct loongson_vbios __loongson_vbios; @@ -27,6 +28,15 @@ static const struct lsdc_kms_funcs ls7a2000_kms_funcs = { .crtc_init = ls7a2000_crtc_init, }; +static const struct lsdc_kms_funcs ls2k2000_kms_funcs = { + .create_i2c = lsdc_create_i2c_chan, + .irq_handler = ls7a2000_dc_irq_handler, + .output_init = ls2k2000_output_init, + .cursor_plane_init = ls7a2000_cursor_plane_init, + .primary_plane_init = lsdc_primary_plane_init, + .crtc_init = ls7a2000_crtc_init, +}; + static const struct loongson_gfx_desc ls7a1000_gfx = { .dc = { .num_of_crtc = 2, @@ -93,14 +103,50 @@ static const struct loongson_gfx_desc ls7a2000_gfx = { .model = "LS7A2000 bridge chipset", }; +static const struct loongson_gfx_desc ls2k2000_gfx = { + .dc = { + .num_of_crtc = 2, + .max_pixel_clk = 350000, + .max_width = 4096, + .max_height = 4096, + .num_of_hw_cursor = 2, + .hw_cursor_w = 64, + .hw_cursor_h = 64, + .pitch_align = 64, + .has_vblank_counter = true, + .funcs = &ls2k2000_kms_funcs, + }, + .conf_reg_base = LS7A2000_CONF_REG_BASE, + .gfxpll = { + .reg_offset = LS7A2000_PLL_GFX_REG, + .reg_size = 8, + }, + .pixpll = { + [0] = { + .reg_offset = LS7A2000_PIXPLL0_REG, + .reg_size = 8, + }, + [1] = { + .reg_offset = LS7A2000_PIXPLL1_REG, + .reg_size = 8, + }, + }, + .vbios = &__loongson_vbios, + .chip_id = CHIP_LS2K2000, + .model = "LS2K2000 SoC", +}; + static const struct lsdc_desc *__chip_id_desc_table[] = { [CHIP_LS7A1000] = &ls7a1000_gfx.dc, [CHIP_LS7A2000] = &ls7a2000_gfx.dc, + [CHIP_LS2K2000] = &ls2k2000_gfx.dc, [CHIP_LS_LAST] = NULL, }; const struct lsdc_desc * lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip_id) { + chip_id = loongson_chip_id_fixup(chip_id); + return __chip_id_desc_table[chip_id]; } diff --git a/drivers/gpu/drm/loongson/lsdc_output.h b/drivers/gpu/drm/loongson/lsdc_output.h index a37a72687bdf..463d59d680c2 100644 --- a/drivers/gpu/drm/loongson/lsdc_output.h +++ b/drivers/gpu/drm/loongson/lsdc_output.h @@ -61,6 +61,11 @@ int ls7a2000_output_init(struct drm_device *ddev, struct i2c_adapter *ddc, unsigned int index); +int ls2k2000_output_init(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int pipe); + int lsdc_output_init(struct drm_device *ddev, struct lsdc_display_pipe *dispipe, struct i2c_adapter *ddc, diff --git a/drivers/gpu/drm/loongson/lsdc_output_2k2000.c b/drivers/gpu/drm/loongson/lsdc_output_2k2000.c new file mode 100644 index 000000000000..350af51da541 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output_2k2000.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include "lsdc_drv.h" +#include "lsdc_output.h" + +/* + * The DC in LS2K2000 is nearly same with the DC in LS7A2000, except that + * LS2K2000 has only one built-in HDMI encoder which is connected with the + * display pipe 0. Display pipe 1 is a DVO output interface. + * ________________________ + * | | ______________ + * | +----------| | | + * | CRTC-0 ---> | HDMI phy ---> HDMI Connector --> | HDMI Monitor |<--+ + * | +----------| |______________| | + * | +-------+ | | + * | | i2c-x | <------------------------------------------+ + * | +-------+ | + * | | + * | DC in LS2K2000 | + * | | + * | +-------+ | + * | | i2c-y | <------------------------------------+ + * | +-------+ | | + * | | ____|____ + * | +-------| | | + * | CRTC-1 ------> | DVO --> Encoder1 --> Connector1 --> | Display | + * | +-------| |_________| + * |________________________| + */ + +static void ls2k2000_pipe1_dvo_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); +} + +const struct drm_encoder_funcs ls2k2000_pipe1_dvo_encoder_funcs = { + .reset = ls2k2000_pipe1_dvo_encoder_reset, + .destroy = drm_encoder_cleanup, +}; + +static const struct lsdc_output_desc ls2k2000_output_desc[2] = { + { + .pipe = 0, + .encoder_type = DRM_MODE_ENCODER_TMDS, + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + .encoder_funcs = &lsdc_pipe0_hdmi_encoder_funcs, + .encoder_helper_funcs = &lsdc_pipe0_hdmi_encoder_helper_funcs, + .connector_funcs = &lsdc_pipe0_hdmi_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "HDMI-0", + }, + { + .pipe = 1, + .encoder_type = DRM_MODE_ENCODER_DPI, + .connector_type = DRM_MODE_CONNECTOR_DPI, + .encoder_funcs = &ls2k2000_pipe1_dvo_encoder_funcs, + .encoder_helper_funcs = &lsdc_encoder_helper_funcs, + .connector_funcs = &lsdc_connector_funcs, + .connector_helper_funcs = &lsdc_connector_helper_funcs, + .name = "DVO-1", + }, +}; + +int ls2k2000_output_init(struct drm_device *ddev, + struct lsdc_display_pipe *dispipe, + struct i2c_adapter *ddc, + unsigned int pipe) +{ + struct lsdc_output *output = &dispipe->output; + + output->descp = &ls2k2000_output_desc[pipe]; + + lsdc_output_init(ddev, dispipe, ddc, pipe); + + return 0; +}