From patchwork Mon Dec 4 12:33:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Zhao X-Patchwork-Id: 173270 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:bcd1:0:b0:403:3b70:6f57 with SMTP id r17csp2728275vqy; Mon, 4 Dec 2023 04:34:21 -0800 (PST) X-Google-Smtp-Source: AGHT+IHxVRSoBvggULilJuAr5aeSC1efDv3FXJaEsLwQ/7oe9enF3HBtR2FTkDHWKnK6CANu4avk X-Received: by 2002:a05:6a20:4286:b0:18f:97c:4f3d with SMTP id o6-20020a056a20428600b0018f097c4f3dmr1809692pzj.73.1701693261528; Mon, 04 Dec 2023 04:34:21 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701693261; cv=none; d=google.com; s=arc-20160816; b=nSZ6dfn4KMkbKs9xgyFFbbxpXUIL7IhCDnb/+R6wNw4f2D2AWGBLhe1fT4H/vKbYwS w2Pt/Uxy5eruOgVxH0+GkHWM1ievLPcFlivNAwx9z+muiTkCzO2j3XAJJCeby1vxXege xkUOjTplFaUOGjJ3gNzfl9uuMUif9s8Un9tdda3Ahqz3cKfYbjyOnptWM2L6ZE2Hyxpa EaEEh8BeKrshMGkKmKtwX93PlPtJBoYAelrWCakUMgMjVHi9G2j72AP1tZzPBxBnG0Tp A+A1n3zu1hqBuB/mnyflRKWpirU3NuEBPDD9CAIp0EykwQLNBUY9e4pOdrxG86DjRTC5 VZBA== 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=GIbaUxbww7tu797XsVHAQBK0w8IScRqIOywkUCJ1h+E=; fh=L9GvMpNmkCQ40FkEXhE+Kr1M4TRUe9ggZvxCXrKhwD4=; b=lDv3t3Cqvmo5Ayim4PT1QbE01bmQHX3+yqXbsiU6VG8WN1f8/ALUaUEIWEWxYeY4Mz pZdPv006kgLA+f23Efo/vskffJPBzExhg8b46UOYW82JkdopLLk1pIJq/B7rVRP/irzq B+W5wV7NeF5SDCt818L0gl/SW2oTTzETjLdKj5EH9QD2F9KcXUSccTkiu4w8g9QWS/7C 6hESw2LAFXG5EiJKdnDkq5G3c9Fv1SsKi3NxBfWncqE7g/oWVfgOJTwJoWfgaN2rIs3V fXxloZwQ6f4sqsU+dMpsK+TpAUmJ7exSyslVOk+izuqlr0b+D77iCBJv6+YkQIlcGR9U OhAg== 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:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from pete.vger.email (pete.vger.email. [2620:137:e000::3:6]) by mx.google.com with ESMTPS id im7-20020a056a008d8700b006cde2f43c62si3097688pfb.354.2023.12.04.04.34.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Dec 2023 04:34:21 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) client-ip=2620:137:e000::3:6; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 1317A80473A6; Mon, 4 Dec 2023 04:34:05 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344234AbjLDMdz convert rfc822-to-8bit (ORCPT + 99 others); Mon, 4 Dec 2023 07:33:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37964 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235482AbjLDMdY (ORCPT ); Mon, 4 Dec 2023 07:33:24 -0500 Received: from fd01.gateway.ufhost.com (fd01.gateway.ufhost.com [61.152.239.71]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0A01618D; Mon, 4 Dec 2023 04:33:20 -0800 (PST) Received: from EXMBX166.cuchost.com (unknown [175.102.18.54]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "EXMBX166", Issuer "EXMBX166" (not verified)) by fd01.gateway.ufhost.com (Postfix) with ESMTP id A08C68043; Mon, 4 Dec 2023 20:33:18 +0800 (CST) Received: from EXMBX061.cuchost.com (172.16.6.61) by EXMBX166.cuchost.com (172.16.6.76) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Mon, 4 Dec 2023 20:33:18 +0800 Received: from xiaofei.localdomain (180.164.60.184) by EXMBX061.cuchost.com (172.16.6.61) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Mon, 4 Dec 2023 20:33:17 +0800 From: Keith Zhao To: Keith Zhao , , , , CC: , , , , , , , , , , , , , , Subject: [v3 4/6] drm/vs: Add KMS crtc&plane Date: Mon, 4 Dec 2023 20:33:13 +0800 Message-ID: <20231204123315.28456-5-keith.zhao@starfivetech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231204123315.28456-1-keith.zhao@starfivetech.com> References: <20231204123315.28456-1-keith.zhao@starfivetech.com> MIME-Version: 1.0 X-Originating-IP: [180.164.60.184] X-ClientProxiedBy: EXCAS066.cuchost.com (172.16.6.26) To EXMBX061.cuchost.com (172.16.6.61) X-YovoleRuleAgent: yovoleflag X-Spam-Status: No, score=-0.8 required=5.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE 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]); Mon, 04 Dec 2023 04:34:05 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1784354713639148690 X-GMAIL-MSGID: 1784354713639148690 add 2 crtcs and 8 planes in vs-drm Signed-off-by: Keith Zhao --- drivers/gpu/drm/verisilicon/Makefile | 9 +- drivers/gpu/drm/verisilicon/vs_crtc.c | 208 +++++ drivers/gpu/drm/verisilicon/vs_crtc.h | 42 + drivers/gpu/drm/verisilicon/vs_dc.c | 1192 ++++++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_dc.h | 67 ++ drivers/gpu/drm/verisilicon/vs_dc_hw.c | 1022 ++++++++++++++++++++ drivers/gpu/drm/verisilicon/vs_dc_hw.h | 580 ++++++++++++ drivers/gpu/drm/verisilicon/vs_drv.c | 2 + drivers/gpu/drm/verisilicon/vs_plane.c | 301 ++++++ drivers/gpu/drm/verisilicon/vs_plane.h | 39 + drivers/gpu/drm/verisilicon/vs_type.h | 69 ++ 11 files changed, 3528 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.c create mode 100644 drivers/gpu/drm/verisilicon/vs_crtc.h create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.c create mode 100644 drivers/gpu/drm/verisilicon/vs_dc.h create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.c create mode 100644 drivers/gpu/drm/verisilicon/vs_dc_hw.h create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.c create mode 100644 drivers/gpu/drm/verisilicon/vs_plane.h create mode 100644 drivers/gpu/drm/verisilicon/vs_type.h diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile index d785a1dfaa7f..bf6f2b7ee480 100644 --- a/drivers/gpu/drm/verisilicon/Makefile +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -vs_drm-objs := vs_drv.o \ - vs_modeset.o - +vs_drm-objs := vs_dc_hw.o \ + vs_dc.o \ + vs_crtc.o \ + vs_drv.o \ + vs_modeset.o \ + vs_plane.o obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c new file mode 100644 index 000000000000..5581219b1230 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vs_crtc.h" +#include "vs_dc.h" +#include "vs_drv.h" + +static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(to_vs_crtc_state(state)); +} + +static void vs_crtc_reset(struct drm_crtc *crtc) +{ + struct vs_crtc_state *state; + + if (crtc->state) + vs_crtc_atomic_destroy_state(crtc, crtc->state); + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &state->base); +} + +static struct drm_crtc_state * +vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct vs_crtc_state *old_state; + struct vs_crtc_state *state; + + if (!crtc->state) + return NULL; + + old_state = to_vs_crtc_state(crtc->state); + + state = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + return &state->base; +} + +static int vs_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_enable_vblank(dc, true); + + return 0; +} + +static void vs_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_enable_vblank(dc, false); +} + +static void vs_crtc_atomic_print_state(struct drm_printer *p, + const struct drm_crtc_state *state) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(state->crtc); + + drm_printf(p, "vs crtc State\n"); + drm_printf(p, "\tcolor_formats: %d\n", vs_crtc->color_formats); + drm_printf(p, "\tmax_bpc: %d\n", vs_crtc->max_bpc); +} + +static const struct drm_crtc_funcs vs_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .reset = vs_crtc_reset, + .atomic_duplicate_state = vs_crtc_atomic_duplicate_state, + .atomic_destroy_state = vs_crtc_atomic_destroy_state, + .enable_vblank = vs_crtc_enable_vblank, + .disable_vblank = vs_crtc_disable_vblank, + .atomic_print_state = vs_crtc_atomic_print_state, +}; + +static void vs_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_enable(dc, crtc); + drm_crtc_vblank_on(crtc); +} + +static void vs_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + drm_crtc_vblank_off(crtc); + + vs_dc_disable(dc, crtc); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); + } +} + +static void vs_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, + crtc); + + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct device *dev = vs_crtc->dev; + struct drm_property_blob *blob = new_state->gamma_lut; + struct drm_color_lut *lut; + struct vs_dc *dc = dev_get_drvdata(dev); + + if (new_state->color_mgmt_changed) { + if (blob && blob->length) { + lut = blob->data; + vs_dc_set_gamma(dc, crtc, lut, + blob->length / sizeof(*lut)); + vs_dc_enable_gamma(dc, crtc, true); + } else { + vs_dc_enable_gamma(dc, crtc, false); + } + } +} + +static void vs_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct drm_pending_vblank_event *event = crtc->state->event; + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_commit(dc); + + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_arm_vblank_event(crtc, event); + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); + } +} + +static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = { + .atomic_check = drm_crtc_helper_atomic_check, + .atomic_enable = vs_crtc_atomic_enable, + .atomic_disable = vs_crtc_atomic_disable, + .atomic_begin = vs_crtc_atomic_begin, + .atomic_flush = vs_crtc_atomic_flush, +}; + +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev, + struct vs_dc_info *info) +{ + struct vs_crtc *crtc; + int ret; + + if (!info) + return NULL; + + crtc = drmm_crtc_alloc_with_planes(drm_dev, struct vs_crtc, base, NULL, + NULL, &vs_crtc_funcs, + info->name ? info->name : NULL); + + drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs); + + if (info->gamma_size) { + ret = drm_mode_crtc_set_gamma_size(&crtc->base, + info->gamma_size); + if (ret) + return NULL; + + drm_crtc_enable_color_mgmt(&crtc->base, 0, false, + info->gamma_size); + } + + crtc->max_bpc = info->max_bpc; + crtc->color_formats = info->color_formats; + return crtc; +} diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.h b/drivers/gpu/drm/verisilicon/vs_crtc.h new file mode 100644 index 000000000000..8f5b5718283f --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_crtc.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_CRTC_H__ +#define __VS_CRTC_H__ + +#include +#include + +#include "vs_type.h" + +struct vs_crtc_state { + struct drm_crtc_state base; + + u32 output_fmt; + u8 encoder_type; + u8 bpp; +}; + +struct vs_crtc { + struct drm_crtc base; + struct device *dev; + unsigned int max_bpc; + unsigned int color_formats; +}; + +struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev, + struct vs_dc_info *info); + +static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct vs_crtc, base); +} + +static inline struct vs_crtc_state * +to_vs_crtc_state(struct drm_crtc_state *state) +{ + return container_of(state, struct vs_crtc_state, base); +} +#endif /* __VS_CRTC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c new file mode 100644 index 000000000000..3f1e70ba4261 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.c @@ -0,0 +1,1192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vs_dc_hw.h" +#include "vs_dc.h" +#include "vs_drv.h" + +static const char * const vout_clocks[] = { + "channel0", + "channel1", + "hdmi_tx", + "dc_parent", +}; + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static const u32 primary_overlay_format0[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_XRGB4444, + DRM_FORMAT_XBGR4444, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_BGRA4444, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YVU420, + DRM_FORMAT_YUV420, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV16, + DRM_FORMAT_NV61, + DRM_FORMAT_P010, +}; + +static const u32 primary_overlay_format1[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_YUV444, +}; + +static const u32 cursor_formats[] = { + DRM_FORMAT_ARGB8888 +}; + +static const u64 format_modifier0[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const u64 secondary_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static const struct vs_plane_info dc_hw_planes_rev0[PLANE_NUM] = { + { + .name = "Primary", + .id = PRIMARY_PLANE_0, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 0, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay", + .id = OVERLAY_PLANE_0, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 1, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_1", + .id = OVERLAY_PLANE_1, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 2, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Primary_1", + .id = PRIMARY_PLANE_1, + .type = DRM_PLANE_TYPE_PRIMARY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 3, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_2", + .id = OVERLAY_PLANE_2, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(format_modifier0), + .modifiers = format_modifier0, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_90 | + DRM_MODE_ROTATE_180 | + DRM_MODE_ROTATE_270 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = FRAC_16_16(1, 3), + .max_scale = FRAC_16_16(10, 1), + .zpos = 4, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Overlay_3", + .id = OVERLAY_PLANE_3, + .type = DRM_PLANE_TYPE_OVERLAY, + .num_formats = ARRAY_SIZE(primary_overlay_format0), + .formats = primary_overlay_format0, + .num_modifiers = ARRAY_SIZE(secondary_format_modifiers), + .modifiers = secondary_format_modifiers, + .min_width = 0, + .min_height = 0, + .max_width = 4096, + .max_height = 4096, + .rotation = 0, + .blend_mode = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE), + .color_encoding = BIT(DRM_COLOR_YCBCR_BT709) | + BIT(DRM_COLOR_YCBCR_BT2020), + .degamma_size = DEGAMMA_SIZE, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 5, + .watermark = true, + .color_mgmt = true, + .roi = true, + }, + { + .name = "Cursor", + .id = CURSOR_PLANE_0, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, + { + .name = "Cursor_1", + .id = CURSOR_PLANE_1, + .type = DRM_PLANE_TYPE_CURSOR, + .num_formats = ARRAY_SIZE(cursor_formats), + .formats = cursor_formats, + .num_modifiers = 0, + .modifiers = NULL, + .min_width = 32, + .min_height = 32, + .max_width = 64, + .max_height = 64, + .rotation = 0, + .degamma_size = 0, + .min_scale = DRM_PLANE_NO_SCALING, + .max_scale = DRM_PLANE_NO_SCALING, + .zpos = 255, + .watermark = false, + .color_mgmt = false, + .roi = false, + }, +}; + +static const struct vs_dc_info dc8200_info = { + .name = "DC8200", + .panel_num = 2, + .plane_num = 8, + .planes = dc_hw_planes_rev0, + .layer_num = 6, + .max_bpc = 10, + .color_formats = DRM_COLOR_FORMAT_RGB444 | + DRM_COLOR_FORMAT_YCBCR444 | + DRM_COLOR_FORMAT_YCBCR422 | + DRM_COLOR_FORMAT_YCBCR420, + .gamma_size = GAMMA_EX_SIZE, + .gamma_bits = 12, + .pitch_alignment = 128, + .pipe_sync = false, + .background = true, + .panel_sync = true, + .cap_dec = true, +}; + +static inline void update_format(u32 format, u64 mod, struct dc_hw_fb *fb) +{ + u8 f = FORMAT_A8R8G8B8; + + switch (format) { + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_BGRX4444: + f = FORMAT_X4R4G4B4; + break; + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_BGRA4444: + f = FORMAT_A4R4G4B4; + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_BGRX5551: + f = FORMAT_X1R5G5B5; + break; + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_BGRA5551: + f = FORMAT_A1R5G5B5; + break; + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + f = FORMAT_R5G6B5; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_BGRX8888: + f = FORMAT_X8R8G8B8; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGRA8888: + f = FORMAT_A8R8G8B8; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + f = FORMAT_YUY2; + break; + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + f = FORMAT_UYVY; + break; + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + f = FORMAT_YV12; + break; + case DRM_FORMAT_NV21: + f = FORMAT_NV12; + break; + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + f = FORMAT_NV16; + break; + case DRM_FORMAT_P010: + f = FORMAT_P010; + break; + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_BGRA1010102: + f = FORMAT_A2R10G10B10; + break; + case DRM_FORMAT_NV12: + f = FORMAT_NV12; + break; + case DRM_FORMAT_YUV444: + f = FORMAT_YUV444; + break; + default: + break; + } + + fb->format = f; +} + +static inline void update_swizzle(u32 format, struct dc_hw_fb *fb) +{ + fb->swizzle = SWIZZLE_ARGB; + fb->uv_swizzle = 0; + + switch (format) { + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBA1010102: + fb->swizzle = SWIZZLE_RGBA; + break; + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_ABGR2101010: + fb->swizzle = SWIZZLE_ABGR; + break; + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRA1010102: + fb->swizzle = SWIZZLE_BGRA; + break; + case DRM_FORMAT_YVYU: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV61: + fb->uv_swizzle = 1; + break; + default: + break; + } +} + +static inline u8 to_vs_rotation(unsigned int rotation) +{ + u8 rot; + + switch (rotation & DRM_MODE_REFLECT_MASK) { + case DRM_MODE_REFLECT_X: + rot = FLIP_X; + return rot; + case DRM_MODE_REFLECT_Y: + rot = FLIP_Y; + return rot; + case DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y: + rot = FLIP_XY; + return rot; + default: + break; + } + + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + rot = ROT_0; + break; + case DRM_MODE_ROTATE_90: + rot = ROT_90; + break; + case DRM_MODE_ROTATE_180: + rot = ROT_180; + break; + case DRM_MODE_ROTATE_270: + rot = ROT_270; + break; + default: + rot = ROT_0; + break; + } + + return rot; +} + +static inline u8 to_vs_yuv_color_space(u32 color_space) +{ + u8 cs; + + switch (color_space) { + case DRM_COLOR_YCBCR_BT601: + cs = COLOR_SPACE_601; + break; + case DRM_COLOR_YCBCR_BT709: + cs = COLOR_SPACE_709; + break; + case DRM_COLOR_YCBCR_BT2020: + cs = COLOR_SPACE_2020; + break; + default: + cs = COLOR_SPACE_601; + break; + } + + return cs; +} + +static inline u8 to_vs_display_id(struct vs_dc *dc, struct drm_crtc *crtc) +{ + u8 panel_num = dc->hw.info->panel_num; + u32 index = drm_crtc_index(crtc); + int i; + + for (i = 0; i < panel_num; i++) { + if (index == dc->crtc[i]->base.index) + return i; + } + + return 0; +} + +static void vs_drm_update_pitch_alignment(struct drm_device *drm_dev, + unsigned int alignment) +{ + struct vs_drm_device *priv = to_vs_drm_private(drm_dev); + + if (alignment > priv->pitch_alignment) + priv->pitch_alignment = alignment; +} + +static int plda_clk_rst_init(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(dc->nclks, dc->clk_vout); +} + +static void plda_clk_rst_deinit(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(dc->nclks, dc->clk_vout); +} + +static void dc_deinit(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + + dc_hw_enable_interrupt(&dc->hw, 0); + dc_hw_deinit(&dc->hw); + plda_clk_rst_deinit(dev); +} + +static int dc_init(struct device *dev) +{ + struct vs_dc *dc = dev_get_drvdata(dev); + int ret; + + ret = plda_clk_rst_init(dev); + if (ret < 0) { + dev_err(dev, "failed to init dc clk reset: %d\n", ret); + return ret; + } + + ret = dc_hw_init(&dc->hw); + if (ret) { + dev_err(dev, "failed to init DC HW\n"); + return ret; + } + return 0; +} + +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc) +{ + struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct dc_hw_display display; + + display.bus_format = crtc_state->output_fmt; + display.h_active = mode->hdisplay; + display.h_total = mode->htotal; + display.h_sync_start = mode->hsync_start; + display.h_sync_end = mode->hsync_end; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + display.h_sync_polarity = true; + else + display.h_sync_polarity = false; + + display.v_active = mode->vdisplay; + display.v_total = mode->vtotal; + display.v_sync_start = mode->vsync_start; + display.v_sync_end = mode->vsync_end; + + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + display.v_sync_polarity = true; + else + display.v_sync_polarity = false; + + display.id = to_vs_display_id(dc, crtc); + + display.enable = true; + + if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) { + dc_hw_set_out(&dc->hw, OUT_DPI, display.id); + clk_set_rate(dc->clk_vout[CLK_VOUT_SOC_PIX].clk, mode->clock * 1000); + clk_set_parent(dc->clk_vout[CLK_VOUT_PIX1].clk, + dc->clk_vout[CLK_VOUT_SOC_PIX].clk); + } else { + dc_hw_set_out(&dc->hw, OUT_DP, display.id); + clk_set_parent(dc->clk_vout[CLK_VOUT_PIX0].clk, + dc->clk_vout[CLK_VOUT_HDMI_PIX].clk); + } + + dc_hw_setup_display(&dc->hw, &display); + + enable_irq(dc->irq); +} + +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc) +{ + struct dc_hw_display display; + + disable_irq(dc->irq); + + display.id = to_vs_display_id(dc, crtc); + display.enable = false; + + dc_hw_setup_display(&dc->hw, &display); +} + +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc, + struct drm_color_lut *lut, unsigned int size) +{ + u16 i, r, g, b; + u8 bits, id; + + if (size != dc->hw.info->gamma_size) { + drm_err(crtc->dev, "gamma size does not match!\n"); + return; + } + + id = to_vs_display_id(dc, crtc); + + bits = dc->hw.info->gamma_bits; + for (i = 0; i < size; i++) { + r = drm_color_lut_extract(lut[i].red, bits); + g = drm_color_lut_extract(lut[i].green, bits); + b = drm_color_lut_extract(lut[i].blue, bits); + dc_hw_update_gamma(&dc->hw, id, i, r, g, b); + } +} + +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, + bool enable) +{ + u8 id; + + id = to_vs_display_id(dc, crtc); + dc_hw_enable_gamma(&dc->hw, id, enable); +} + +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable) +{ + dc_hw_enable_interrupt(&dc->hw, enable); +} + +static u32 calc_factor(u32 src, u32 dest) +{ + u32 factor = 1 << 16; + + if (src > 1 && dest > 1) + factor = ((src - 1) << 16) / (dest - 1); + + return factor; +} + +static void update_scale(struct drm_plane_state *state, struct dc_hw_roi *roi, + struct dc_hw_scale *scale) +{ + int dst_w = drm_rect_width(&state->dst); + int dst_h = drm_rect_height(&state->dst); + int src_w, src_h, temp; + + scale->enable = false; + + if (roi->enable) { + src_w = roi->width; + src_h = roi->height; + } else { + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + } + + if (drm_rotation_90_or_270(state->rotation)) { + temp = src_w; + src_w = src_h; + src_h = temp; + } + + if (src_w != dst_w) { + scale->scale_factor_x = calc_factor(src_w, dst_w); + scale->enable = true; + } else { + scale->scale_factor_x = 1 << 16; + } + if (src_h != dst_h) { + scale->scale_factor_y = calc_factor(src_h, dst_h); + scale->enable = true; + } else { + scale->scale_factor_y = 1 << 16; + } +} + +static void update_fb(struct vs_plane *plane, u8 display_id, + struct dc_hw_fb *fb, struct drm_plane_state *state) +{ + struct vs_plane_state *plane_state = to_vs_plane_state(state); + struct drm_framebuffer *drm_fb = state->fb; + struct drm_rect *src = &state->src; + + fb->display_id = display_id; + fb->y_address = plane_state->dma_addr[0]; + fb->y_stride = drm_fb->pitches[0]; + if (drm_fb->format->format == DRM_FORMAT_YVU420) { + fb->u_address = plane_state->dma_addr[2]; + fb->v_address = plane_state->dma_addr[1]; + fb->u_stride = drm_fb->pitches[2]; + fb->v_stride = drm_fb->pitches[1]; + } else { + fb->u_address = plane_state->dma_addr[1]; + fb->v_address = plane_state->dma_addr[2]; + fb->u_stride = drm_fb->pitches[1]; + fb->v_stride = drm_fb->pitches[2]; + } + fb->width = drm_rect_width(src) >> 16; + fb->height = drm_rect_height(src) >> 16; + fb->tile_mode = DRM_FORMAT_MOD_LINEAR; + fb->rotation = to_vs_rotation(state->rotation); + fb->yuv_color_space = to_vs_yuv_color_space(state->color_encoding); + fb->zpos = state->zpos; + fb->enable = state->visible; + update_format(drm_fb->format->format, drm_fb->modifier, fb); + update_swizzle(drm_fb->format->format, fb); +} + +static void update_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct dc_hw_fb fb = {0}; + struct dc_hw_scale scale; + struct dc_hw_position pos; + struct dc_hw_blend blend; + struct dc_hw_roi roi; + struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state, + drm_plane); + struct drm_rect *dest = &state->dst; + bool dec_enable = false; + u8 display_id = 0; + + display_id = to_vs_display_id(dc, state->crtc); + update_fb(plane, display_id, &fb, state); + fb.dec_enable = dec_enable; + roi.enable = false; + + update_scale(state, &roi, &scale); + + pos.start_x = dest->x1; + pos.start_y = dest->y1; + pos.end_x = dest->x2; + pos.end_y = dest->y2; + + blend.alpha = (u8)(state->alpha >> 8); + blend.blend_mode = (u8)(state->pixel_blend_mode); + + dc_hw_update_plane(&dc->hw, plane->id, &fb, &scale, &pos, &blend); +} + +static void update_cursor_size(struct drm_plane_state *state, struct dc_hw_cursor *cursor) +{ + u8 size_type; + + switch (state->crtc_w) { + case 32: + size_type = CURSOR_SIZE_32X32; + break; + case 64: + size_type = CURSOR_SIZE_64X64; + break; + default: + size_type = CURSOR_SIZE_32X32; + break; + } + + cursor->size = size_type; +} + +static void update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(drm_state, + drm_plane); + struct vs_plane_state *plane_state = to_vs_plane_state(state); + struct drm_framebuffer *drm_fb = state->fb; + struct dc_hw_cursor cursor; + + cursor.address = plane_state->dma_addr[0]; + cursor.x = state->crtc_x; + cursor.y = state->crtc_y; + cursor.hot_x = drm_fb->hot_x; + cursor.hot_y = drm_fb->hot_y; + cursor.display_id = to_vs_display_id(dc, state->crtc); + update_cursor_size(state, &cursor); + cursor.enable = true; + + dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor); +} + +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + update_plane(dc, plane, drm_plane, drm_state); +} + +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state) +{ + update_cursor_plane(dc, plane, drm_plane, drm_state); +} + +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane_state *old_state) +{ + struct dc_hw_fb fb = {0}; + + fb.enable = false; + dc_hw_update_plane(&dc->hw, plane->id, &fb, NULL, NULL, NULL); +} + +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane_state *old_state) +{ + struct dc_hw_cursor cursor = {0}; + + cursor.enable = false; + cursor.display_id = to_vs_display_id(dc, old_state->crtc); + dc_hw_update_cursor(&dc->hw, cursor.display_id, &cursor); +} + +static bool vs_dc_mod_supported(const struct vs_plane_info *plane_info, + u64 modifier) +{ + const u64 *mods; + + if (!plane_info->modifiers) + return false; + + for (mods = plane_info->modifiers; *mods != DRM_FORMAT_MOD_INVALID; mods++) { + if (*mods == modifier) + return true; + } + + return false; +} + +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + + struct drm_framebuffer *fb = new_plane_state->fb; + const struct vs_plane_info *plane_info; + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *crtc_state; + struct vs_plane *vs_plane = to_vs_plane(plane); + + plane_info = &dc->hw.info->planes[vs_plane->id]; + + if (fb->width < plane_info->min_width || + fb->width > plane_info->max_width || + fb->height < plane_info->min_height || + fb->height > plane_info->max_height) + drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", + vs_plane->id); + + if (!vs_dc_mod_supported(plane_info, fb->modifier)) { + drm_err(plane->dev, "unsupported modifier on plane%d.\n", vs_plane->id); + return -EINVAL; + } + + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, + plane_info->min_scale, + plane_info->max_scale, + true, true); +} + +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_framebuffer *fb = new_plane_state->fb; + const struct vs_plane_info *plane_info; + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *crtc_state; + struct vs_plane *vs_plane = to_vs_plane(plane); + + plane_info = &dc->hw.info->planes[vs_plane->id]; + + if (fb->width < plane_info->min_width || + fb->width > plane_info->max_width || + fb->height < plane_info->min_height || + fb->height > plane_info->max_height) + drm_err_once(plane->dev, "buffer size may not support on plane%d.\n", vs_plane->id); + + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, + plane_info->min_scale, + plane_info->max_scale, + true, true); +} + +static irqreturn_t dc_isr(int irq, void *data) +{ + struct vs_dc *dc = data; + struct vs_dc_info *dc_info = dc->hw.info; + u32 i, ret; + + if (!dc_info) + return IRQ_HANDLED; + + ret = dc_hw_get_interrupt(&dc->hw); + + for (i = 0; i < dc_info->panel_num; i++) + drm_crtc_handle_vblank(&dc->crtc[i]->base); + + return IRQ_HANDLED; +} + +void vs_dc_commit(struct vs_dc *dc) +{ + dc_hw_enable_shadow_register(&dc->hw, false); + + dc_hw_commit(&dc->hw); + + dc_hw_enable_shadow_register(&dc->hw, true); +} + +static int dc_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm_dev = data; + struct vs_dc *dc = dev_get_drvdata(dev); + struct device_node *port; + struct vs_crtc *crtc; + struct vs_dc_info *dc_info; + struct vs_plane *plane; + struct vs_plane_info *plane_info; + int i, ret; + u32 max_width = 0, max_height = 0; + u32 min_width = 0xffff, min_heigth = 0xffff; + u32 possible_crtc = 0; + + if (!drm_dev || !dc) { + dev_err(dev, "devices are not created.\n"); + return -ENODEV; + } + + ret = dc_init(dev); + if (ret < 0) { + drm_err(drm_dev, "Failed to initialize DC hardware.\n"); + return ret; + } + + dc_info = dc->hw.info; + + for (i = 0; i < dc_info->panel_num; i++) { + crtc = vs_crtc_create(drm_dev, dc_info); + if (!crtc) { + drm_err(drm_dev, "Failed to create CRTC.\n"); + ret = -ENOMEM; + return ret; + } + + port = of_graph_get_port_by_id(dev->of_node, i); + if (!port) { + drm_err(drm_dev, "no port node found for crtc_port%d\n", i); + return -ENOENT; + } + + crtc->base.port = port; + crtc->dev = dev; + dc->crtc[i] = crtc; + } + + for (i = 0; i < dc_info->plane_num; i++) { + plane_info = (struct vs_plane_info *)&dc_info->planes[i]; + + if (!dc->crtc[0]->base.port || !dc->crtc[1]->base.port) { + drm_err(drm_dev, "no port no crtc mask, fail to create plane\n"); + return -ENOENT; + } + + if (plane_info->id == PRIMARY_PLANE_0 || + plane_info->id == CURSOR_PLANE_0) { + possible_crtc = drm_crtc_mask(&dc->crtc[0]->base); + } else if (plane_info->id == PRIMARY_PLANE_1 || + plane_info->id == CURSOR_PLANE_1) { + possible_crtc = drm_crtc_mask(&dc->crtc[1]->base); + } else { + possible_crtc = drm_crtc_mask(&dc->crtc[0]->base) | + drm_crtc_mask(&dc->crtc[1]->base); + } + + plane = vs_plane_create(drm_dev, plane_info, + dc_info->layer_num, possible_crtc); + if (IS_ERR(plane)) { + dev_err(dev, "failed to construct plane\n"); + return PTR_ERR(plane); + } + + plane->id = i; + dc->planes[i].id = plane_info->id; + + if (plane_info->type == DRM_PLANE_TYPE_PRIMARY) { + if (plane_info->id == PRIMARY_PLANE_0) + dc->crtc[0]->base.primary = &plane->base; + else + dc->crtc[1]->base.primary = &plane->base; + + min_width = min_t(u32, min_width, plane_info->min_width); + min_heigth = min_t(u32, min_heigth, plane_info->min_height); + /* + * Note: these values are used for multiple independent things: + * hw display mode filtering, plane buffer sizes ... + * Use the combined maximum values here to cover all use cases, + * and do more specific checking in the respective code paths. + */ + max_width = max_t(u32, max_width, plane_info->max_width); + max_height = max_t(u32, max_height, plane_info->max_height); + } + + if (plane_info->type == DRM_PLANE_TYPE_CURSOR) { + if (plane_info->id == CURSOR_PLANE_0) + dc->crtc[0]->base.cursor = &plane->base; + else + dc->crtc[1]->base.cursor = &plane->base; + drm_dev->mode_config.cursor_width = + max_t(u32, drm_dev->mode_config.cursor_width, + plane_info->max_width); + drm_dev->mode_config.cursor_height = + max_t(u32, drm_dev->mode_config.cursor_height, + plane_info->max_height); + } + } + + drm_dev->mode_config.min_width = min_width; + drm_dev->mode_config.min_height = min_heigth; + drm_dev->mode_config.max_width = max_width; + drm_dev->mode_config.max_height = max_height; + + vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment); + return 0; +} + +static void dc_unbind(struct device *dev, struct device *master, void *data) +{ + dc_deinit(dev); +} + +const struct component_ops dc_component_ops = { + .bind = dc_bind, + .unbind = dc_unbind, +}; + +static const struct of_device_id dc_driver_dt_match[] = { + { + .compatible = "starfive,jh7110-dc8200", + .data = &dc8200_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, dc_driver_dt_match); + +static int dc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vs_dc *dc; + int ret, i; + + dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL); + if (!dc) + return -ENOMEM; + + dc->hw.hi_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dc->hw.hi_base)) + return PTR_ERR(dc->hw.hi_base); + + dc->hw.reg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(dc->hw.reg_base)) + return PTR_ERR(dc->hw.reg_base); + + dc->nclks = ARRAY_SIZE(dc->clk_vout); + for (i = 0; i < dc->nclks; ++i) + dc->clk_vout[i].id = vout_clocks[i]; + ret = devm_clk_bulk_get(dev, dc->nclks, dc->clk_vout); + if (ret) { + dev_err(dev, "Failed to get clk controls\n"); + return ret; + } + + dc->irq = platform_get_irq(pdev, 0); + + /* do not autoenable, will be enabled later */ + ret = devm_request_irq(dev, dc->irq, dc_isr, IRQF_NO_AUTOEN, dev_name(dev), dc); + if (ret < 0) { + dev_err(dev, "Failed to install irq:%u.\n", dc->irq); + return ret; + } + + dc->hw.info = (struct vs_dc_info *)of_device_get_match_data(dev); + + dev_set_drvdata(dev, dc); + + return component_add(dev, &dc_component_ops); +} + +static int dc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + component_del(dev, &dc_component_ops); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +struct platform_driver dc_platform_driver = { + .probe = dc_probe, + .remove = dc_remove, + .driver = { + .name = "vs-dc", + .of_match_table = of_match_ptr(dc_driver_dt_match), + }, +}; + +MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("VeriSilicon DC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h new file mode 100644 index 000000000000..eeb5be0499ea --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_DC_H__ +#define __VS_DC_H__ + +#include +#include +#include + +#include +#include + +#include "vs_crtc.h" +#include "vs_dc_hw.h" +#include "vs_plane.h" + +struct vs_dc_plane { + enum dc_hw_plane_id id; +}; + +enum vout_clk { + CLK_VOUT_PIX0, + CLK_VOUT_PIX1, + CLK_VOUT_HDMI_PIX, + CLK_VOUT_SOC_PIX, + CLK_VOUT_NUM +}; + +struct vs_dc { + struct vs_crtc *crtc[DC_DISPLAY_NUM]; + struct dc_hw hw; + + struct vs_dc_plane planes[PLANE_NUM]; + struct clk_bulk_data clk_vout[CLK_VOUT_NUM]; + int nclks; + int irq; +}; + +void vs_dc_enable(struct vs_dc *dc, struct drm_crtc *crtc); +void vs_dc_disable(struct vs_dc *dc, struct drm_crtc *crtc); + +void vs_dc_set_gamma(struct vs_dc *dc, struct drm_crtc *crtc, + struct drm_color_lut *lut, unsigned int size); +void vs_dc_enable_gamma(struct vs_dc *dc, struct drm_crtc *crtc, bool enable); +void vs_dc_enable_vblank(struct vs_dc *dc, bool enable); +void vs_dc_commit(struct vs_dc *dc); +void vs_dc_update_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state); +void vs_dc_disable_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane_state *old_state); +int vs_dc_check_plane(struct vs_dc *dc, struct drm_plane *plane, + struct drm_atomic_state *state); +void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane *drm_plane, + struct drm_atomic_state *drm_state); +void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane, + struct drm_plane_state *old_state); +int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane, + struct drm_atomic_state *state); + +extern struct platform_driver dc_platform_driver; + +#endif /* __VS_DC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c new file mode 100644 index 000000000000..d05ad3f00881 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c @@ -0,0 +1,1022 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include +#include +#include +//#include +#include +#include + +#include "vs_dc_hw.h" +#include "vs_type.h" + +static const u32 horkernel[] = { + 0x00000000, 0x20000000, 0x00002000, 0x00000000, + 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000, + 0x00000000, 0x00000000, 0x181f0000, 0x000027e1, + 0x00000000, 0x00000000, 0x00000000, 0x2b981468, + 0x00000000, 0x00000000, 0x00000000, 0x10f00000, + 0x00002f10, 0x00000000, 0x00000000, 0x00000000, + 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, + 0x0af50000, 0x0000350b, 0x00000000, 0x00000000, + 0x00000000, 0x3781087f, 0x00000000, 0x00000000, + 0x00000000, 0x06660000, 0x0000399a, 0x00000000, + 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000, + 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4, + 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, + 0x00000000, 0x00000000, 0x00000000, 0x01470000, + 0x00003eb9, 0x00000000, 0x00000000, 0x00000000, + 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, + 0x00480000, 0x00003fb8, 0x00000000, 0x00000000, + 0x00000000, 0x3fef0011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00004000, 0x00000000, + 0x00000000, 0x00000000, 0x20002000, 0x00000000, + 0x00000000, 0x00000000, 0x1c030000, 0x000023fd, + 0x00000000, 0x00000000, 0x00000000, 0x27e1181f, + 0x00000000, 0x00000000, 0x00000000, 0x14680000, + 0x00002b98, 0x00000000, 0x00000000, 0x00000000, + 0x2f1010f0, 0x00000000, 0x00000000, 0x00000000, + 0x0dc70000, 0x00003239, 0x00000000, 0x00000000, + 0x00000000, 0x350b0af5, 0x00000000, 0x00000000, + 0x00000000, 0x087f0000, 0x00003781, 0x00000000, + 0x00000000, 0x00000000, 0x399a0666, 0x00000000, + 0x00000000, 0x00000000, 0x04a70000, 0x00003b59, + 0x00000000, 0x00000000, 0x00000000, 0x3cc4033c, + 0x00000000, 0x00000000, 0x00000000, 0x021f0000, +}; + +#define H_COEF_SIZE ARRAY_SIZE(horkernel) + +static const u32 verkernel[] = { + 0x00000000, 0x20000000, 0x00002000, 0x00000000, + 0x00000000, 0x00000000, 0x23fd1c03, 0x00000000, + 0x00000000, 0x00000000, 0x181f0000, 0x000027e1, + 0x00000000, 0x00000000, 0x00000000, 0x2b981468, + 0x00000000, 0x00000000, 0x00000000, 0x10f00000, + 0x00002f10, 0x00000000, 0x00000000, 0x00000000, + 0x32390dc7, 0x00000000, 0x00000000, 0x00000000, + 0x0af50000, 0x0000350b, 0x00000000, 0x00000000, + 0x00000000, 0x3781087f, 0x00000000, 0x00000000, + 0x00000000, 0x06660000, 0x0000399a, 0x00000000, + 0x00000000, 0x00000000, 0x3b5904a7, 0x00000000, + 0x00000000, 0x00000000, 0x033c0000, 0x00003cc4, + 0x00000000, 0x00000000, 0x00000000, 0x3de1021f, + 0x00000000, 0x00000000, 0x00000000, 0x01470000, + 0x00003eb9, 0x00000000, 0x00000000, 0x00000000, + 0x3f5300ad, 0x00000000, 0x00000000, 0x00000000, + 0x00480000, 0x00003fb8, 0x00000000, 0x00000000, + 0x00000000, 0x3fef0011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00004000, 0x00000000, + 0xcdcd0000, 0xfdfdfdfd, 0xabababab, 0xabababab, + 0x00000000, 0x00000000, 0x5ff5f456, 0x000f5f58, + 0x02cc6c78, 0x02cc0c28, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, + 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, 0xfeeefeee, +}; + +#define V_COEF_SIZE ARRAY_SIZE(verkernel) + +/* + * RGB 709->2020 conversion parameters + */ +static const u16 RGB2RGB[RGB_TO_RGB_TABLE_SIZE] = { + 10279, 5395, 709, + 1132, 15065, 187, + 269, 1442, 14674 +}; + +/* + * YUV601 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static const s32 YUV601_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1640, 1196, + -404, -836, 1196, 2076, + 0, -916224, 558336, -1202944, + 64, 940, 64, 960 +}; + +/* + * YUV709 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static s32 YUV709_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1844, 1196, + -220, -548, 1196, 2172, + 0, -1020672, 316672, -1188608, + 64, 940, 64, 960 +}; + +/* + * YUV2020 to RGB conversion parameters + * YUV2RGB[0] - [8] : C0 - C8; + * YUV2RGB[9] - [11]: D0 - D2; + * YUV2RGB[12] - [13]: Y clamp min & max calue; + * YUV2RGB[14] - [15]: UV clamp min & max calue; + */ +static s32 YUV2020_2RGB[YUV_TO_RGB_TABLE_SIZE] = { + 1196, 0, 1724, 1196, + -192, -668, 1196, 2200, + 0, -959232, 363776, -1202944, + 64, 940, 64, 960 +}; + +/* + * RGB to YUV2020 conversion parameters + * RGB2YUV[0] - [8] : C0 - C8; + * RGB2YUV[9] - [11]: D0 - D2; + */ +static s16 RGB2YUV[RGB_TO_YUV_TABLE_SIZE] = { + 230, 594, 52, + -125, -323, 448, + 448, -412, -36, + 64, 512, 512 +}; + +/* one is for primary plane and the other is for all overlay planes */ +static const struct dc_hw_plane_reg dc_plane_reg[] = { + { + .y_address = DC_FRAMEBUFFER_ADDRESS, + .u_address = DC_FRAMEBUFFER_U_ADDRESS, + .v_address = DC_FRAMEBUFFER_V_ADDRESS, + .y_stride = DC_FRAMEBUFFER_STRIDE, + .u_stride = DC_FRAMEBUFFER_U_STRIDE, + .v_stride = DC_FRAMEBUFFER_V_STRIDE, + .size = DC_FRAMEBUFFER_SIZE, + .top_left = DC_FRAMEBUFFER_TOP_LEFT, + .bottom_right = DC_FRAMEBUFFER_BOTTOM_RIGHT, + .scale_factor_x = DC_FRAMEBUFFER_SCALE_FACTOR_X, + .scale_factor_y = DC_FRAMEBUFFER_SCALE_FACTOR_Y, + .h_filter_coef_index = DC_FRAMEBUFFER_H_FILTER_COEF_INDEX, + .h_filter_coef_data = DC_FRAMEBUFFER_H_FILTER_COEF_DATA, + .v_filter_coef_index = DC_FRAMEBUFFER_V_FILTER_COEF_INDEX, + .v_filter_coef_data = DC_FRAMEBUFFER_V_FILTER_COEF_DATA, + .init_offset = DC_FRAMEBUFFER_INIT_OFFSET, + .color_key = DC_FRAMEBUFFER_COLOR_KEY, + .color_key_high = DC_FRAMEBUFFER_COLOR_KEY_HIGH, + .clear_value = DC_FRAMEBUFFER_CLEAR_VALUE, + .color_table_index = DC_FRAMEBUFFER_COLOR_TABLE_INDEX, + .color_table_data = DC_FRAMEBUFFER_COLOR_TABLE_DATA, + .scale_config = DC_FRAMEBUFFER_SCALE_CONFIG, + .water_mark = DC_FRAMEBUFFER_WATER_MARK, + .degamma_index = DC_FRAMEBUFFER_DEGAMMA_INDEX, + .degamma_data = DC_FRAMEBUFFER_DEGAMMA_DATA, + .degamma_ex_data = DC_FRAMEBUFFER_DEGAMMA_EX_DATA, + .src_global_color = DC_FRAMEBUFFER_SRC_GLOBAL_COLOR, + .dst_global_color = DC_FRAMEBUFFER_DST_GLOBAL_COLOR, + .blend_config = DC_FRAMEBUFFER_BLEND_CONFIG, + .roi_origin = DC_FRAMEBUFFER_ROI_ORIGIN, + .roi_size = DC_FRAMEBUFFER_ROI_SIZE, + .yuv_to_rgb_coef0 = DC_FRAMEBUFFER_YUVTORGB_COEF0, + .yuv_to_rgb_coef1 = DC_FRAMEBUFFER_YUVTORGB_COEF1, + .yuv_to_rgb_coef2 = DC_FRAMEBUFFER_YUVTORGB_COEF2, + .yuv_to_rgb_coef3 = DC_FRAMEBUFFER_YUVTORGB_COEF3, + .yuv_to_rgb_coef4 = DC_FRAMEBUFFER_YUVTORGB_COEF4, + .yuv_to_rgb_coefd0 = DC_FRAMEBUFFER_YUVTORGB_COEFD0, + .yuv_to_rgb_coefd1 = DC_FRAMEBUFFER_YUVTORGB_COEFD1, + .yuv_to_rgb_coefd2 = DC_FRAMEBUFFER_YUVTORGB_COEFD2, + .y_clamp_bound = DC_FRAMEBUFFER_Y_CLAMP_BOUND, + .uv_clamp_bound = DC_FRAMEBUFFER_UV_CLAMP_BOUND, + .rgb_to_rgb_coef0 = DC_FRAMEBUFFER_RGBTORGB_COEF0, + .rgb_to_rgb_coef1 = DC_FRAMEBUFFER_RGBTORGB_COEF1, + .rgb_to_rgb_coef2 = DC_FRAMEBUFFER_RGBTORGB_COEF2, + .rgb_to_rgb_coef3 = DC_FRAMEBUFFER_RGBTORGB_COEF3, + .rgb_to_rgb_coef4 = DC_FRAMEBUFFER_RGBTORGB_COEF4, + }, + { + .y_address = DC_OVERLAY_ADDRESS, + .u_address = DC_OVERLAY_U_ADDRESS, + .v_address = DC_OVERLAY_V_ADDRESS, + .y_stride = DC_OVERLAY_STRIDE, + .u_stride = DC_OVERLAY_U_STRIDE, + .v_stride = DC_OVERLAY_V_STRIDE, + .size = DC_OVERLAY_SIZE, + .top_left = DC_OVERLAY_TOP_LEFT, + .bottom_right = DC_OVERLAY_BOTTOM_RIGHT, + .scale_factor_x = DC_OVERLAY_SCALE_FACTOR_X, + .scale_factor_y = DC_OVERLAY_SCALE_FACTOR_Y, + .h_filter_coef_index = DC_OVERLAY_H_FILTER_COEF_INDEX, + .h_filter_coef_data = DC_OVERLAY_H_FILTER_COEF_DATA, + .v_filter_coef_index = DC_OVERLAY_V_FILTER_COEF_INDEX, + .v_filter_coef_data = DC_OVERLAY_V_FILTER_COEF_DATA, + .init_offset = DC_OVERLAY_INIT_OFFSET, + .color_key = DC_OVERLAY_COLOR_KEY, + .color_key_high = DC_OVERLAY_COLOR_KEY_HIGH, + .clear_value = DC_OVERLAY_CLEAR_VALUE, + .color_table_index = DC_OVERLAY_COLOR_TABLE_INDEX, + .color_table_data = DC_OVERLAY_COLOR_TABLE_DATA, + .scale_config = DC_OVERLAY_SCALE_CONFIG, + .water_mark = DC_OVERLAY_WATER_MARK, + .degamma_index = DC_OVERLAY_DEGAMMA_INDEX, + .degamma_data = DC_OVERLAY_DEGAMMA_DATA, + .degamma_ex_data = DC_OVERLAY_DEGAMMA_EX_DATA, + .src_global_color = DC_OVERLAY_SRC_GLOBAL_COLOR, + .dst_global_color = DC_OVERLAY_DST_GLOBAL_COLOR, + .blend_config = DC_OVERLAY_BLEND_CONFIG, + .roi_origin = DC_OVERLAY_ROI_ORIGIN, + .roi_size = DC_OVERLAY_ROI_SIZE, + .yuv_to_rgb_coef0 = DC_OVERLAY_YUVTORGB_COEF0, + .yuv_to_rgb_coef1 = DC_OVERLAY_YUVTORGB_COEF1, + .yuv_to_rgb_coef2 = DC_OVERLAY_YUVTORGB_COEF2, + .yuv_to_rgb_coef3 = DC_OVERLAY_YUVTORGB_COEF3, + .yuv_to_rgb_coef4 = DC_OVERLAY_YUVTORGB_COEF4, + .yuv_to_rgb_coefd0 = DC_OVERLAY_YUVTORGB_COEFD0, + .yuv_to_rgb_coefd1 = DC_OVERLAY_YUVTORGB_COEFD1, + .yuv_to_rgb_coefd2 = DC_OVERLAY_YUVTORGB_COEFD2, + .y_clamp_bound = DC_OVERLAY_Y_CLAMP_BOUND, + .uv_clamp_bound = DC_OVERLAY_UV_CLAMP_BOUND, + .rgb_to_rgb_coef0 = DC_OVERLAY_RGBTORGB_COEF0, + .rgb_to_rgb_coef1 = DC_OVERLAY_RGBTORGB_COEF1, + .rgb_to_rgb_coef2 = DC_OVERLAY_RGBTORGB_COEF2, + .rgb_to_rgb_coef3 = DC_OVERLAY_RGBTORGB_COEF3, + .rgb_to_rgb_coef4 = DC_OVERLAY_RGBTORGB_COEF4, + }, +}; + +static const struct dc_hw_funcs hw_func; + +static inline u32 hi_read(struct dc_hw *hw, u32 reg) +{ + return readl(hw->hi_base + reg); +} + +static inline void hi_write(struct dc_hw *hw, u32 reg, u32 value) +{ + writel(value, hw->hi_base + reg); +} + +static inline void dc_write(struct dc_hw *hw, u32 reg, u32 value) +{ + writel(value, hw->reg_base + reg - DC_REG_BASE); +} + +static inline u32 dc_read(struct dc_hw *hw, u32 reg) +{ + u32 value = readl(hw->reg_base + reg - DC_REG_BASE); + + return value; +} + +static inline void dc_set_clear(struct dc_hw *hw, u32 reg, u32 set, u32 clear) +{ + u32 value = dc_read(hw, reg); + + value &= ~clear; + value |= set; + dc_write(hw, reg, value); +} + +static void load_default_filter(struct dc_hw *hw, + const struct dc_hw_plane_reg *reg, u32 offset) +{ + u8 i; + + dc_write(hw, reg->scale_config + offset, 0x33); + dc_write(hw, reg->init_offset + offset, 0x80008000); + dc_write(hw, reg->h_filter_coef_index + offset, 0x00); + for (i = 0; i < H_COEF_SIZE; i++) + dc_write(hw, reg->h_filter_coef_data + offset, horkernel[i]); + + dc_write(hw, reg->v_filter_coef_index + offset, 0x00); + for (i = 0; i < V_COEF_SIZE; i++) + dc_write(hw, reg->v_filter_coef_data + offset, verkernel[i]); +} + +static void load_rgb_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg, + u32 offset, const u16 *table) +{ + dc_write(hw, reg->rgb_to_rgb_coef0 + offset, table[0] | (table[1] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef1 + offset, table[2] | (table[3] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef2 + offset, table[4] | (table[5] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef3 + offset, table[6] | (table[7] << 16)); + dc_write(hw, reg->rgb_to_rgb_coef4 + offset, table[8]); +} + +static void load_yuv_to_rgb(struct dc_hw *hw, const struct dc_hw_plane_reg *reg, + u32 offset, const s32 *table) +{ + dc_write(hw, reg->yuv_to_rgb_coef0 + offset, + (0xFFFF & table[0]) | (table[1] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef1 + offset, + (0xFFFF & table[2]) | (table[3] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef2 + offset, + (0xFFFF & table[4]) | (table[5] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef3 + offset, + (0xFFFF & table[6]) | (table[7] << 16)); + dc_write(hw, reg->yuv_to_rgb_coef4 + offset, table[8]); + dc_write(hw, reg->yuv_to_rgb_coefd0 + offset, table[9]); + dc_write(hw, reg->yuv_to_rgb_coefd1 + offset, table[10]); + dc_write(hw, reg->yuv_to_rgb_coefd2 + offset, table[11]); + dc_write(hw, reg->y_clamp_bound + offset, table[12] | (table[13] << 16)); + dc_write(hw, reg->uv_clamp_bound + offset, table[14] | (table[15] << 16)); +} + +static void load_rgb_to_yuv(struct dc_hw *hw, u32 offset, s16 *table) +{ + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF0 + offset, + table[0] | (table[1] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF1 + offset, + table[2] | (table[3] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF2 + offset, + table[4] | (table[5] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF3 + offset, + table[6] | (table[7] << 16)); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEF4 + offset, table[8]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD0 + offset, table[9]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD1 + offset, table[10]); + dc_write(hw, DC_DISPLAY_RGBTOYUV_COEFD2 + offset, table[11]); +} + +static bool is_rgb(enum dc_hw_color_format format) +{ + switch (format) { + case FORMAT_X4R4G4B4: + case FORMAT_A4R4G4B4: + case FORMAT_X1R5G5B5: + case FORMAT_A1R5G5B5: + case FORMAT_R5G6B5: + case FORMAT_X8R8G8B8: + case FORMAT_A8R8G8B8: + case FORMAT_A2R10G10B10: + return true; + default: + return false; + } +} + +static u32 get_addr_offset(u32 id) +{ + u32 offset = 0; + + switch (id) { + case PRIMARY_PLANE_1: + case OVERLAY_PLANE_1: + offset = 0x04; + break; + case OVERLAY_PLANE_2: + offset = 0x08; + break; + case OVERLAY_PLANE_3: + offset = 0x0C; + break; + default: + break; + } + + return offset; +} + +int dc_hw_init(struct dc_hw *hw) +{ + u8 i, id, panel_num, layer_num; + u32 offset; + + hw->func = (struct dc_hw_funcs *)&hw_func; + + layer_num = hw->info->layer_num; + for (i = 0; i < layer_num; i++) { + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + hw->reg[i] = dc_plane_reg[0]; + else + hw->reg[i] = dc_plane_reg[1]; + + load_default_filter(hw, &hw->reg[i], offset); + load_rgb_to_rgb(hw, &hw->reg[i], offset, RGB2RGB); + } + + panel_num = hw->info->panel_num; + for (i = 0; i < panel_num; i++) { + offset = i << 2; + + load_rgb_to_yuv(hw, offset, RGB2YUV); + dc_write(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0x111); + + offset = i ? DC_CURSOR_OFFSET : 0; + dc_write(hw, DC_CURSOR_BACKGROUND + offset, 0x00FFFFFF); + dc_write(hw, DC_CURSOR_FOREGROUND + offset, 0x00AAAAAA); + } + + return 0; +} + +void dc_hw_deinit(struct dc_hw *hw) +{ + /* Nothing to do */ +} + +void dc_hw_update_plane(struct dc_hw *hw, u8 id, + struct dc_hw_fb *fb, struct dc_hw_scale *scale, + struct dc_hw_position *pos, struct dc_hw_blend *blend) +{ + struct dc_hw_plane *plane = &hw->plane[id]; + + if (plane) { + if (fb) { + if (!fb->enable) + plane->fb.enable = false; + else + memcpy(&plane->fb, fb, + sizeof(*fb) - sizeof(fb->dirty)); + plane->fb.dirty = true; + } + if (scale) { + memcpy(&plane->scale, scale, + sizeof(*scale) - sizeof(scale->dirty)); + plane->scale.dirty = true; + } + if (pos) { + memcpy(&plane->pos, pos, + sizeof(*pos) - sizeof(pos->dirty)); + plane->pos.dirty = true; + } + if (blend) { + memcpy(&plane->blend, blend, + sizeof(*blend) - sizeof(blend->dirty)); + plane->blend.dirty = true; + } + } +} + +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor) +{ + memcpy(&hw->cursor[id], cursor, sizeof(*cursor) - sizeof(cursor->dirty)); + hw->cursor[id].dirty = true; +} + +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index, + u16 r, u16 g, u16 b) +{ + if (index >= hw->info->gamma_size) + return; + + hw->gamma[id].gamma[index][0] = r; + hw->gamma[id].gamma[index][1] = g; + hw->gamma[id].gamma[index][2] = b; + hw->gamma[id].dirty = true; +} + +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable) +{ + hw->gamma[id].enable = enable; + hw->gamma[id].dirty = true; +} + +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + + memcpy(&hw->display[id], display, sizeof(*display)); + + hw->func->display(hw, display); +} + +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable) +{ + if (enable) + hi_write(hw, AQ_INTR_ENBL, 0xFFFFFFFF); + else + hi_write(hw, AQ_INTR_ENBL, 0); +} + +u32 dc_hw_get_interrupt(struct dc_hw *hw) +{ + return hi_read(hw, AQ_INTR_ACKNOWLEDGE); +} + +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable) +{ + u32 i, offset; + u8 id, layer_num = hw->info->layer_num; + u8 panel_num = hw->info->panel_num; + + for (i = 0; i < layer_num; i++) { + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (enable) { + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + PRIMARY_SHADOW_EN, 0); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + OVERLAY_SHADOW_EN, 0); + } else { + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + 0, PRIMARY_SHADOW_EN); + else + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + 0, OVERLAY_SHADOW_EN); + } + } + + for (i = 0; i < panel_num; i++) { + offset = i << 2; + if (enable) + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, 0, PANEL_SHADOW_EN); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG_EX + offset, PANEL_SHADOW_EN, 0); + } +} + +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id) +{ + if (out <= OUT_DP) + hw->out[id] = out; +} + +static void gamma_ex_commit(struct dc_hw *hw) +{ + u8 panel_num = hw->info->panel_num; + u16 i, j; + u32 value; + + for (j = 0; j < panel_num; j++) { + if (hw->gamma[j].dirty) { + if (hw->gamma[j].enable) { + dc_write(hw, DC_DISPLAY_GAMMA_EX_INDEX + (j << 2), 0x00); + for (i = 0; i < GAMMA_EX_SIZE; i++) { + value = hw->gamma[j].gamma[i][2] | + (hw->gamma[j].gamma[i][1] << 12); + dc_write(hw, DC_DISPLAY_GAMMA_EX_DATA + (j << 2), value); + dc_write(hw, DC_DISPLAY_GAMMA_EX_ONE_DATA + (j << 2), + hw->gamma[j].gamma[i][0]); + } + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2), + PANEL_GAMMA_EN, 0); + } else { + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + (j << 2), + 0, PANEL_GAMMA_EN); + } + hw->gamma[j].dirty = false; + } + } +} + +static void plane_ex_commit_primary(struct dc_hw *hw, struct dc_hw_plane *plane, u32 i, u32 offset) +{ + if (plane->fb.dirty) { + if (is_rgb(plane->fb.format)) { + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + PRIMARY_RGB2RGB_EN, PRIMARY_YUVCLAMP_EN); + } else { + dc_set_clear(hw, + DC_FRAMEBUFFER_CONFIG_EX + offset, + PRIMARY_YUVCLAMP_EN, PRIMARY_RGB2RGB_EN); + + switch (plane->fb.yuv_color_space) { + case COLOR_SPACE_601: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB); + break; + case COLOR_SPACE_709: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB); + break; + case COLOR_SPACE_2020: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB); + break; + default: + break; + } + } + + if (plane->fb.enable) { + dc_write(hw, hw->reg[i].y_address + offset, + plane->fb.y_address); + dc_write(hw, hw->reg[i].u_address + offset, + plane->fb.u_address); + dc_write(hw, hw->reg[i].v_address + offset, + plane->fb.v_address); + dc_write(hw, hw->reg[i].y_stride + offset, + plane->fb.y_stride); + dc_write(hw, hw->reg[i].u_stride + offset, + plane->fb.u_stride); + dc_write(hw, hw->reg[i].v_stride + offset, + plane->fb.v_stride); + dc_write(hw, hw->reg[i].size + offset, + FB_SIZE(plane->fb.width, plane->fb.height)); + dc_write(hw, hw->reg[i].water_mark + offset, + plane->fb.water_mark); + + if (plane->fb.clear_enable) + dc_write(hw, hw->reg[i].clear_value + offset, + plane->fb.clear_value); + } + + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset, + PRIMARY_FORMAT(plane->fb.format) | + PRIMARY_UV_SWIZ(plane->fb.uv_swizzle) | + PRIMARY_SWIZ(plane->fb.swizzle) | + PRIMARY_TILE(plane->fb.tile_mode) | + PRIMARY_YUV_COLOR(plane->fb.yuv_color_space) | + PRIMARY_ROTATION(plane->fb.rotation) | + PRIMARY_CLEAR_EN(plane->fb.clear_enable), + PRIMARY_FORMAT_MASK | + PRIMARY_UV_SWIZ_MASK | + PRIMARY_SWIZ_MASK | + PRIMARY_TILE_MASK | + PRIMARY_YUV_COLOR_MASK | + PRIMARY_ROTATION_MASK | + PRIMARY_CLEAR_EN_MASK); + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX + offset, + PRIMARY_DECODER_EN(plane->fb.dec_enable) | + PRIMARY_EN(plane->fb.enable) | + PRIMARY_ZPOS(plane->fb.zpos) | + PRIMARY_CHANNEL(plane->fb.display_id), + PRIMARY_DECODER_EN_EN_MASK | + PRIMARY_EN_MASK | + PRIMARY_ZPOS_MASK | + PRIMARY_CHANNEL_MASK); + + plane->fb.dirty = false; + } + + if (plane->scale.dirty) { + if (plane->scale.enable) { + dc_write(hw, hw->reg[i].scale_factor_x + offset, + plane->scale.scale_factor_x); + dc_write(hw, hw->reg[i].scale_factor_y + offset, + plane->scale.scale_factor_y); + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset, + PRIMARY_SCALE_EN, 0); + } else { + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG + offset, + 0, PRIMARY_SCALE_EN); + } + plane->scale.dirty = false; + } + + if (plane->pos.dirty) { + dc_write(hw, hw->reg[i].top_left + offset, + X_POS(plane->pos.start_x) | + Y_POS(plane->pos.start_y)); + dc_write(hw, hw->reg[i].bottom_right + offset, + X_POS(plane->pos.end_x) | + Y_POS(plane->pos.end_y)); + plane->pos.dirty = false; + } + + if (plane->blend.dirty) { + dc_write(hw, hw->reg[i].src_global_color + offset, + PRIMARY_ALPHA_LEN(plane->blend.alpha)); + dc_write(hw, hw->reg[i].dst_global_color + offset, + PRIMARY_ALPHA_LEN(plane->blend.alpha)); + switch (plane->blend.blend_mode) { + case DRM_MODE_BLEND_PREMULTI: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI); + break; + case DRM_MODE_BLEND_COVERAGE: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE); + break; + case DRM_MODE_BLEND_PIXEL_NONE: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE); + break; + default: + break; + } + plane->blend.dirty = false; + } +} + +static void plane_ex_commit_overlay(struct dc_hw *hw, struct dc_hw_plane *plane, + u32 i, u32 offset) +{ + if (plane->fb.dirty) { + if (is_rgb(plane->fb.format)) { + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + OVERLAY_RGB2RGB_EN, OVERLAY_CLAMP_EN); + } else { + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + OVERLAY_CLAMP_EN, OVERLAY_RGB2RGB_EN); + + switch (plane->fb.yuv_color_space) { + case COLOR_SPACE_601: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV601_2RGB); + break; + case COLOR_SPACE_709: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV709_2RGB); + break; + case COLOR_SPACE_2020: + load_yuv_to_rgb(hw, &hw->reg[i], offset, YUV2020_2RGB); + break; + default: + break; + } + } + + if (plane->fb.enable) { + dc_write(hw, hw->reg[i].y_address + offset, + plane->fb.y_address); + dc_write(hw, hw->reg[i].u_address + offset, + plane->fb.u_address); + dc_write(hw, hw->reg[i].v_address + offset, + plane->fb.v_address); + dc_write(hw, hw->reg[i].y_stride + offset, + plane->fb.y_stride); + dc_write(hw, hw->reg[i].u_stride + offset, + plane->fb.u_stride); + dc_write(hw, hw->reg[i].v_stride + offset, + plane->fb.v_stride); + dc_write(hw, hw->reg[i].size + offset, + FB_SIZE(plane->fb.width, plane->fb.height)); + dc_write(hw, hw->reg[i].water_mark + offset, + plane->fb.water_mark); + + if (plane->fb.clear_enable) + dc_write(hw, hw->reg[i].clear_value + offset, + plane->fb.clear_value); + } + + dc_set_clear(hw, DC_OVERLAY_CONFIG + offset, + OVERLAY_DEC_EN(plane->fb.dec_enable) | + OVERLAY_CLEAR_EN(plane->fb.clear_enable) | + OVERLAY_FB_EN(plane->fb.enable) | + OVERLAY_FORMAT(plane->fb.format) | + OVERLAY_UV_SWIZ(plane->fb.uv_swizzle) | + OVERLAY_SWIZ(plane->fb.swizzle) | + OVERLAY_TILE(plane->fb.tile_mode) | + OVERLAY_YUV_COLOR(plane->fb.yuv_color_space) | + OVERLAY_ROTATION(plane->fb.rotation), + OVERLAY_DEC_EN_MASK | + OVERLAY_CLEAR_EN_MASK | + OVERLAY_FB_EN_MASK | + OVERLAY_FORMAT_MASK | + OVERLAY_UV_SWIZ_MASK | + OVERLAY_SWIZ_MASK | + OVERLAY_TILE_MASK | + OVERLAY_YUV_COLOR_MASK | + OVERLAY_ROTATION_MASK); + + dc_set_clear(hw, DC_OVERLAY_CONFIG_EX + offset, + OVERLAY_LAYER_SEL(plane->fb.zpos) | + OVERLAY_PANEL_SEL(plane->fb.display_id), + OVERLAY_LAYER_SEL_MASK | + OVERLAY_PANEL_SEL_MASK); + + plane->fb.dirty = false; + } + + if (plane->scale.dirty) { + if (plane->scale.enable) { + dc_write(hw, hw->reg[i].scale_factor_x + offset, + plane->scale.scale_factor_x); + dc_write(hw, hw->reg[i].scale_factor_y + offset, + plane->scale.scale_factor_y); + dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset, + OVERLAY_SCALE_EN, 0); + } else { + dc_set_clear(hw, DC_OVERLAY_SCALE_CONFIG + offset, + 0, OVERLAY_SCALE_EN); + } + plane->scale.dirty = false; + } + + if (plane->pos.dirty) { + dc_write(hw, hw->reg[i].top_left + offset, + X_POS(plane->pos.start_x) | + Y_POS(plane->pos.start_y)); + dc_write(hw, hw->reg[i].bottom_right + offset, + X_POS(plane->pos.end_x) | + Y_POS(plane->pos.end_y)); + plane->pos.dirty = false; + } + + if (plane->blend.dirty) { + dc_write(hw, hw->reg[i].src_global_color + offset, + OVERLAY_ALPHA_LEN(plane->blend.alpha)); + dc_write(hw, hw->reg[i].dst_global_color + offset, + OVERLAY_ALPHA_LEN(plane->blend.alpha)); + switch (plane->blend.blend_mode) { + case DRM_MODE_BLEND_PREMULTI: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PREMULTI); + break; + case DRM_MODE_BLEND_COVERAGE: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_COVERAGE); + break; + case DRM_MODE_BLEND_PIXEL_NONE: + dc_write(hw, hw->reg[i].blend_config + offset, BLEND_PIXEL_NONE); + break; + default: + break; + } + plane->blend.dirty = false; + } +} + +static void plane_ex_commit(struct dc_hw *hw) +{ + struct dc_hw_plane *plane; + u8 id, layer_num = hw->info->layer_num; + u32 i, offset; + + for (i = 0; i < layer_num; i++) { + plane = &hw->plane[i]; + id = hw->info->planes[i].id; + offset = get_addr_offset(id); + if (id == PRIMARY_PLANE_0 || id == PRIMARY_PLANE_1) + plane_ex_commit_primary(hw, plane, i, offset); + else + plane_ex_commit_overlay(hw, plane, i, offset); + } +} + +static void setup_display(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + u32 dpi_cfg, offset = id << 2; + + if (hw->display[id].enable) { + switch (display->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + dpi_cfg = 0; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + dpi_cfg = 3; + break; + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + dpi_cfg = 4; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + dpi_cfg = 5; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + dpi_cfg = 6; + break; + default: + dpi_cfg = 5; + break; + } + dc_write(hw, DC_DISPLAY_DPI_CONFIG + offset, dpi_cfg); + + if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL0_EN | TWO_PANEL_EN); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN); + + dc_write(hw, DC_DISPLAY_H + offset, + H_ACTIVE_LEN(hw->display[id].h_active) | + H_TOTAL_LEN(hw->display[id].h_total)); + + dc_write(hw, DC_DISPLAY_H_SYNC + offset, + H_SYNC_START_LEN(hw->display[id].h_sync_start) | + H_SYNC_END_LEN(hw->display[id].h_sync_end) | + H_POLARITY_LEN(hw->display[id].h_sync_polarity ? 0 : 1) | + H_PLUS_LEN(1)); + + dc_write(hw, DC_DISPLAY_V + offset, + V_ACTIVE_LEN(hw->display[id].v_active) | + V_TOTAL_LEN(hw->display[id].v_total)); + + dc_write(hw, DC_DISPLAY_V_SYNC + offset, + V_SYNC_START_LEN(hw->display[id].v_sync_start) | + V_SYNC_END_LEN(hw->display[id].v_sync_end) | + V_POLARITY_LEN(hw->display[id].v_sync_polarity ? 0 : 1) | + V_PLUS_LEN(1)); + + if (hw->info->pipe_sync) + dc_set_clear(hw, DC_FRAMEBUFFER_CONFIG_EX, + 0, PRIMARY_SYNC0_EN | PRIMARY_SYNC1_EN); + + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, PANEL_OUTPUT_EN, 0); + if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL0_EN, SYNC_EN); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, PANEL1_EN, SYNC_EN); + } else { + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, 0, PANEL_OUTPUT_EN); + + if (id == 0) + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL0_EN | TWO_PANEL_EN); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, PANEL1_EN | TWO_PANEL_EN); + } +} + +static void setup_display_ex(struct dc_hw *hw, struct dc_hw_display *display) +{ + u8 id = display->id; + u32 dp_cfg, offset = id << 2; + bool is_yuv = false; + + if (hw->display[id].enable && hw->out[id] == OUT_DP) { + switch (display->bus_format) { + case MEDIA_BUS_FMT_RGB565_1X16: + dp_cfg = 0; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + dp_cfg = 1; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + dp_cfg = 2; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + dp_cfg = 3; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + dp_cfg = 2 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_YUV8_1X24: + dp_cfg = 4 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + dp_cfg = 8 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_YUV10_1X30: + dp_cfg = 10 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + dp_cfg = 12 << 4; + is_yuv = true; + break; + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + dp_cfg = 13 << 4; + is_yuv = true; + break; + default: + dp_cfg = 2; + break; + } + if (is_yuv) + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, + PANEL_RGB2YUV_EN, 0); + else + dc_set_clear(hw, DC_DISPLAY_PANEL_CONFIG + offset, + 0, PANEL_RGB2YUV_EN); + dc_write(hw, DC_DISPLAY_DP_CONFIG + offset, dp_cfg | DP_SELECT); + } + + if (hw->out[id] == OUT_DPI) + dc_set_clear(hw, DC_DISPLAY_DP_CONFIG + offset, 0, DP_SELECT); + + setup_display(hw, display); +} + +static const struct dc_hw_funcs hw_func = { + .gamma = &gamma_ex_commit, + .plane = &plane_ex_commit, + .display = setup_display_ex, +}; + +void dc_hw_commit(struct dc_hw *hw) +{ + u32 i, offset = 0; + u8 plane_num = hw->info->plane_num; + u8 layer_num = hw->info->layer_num; + u8 cursor_num = plane_num - layer_num; + + hw->func->gamma(hw); + hw->func->plane(hw); + + for (i = 0; i < cursor_num; i++) { + if (hw->cursor[i].dirty) { + offset = hw->cursor[i].display_id ? DC_CURSOR_OFFSET : 0; + if (hw->cursor[i].enable) { + dc_write(hw, DC_CURSOR_ADDRESS + offset, + hw->cursor[i].address); + dc_write(hw, DC_CURSOR_LOCATION + offset, + X_LCOTION(hw->cursor[i].x) | + Y_LCOTION(hw->cursor[i].y)); + dc_set_clear(hw, DC_CURSOR_CONFIG + offset, + CURSOR_HOT_X(hw->cursor[i].hot_x) | + CURSOR_HOT_y(hw->cursor[i].hot_y) | + CURSOR_SIZE(hw->cursor[i].size) | + CURSOR_VALID(1) | + CURSOR_TRIG_FETCH(1) | + CURSOR_FORMAT(CURSOR_FORMAT_A8R8G8B8), + CURSOR_HOT_X_MASK | + CURSOR_HOT_y_MASK | + CURSOR_SIZE_MASK | + CURSOR_VALID_MASK | + CURSOR_TRIG_FETCH_MASK | + CURSOR_FORMAT_MASK); + } else { + dc_set_clear(hw, DC_CURSOR_CONFIG + offset, + CURSOR_VALID(1), + CURSOR_FORMAT_MASK); + } + hw->cursor[i].dirty = false; + } + } +} diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.h b/drivers/gpu/drm/verisilicon/vs_dc_hw.h new file mode 100644 index 000000000000..fcf51f00adaa --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.h @@ -0,0 +1,580 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_DC_HW_H__ +#define __VS_DC_HW_H__ + +#include +#include + +#define UPDATE(x, h, l) FIELD_PREP(GENMASK(h, l), x) + +#define AQ_INTR_ACKNOWLEDGE 0x0010 +#define AQ_INTR_ENBL 0x0014 +#define DC_HW_REVISION 0x0024 +#define DC_HW_CHIP_CID 0x0030 + +#define DC_REG_BASE 0x0800 +#define DC_REG_RANGE 0x2000 +#define DC_SEC_REG_OFFSET 0x100000 + +#define DC_FRAMEBUFFER_CONFIG 0x1518 +# define PRIMARY_FORMAT(x) ((x) << 26) +# define PRIMARY_FORMAT_MASK GENMASK(31, 26) +# define PRIMARY_UV_SWIZ(x) ((x) << 25) +# define PRIMARY_UV_SWIZ_MASK GENMASK(25, 25) +# define PRIMARY_SWIZ(x) ((x) << 23) +# define PRIMARY_SWIZ_MASK GENMASK(24, 23) +# define PRIMARY_SCALE_EN BIT(12) +# define PRIMARY_TILE(x) ((x) << 17) +# define PRIMARY_TILE_MASK GENMASK(21, 17) +# define PRIMARY_YUV_COLOR(x) ((x) << 14) +# define PRIMARY_YUV_COLOR_MASK GENMASK(16, 14) +# define PRIMARY_ROTATION(x) ((x) << 11) +# define PRIMARY_ROTATION_MASK GENMASK(13, 11) +# define PRIMARY_CLEAR_EN(x) ((x) << 8) +# define PRIMARY_CLEAR_EN_MASK GENMASK(8, 8) + +#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0 +# define PRIMARY_CHANNEL(x) ((x) << 19) +# define PRIMARY_CHANNEL_MASK GENMASK(19, 19) +# define PRIMARY_ZPOS(x) ((x) << 16) +# define PRIMARY_ZPOS_MASK GENMASK(18, 16) +# define PRIMARY_EN(x) ((x) << 13) +# define PRIMARY_EN_MASK GENMASK(13, 13) +# define PRIMARY_SHADOW_EN BIT(12) +# define PRIMARY_YUVCLAMP_EN BIT(8) +# define PRIMARY_RGB2RGB_EN BIT(6) +# define PRIMARY_SYNC1_EN BIT(4) +# define PRIMARY_SYNC0_EN BIT(3) +# define PRIMARY_DECODER_EN(x) ((x) << 1) +# define PRIMARY_DECODER_EN_EN_MASK GENMASK(1, 1) + +#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520 +#define DC_FRAMEBUFFER_TOP_LEFT 0x24D8 +#define X_POS(x) (x) +#define Y_POS(x) ((x) << 15) + +#define DC_FRAMEBUFFER_BOTTOM_RIGHT 0x24E0 +#define DC_FRAMEBUFFER_ADDRESS 0x1400 +#define DC_FRAMEBUFFER_U_ADDRESS 0x1530 +#define DC_FRAMEBUFFER_V_ADDRESS 0x1538 +#define DC_FRAMEBUFFER_STRIDE 0x1408 +#define DC_FRAMEBUFFER_U_STRIDE 0x1800 +#define DC_FRAMEBUFFER_V_STRIDE 0x1808 +#define DC_FRAMEBUFFER_SIZE 0x1810 +#define FB_SIZE(w, h) ((w) | ((h) << 15)) + +#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828 +#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830 +#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838 +#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00 +#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08 +#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10 +#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20 +#define DC_FRAMEBUFFER_COLOR_KEY 0x1508 +#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510 +#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18 +#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818 +#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820 +#define DC_FRAMEBUFFER_BG_COLOR 0x1528 +#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0 +#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8 +#define DC_FRAMEBUFFER_WATER_MARK 0x1CE8 +#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88 +#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90 +#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98 +#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0 +#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8 +#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10 +#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18 +#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88 +#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90 +#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20 +#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28 +#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30 +#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38 +#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40 +#define DC_FRAMEBUFFER_BLEND_CONFIG 0x2510 +# define BLEND_PREMULTI 0x3450 +# define BLEND_COVERAGE 0x3950 +# define BLEND_PIXEL_NONE 0x3548 + +#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR 0x2500 +# define PRIMARY_ALPHA_LEN(x) ((x) << 24) + +#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR 0x2508 + +#define DC_OVERLAY_CONFIG 0x1540 +# define OVERLAY_SHADOW_EN BIT(31) +# define OVERLAY_CLAMP_EN BIT(30) +# define OVERLAY_RGB2RGB_EN BIT(29) +# define OVERLAY_DEC_EN(x) ((x) << 27) +# define OVERLAY_DEC_EN_MASK GENMASK(27, 27) +# define OVERLAY_CLEAR_EN(x) ((x) << 25) +# define OVERLAY_CLEAR_EN_MASK GENMASK(25, 25) +# define OVERLAY_FB_EN(x) ((x) << 24) +# define OVERLAY_FB_EN_MASK GENMASK(24, 24) +# define OVERLAY_FORMAT(x) ((x) << 16) +# define OVERLAY_FORMAT_MASK GENMASK(21, 16) +# define OVERLAY_UV_SWIZ(x) ((x) << 15) +# define OVERLAY_UV_SWIZ_MASK GENMASK(15, 15) +# define OVERLAY_SWIZ(x) ((x) << 13) +# define OVERLAY_SWIZ_MASK GENMASK(14, 13) +# define OVERLAY_TILE(x) ((x) << 8) +# define OVERLAY_TILE_MASK GENMASK(12, 8) +# define OVERLAY_YUV_COLOR(x) ((x) << 5) +# define OVERLAY_YUV_COLOR_MASK GENMASK(7, 5) +# define OVERLAY_ROTATION(x) ((x) << 2) +# define OVERLAY_ROTATION_MASK GENMASK(4, 2) + +#define DC_OVERLAY_CONFIG_EX 0x2540 +# define OVERLAY_LAYER_SEL(x) ((x) << 0) +# define OVERLAY_LAYER_SEL_MASK GENMASK(2, 0) +# define OVERLAY_PANEL_SEL(x) ((x) << 3) +# define OVERLAY_PANEL_SEL_MASK GENMASK(3, 3) + +#define DC_OVERLAY_SCALE_CONFIG 0x1C00 +# define OVERLAY_SCALE_EN BIT(8) + +#define DC_OVERLAY_BLEND_CONFIG 0x1580 +#define DC_OVERLAY_TOP_LEFT 0x1640 +#define DC_OVERLAY_BOTTOM_RIGHT 0x1680 +#define DC_OVERLAY_ADDRESS 0x15C0 +#define DC_OVERLAY_U_ADDRESS 0x1840 +#define DC_OVERLAY_V_ADDRESS 0x1880 +#define DC_OVERLAY_STRIDE 0x1600 +#define DC_OVERLAY_U_STRIDE 0x18C0 +#define DC_OVERLAY_V_STRIDE 0x1900 +#define DC_OVERLAY_SIZE 0x17C0 +#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40 +#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80 +#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0 +#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00 +#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40 +#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80 +#define DC_OVERLAY_INIT_OFFSET 0x1BC0 +#define DC_OVERLAY_COLOR_KEY 0x1740 +#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780 +#define DC_OVERLAY_CLEAR_VALUE 0x1940 +#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980 +#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0 +#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0 +# define OVERLAY_ALPHA_LEN(x) ((x) << 24) + +#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700 +#define DC_OVERLAY_ROI_ORIGIN 0x1D00 +#define DC_OVERLAY_ROI_SIZE 0x1D40 +#define DC_OVERLAY_WATER_MARK 0x1DC0 +#define DC_OVERLAY_DEGAMMA_INDEX 0x2200 +#define DC_OVERLAY_DEGAMMA_DATA 0x2240 +#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280 +#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0 +#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00 +#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40 +#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80 +#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0 +#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000 +#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040 +#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080 +#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0 +#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300 +#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0 +#define DC_OVERLAY_RGBTORGB_COEF1 0x2100 +#define DC_OVERLAY_RGBTORGB_COEF2 0x2140 +#define DC_OVERLAY_RGBTORGB_COEF3 0x2180 +#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0 + +#define DC_CURSOR_CONFIG 0x1468 +# define CURSOR_HOT_X(x) ((x) << 16) +# define CURSOR_HOT_X_MASK GENMASK(23, 16) +# define CURSOR_HOT_y(x) ((x) << 8) +# define CURSOR_HOT_y_MASK GENMASK(15, 8) +# define CURSOR_SIZE(x) ((x) << 5) +# define CURSOR_SIZE_MASK GENMASK(7, 5) +# define CURSOR_VALID(x) ((x) << 3) +# define CURSOR_VALID_MASK GENMASK(3, 3) +# define CURSOR_TRIG_FETCH(x) ((x) << 2) +# define CURSOR_TRIG_FETCH_MASK GENMASK(2, 2) +# define CURSOR_FORMAT(x) ((x) << 0) +# define CURSOR_FORMAT_MASK GENMASK(1, 0) +# define CURSOR_FORMAT_DISABLE 0 +# define CURSOR_FORMAT_MARK 1 +# define CURSOR_FORMAT_A8R8G8B8 2 + +#define DC_CURSOR_ADDRESS 0x146C +#define DC_CURSOR_LOCATION 0x1470 +# define X_LCOTION(x) (x) +# define Y_LCOTION(x) ((x) << 16) + +#define DC_CURSOR_BACKGROUND 0x1474 +#define DC_CURSOR_FOREGROUND 0x1478 +#define DC_CURSOR_CLK_GATING 0x1484 +#define DC_CURSOR_CONFIG_EX 0x24E8 +#define DC_CURSOR_OFFSET 0x1080 + +#define DC_DISPLAY_DITHER_CONFIG 0x1410 +#define DC_DISPLAY_PANEL_CONFIG 0x1418 +# define PANEL_RGB2YUV_EN BIT(16) +# define PANEL_GAMMA_EN BIT(13) +# define PANEL_OUTPUT_EN BIT(12) + +#define DC_DISPLAY_PANEL_CONFIG_EX 0x2518 +# define PANEL_SHADOW_EN BIT(0) + +#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420 +#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428 +#define DC_DISPLAY_H 0x1430 +# define H_ACTIVE_LEN(x) (x) +# define H_TOTAL_LEN(x) ((x) << 16) + +#define DC_DISPLAY_H_SYNC 0x1438 +# define H_SYNC_START_LEN(x) (x) +# define H_SYNC_END_LEN(x) ((x) << 15) +# define H_PLUS_LEN(x) ((x) << 30) +# define H_POLARITY_LEN(x) ((x) << 31) + +#define DC_DISPLAY_V 0x1440 +# define V_ACTIVE_LEN(x) (x) +# define V_TOTAL_LEN(x) ((x) << 16) + +#define DC_DISPLAY_V_SYNC 0x1448 +# define V_SYNC_START_LEN(x) (x) +# define V_SYNC_END_LEN(x) ((x) << 15) +# define V_PLUS_LEN(x) ((x) << 30) +# define V_POLARITY_LEN(x) ((x) << 31) + +#define DC_DISPLAY_CURRENT_LOCATION 0x1450 +#define DC_DISPLAY_GAMMA_INDEX 0x1458 +#define DC_DISPLAY_GAMMA_DATA 0x1460 +#define DC_DISPLAY_INT 0x147C +#define DC_DISPLAY_INT_ENABLE 0x1480 +#define DC_DISPLAY_DBI_CONFIG 0x1488 +#define DC_DISPLAY_GENERAL_CONFIG 0x14B0 +#define DC_DISPLAY_DPI_CONFIG 0x14B8 +#define DC_DISPLAY_PANEL_START 0x1CCC +# define PANEL0_EN BIT(0) +# define PANEL1_EN BIT(1) +# define TWO_PANEL_EN BIT(2) +# define SYNC_EN BIT(3) + +#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0 +#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8 +#define DC_DISPLAY_DP_CONFIG 0x1CD0 +# define DP_SELECT BIT(3) + +#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0 +#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8 +#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80 +#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48 +#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50 +#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58 +#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60 +#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68 +#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70 +#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78 +#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80 + +#define DC_CLK_GATTING 0x1A28 +#define DC_QOS_CONFIG 0x1A38 + +#define DC_TRANSPARENCY_OPAQUE 0x00 +#define DC_TRANSPARENCY_KEY 0x02 +#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0 +#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2 + +#define GAMMA_SIZE 256 +#define GAMMA_EX_SIZE 300 +#define DEGAMMA_SIZE 260 + +#define RGB_TO_RGB_TABLE_SIZE 9 +#define YUV_TO_RGB_TABLE_SIZE 16 +#define RGB_TO_YUV_TABLE_SIZE 12 + +#define DC_LAYER_NUM 6 +#define DC_DISPLAY_NUM 2 +#define DC_CURSOR_NUM 2 + +#define DC_TILE_MODE4X4 0x15 + +enum dc_hw_plane_id { + PRIMARY_PLANE_0, + OVERLAY_PLANE_0, + OVERLAY_PLANE_1, + PRIMARY_PLANE_1, + OVERLAY_PLANE_2, + OVERLAY_PLANE_3, + CURSOR_PLANE_0, + CURSOR_PLANE_1, + PLANE_NUM +}; + +enum dc_hw_color_format { + FORMAT_X4R4G4B4, + FORMAT_A4R4G4B4, + FORMAT_X1R5G5B5, + FORMAT_A1R5G5B5, + FORMAT_R5G6B5, + FORMAT_X8R8G8B8, + FORMAT_A8R8G8B8, + FORMAT_YUY2, + FORMAT_UYVY, + FORMAT_INDEX8, + FORMAT_MONOCHROME, + FORMAT_YV12 = 0xf, + FORMAT_A8, + FORMAT_NV12, + FORMAT_NV16, + FORMAT_RG16, + FORMAT_R8, + FORMAT_NV12_10BIT, + FORMAT_A2R10G10B10, + FORMAT_NV16_10BIT, + FORMAT_INDEX1, + FORMAT_INDEX2, + FORMAT_INDEX4, + FORMAT_P010, + FORMAT_YUV444, + FORMAT_YUV444_10BIT, +}; + +enum dc_hw_yuv_color_space { + COLOR_SPACE_601 = 0, + COLOR_SPACE_709 = 1, + COLOR_SPACE_2020 = 3, +}; + +enum dc_hw_rotation { + ROT_0 = 0, + ROT_90 = 4, + ROT_180 = 5, + ROT_270 = 6, + FLIP_X = 1, + FLIP_Y = 2, + FLIP_XY = 3, +}; + +enum dc_hw_swizzle { + SWIZZLE_ARGB = 0, + SWIZZLE_RGBA, + SWIZZLE_ABGR, + SWIZZLE_BGRA, +}; + +enum dc_hw_out { + OUT_DPI, + OUT_DP, +}; + +enum dc_hw_cursor_size { + CURSOR_SIZE_32X32 = 0, + CURSOR_SIZE_64X64, +}; + +struct dc_hw_plane_reg { + u32 y_address; + u32 u_address; + u32 v_address; + u32 y_stride; + u32 u_stride; + u32 v_stride; + u32 size; + u32 top_left; + u32 bottom_right; + u32 scale_factor_x; + u32 scale_factor_y; + u32 h_filter_coef_index; + u32 h_filter_coef_data; + u32 v_filter_coef_index; + u32 v_filter_coef_data; + u32 init_offset; + u32 color_key; + u32 color_key_high; + u32 clear_value; + u32 color_table_index; + u32 color_table_data; + u32 scale_config; + u32 water_mark; + u32 degamma_index; + u32 degamma_data; + u32 degamma_ex_data; + u32 src_global_color; + u32 dst_global_color; + u32 blend_config; + u32 roi_origin; + u32 roi_size; + u32 yuv_to_rgb_coef0; + u32 yuv_to_rgb_coef1; + u32 yuv_to_rgb_coef2; + u32 yuv_to_rgb_coef3; + u32 yuv_to_rgb_coef4; + u32 yuv_to_rgb_coefd0; + u32 yuv_to_rgb_coefd1; + u32 yuv_to_rgb_coefd2; + u32 y_clamp_bound; + u32 uv_clamp_bound; + u32 rgb_to_rgb_coef0; + u32 rgb_to_rgb_coef1; + u32 rgb_to_rgb_coef2; + u32 rgb_to_rgb_coef3; + u32 rgb_to_rgb_coef4; +}; + +struct dc_hw_fb { + u32 y_address; + u32 u_address; + u32 v_address; + u32 clear_value; + u32 water_mark; + u16 y_stride; + u16 u_stride; + u16 v_stride; + u16 width; + u16 height; + u8 format; + u8 tile_mode; + u8 rotation; + u8 yuv_color_space; + u8 swizzle; + u8 uv_swizzle; + u8 zpos; + u8 display_id; + bool clear_enable; + bool dec_enable; + bool enable; + bool dirty; +}; + +struct dc_hw_scale { + u32 scale_factor_x; + u32 scale_factor_y; + bool enable; + bool dirty; +}; + +struct dc_hw_position { + u16 start_x; + u16 start_y; + u16 end_x; + u16 end_y; + bool dirty; +}; + +struct dc_hw_blend { + u8 alpha; + u8 blend_mode; + bool dirty; +}; + +struct dc_hw_colorkey { + u32 colorkey; + u32 colorkey_high; + u8 transparency; + bool dirty; +}; + +struct dc_hw_roi { + u16 x; + u16 y; + u16 width; + u16 height; + bool enable; + bool dirty; +}; + +struct dc_hw_cursor { + u32 address; + u16 x; + u16 y; + u16 hot_x; + u16 hot_y; + u8 size; + u8 display_id; + bool enable; + bool dirty; +}; + +struct dc_hw_display { + u32 bus_format; + u16 h_active; + u16 h_total; + u16 h_sync_start; + u16 h_sync_end; + u16 v_active; + u16 v_total; + u16 v_sync_start; + u16 v_sync_end; + u8 id; + bool h_sync_polarity; + bool v_sync_polarity; + bool enable; +}; + +struct dc_hw_gamma { + u16 gamma[GAMMA_EX_SIZE][3]; + bool enable; + bool dirty; +}; + +struct dc_hw_plane { + struct dc_hw_fb fb; + struct dc_hw_position pos; + struct dc_hw_scale scale; + struct dc_hw_blend blend; +}; + +struct dc_hw_qos { + u8 low_value; + u8 high_value; + bool dirty; +}; + +struct dc_hw_read { + u32 reg; + u32 value; +}; + +struct dc_hw; +struct dc_hw_funcs { + void (*gamma)(struct dc_hw *hw); + void (*plane)(struct dc_hw *hw); + void (*display)(struct dc_hw *hw, struct dc_hw_display *display); +}; + +struct dc_hw { + enum dc_hw_out out[DC_DISPLAY_NUM]; + void *hi_base; + void *reg_base; + struct dc_hw_plane_reg reg[DC_LAYER_NUM]; + + struct dc_hw_display display[DC_DISPLAY_NUM]; + struct dc_hw_gamma gamma[DC_DISPLAY_NUM]; + struct dc_hw_plane plane[DC_LAYER_NUM]; + struct dc_hw_cursor cursor[DC_CURSOR_NUM]; + struct dc_hw_funcs *func; + struct vs_dc_info *info; +}; + +int dc_hw_init(struct dc_hw *hw); +void dc_hw_deinit(struct dc_hw *hw); +void dc_hw_update_plane(struct dc_hw *hw, u8 id, + struct dc_hw_fb *fb, struct dc_hw_scale *scale, + struct dc_hw_position *pos, struct dc_hw_blend *blend); +void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor); +void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index, + u16 r, u16 g, u16 b); +void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable); +void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display); +void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable); +u32 dc_hw_get_interrupt(struct dc_hw *hw); +void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable); +void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id); +void dc_hw_commit(struct dc_hw *hw); + +#endif /* __VS_DC_HW_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c index 4fb1f29ef84b..3ef90c8238a0 100644 --- a/drivers/gpu/drm/verisilicon/vs_drv.c +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -22,6 +22,7 @@ #include "vs_drv.h" #include "vs_modeset.h" +#include "vs_dc.h" #define DRV_NAME "verisilicon" #define DRV_DESC "Verisilicon DRM driver" @@ -212,6 +213,7 @@ static const struct component_master_ops vs_drm_ops = { }; static struct platform_driver *drm_sub_drivers[] = { + &dc_platform_driver, }; static struct component_match *vs_drm_match_add(struct device *dev) diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c new file mode 100644 index 000000000000..53580fbe05b1 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vs_plane.h" +#include "vs_drv.h" +#include "vs_dc.h" + +static void vs_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vs_plane_state *vs_plane_state = to_vs_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + + kfree(vs_plane_state); +} + +static void vs_plane_reset(struct drm_plane *plane) +{ + struct vs_plane_state *state; + struct vs_plane *vs_plane = to_vs_plane(plane); + + if (plane->state) + vs_plane_atomic_destroy_state(plane, plane->state); + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return; + + state->base.zpos = vs_plane->id; + __drm_atomic_helper_plane_reset(plane, &state->base); +} + +static struct drm_plane_state * +vs_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct vs_plane_state *old_state; + struct vs_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + old_state = to_vs_plane_state(plane->state); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + + return &state->base; +} + +static bool vs_format_mod_supported(struct drm_plane *plane, + u32 format, + u64 modifier) +{ + int i; + + /* We always have to allow these modifiers: + * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers. + * 2. Not passing any modifiers is the same as explicitly passing INVALID. + */ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + /* Check that the modifier is on the list of the plane's supported modifiers. */ + for (i = 0; i < plane->modifier_count; i++) { + if (modifier == plane->modifiers[i]) + break; + } + + if (i == plane->modifier_count) + return false; + + return true; +} + +const struct drm_plane_funcs vs_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = vs_plane_reset, + .atomic_duplicate_state = vs_plane_atomic_duplicate_state, + .atomic_destroy_state = vs_plane_atomic_destroy_state, + .format_mod_supported = vs_format_mod_supported, +}; + +static unsigned char vs_get_plane_number(struct drm_framebuffer *fb) +{ + const struct drm_format_info *info; + + if (!fb) + return 0; + + info = drm_format_info(fb->format->format); + if (!info || info->num_planes > DRM_FORMAT_MAX_PLANES) + return 0; + + return info->num_planes; +} + +static int vs_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + unsigned char i, num_planes; + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc *crtc = new_plane_state->crtc; + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state); + + if (!crtc || !fb) + return 0; + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + dma_addr_t dma_addr; + + dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i); + plane_state->dma_addr[i] = dma_addr; + } + + return vs_dc_check_plane(dc, plane, state); +} + +static int vs_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + unsigned char i, num_planes; + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc *crtc = new_plane_state->crtc; + struct vs_crtc *vs_crtc = to_vs_crtc(crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state); + + if (!crtc || !fb) + return 0; + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + dma_addr_t dma_addr; + + dma_addr = drm_fb_dma_get_gem_addr(fb, new_plane_state, i); + plane_state->dma_addr[i] = dma_addr; + } + + return vs_dc_check_cursor_plane(dc, plane, state); +} + +static void vs_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + + unsigned char i, num_planes; + struct drm_framebuffer *fb; + struct vs_plane *vs_plane = to_vs_plane(plane); + struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc); + struct vs_plane_state *plane_state = to_vs_plane_state(new_state); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + if (!new_state->fb || !new_state->crtc) + return; + + fb = new_state->fb; + + drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state); + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + dma_addr_t dma_addr; + + dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i); + plane_state->dma_addr[i] = dma_addr; + } + + vs_dc_update_plane(dc, vs_plane, plane, state); +} + +static void vs_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + unsigned char i, num_planes; + struct drm_framebuffer *fb; + struct vs_plane *vs_plane = to_vs_plane(plane); + struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc); + struct vs_plane_state *plane_state = to_vs_plane_state(new_state); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + if (!new_state->fb || !new_state->crtc) + return; + + fb = new_state->fb; + drm_fb_dma_sync_non_coherent(fb->dev, old_state, new_state); + + num_planes = vs_get_plane_number(fb); + + for (i = 0; i < num_planes; i++) { + dma_addr_t dma_addr; + + dma_addr = drm_fb_dma_get_gem_addr(fb, new_state, i); + plane_state->dma_addr[i] = dma_addr; + } + + vs_dc_update_cursor_plane(dc, vs_plane, plane, state); +} + +static void vs_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct vs_plane *vs_plane = to_vs_plane(plane); + struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_disable_plane(dc, vs_plane, old_state); +} + +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct vs_plane *vs_plane = to_vs_plane(plane); + struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc); + struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev); + + vs_dc_disable_cursor_plane(dc, vs_plane, old_state); +} + +const struct drm_plane_helper_funcs primary_plane_helpers = { + .atomic_check = vs_plane_atomic_check, + .atomic_update = vs_plane_atomic_update, + .atomic_disable = vs_plane_atomic_disable, +}; + +const struct drm_plane_helper_funcs overlay_plane_helpers = { + .atomic_check = vs_plane_atomic_check, + .atomic_update = vs_plane_atomic_update, + .atomic_disable = vs_plane_atomic_disable, +}; + +const struct drm_plane_helper_funcs cursor_plane_helpers = { + .atomic_check = vs_cursor_plane_atomic_check, + .atomic_update = vs_cursor_plane_atomic_update, + .atomic_disable = vs_cursor_plane_atomic_disable, +}; + +struct vs_plane *vs_plane_create(struct drm_device *drm_dev, + struct vs_plane_info *info, + unsigned int layer_num, + unsigned int possible_crtcs) +{ + struct vs_plane *plane; + + if (!info) + return NULL; + + plane = drmm_universal_plane_alloc(drm_dev, struct vs_plane, base, + possible_crtcs, + &vs_plane_funcs, + info->formats, info->num_formats, + info->modifiers, info->type, + info->name ? info->name : NULL); + if (IS_ERR(plane)) + return ERR_CAST(plane); + + if (info->type == DRM_PLANE_TYPE_PRIMARY) + drm_plane_helper_add(&plane->base, &primary_plane_helpers); + else if (info->type == DRM_PLANE_TYPE_CURSOR) + drm_plane_helper_add(&plane->base, &cursor_plane_helpers); + else + drm_plane_helper_add(&plane->base, &overlay_plane_helpers); + + return plane; +} diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h new file mode 100644 index 000000000000..7174ac298ffd --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_PLANE_H__ +#define __VS_PLANE_H__ + +#include +#include + +#include "vs_type.h" + +struct vs_plane_state { + struct drm_plane_state base; + dma_addr_t dma_addr[DRM_FORMAT_MAX_PLANES]; +}; + +struct vs_plane { + struct drm_plane base; + u8 id; +}; + +struct vs_plane *vs_plane_create(struct drm_device *drm_dev, + struct vs_plane_info *info, + unsigned int layer_num, + unsigned int possible_crtcs); + +static inline struct vs_plane *to_vs_plane(struct drm_plane *plane) +{ + return container_of(plane, struct vs_plane, base); +} + +static inline struct vs_plane_state * +to_vs_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct vs_plane_state, base); +} +#endif /* __VS_PLANE_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_type.h b/drivers/gpu/drm/verisilicon/vs_type.h new file mode 100644 index 000000000000..7d3378e29c87 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_type.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef __VS_TYPE_H__ +#define __VS_TYPE_H__ + +#include +#include + +struct vs_plane_info { + const char *name; + u8 id; + enum drm_plane_type type; + unsigned int num_formats; + const u32 *formats; + u8 num_modifiers; + const u64 *modifiers; + unsigned int min_width; + unsigned int min_height; + unsigned int max_width; + unsigned int max_height; + unsigned int rotation; + unsigned int blend_mode; + unsigned int color_encoding; + + /* 0 means no de-gamma LUT */ + unsigned int degamma_size; + + int min_scale; /* 16.16 fixed point */ + int max_scale; /* 16.16 fixed point */ + + /* default zorder value, + * and 255 means unsupported zorder capability + */ + u8 zpos; + + bool watermark; + bool color_mgmt; + bool roi; +}; + +struct vs_dc_info { + const char *name; + + u8 panel_num; + + /* planes */ + u8 plane_num; + const struct vs_plane_info *planes; + + u8 layer_num; + unsigned int max_bpc; + unsigned int color_formats; + + /* 0 means no gamma LUT */ + u16 gamma_size; + u8 gamma_bits; + + u16 pitch_alignment; + + bool pipe_sync; + bool background; + bool panel_sync; + bool cap_dec; +}; + +#endif /* __VS_TYPE_H__ */