From patchwork Wed Apr 13 22:19:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560953 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 72476C433EF for ; Wed, 13 Apr 2022 22:19:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236067AbiDMWVu (ORCPT ); Wed, 13 Apr 2022 18:21:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38350 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231311AbiDMWVt (ORCPT ); Wed, 13 Apr 2022 18:21:49 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1BD392DC7; Wed, 13 Apr 2022 15:19:25 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id 773CB5C01F8; Wed, 13 Apr 2022 18:19:24 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 13 Apr 2022 18:19:24 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888364; x=1649974764; bh=t3 R9K4RmpPHgFV2MCz+ahY35ugv1/r5JX9ttGYH/p/s=; b=N+/kv28XEg7cqCbiqW +pxb2lwnBU9wmAVqImOnBSGM6WsUogv+MVcj58IxCD0hv22fukH8eyBCi0+tkGAy zL5gGcPeF8rkgjdpTJlVjvNKfPu29vfnuj0o86hhvi1tHfCzx4mgtcSGRCUmG/Zz b7LhKiXdEJMT+gGqOnC4YDiPWMJR+MFgVkwDLeCdqF5qz2cAoXIyzzCp7ByF9DaE JkmoslCswHsptydVxVyqpDn+JiJBlDRtcSBrefLO5iMViqA/shxPf0x8+h2kY6X/ vkiRO/YJBWVZt5V+72gAvHlcmgoi5bjc5TC0dNuTZhoVOcKFd9bdpl6WB+6lwUWJ KAzw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888364; x=1649974764; bh=t3R9K4RmpPHgFV2MCz+ahY35ugv1/r5JX9t tGYH/p/s=; b=hEfVZV3vugr1D11KTaj8Wdd4Be/EOWwCeBqZW5PuCcqls8jxMWL X96i3gmapN05hcJ1xKnz6ce8RPbitCbIiCY4VaKFQ4A0uRJG7xuWxYFM90WAzXbG E5GbOn1djKtTI26X3Q5NdwzKcTzXm4ZNGsDbBJm10iJ3WQ5DXkurqG69CvwyQdup 0sSajJIDZrr6wDeszGG2SgCeBygrW04M3MEQdk9Ji3dRhvy9rahZR4cAepDiBrps 3Rw0OrUFbqxUSXNhaoOD1f1jgBWlfyDi9MByhETjQ1ii2SNQUVEyGP8PpVILeP9I GiTXcIklWmXxhDBRLrlTM9GBiC2lvSHR4Dw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:22 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 01/16] drm: Add a helper library for EPD drivers Date: Wed, 13 Apr 2022 17:19:01 -0500 Message-Id: <20220413221916.50995-2-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Currently, this library is focused on LUTs and LUT files, specifically the PVI wbf-format files shipped with E-Ink displays. It provides helpers to load and validate LUT files, and extract LUTs from them. Since EPD controllers need the LUTs in various formats, this library allows drivers to declare their desired format. It will then convert LUTs to that format before returning them. Signed-off-by: Samuel Holland --- drivers/gpu/drm/Kconfig | 6 + drivers/gpu/drm/Makefile | 2 + drivers/gpu/drm/drm_epd_helper.c | 663 +++++++++++++++++++++++++++++++ include/drm/drm_epd_helper.h | 104 +++++ 4 files changed, 775 insertions(+) create mode 100644 drivers/gpu/drm/drm_epd_helper.c create mode 100644 include/drm/drm_epd_helper.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f1422bee3dcc..ad96cf605444 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -198,6 +198,12 @@ config DRM_DP_CEC Note: not all adapters support this feature, and even for those that do support this they often do not hook up the CEC pin. +config DRM_EPD_HELPER + tristate + depends on DRM + help + Choose this if you need the EPD (LUT, etc.) helper functions + config DRM_TTM tristate depends on DRM && MMU diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c2ef5f9fce54..49380ccfe9d6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -33,6 +33,8 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += drm_privacy_screen.o drm_privacy_screen_x86. obj-$(CONFIG_DRM_NOMODESET) += drm_nomodeset.o +obj-$(CONFIG_DRM_EPD_HELPER) += drm_epd_helper.o + drm_cma_helper-y := drm_gem_cma_helper.o drm_cma_helper-$(CONFIG_DRM_KMS_HELPER) += drm_fb_cma_helper.o obj-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_cma_helper.o diff --git a/drivers/gpu/drm/drm_epd_helper.c b/drivers/gpu/drm/drm_epd_helper.c new file mode 100644 index 000000000000..433a6728ef3e --- /dev/null +++ b/drivers/gpu/drm/drm_epd_helper.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Electrophoretic Display Helper Library + * + * Copyright (C) 2022 Samuel Holland + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +/** + * DOC: overview + * + * This library provides functions for working with the lookup tables (LUTs) + * used by electrophoretic displays (EPDs). It fills in a LUT buffer based on + * the selected waveform, the panel temperature, and the buffer format needed + * by the driver. + */ + +struct pvi_wbf_offset { + u8 b[3]; +}; + +struct pvi_wbf_pointer { + struct pvi_wbf_offset offset; + u8 checksum; +}; + +struct pvi_wbf_file_header { + __le32 checksum; // 0x00 + __le32 file_size; // 0x04 + __le32 serial; // 0x08 + u8 run_type; // 0x0c + u8 fpl_platform; // 0x0d + __le16 fpl_lot; // 0x0e + u8 mode_version; // 0x10 + u8 wf_version; // 0x11 + u8 wf_subversion; // 0x12 + u8 wf_type; // 0x13 + u8 panel_size; // 0x14 + u8 amepd_part_number; // 0x15 + u8 wf_rev; // 0x16 + u8 frame_rate_bcd; // 0x17 + u8 frame_rate_hex; // 0x18 + u8 vcom_offset; // 0x19 + u8 unknown[2]; // 0x1a + struct pvi_wbf_offset xwia; // 0x1c + u8 cs1; // 0x1f + struct pvi_wbf_offset wmta; // 0x20 + u8 fvsn; // 0x23 + u8 luts; // 0x24 + u8 mode_count; // 0x25 + u8 temp_range_count; // 0x26 + u8 advanced_wf_flags; // 0x27 + u8 eb; // 0x28 + u8 sb; // 0x29 + u8 reserved[5]; // 0x2a + u8 cs2; // 0x2f + u8 temp_range_table[]; // 0x30 +}; +static_assert(sizeof(struct pvi_wbf_file_header) == 0x30); + +struct pvi_wbf_mode_info { + u8 versions[2]; + u8 format; + u8 modes[DRM_EPD_WF_MAX]; +}; + +static const struct pvi_wbf_mode_info pvi_wbf_mode_info_table[] = { + { + .versions = { + 0x09, + }, + .format = DRM_EPD_LUT_4BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 1, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 3, + [DRM_EPD_WF_GLD16] = 3, + [DRM_EPD_WF_A2] = 4, + [DRM_EPD_WF_GCC16] = 3, + }, + }, + { + .versions = { + 0x12, + }, + .format = DRM_EPD_LUT_4BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 7, + [DRM_EPD_WF_GC16] = 3, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 5, + [DRM_EPD_WF_GLD16] = 6, + [DRM_EPD_WF_A2] = 4, + [DRM_EPD_WF_GCC16] = 5, + }, + }, + { + .versions = { + 0x16, + }, + .format = DRM_EPD_LUT_5BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 1, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 4, + [DRM_EPD_WF_GLD16] = 4, + [DRM_EPD_WF_A2] = 6, + [DRM_EPD_WF_GCC16] = 5, + }, + }, + { + .versions = { + 0x18, + 0x20, + }, + .format = DRM_EPD_LUT_5BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 1, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 4, + [DRM_EPD_WF_GLD16] = 5, + [DRM_EPD_WF_A2] = 6, + [DRM_EPD_WF_GCC16] = 4, + }, + }, + { + .versions = { + 0x19, + 0x43, + }, + .format = DRM_EPD_LUT_5BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 7, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 4, + [DRM_EPD_WF_GLD16] = 5, + [DRM_EPD_WF_A2] = 6, + [DRM_EPD_WF_GCC16] = 4, + }, + }, + { + .versions = { + 0x23, + }, + .format = DRM_EPD_LUT_4BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 5, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 3, + [DRM_EPD_WF_GLD16] = 3, + [DRM_EPD_WF_A2] = 4, + [DRM_EPD_WF_GCC16] = 3, + }, + }, + { + .versions = { + 0x54, + }, + .format = DRM_EPD_LUT_4BIT_PACKED, + .modes = { + [DRM_EPD_WF_RESET] = 0, + [DRM_EPD_WF_DU] = 1, + [DRM_EPD_WF_DU4] = 1, + [DRM_EPD_WF_GC16] = 2, + [DRM_EPD_WF_GL16] = 3, + [DRM_EPD_WF_GLR16] = 4, + [DRM_EPD_WF_GLD16] = 4, + [DRM_EPD_WF_A2] = 5, + [DRM_EPD_WF_GCC16] = 4, + }, + }, +}; + +static const void *pvi_wbf_apply_offset(const struct drm_epd_lut_file *file, + const struct pvi_wbf_offset *offset) +{ + u32 bytes = offset->b[0] | offset->b[1] << 8 | offset->b[2] << 16; + + if (bytes >= file->fw->size) + return NULL; + + return (const void *)file->header + bytes; +} + +static const void *pvi_wbf_dereference(const struct drm_epd_lut_file *file, + const struct pvi_wbf_pointer *ptr) +{ + u8 sum = ptr->offset.b[0] + ptr->offset.b[1] + ptr->offset.b[2]; + + if (ptr->checksum != sum) + return NULL; + + return pvi_wbf_apply_offset(file, &ptr->offset); +} + +static int pvi_wbf_get_mode_index(const struct drm_epd_lut_file *file, + enum drm_epd_waveform waveform) +{ + if (waveform >= DRM_EPD_WF_MAX) + return -EINVAL; + + return file->mode_info->modes[waveform]; +} + +static int pvi_wbf_get_mode_info(struct drm_epd_lut_file *file) +{ + u8 mode_version = file->header->mode_version; + const struct pvi_wbf_mode_info *mode_info; + int i, v; + + for (i = 0; i < ARRAY_SIZE(pvi_wbf_mode_info_table); i++) { + mode_info = &pvi_wbf_mode_info_table[i]; + + for (v = 0; v < ARRAY_SIZE(mode_info->versions); v++) { + if (mode_info->versions[v] == mode_version) { + file->mode_info = mode_info; + return 0; + } + } + } + + drm_err(file->dev, "Unknown PVI waveform version 0x%02x\n", + mode_version); + + return -EOPNOTSUPP; +} + +static int pvi_wbf_get_temp_index(const struct drm_epd_lut_file *file, + int temperature) +{ + const struct pvi_wbf_file_header *header = file->header; + int i; + + for (i = 0; i < header->temp_range_count; i++) + if (temperature < header->temp_range_table[i]) + return i - 1; + + return header->temp_range_count - 1; +} + +static int pvi_wbf_validate_header(struct drm_epd_lut_file *file) +{ + const struct pvi_wbf_file_header *header = file->header; + int ret; + + if (le32_to_cpu(header->file_size) > file->fw->size) + return -EINVAL; + + ret = pvi_wbf_get_mode_info(file); + if (ret) + return ret; + + drm_info(file->dev, "Loaded %d-bit PVI waveform version 0x%02x\n", + file->mode_info->format == DRM_EPD_LUT_5BIT_PACKED ? 5 : 4, + header->mode_version); + + return 0; +} + +static void drm_epd_lut_file_free(struct drm_device *dev, void *res) +{ + struct drm_epd_lut_file *file = res; + + release_firmware(file->fw); +} + +/** + * drmm_epd_lut_file_init - Initialize a managed EPD LUT file + * + * @dev: The DRM device owning this LUT file + * @file: The LUT file to initialize + * @file_name: The filesystem name of the LUT file + * + * Return: negative errno on failure, 0 otherwise + */ +int drmm_epd_lut_file_init(struct drm_device *dev, + struct drm_epd_lut_file *file, + const char *file_name) +{ + int ret; + + ret = request_firmware(&file->fw, file_name, dev->dev); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drm_epd_lut_file_free, file); + if (ret) + return ret; + + file->dev = dev; + file->header = (const void *)file->fw->data; + + ret = pvi_wbf_validate_header(file); + if (ret) + return ret; + + /* Only 5-bit waveform files are supported by drm_epd_lut_convert. */ + if (file->mode_info->format != DRM_EPD_LUT_5BIT_PACKED) + return -EOPNOTSUPP; + + return 0; +} +EXPORT_SYMBOL(drmm_epd_lut_file_init); + +/** + * drm_epd_lut_size_shift - Get the size of a LUT phase in power-of-2 bytes + * + * @format: One of the LUT buffer formats + * + * Return: buffer size shift amount + */ +static int drm_epd_lut_size_shift(enum drm_epd_lut_format format) +{ + switch (format) { + case DRM_EPD_LUT_4BIT: + /* (4 bits/pixel)^2 / 1 pixel/byte */ + return 4 + 4; + case DRM_EPD_LUT_4BIT_PACKED: + /* (4 bits/pixel)^2 / 4 pixels/byte */ + return 4 + 4 - 2; + case DRM_EPD_LUT_5BIT: + /* (5 bits/pixel)^2 / 1 pixel/byte */ + return 5 + 5; + case DRM_EPD_LUT_5BIT_PACKED: + /* (5 bits/pixel)^2 / 4 pixels/byte */ + return 5 + 5 - 2; + } + + unreachable(); +} + +static int pvi_wbf_decode_lut(struct drm_epd_lut *lut, const u8 *lut_data) +{ + + unsigned int copies, max_bytes, size_shift, state, total_bytes; + struct drm_device *dev = lut->file->dev; + const u8 *in = lut_data; + u8 *out = lut->buf; + u8 token; + + size_shift = drm_epd_lut_size_shift(lut->file->mode_info->format); + max_bytes = lut->max_phases << size_shift; + total_bytes = 0; + state = 1; + + /* Read tokens until reaching the end-of-input marker. */ + while ((token = *in++) != 0xff) { + /* Special handling for the state switch token. */ + if (token == 0xfc) { + state = !state; + token = *in++; + } + + /* + * State 0 is a sequence of data bytes. + * State 1 is a sequence of [data byte, extra copies] pairs. + */ + copies = 1 + (state ? *in++ : 0); + + total_bytes += copies; + if (total_bytes > max_bytes) { + drm_err(dev, "LUT contains too many phases\n"); + lut->num_phases = 0; + return -EILSEQ; + } + + while (copies--) + *out++ = token; + } + + lut->num_phases = total_bytes >> size_shift; + if (total_bytes != lut->num_phases << size_shift) { + drm_err(dev, "LUT contains a partial phase\n"); + lut->num_phases = 0; + return -EILSEQ; + } + + drm_dbg_core(dev, "LUT contains %d phases (%ld => %ld bytes)\n", + lut->num_phases, in - lut_data, out - lut->buf); + + return 0; +} + +static int pvi_wbf_get_lut(struct drm_epd_lut *lut, + int mode_index, int temp_index) +{ + const struct pvi_wbf_pointer *mode_table, *temp_table; + const struct drm_epd_lut_file *file = lut->file; + const u8 *lut_data; + int ret; + + mode_table = pvi_wbf_apply_offset(file, &file->header->wmta); + if (!mode_table) + return -EFAULT; + + temp_table = pvi_wbf_dereference(file, &mode_table[mode_index]); + if (!temp_table) + return -EFAULT; + + lut_data = pvi_wbf_dereference(file, &temp_table[temp_index]); + if (!lut_data) + return -EFAULT; + + ret = pvi_wbf_decode_lut(lut, lut_data); + if (ret) + return ret; + + return 0; +} + +static void drm_epd_lut_convert(const struct drm_epd_lut *lut) +{ + enum drm_epd_lut_format from = lut->file->mode_info->format; + enum drm_epd_lut_format to = lut->format; + u8 *buf = lut->buf; + size_t x, y; + + if (to == from) + return; + + switch (to) { + case DRM_EPD_LUT_4BIT: + for (y = 0; y < 16 * lut->num_phases; ++y) { + for (x = 8; x--;) { + u8 byte = buf[16 * y + x]; + + buf[16 * y + 2 * x + 0] = (byte >> 0) & 0x03; + buf[16 * y + 2 * x + 1] = (byte >> 4) & 0x03; + } + } + for (; y < 16 * lut->max_phases; ++y) { + for (x = 8; x--;) { + buf[16 * y + 2 * x + 0] = 0; + buf[16 * y + 2 * x + 1] = 0; + } + } + break; + case DRM_EPD_LUT_4BIT_PACKED: + for (y = 0; y < 16 * lut->num_phases; ++y) { + for (x = 4; x--;) { + u8 lo_byte = buf[16 * y + 2 * x + 0] & 0x33; + u8 hi_byte = buf[16 * y + 2 * x + 1] & 0x33; + + /* Copy bits 4:5 => bits 2:3. */ + lo_byte |= lo_byte >> 2; + hi_byte |= hi_byte >> 2; + + buf[4 * y + x] = (lo_byte & 0xf) | + (hi_byte << 4); + } + } + for (; y < 16 * lut->max_phases; ++y) { + for (x = 4; x--;) + buf[4 * y + x] = 0; + } + break; + case DRM_EPD_LUT_5BIT: + memset(buf + 256 * lut->num_phases, 0, + 256 * (lut->max_phases - lut->num_phases)); + for (x = 256 * lut->num_phases; x--;) { + u8 byte = buf[x]; + + buf[4 * x + 0] = (byte >> 0) & 0x03; + buf[4 * x + 1] = (byte >> 2) & 0x03; + buf[4 * x + 2] = (byte >> 4) & 0x03; + buf[4 * x + 3] = (byte >> 6) & 0x03; + } + break; + case DRM_EPD_LUT_5BIT_PACKED: + /* Nothing to do. */ + break; + } +} + +static int drm_epd_lut_update(struct drm_epd_lut *lut, + int mode_index, int temp_index) +{ + int ret; + + ret = pvi_wbf_get_lut(lut, mode_index, temp_index); + if (ret) + return ret; + + drm_epd_lut_convert(lut); + + return 0; +} + +/** + * drm_epd_lut_set_temperature - Update the LUT due to panel temperature change + * + * @lut: The LUT structure + * @temperateure: The current panel temperature in degrees Celsius + * + * Return: negative errno on failure, 1 if LUT was changed, 0 otherwise + */ +int drm_epd_lut_set_temperature(struct drm_epd_lut *lut, + int temperature) +{ + int temp_index; + int ret; + + temp_index = pvi_wbf_get_temp_index(lut->file, temperature); + if (temp_index < 0) + return -ENOENT; + + if (temp_index == lut->temp_index) + return 0; + + drm_dbg_core(lut->file->dev, "LUT temperature changed (%d)\n", + temperature); + + ret = drm_epd_lut_update(lut, lut->mode_index, temp_index); + if (ret) + return ret; + + lut->temp_index = temp_index; + + return 1; +} +EXPORT_SYMBOL(drm_epd_lut_set_temperature); + +/** + * drm_epd_lut_set_waveform - Update the LUT due to waveform selection change + * + * @lut: The LUT structure + * @waveform: The desired waveform + * + * Return: negative errno on failure, 1 if LUT was changed, 0 otherwise + */ +int drm_epd_lut_set_waveform(struct drm_epd_lut *lut, + enum drm_epd_waveform waveform) +{ + int mode_index; + int ret; + + mode_index = pvi_wbf_get_mode_index(lut->file, waveform); + if (mode_index < 0) + return -ENOENT; + + if (mode_index == lut->mode_index) + return 0; + + drm_dbg_core(lut->file->dev, "LUT waveform changed (%d)\n", + waveform); + + ret = drm_epd_lut_update(lut, mode_index, lut->temp_index); + if (ret) + return ret; + + lut->mode_index = mode_index; + + return 1; +} +EXPORT_SYMBOL(drm_epd_lut_set_waveform); + +static void drm_epd_lut_free(struct drm_device *dev, void *res) +{ + struct drm_epd_lut *lut = res; + + vfree(lut->buf); +} + +/** + * drmm_epd_lut_init - Initialize a managed EPD LUT from a LUT file + * + * @dev: The DRM device owning this LUT + * @lut: The LUT to initialize + * @file_name: The file name of the waveform firmware + * @format: The LUT buffer format needed by the driver + * @max_phases: The maximum number of waveform phases supported by the driver + * + * Return: negative errno on failure, 0 otherwise + */ +int drmm_epd_lut_init(struct drm_epd_lut_file *file, + struct drm_epd_lut *lut, + enum drm_epd_lut_format format, + unsigned int max_phases) +{ + size_t max_order; + int ret; + + /* Allocate a buffer large enough to convert the LUT in place. */ + max_order = max(drm_epd_lut_size_shift(file->mode_info->format), + drm_epd_lut_size_shift(format)); + lut->buf = vmalloc(max_phases << max_order); + if (!lut->buf) + return -ENOMEM; + + ret = drmm_add_action_or_reset(file->dev, drm_epd_lut_free, lut); + if (ret) + return ret; + + lut->file = file; + lut->format = format; + lut->max_phases = max_phases; + lut->num_phases = 0; + + /* Set sane initial values for the waveform and temperature. */ + lut->mode_index = pvi_wbf_get_mode_index(file, DRM_EPD_WF_RESET); + if (lut->mode_index < 0) + return -ENOENT; + + lut->temp_index = pvi_wbf_get_temp_index(file, 25); + if (lut->temp_index < 0) + return -ENOENT; + + ret = drm_epd_lut_update(lut, lut->mode_index, lut->temp_index); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_epd_lut_init); + +MODULE_AUTHOR("Samuel Holland "); +MODULE_DESCRIPTION("DRM EPD waveform LUT library"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/include/drm/drm_epd_helper.h b/include/drm/drm_epd_helper.h new file mode 100644 index 000000000000..290f5d48c0cb --- /dev/null +++ b/include/drm/drm_epd_helper.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +#ifndef __DRM_EPD_HELPER_H__ +#define __DRM_EPD_HELPER_H__ + +#define DRM_EPD_DEFAULT_TEMPERATURE 25 + +struct drm_device; +struct firmware; +struct pvi_wbf_file_header; +struct pvi_wbf_mode_info; + +/** + * enum drm_epd_waveform - Identifiers for waveforms used to drive EPD pixels + * + * @DRM_EPD_WF_RESET: Used to initialize the panel, ends with white + * @DRM_EPD_WF_A2: Fast transitions between black and white only + * @DRM_EPD_WF_DU: Transitions 16-level grayscale to monochrome + * @DRM_EPD_WF_DU4: Transitions 16-level grayscale to 4-level grayscale + * @DRM_EPD_WF_GC16: High-quality but flashy 16-level grayscale + * @DRM_EPD_WF_GCC16: Less flashy 16-level grayscale + * @DRM_EPD_WF_GL16: Less flashy 16-level grayscale + * @DRM_EPD_WF_GLR16: Less flashy 16-level grayscale, plus anti-ghosting + * @DRM_EPD_WF_GLD16: Less flashy 16-level grayscale, plus anti-ghosting + */ +enum drm_epd_waveform { + DRM_EPD_WF_RESET, + DRM_EPD_WF_A2, + DRM_EPD_WF_DU, + DRM_EPD_WF_DU4, + DRM_EPD_WF_GC16, + DRM_EPD_WF_GCC16, + DRM_EPD_WF_GL16, + DRM_EPD_WF_GLR16, + DRM_EPD_WF_GLD16, + DRM_EPD_WF_MAX +}; + +/** + * enum drm_epd_lut_format - EPD LUT buffer format + * + * @DRM_EPD_LUT_4BIT: 4-bit grayscale indexes, 1 byte per element + * @DRM_EPD_LUT_4BIT_PACKED: 4-bit grayscale indexes, 2 bits per element + * @DRM_EPD_LUT_5BIT: 5-bit grayscale indexes, 1 byte per element + * @DRM_EPD_LUT_5BIT_PACKED: 5-bit grayscale indexes, 2 bits per element + */ +enum drm_epd_lut_format { + DRM_EPD_LUT_4BIT, + DRM_EPD_LUT_4BIT_PACKED, + DRM_EPD_LUT_5BIT, + DRM_EPD_LUT_5BIT_PACKED, +}; + +/** + * struct drm_epd_lut_file - Describes a file containing EPD LUTs + * + * @dev: The DRM device owning this LUT file + * @fw: The firmware object holding the raw file contents + * @header: Vendor-specific LUT file header + * @mode_info: Vendor-specific information about available waveforms + */ +struct drm_epd_lut_file { + struct drm_device *dev; + const struct firmware *fw; + const struct pvi_wbf_file_header *header; + const struct pvi_wbf_mode_info *mode_info; +}; + +/** + * struct drm_epd_lut - Describes a particular LUT buffer + * + * @buf: The LUT, in the format requested by the driver + * @file: The file where this LUT was loaded from + * @format: The LUT buffer format needed by the driver + * @max_phases: The maximum number of waveform phases supported by the driver + * @num_phases: The number of waveform phases in the current LUT + * @mode_index: Private identifier for the current waveform + * @temp_index: Private identifier for the current temperature + */ +struct drm_epd_lut { + u8 *buf; + const struct drm_epd_lut_file *file; + enum drm_epd_lut_format format; + unsigned int max_phases; + unsigned int num_phases; + int mode_index; + int temp_index; +}; + +int drmm_epd_lut_file_init(struct drm_device *dev, + struct drm_epd_lut_file *file, + const char *file_name); + +int drmm_epd_lut_init(struct drm_epd_lut_file *file, + struct drm_epd_lut *lut, + enum drm_epd_lut_format format, + unsigned int max_phases); + +int drm_epd_lut_set_temperature(struct drm_epd_lut *lut, + int temperature); +int drm_epd_lut_set_waveform(struct drm_epd_lut *lut, + enum drm_epd_waveform waveform); + +#endif /* __DRM_EPD_HELPER_H__ */ From patchwork Wed Apr 13 22:19:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560952 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3A665C4332F for ; Wed, 13 Apr 2022 22:19:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236394AbiDMWV4 (ORCPT ); Wed, 13 Apr 2022 18:21:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38458 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236127AbiDMWVu (ORCPT ); Wed, 13 Apr 2022 18:21:50 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3F3DB1571D; Wed, 13 Apr 2022 15:19:27 -0700 (PDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 928445C029A; Wed, 13 Apr 2022 18:19:26 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Wed, 13 Apr 2022 18:19:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888366; x=1649974766; bh=DL jgBEQ1+1vq/nXxADtpNaSm7RE9fz3MOj06zwzz+gk=; b=tGxVfhfS18dInH3xbp 5sviT7voXBlvIfsRKOFqahGWxhmQ0I45KaxXfmZrHrvHAI05e1mxWLpAcwrTVpE9 l3a2QvebuXT9NEDuEezPLQ0nB722xgb3y6501RJVOo69DNmP+9Dg1wGY1oDbnRBZ zMn6SHaJT4E5Pwvjqixxh2K80zMdS/3Z2etec2NBayR/DE/AEbZFBI1Ao+u5fP4w OBrC0QKxFuerB3CDqoNN6A4srQ9s3g4mCvmgDhm6/yXANvKt0ZRc4U2CrgWi1FQN u40mCxuovnafzAF0kapGjr/WOtXT775Rk3z7LsVDMCUAVkLAgkYLmKvoKATXJ2WN J3zw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888366; x=1649974766; bh=DLjgBEQ1+1vq/nXxADtpNaSm7RE9fz3MOj0 6zwzz+gk=; b=eURCH2ejrVMvU8WXiCnab0H5zIEDr6JEIyVuXiSKv3CudCl48P6 0LwWY57vou4hK7qbUaxAk4P31aGRd8p0CgKQg1QHgk/wHBFIasUqKwLNJVREf4uy 0YYkediuIGp29mM79xkN6LbUMANoKLMeAvdeTI/S5IKxhDQjo3XXhnyMGpblWpwS Q8JRniJQn0OzYPhuxUX5zpT0dtnE+8aehBaypiJns0b+coPG2yPqEvXQlp8437W/ vaAJAP2z49t4RFJg8Hvx2QaN9F9pBx5PdLQ5GY2dFr2wxwm1Gmf9peRg70tWviNo N0MkLfh9OLevW1TeGUAyQZhc9iq4W9HFgLA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddguddtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeelueelgeettdfggfeuffevkefhuddtteeigfevhfdtffdtjefgteeg leeggedvudenucffohhmrghinhepuggvvhhitggvthhrvggvrdhorhhgnecuvehluhhsth gvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepshgrmhhuvghlsehshhho lhhlrghnugdrohhrgh X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:25 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 02/16] dt-bindings: display: rockchip: Add EBC binding Date: Wed, 13 Apr 2022 17:19:02 -0500 Message-Id: <20220413221916.50995-3-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The Rockchip E-Book Controller (EBC) is a controller for Electrophoretic Displays (EPDs). It is self-contained; it does not interact directly with the VOP or the RGA. While two of the regulator consumers here actually power the display panel, not the EBC hardware, they are consumed here because they are only needed during display refreshes. They do not match the normal panel prepare/enable lifecycle. Signed-off-by: Samuel Holland --- .../display/rockchip/rockchip,rk3568-ebc.yaml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml new file mode 100644 index 000000000000..957ca874ab02 --- /dev/null +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3568-ebc.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/rockchip/rockchip,rk3568-ebc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip SoC E-Book Controller (EBC) + +description: + Rockchip EBC is a controller for Electrophoretic Displays (EPDs). + +maintainers: + - Samuel Holland + +properties: + compatible: + enum: + - rockchip,rk3568-ebc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: AHB register clock + - description: Pixel clock + + clock-names: + items: + - const: hclk + - const: dclk + + resets: + items: + - description: hclk domain reset + - description: dclk domain reset + + reset-names: + items: + - const: hclk + - const: dclk + + io-channels: + maxItems: 1 + description: I/O channel for panel temperature measurement + + panel-supply: + description: Regulator supplying the panel's logic voltage + + power-domains: + maxItems: 1 + + vcom-supply: + description: Regulator supplying the panel's compensation voltage + + vdrive-supply: + description: Regulator supplying the panel's gate and source drivers + + port: + $ref: /schemas/graph.yaml#/properties/port + description: OF graph port for the attached display panel + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + - power-domains + - panel-supply + - vcom-supply + - vdrive-supply + +additionalProperties: false + +examples: + - | + #include + #include + #include + + ebc: ebc@fdec0000 { + compatible = "rockchip,rk3568-ebc"; + reg = <0x0 0xfdec0000 0x0 0x5000>; + interrupts = ; + clocks = <&cru HCLK_EBC>, <&cru DCLK_EBC>; + clock-names = "hclk", "dclk"; + resets = <&cru SRST_H_EBC>, <&cru SRST_D_EBC>; + reset-names = "hclk", "dclk"; + power-domains = <&power RK3568_PD_RGA>; + + panel-supply = <&v3p3>; + vcom-supply = <&vcom>; + vdrive-supply = <&vdrive>; + + port { + ebc_out_panel: endpoint { + remote-endpoint = <&panel_in_ebc>; + }; + }; + }; From patchwork Wed Apr 13 22:19:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560951 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E08D0C4321E for ; Wed, 13 Apr 2022 22:19:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236375AbiDMWV6 (ORCPT ); Wed, 13 Apr 2022 18:21:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38712 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236351AbiDMWVz (ORCPT ); Wed, 13 Apr 2022 18:21:55 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2F80C15A3D; Wed, 13 Apr 2022 15:19:31 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 769015C031C; Wed, 13 Apr 2022 18:19:30 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Wed, 13 Apr 2022 18:19:30 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888370; x=1649974770; bh=li VdDnZWRFN/01EDWJ04iBIR5q+lQrzOvuZ68gkGyyw=; b=oDd7p6QrclsdB2kEr+ b4VXLYx8zVfp6/GGPo20iFE9nO19iLxL60kn/u+BFK43vwaL2BEIAcUicxjZHxNE OltBlAHGzFxh57XwjsNHlsHstZwc2PYrHsXBm5TZkXO0h31lro2PHOUPmkzOvrYD XBNy4Q0wgJQLnLltqB6YGQhT9TezApB6qXSl0+6u0eXjljLGKstN3rQ4ZAH/+XAg C9swaIMLkCb0IG2ohUCBEhrNSubzOCxiE1cU67Yp3ooxrqTPZLYWuAxKKsDMXlVI xEh9redb5YACaA6M9Yojq/vChXeADcP1dzuZ9Qn832n0wCPJWYeZDBd9nwQBrV14 2+HQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888370; x=1649974770; bh=liVdDnZWRFN/01EDWJ04iBIR5q+lQrzOvuZ 68gkGyyw=; b=m5N3aDVtE1a1pPJ6sChRefOD8H8bwexJlBtFeBquBiTqGWJf3hr XX0CeZnFDLknqQMb4B/OlNM8pCEgCsvxhEcWwcxdbqRSFjbHIp4Ui+sod4gphPzi /RT7YwkPbeLri8BKZasQf0pdkchph/5c0M0ru/SH6lf7KFo4IJ/MIAAztUzkhLrS lnFiRJmKMwlDTPDBiIV7f5g6ZG2DHEr000Kyeipm0vVqXATApe5XjE3kFoZEnqJP sWlfnGk1UhB8jKILM79N8wcWnq3RsfY1rnOQ/QstXHIk8VEA+UpFVBCZfUmk+y1X ICvmIvrkjUYYJvXwVPSkkpmCu15ZbonD8hg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:29 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 04/16] drm/rockchip: ebc: Add DRM driver skeleton Date: Wed, 13 Apr 2022 17:19:04 -0500 Message-Id: <20220413221916.50995-5-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The Rockchip E-Book Controller (EBC) has a relatively simple and self- contained display pipeline. The pipeline consists of a single CRTC, encoder, and bridge, with the bridge normally attached to a panel. Initially, there is also a single plane. Since all blitting is done in software, the driver could eventually support any number of planes. For example, it could expose one plane for each supported waveform. However, EPD controller hardware has some unique properties which complicate using drm_simple_display_pipe: - EPDs operate on relative pixel values, not absolute pixel values. This requires the driver to maintain multiple shadow buffers for the "previous" and "next" framebuffer contents. - It also means that disabling the CRTC (i.e. clearing the screen) requires access to these shadow buffers, as it requires knowing the previous contents of the framebuffer. And of course it requires a buffer for the blank image. - Atomically managing these shadow buffers needs reference counting in .atomic_check. However, drm_simple_display_pipe_funcs::check is only called while the plane is visible, complicating this. - Furthermore, because all plane blitting/blending must be done in software, the number and location of these planes is arbitrary. drm_simple_display_pipe enforces an unnecessary limitation that a single plane covers the entire CRTC. For these reasons, drm_simple_display_pipe is not used. This commit adds the structure for this pipeline. The atomic helper callbacks are left empty. They will be filled in incrementally by the next several commits. Both the CRTC and the pipe need extra state information, so this commit adds the state hook boilerplate. Additionally, the plane takes advantage of the shadow plane helpers. Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 359 +++++++++++++++++++++++- 1 file changed, 356 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index 5ed66c6cd2f0..f75fd23adda2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -12,6 +12,17 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #define EBC_DSP_START 0x0000 #define EBC_DSP_START_DSP_OUT_LOW BIT(31) #define EBC_DSP_START_DSP_SDCE_WIDTH(x) ((x) << 16) @@ -118,10 +129,332 @@ struct rockchip_ebc { struct clk *dclk; struct clk *hclk; struct completion display_end; + struct drm_crtc crtc; + struct drm_device drm; + struct drm_encoder encoder; + struct drm_plane plane; struct regmap *regmap; struct regulator_bulk_data supplies[EBC_NUM_SUPPLIES]; }; +DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops); + +static const struct drm_driver rockchip_ebc_drm_driver = { + .lastclose = drm_fb_helper_lastclose, + DRM_GEM_SHMEM_DRIVER_OPS, + .major = 0, + .minor = 3, + .name = "rockchip-ebc", + .desc = "Rockchip E-Book Controller", + .date = "20220303", + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops = &rockchip_ebc_fops, +}; + +static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +/* + * CRTC + */ + +struct ebc_crtc_state { + struct drm_crtc_state base; +}; + +static inline struct ebc_crtc_state * +to_ebc_crtc_state(struct drm_crtc_state *crtc_state) +{ + return container_of(crtc_state, struct ebc_crtc_state, base); +} + +static inline struct rockchip_ebc *crtc_to_ebc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct rockchip_ebc, crtc); +} + +static void rockchip_ebc_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +static int rockchip_ebc_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + return 0; +} + +static void rockchip_ebc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = { + .mode_set_nofb = rockchip_ebc_crtc_mode_set_nofb, + .atomic_check = rockchip_ebc_crtc_atomic_check, + .atomic_flush = rockchip_ebc_crtc_atomic_flush, + .atomic_enable = rockchip_ebc_crtc_atomic_enable, + .atomic_disable = rockchip_ebc_crtc_atomic_disable, +}; + +static void rockchip_ebc_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state); + +static void rockchip_ebc_crtc_reset(struct drm_crtc *crtc) +{ + struct ebc_crtc_state *ebc_crtc_state; + + if (crtc->state) + rockchip_ebc_crtc_destroy_state(crtc, crtc->state); + + ebc_crtc_state = kzalloc(sizeof(*ebc_crtc_state), GFP_KERNEL); + if (!ebc_crtc_state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &ebc_crtc_state->base); +} + +static struct drm_crtc_state * +rockchip_ebc_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct ebc_crtc_state *ebc_crtc_state; + + if (!crtc->state) + return NULL; + + ebc_crtc_state = kzalloc(sizeof(*ebc_crtc_state), GFP_KERNEL); + if (!ebc_crtc_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &ebc_crtc_state->base); + + return &ebc_crtc_state->base; +} + +static void rockchip_ebc_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct ebc_crtc_state *ebc_crtc_state = to_ebc_crtc_state(crtc_state); + + __drm_atomic_helper_crtc_destroy_state(&ebc_crtc_state->base); + + kfree(ebc_crtc_state); +} + +static const struct drm_crtc_funcs rockchip_ebc_crtc_funcs = { + .reset = rockchip_ebc_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = rockchip_ebc_crtc_duplicate_state, + .atomic_destroy_state = rockchip_ebc_crtc_destroy_state, +}; + +/* + * Plane + */ + +struct ebc_plane_state { + struct drm_shadow_plane_state base; +}; + +static inline struct ebc_plane_state * +to_ebc_plane_state(struct drm_plane_state *plane_state) +{ + return container_of(plane_state, struct ebc_plane_state, base.base); +} + +static inline struct rockchip_ebc *plane_to_ebc(struct drm_plane *plane) +{ + return container_of(plane, struct rockchip_ebc, plane); +} + +static int rockchip_ebc_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + int ret; + + plane_state = drm_atomic_get_new_plane_state(state, plane); + if (!plane_state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); + if (ret) + return ret; + + return 0; +} + +static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ +} + +static const struct drm_plane_helper_funcs rockchip_ebc_plane_helper_funcs = { + .prepare_fb = drm_gem_prepare_shadow_fb, + .cleanup_fb = drm_gem_cleanup_shadow_fb, + .atomic_check = rockchip_ebc_plane_atomic_check, + .atomic_update = rockchip_ebc_plane_atomic_update, +}; + +static void rockchip_ebc_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *plane_state); + +static void rockchip_ebc_plane_reset(struct drm_plane *plane) +{ + struct ebc_plane_state *ebc_plane_state; + + if (plane->state) + rockchip_ebc_plane_destroy_state(plane, plane->state); + + ebc_plane_state = kzalloc(sizeof(*ebc_plane_state), GFP_KERNEL); + if (!ebc_plane_state) + return; + + __drm_gem_reset_shadow_plane(plane, &ebc_plane_state->base); +} + +static struct drm_plane_state * +rockchip_ebc_plane_duplicate_state(struct drm_plane *plane) +{ + struct ebc_plane_state *ebc_plane_state; + + if (!plane->state) + return NULL; + + ebc_plane_state = kzalloc(sizeof(*ebc_plane_state), GFP_KERNEL); + if (!ebc_plane_state) + return NULL; + + __drm_gem_duplicate_shadow_plane_state(plane, &ebc_plane_state->base); + + return &ebc_plane_state->base.base; +} + +static void rockchip_ebc_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + struct ebc_plane_state *ebc_plane_state = to_ebc_plane_state(plane_state); + + __drm_gem_destroy_shadow_plane_state(&ebc_plane_state->base); + + kfree(ebc_plane_state); +} + +static const struct drm_plane_funcs rockchip_ebc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = rockchip_ebc_plane_reset, + .atomic_duplicate_state = rockchip_ebc_plane_duplicate_state, + .atomic_destroy_state = rockchip_ebc_plane_destroy_state, +}; + +static const u32 rockchip_ebc_plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const u64 rockchip_ebc_plane_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc) +{ + struct drm_device *drm = &ebc->drm; + struct drm_bridge *bridge; + int ret; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH; + drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT; + drm->mode_config.funcs = &rockchip_ebc_mode_config_funcs; + drm->mode_config.quirk_addfb_prefer_host_byte_order = true; + + drm_plane_helper_add(&ebc->plane, &rockchip_ebc_plane_helper_funcs); + ret = drm_universal_plane_init(drm, &ebc->plane, 0, + &rockchip_ebc_plane_funcs, + rockchip_ebc_plane_formats, + ARRAY_SIZE(rockchip_ebc_plane_formats), + rockchip_ebc_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_plane_enable_fb_damage_clips(&ebc->plane); + + drm_crtc_helper_add(&ebc->crtc, &rockchip_ebc_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(drm, &ebc->crtc, &ebc->plane, NULL, + &rockchip_ebc_crtc_funcs, NULL); + if (ret) + return ret; + + ebc->encoder.possible_crtcs = drm_crtc_mask(&ebc->crtc); + ret = drm_simple_encoder_init(drm, &ebc->encoder, DRM_MODE_ENCODER_NONE); + if (ret) + return ret; + + bridge = devm_drm_of_get_bridge(drm->dev, drm->dev->of_node, 0, 0); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + ret = drm_bridge_attach(&ebc->encoder, bridge, NULL, 0); + if (ret) + return ret; + + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + return ret; + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} + +static int __maybe_unused rockchip_ebc_suspend(struct device *dev) +{ + struct rockchip_ebc *ebc = dev_get_drvdata(dev); + int ret; + + ret = drm_mode_config_helper_suspend(&ebc->drm); + if (ret) + return ret; + + return pm_runtime_force_suspend(dev); +} + +static int __maybe_unused rockchip_ebc_resume(struct device *dev) +{ + struct rockchip_ebc *ebc = dev_get_drvdata(dev); + + pm_runtime_force_resume(dev); + + return drm_mode_config_helper_resume(&ebc->drm); +} + static int rockchip_ebc_runtime_suspend(struct device *dev) { struct rockchip_ebc *ebc = dev_get_drvdata(dev); @@ -173,6 +506,7 @@ static int rockchip_ebc_runtime_resume(struct device *dev) } static const struct dev_pm_ops rockchip_ebc_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rockchip_ebc_suspend, rockchip_ebc_resume) SET_RUNTIME_PM_OPS(rockchip_ebc_runtime_suspend, rockchip_ebc_runtime_resume, NULL) }; @@ -230,9 +564,10 @@ static int rockchip_ebc_probe(struct platform_device *pdev) void __iomem *base; int i, ret; - ebc = devm_kzalloc(dev, sizeof(*ebc), GFP_KERNEL); - if (!ebc) - return -ENOMEM; + ebc = devm_drm_dev_alloc(dev, &rockchip_ebc_drm_driver, + struct rockchip_ebc, drm); + if (IS_ERR(ebc)) + return PTR_ERR(ebc); platform_set_drvdata(pdev, ebc); init_completion(&ebc->display_end); @@ -279,13 +614,28 @@ static int rockchip_ebc_probe(struct platform_device *pdev) return ret; } + ret = rockchip_ebc_drm_init(ebc); + if (ret) + goto err_disable_pm; + return 0; + +err_disable_pm: + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + rockchip_ebc_runtime_suspend(dev); + + return ret; } static int rockchip_ebc_remove(struct platform_device *pdev) { + struct rockchip_ebc *ebc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + drm_dev_unregister(&ebc->drm); + drm_atomic_helper_shutdown(&ebc->drm); + pm_runtime_disable(dev); if (!pm_runtime_status_suspended(dev)) rockchip_ebc_runtime_suspend(dev); @@ -295,8 +645,11 @@ static int rockchip_ebc_remove(struct platform_device *pdev) static void rockchip_ebc_shutdown(struct platform_device *pdev) { + struct rockchip_ebc *ebc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + drm_atomic_helper_shutdown(&ebc->drm); + if (!pm_runtime_status_suspended(dev)) rockchip_ebc_runtime_suspend(dev); } From patchwork Wed Apr 13 22:19:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560950 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1943DC4332F for ; Wed, 13 Apr 2022 22:19:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236710AbiDMWWE (ORCPT ); Wed, 13 Apr 2022 18:22:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38854 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231311AbiDMWV6 (ORCPT ); Wed, 13 Apr 2022 18:21:58 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB4B920BC0; Wed, 13 Apr 2022 15:19:34 -0700 (PDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 4C4035C0322; Wed, 13 Apr 2022 18:19:34 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Wed, 13 Apr 2022 18:19:34 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888374; x=1649974774; bh=nV Ty+l52zCL3CGPsMmkYpIgGpQIKDxR1HFzp1d9DKBM=; b=dVbHkFYEUIWKj3ST7L IBX+AJR97Rtc0cw/qe/xx1IbNAWDzL7cJxz92447dAJ+2XEMH9GU5LnFkNHnneLG SFhR4LUP/MXzeomdimp7OCR8XFDBurxwSK/quHj86LkjRORQYoS4hvfXFakfi8VE CvqOK6ihp9rGtzdL4mkrHJtEZTTImSV8tEdThzKo9Yo7k+uB+DYWO0tw5NGiij6W jif+GY0AaIlBqQX2sFudpXcaOPE752aKTneA6XMeMyMkS4TY3ZNYTKF/CZFfi1Qi SFr40Chx1xag+xteKy77K0SbfqP3GdJQ+fYwazt/FQcS3fbjPdhvyUAqqIR1c2E7 GyMw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888374; x=1649974774; bh=nVTy+l52zCL3CGPsMmkYpIgGpQIKDxR1HFz p1d9DKBM=; b=T5bbug34WLzjJT60oKKAjH1Uv5gPeFjPhVnLKH6/meLNcIczDt9 nSx/qeeOrXFwLv3M/oQKGA64u6ziulT4CyG79Fepyy0WaPk48PO65ZPo+svzfJ2D Di2RzPO33aiXdNSS66Vi0Rxgi4dpH0j9p8N3v7UST129BFiB110/FQBCKos38aow 2YtT1RykvcUk0ZyloPOvXMTL4WFl57qBCJDJvKKU5FsRG+ncL2LrFtm0ZmCGG1qi Do86lfvwo8tFbcAQ8I8Y7EDyYI5mnW/zsmlYaUPOdNuHEvGE4FHCIaz0pCpc5nbv +zI85V/NHO+/5FRlF36zhvV7u4n34c+PpRw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddguddtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:33 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 06/16] drm/rockchip: ebc: Add CRTC refresh thread Date: Wed, 13 Apr 2022 17:19:06 -0500 Message-Id: <20220413221916.50995-7-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org EPD refreshes are extremely slow; they take anywhere between hundreds of milliseconds and several seconds. To avoid blocking userspace, perform these refreshes on a separate thread. The thread will also take care of initializing the display before first use and clearing it when the CRTC is disabled. Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 82 ++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index 5f9502313657..ebe60d5e011a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -135,9 +136,15 @@ struct rockchip_ebc { struct drm_plane plane; struct regmap *regmap; struct regulator_bulk_data supplies[EBC_NUM_SUPPLIES]; + struct task_struct *refresh_thread; u32 dsp_start; + bool reset_complete; }; +static bool skip_reset; +module_param(skip_reset, bool, 0444); +MODULE_PARM_DESC(skip_reset, "skip the initial display reset"); + DEFINE_DRM_GEM_FOPS(rockchip_ebc_fops); static const struct drm_driver rockchip_ebc_drm_driver = { @@ -172,6 +179,42 @@ to_ebc_crtc_state(struct drm_crtc_state *crtc_state) return container_of(crtc_state, struct ebc_crtc_state, base); } +static int rockchip_ebc_refresh_thread(void *data) +{ + struct rockchip_ebc *ebc = data; + + while (!kthread_should_stop()) { + /* + * LUTs use both the old and the new pixel values as inputs. + * However, the initial contents of the display are unknown. + * The special RESET waveform will initialize the display to + * known contents (white) regardless of its current contents. + */ + if (!ebc->reset_complete) { + ebc->reset_complete = true; + drm_dbg(&ebc->drm, "display reset\n"); + } + + while (!kthread_should_park()) { + drm_dbg(&ebc->drm, "display update\n"); + + set_current_state(TASK_IDLE); + schedule(); + __set_current_state(TASK_RUNNING); + } + + /* + * Clear the display before disabling the CRTC. Use the + * highest-quality waveform to minimize visible artifacts. + */ + drm_dbg(&ebc->drm, "display clear\n"); + + kthread_parkme(); + } + + return 0; +} + static inline struct rockchip_ebc *crtc_to_ebc(struct drm_crtc *crtc) { return container_of(crtc, struct rockchip_ebc, crtc); @@ -296,11 +339,23 @@ static void rockchip_ebc_crtc_atomic_flush(struct drm_crtc *crtc, static void rockchip_ebc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = crtc_to_ebc(crtc); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (crtc_state->mode_changed) + kthread_unpark(ebc->refresh_thread); } static void rockchip_ebc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = crtc_to_ebc(crtc); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (crtc_state->mode_changed) + kthread_park(ebc->refresh_thread); } static const struct drm_crtc_helper_funcs rockchip_ebc_crtc_helper_funcs = { @@ -408,6 +463,14 @@ static int rockchip_ebc_plane_atomic_check(struct drm_plane *plane, static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { + struct rockchip_ebc *ebc = plane_to_ebc(plane); + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_new_plane_state(state, plane); + if (!plane_state->crtc) + return; + + wake_up_process(ebc->refresh_thread); } static const struct drm_plane_helper_funcs rockchip_ebc_plane_helper_funcs = { @@ -673,6 +736,7 @@ static int rockchip_ebc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ebc); init_completion(&ebc->display_end); + ebc->reset_complete = skip_reset; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -716,12 +780,26 @@ static int rockchip_ebc_probe(struct platform_device *pdev) return ret; } + ebc->refresh_thread = kthread_create(rockchip_ebc_refresh_thread, + ebc, "ebc-refresh/%s", + dev_name(dev)); + if (IS_ERR(ebc->refresh_thread)) { + ret = dev_err_probe(dev, PTR_ERR(ebc->refresh_thread), + "Failed to start refresh thread\n"); + goto err_disable_pm; + } + + kthread_park(ebc->refresh_thread); + sched_set_fifo(ebc->refresh_thread); + ret = rockchip_ebc_drm_init(ebc); if (ret) - goto err_disable_pm; + goto err_stop_kthread; return 0; +err_stop_kthread: + kthread_stop(ebc->refresh_thread); err_disable_pm: pm_runtime_disable(dev); if (!pm_runtime_status_suspended(dev)) @@ -738,6 +816,8 @@ static int rockchip_ebc_remove(struct platform_device *pdev) drm_dev_unregister(&ebc->drm); drm_atomic_helper_shutdown(&ebc->drm); + kthread_stop(ebc->refresh_thread); + pm_runtime_disable(dev); if (!pm_runtime_status_suspended(dev)) rockchip_ebc_runtime_suspend(dev); From patchwork Wed Apr 13 22:19:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560947 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 32626C433EF for ; Wed, 13 Apr 2022 22:20:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238049AbiDMWWl (ORCPT ); Wed, 13 Apr 2022 18:22:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236884AbiDMWWE (ORCPT ); Wed, 13 Apr 2022 18:22:04 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 202DC220F1; Wed, 13 Apr 2022 15:19:38 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id 017155C0337; Wed, 13 Apr 2022 18:19:38 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 13 Apr 2022 18:19:38 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888377; x=1649974777; bh=tY YXooyzfoVASIVFw46sk2jtQuSFQj7O0CjADjvQKS4=; b=fnbQiRCWZKoa6P2AFl UCN2cUs8hriQsg+DTZJBhg6fKGCwumuG62IEDgTeIiEx9yd5Y1F2hMRzmAcFysU5 cS5hbbwRvTDrqSiI42YGA1xc24+rimiICmi7tDg7maQ+IZmLT79JX6fRm2/kIlnX AdnUOwD50xQ/TZGy5Gk/gMqwVhyiKLYduZRYZ36jurBvH52iY4pT3Nl2aod3hUc5 6iGJqiTgMBuhEAHhi1wcQ/oUMksynJiu0PMEoflM0ten8rn0ZVXh2BO+hHQiVrIB +4BmFZAJ1a7YP5ChIryCqaxBdjOabDNj1b7qNr4rtu3rdHfBlbYicnlssHQHBNvl 0AXQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888377; x=1649974777; bh=tYYXooyzfoVASIVFw46sk2jtQuSFQj7O0Cj ADjvQKS4=; b=JN/u/DVSKTUMgkOEo/iiZMZiOEglinf5P/JGht+fnQMEX2vTL6r O8uAgoyL3b3P8vqeTD6RQk37E9K/+khwTK14Lb31637ClNaOGPkF+em+6t7aBayA eJ/b0tyMz8afdMlBiDmDzZLzCIoHbYvyrFE3iy7fAqjgzemvzHdEPcIhlEwhDlez VKMjbIU8z3XpSxUByu4aKmIosWw+sncCKB2cxPzO32OYF1vrtoEOFEmeU6yHDszL BoNSU8UuuTaEEqA8kMhNz815ly6AKTi5c1Ep8EoYIDNrc1/Ecr2OZ6GBMpO0Z7BJ 2B0/IA6LoAqWwKojy5gqAufaOx4Q9tQ/8MA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedvnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:37 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 08/16] drm/rockchip: ebc: Add LUT loading Date: Wed, 13 Apr 2022 17:19:08 -0500 Message-Id: <20220413221916.50995-9-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The EBC contains a 16 KiB SRAM which stores the current LUT. It needs to be programmed any time the LUT changes or the hardware block is enabled. Since both of these triggers can happen at the same time, use a flag to avoid writing the LUT twice. Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/Kconfig | 3 +- drivers/gpu/drm/rockchip/rockchip_ebc.c | 76 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 9d3273a5fd97..efe4476e336d 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -94,7 +94,8 @@ endif config DRM_ROCKCHIP_EBC tristate "DRM Support for Rockchip EBC" - depends on DRM + depends on DRM && IIO + select DRM_EPD_HELPER select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER help diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index 095d66e67c2f..ca3173b28d1c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -122,6 +123,7 @@ #define EBC_WIN_MST2 0x0058 #define EBC_LUT_DATA 0x1000 +#define EBC_MAX_PHASES 256 #define EBC_NUM_LUT_REGS 0x1000 #define EBC_NUM_SUPPLIES 3 @@ -134,11 +136,15 @@ struct rockchip_ebc { struct drm_crtc crtc; struct drm_device drm; struct drm_encoder encoder; + struct drm_epd_lut lut; + struct drm_epd_lut_file lut_file; struct drm_plane plane; + struct iio_channel *temperature_channel; struct regmap *regmap; struct regulator_bulk_data supplies[EBC_NUM_SUPPLIES]; struct task_struct *refresh_thread; u32 dsp_start; + bool lut_changed; bool reset_complete; }; @@ -282,10 +288,59 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc, bool global_refresh, enum drm_epd_waveform waveform) { + struct drm_device *drm = &ebc->drm; + struct device *dev = drm->dev; + int ret, temperature; + + /* Resume asynchronously while preparing to refresh. */ + ret = pm_runtime_get(dev); + if (ret < 0) { + drm_err(drm, "Failed to request resume: %d\n", ret); + return; + } + + ret = iio_read_channel_processed(ebc->temperature_channel, &temperature); + if (ret < 0) { + drm_err(drm, "Failed to get temperature: %d\n", ret); + } else { + /* Convert from millicelsius to celsius. */ + temperature /= 1000; + + ret = drm_epd_lut_set_temperature(&ebc->lut, temperature); + if (ret < 0) + drm_err(drm, "Failed to set LUT temperature: %d\n", ret); + else if (ret) + ebc->lut_changed = true; + } + + ret = drm_epd_lut_set_waveform(&ebc->lut, waveform); + if (ret < 0) + drm_err(drm, "Failed to set LUT waveform: %d\n", ret); + else if (ret) + ebc->lut_changed = true; + + /* Wait for the resume to complete before writing any registers. */ + ret = pm_runtime_resume(dev); + if (ret < 0) { + drm_err(drm, "Failed to resume: %d\n", ret); + pm_runtime_put(dev); + return; + } + + /* This flag may have been set above, or by the runtime PM callback. */ + if (ebc->lut_changed) { + ebc->lut_changed = false; + regmap_bulk_write(ebc->regmap, EBC_LUT_DATA, + ebc->lut.buf, EBC_NUM_LUT_REGS); + } + if (global_refresh) rockchip_ebc_global_refresh(ebc, ctx); else rockchip_ebc_partial_refresh(ebc, ctx); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static int rockchip_ebc_refresh_thread(void *data) @@ -708,6 +763,15 @@ static int rockchip_ebc_drm_init(struct rockchip_ebc *ebc) struct drm_bridge *bridge; int ret; + ret = drmm_epd_lut_file_init(drm, &ebc->lut_file, "rockchip/ebc.wbf"); + if (ret) + return ret; + + ret = drmm_epd_lut_init(&ebc->lut_file, &ebc->lut, + DRM_EPD_LUT_4BIT_PACKED, EBC_MAX_PHASES); + if (ret) + return ret; + ret = drmm_mode_config_init(drm); if (ret) return ret; @@ -810,6 +874,13 @@ static int rockchip_ebc_runtime_resume(struct device *dev) if (ret) goto err_disable_hclk; + /* + * Do not restore the LUT registers here, because the temperature or + * waveform may have changed since the last refresh. Instead, have the + * refresh thread program the LUT during the next refresh. + */ + ebc->lut_changed = true; + regcache_cache_only(ebc->regmap, false); regcache_mark_dirty(ebc->regmap); regcache_sync(ebc->regmap); @@ -919,6 +990,11 @@ static int rockchip_ebc_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ebc->hclk), "Failed to get hclk\n"); + ebc->temperature_channel = devm_iio_channel_get(dev, NULL); + if (IS_ERR(ebc->temperature_channel)) + return dev_err_probe(dev, PTR_ERR(ebc->temperature_channel), + "Failed to get temperature I/O channel\n"); + for (i = 0; i < EBC_NUM_SUPPLIES; i++) ebc->supplies[i].supply = rockchip_ebc_supplies[i]; From patchwork Wed Apr 13 22:19:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560949 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C68CC433F5 for ; Wed, 13 Apr 2022 22:20:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236762AbiDMWWh (ORCPT ); Wed, 13 Apr 2022 18:22:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39286 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236972AbiDMWWE (ORCPT ); Wed, 13 Apr 2022 18:22:04 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7ED8322B14; Wed, 13 Apr 2022 15:19:40 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id E12FD5C0325; Wed, 13 Apr 2022 18:19:39 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 13 Apr 2022 18:19:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888379; x=1649974779; bh=Dm EyWFCtmS1CP/NJfSjamUUamIi9FMNiWBV0XLSX5tI=; b=mk4goxXUJrAIBWW089 dPCf4FzqENNN0V90qnVFG3Nb+06XXWnUrXuZ+EZ6KlLvmQlaXV9aLyBEheUsB0ew L5IA+VDexu9UDz6dGM6mmgxkKvzzLYGjP3cMHo+hVZIF9kC4V64sjU8qx/jOcs5L jlN3BOhlSz0a0f8q0SsMtOtEcWU+yNnikHz1LQO4RRSxkHIRCE10BA5ek86yu7kT YGh49nlDZ1paW0U8O6XzmoK5UGs8+a9UjY3n4SLqsWmX0JGaC9H0+lMli/eqGBVM cVPioWFJsFXzAiGrvp8NspW1xYpvIJB3aadgENPWLfBujOblEA6ajcicj3TfRgLF hqAA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888379; x=1649974779; bh=DmEyWFCtmS1CP/NJfSjamUUamIi9FMNiWBV 0XLSX5tI=; b=p2cP5wLcgSeBDjXtnvHzgbPSPk4yR/i4fCkA7ZcCtNYo63XOpfs MCD6IQpVv1+Dvi4dKKDfk+b8QKvo5zbtk2n5xvgts/v2zX/asrYoVUsHHdKl9HFe JMyLH2eL4Wed7wNTe/mHykvgiIexwOTkhsl1xC7ch91CbBfLRCmRojvmG3ULGkcE 81bnvhj5CVzIrQwrXEOlTtS2CmAek3tdrY1DYseSt/Qy+M/F9XzVk7cV+73LN0j7 LXhmz0c/y0R+OnY/q2gBsADIuhWFUhCq+nKEuOlqY/X61x3YGdggRtcCqiea/L4Y VKkfy76tni9n76SXAE+h/l2bjLPG34bnGXw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedvnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:38 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 09/16] drm/rockchip: ebc: Implement global refreshes Date: Wed, 13 Apr 2022 17:19:09 -0500 Message-Id: <20220413221916.50995-10-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The global refresh mode is used to initialize and clear the screen. It is the most efficient refresh mode. It uses two pixel buffers (old and new) and a frame count. The frame count is set to the number of phases in the waveform. The hardware then looks up the combination of (old pixel value, new pixel value, frame number) in the LUT and sends the resulting polarity value to the display. Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 48 ++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index ca3173b28d1c..cb6dc567e94c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -127,6 +127,7 @@ #define EBC_NUM_LUT_REGS 0x1000 #define EBC_NUM_SUPPLIES 3 +#define EBC_REFRESH_TIMEOUT msecs_to_jiffies(3000) #define EBC_SUSPEND_DELAY_MS 2000 struct rockchip_ebc { @@ -269,8 +270,23 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc, { struct drm_device *drm = &ebc->drm; u32 gray4_size = ctx->gray4_size; + struct device *dev = drm->dev; - drm_dbg(drm, "global refresh\n"); + dma_sync_single_for_device(dev, virt_to_phys(ctx->next), + gray4_size, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, virt_to_phys(ctx->prev), + gray4_size, DMA_TO_DEVICE); + + reinit_completion(&ebc->display_end); + regmap_write(ebc->regmap, EBC_CONFIG_DONE, + EBC_CONFIG_DONE_REG_CONFIG_DONE); + regmap_write(ebc->regmap, EBC_DSP_START, + ebc->dsp_start | + EBC_DSP_START_DSP_FRM_TOTAL(ebc->lut.num_phases - 1) | + EBC_DSP_START_DSP_FRM_START); + if (!wait_for_completion_timeout(&ebc->display_end, + EBC_REFRESH_TIMEOUT)) + drm_err(drm, "Refresh timed out!\n"); memcpy(ctx->prev, ctx->next, gray4_size); } @@ -289,6 +305,7 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc, enum drm_epd_waveform waveform) { struct drm_device *drm = &ebc->drm; + u32 dsp_ctrl = 0, epd_ctrl = 0; struct device *dev = drm->dev; int ret, temperature; @@ -334,11 +351,40 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc, ebc->lut.buf, EBC_NUM_LUT_REGS); } + regmap_write(ebc->regmap, EBC_DSP_START, + ebc->dsp_start); + + /* + * The hardware has a separate bit for each mode, with some priority + * scheme between them. For clarity, only set one bit at a time. + */ + if (global_refresh) { + dsp_ctrl |= EBC_DSP_CTRL_DSP_LUT_MODE; + } else { + epd_ctrl |= EBC_EPD_CTRL_DSP_THREE_WIN_MODE; + } + regmap_update_bits(ebc->regmap, EBC_EPD_CTRL, + EBC_EPD_CTRL_DSP_THREE_WIN_MODE, + epd_ctrl); + regmap_update_bits(ebc->regmap, EBC_DSP_CTRL, + EBC_DSP_CTRL_DSP_LUT_MODE, + dsp_ctrl); + + regmap_write(ebc->regmap, EBC_WIN_MST0, + virt_to_phys(ctx->next)); + regmap_write(ebc->regmap, EBC_WIN_MST1, + virt_to_phys(ctx->prev)); + if (global_refresh) rockchip_ebc_global_refresh(ebc, ctx); else rockchip_ebc_partial_refresh(ebc, ctx); + /* Drive the output pins low once the refresh is complete. */ + regmap_write(ebc->regmap, EBC_DSP_START, + ebc->dsp_start | + EBC_DSP_START_DSP_OUT_LOW); + pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } From patchwork Wed Apr 13 22:19:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560945 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E6A06C4332F for ; Wed, 13 Apr 2022 22:20:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236884AbiDMWWr (ORCPT ); Wed, 13 Apr 2022 18:22:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39960 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237787AbiDMWW0 (ORCPT ); Wed, 13 Apr 2022 18:22:26 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 937E221256; Wed, 13 Apr 2022 15:19:42 -0700 (PDT) Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id E983F5C033A; Wed, 13 Apr 2022 18:19:41 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Wed, 13 Apr 2022 18:19:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888381; x=1649974781; bh=7b bIqqy2mMtv+LMNn04scZFEsK5Ygh9hQ+hXBLs4DCI=; b=De+yP95f0xqDeVLgAN i0kBEYBFk47F+1kVQkC2z3T/1xVRXqi83dqUEXMoyIhuEnGvqmHND+UKhlpLNa6K ZZ6BlXpFYniLjetsBoi2ge4SgaCs6VH9IbjN/8eH7JjVe+VEFPtcT5ViOxDA1/QY HYClCuLWVPlzk1zfnvYAXNssRzD8byhMMJRGerXNszdvlBcJYbD2DQmEb8Rg5Bhc VFY2mHhPRAX5uCwgQiOfj5UGvYlwYOdjXyzkkQ+I/8CeFxJ7EGcumh4tLwfV+RbY nYIB79MSKpl2JdGk8v9uQA+ATWaeIPFHnU6fIGr756PNCuhcbxPTnk2062uhUU5/ 5fiw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888381; x=1649974781; bh=7bbIqqy2mMtv+LMNn04scZFEsK5Ygh9hQ+h XBLs4DCI=; b=HJcnNONuVResbsVXjNVr3+qBWtwl2lp518gpSXHsuQVfYa311gu WQZTPiy+Irpdchd5Y2OMRK5rcAc/4G9M/q0k9Wd9v9/056aC3KwfBeaYsPx/rUH2 10tqr27CyrtRDVMaZbM6qeC2JNkXwRpXZ9npa668xkA0nnrOifOdbXdLMGsna3tq B/pzt02CepHZLtWEWK7DfPkCx8mIoalbsKg/CBlT155nB9VpwZuV8gfqusrxBrA8 zSQ6lHCxYY+Y8DIB6aHzJ+tEH28JTeVU0FR4/5id0VE6Ioy9B/9P9ycbT4lVjLl0 k3LrioKlZJi0mPdmdZK/q6ubTYH2pMuzqcg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddguddtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:40 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 10/16] drm/rockchip: ebc: Implement partial refreshes Date: Wed, 13 Apr 2022 17:19:10 -0500 Message-Id: <20220413221916.50995-11-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Several areas of the display can be refreshed concurrently, but only if they do not overlap. This commit adds a queue of damaged areas, and schedules them for refresh based on collision with other areas. While the queue is unbounded, there is logic to quickly drop duplicate areas. Because three-window mode disables the hardware's frame counter, a separate buffer is required for each frame. (In other words, there is no automatic increment.) To minimize overhead, swap between two buffers for phase numbers. This requires extending the loop for one extra frame to clear the phase numbers in both buffers when an area completes. (This extra frame is a no-op and is not sent to the hardware.) Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 346 +++++++++++++++++++++++- 1 file changed, 344 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index cb6dc567e94c..c3e4b65bdee6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -123,10 +124,14 @@ #define EBC_WIN_MST2 0x0058 #define EBC_LUT_DATA 0x1000 +#define EBC_FRAME_PENDING (-1U) + #define EBC_MAX_PHASES 256 + #define EBC_NUM_LUT_REGS 0x1000 #define EBC_NUM_SUPPLIES 3 +#define EBC_FRAME_TIMEOUT msecs_to_jiffies(25) #define EBC_REFRESH_TIMEOUT msecs_to_jiffies(3000) #define EBC_SUSPEND_DELAY_MS 2000 @@ -177,10 +182,25 @@ static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +/** + * struct rockchip_ebc_area - describes a damaged area of the display + * + * @list: Used to put this area in the state/context/refresh thread list + * @clip: The rectangular clip of this damage area + * @frame_begin: The frame number when this damage area starts being refreshed + */ +struct rockchip_ebc_area { + struct list_head list; + struct drm_rect clip; + u32 frame_begin; +}; + /** * struct rockchip_ebc_ctx - context for performing display refreshes * * @kref: Reference count, maintained as part of the CRTC's atomic state + * @queue: Queue of damaged areas to be refreshed + * @queue_lock: Lock protecting access to @queue * @prev: Display contents (Y4) before this refresh * @next: Display contents (Y4) after this refresh * @final: Display contents (Y4) after all pending refreshes @@ -192,6 +212,8 @@ static const struct drm_mode_config_funcs rockchip_ebc_mode_config_funcs = { */ struct rockchip_ebc_ctx { struct kref kref; + struct list_head queue; + spinlock_t queue_lock; u8 *prev; u8 *next; u8 *final; @@ -204,6 +226,10 @@ struct rockchip_ebc_ctx { static void rockchip_ebc_ctx_free(struct rockchip_ebc_ctx *ctx) { + struct rockchip_ebc_area *area; + + list_for_each_entry(area, &ctx->queue, list) + kfree(area); kfree(ctx->prev); kfree(ctx->next); kfree(ctx->final); @@ -234,6 +260,8 @@ static struct rockchip_ebc_ctx *rockchip_ebc_ctx_alloc(u32 width, u32 height) } kref_init(&ctx->kref); + INIT_LIST_HEAD(&ctx->queue); + spin_lock_init(&ctx->queue_lock); ctx->gray4_pitch = width / 2; ctx->gray4_size = gray4_size; ctx->phase_pitch = width; @@ -291,12 +319,204 @@ static void rockchip_ebc_global_refresh(struct rockchip_ebc *ebc, memcpy(ctx->prev, ctx->next, gray4_size); } +static bool rockchip_ebc_schedule_area(struct list_head *areas, + struct rockchip_ebc_area *area, + u32 current_frame, u32 num_phases) +{ + struct rockchip_ebc_area *other; + u32 frame_begin = current_frame; + + list_for_each_entry(other, areas, list) { + struct drm_rect intersection; + u32 other_end; + + /* Only consider areas before this one in the list. */ + if (other == area) + break; + + /* Skip areas that finish refresh before this area begins. */ + other_end = other->frame_begin + num_phases; + if (other_end <= frame_begin) + continue; + + /* If there is no collision, the areas are independent. */ + intersection = area->clip; + if (!drm_rect_intersect(&intersection, &other->clip)) + continue; + + /* If the other area already started, wait until it finishes. */ + if (other->frame_begin < current_frame) { + frame_begin = other_end; + continue; + } + + /* + * If the other area has not started yet, and completely + * contains this area, then this area is redundant. + */ + if (drm_rect_equals(&area->clip, &intersection)) + return false; + + /* Otherwise, start at the same time as the other area. */ + frame_begin = other->frame_begin; + } + + area->frame_begin = frame_begin; + + return true; +} + +static void rockchip_ebc_blit_phase(const struct rockchip_ebc_ctx *ctx, + u8 *dst, u8 phase, + const struct drm_rect *clip) +{ + unsigned int pitch = ctx->phase_pitch; + unsigned int width = clip->x2 - clip->x1; + unsigned int y; + u8 *dst_line; + + dst_line = dst + clip->y1 * pitch + clip->x1; + + for (y = clip->y1; y < clip->y2; y++) { + memset(dst_line, phase, width); + + dst_line += pitch; + } +} + +static void rockchip_ebc_blit_pixels(const struct rockchip_ebc_ctx *ctx, + u8 *dst, const u8 *src, + const struct drm_rect *clip) +{ + unsigned int x1_bytes = clip->x1 / 2; + unsigned int x2_bytes = clip->x2 / 2; + unsigned int pitch = ctx->gray4_pitch; + unsigned int width = x2_bytes - x1_bytes; + const u8 *src_line; + unsigned int y; + u8 *dst_line; + + dst_line = dst + clip->y1 * pitch + x1_bytes; + src_line = src + clip->y1 * pitch + x1_bytes; + + for (y = clip->y1; y < clip->y2; y++) { + memcpy(dst_line, src_line, width); + + dst_line += pitch; + src_line += pitch; + } +} + static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc, struct rockchip_ebc_ctx *ctx) { + dma_addr_t next_handle = virt_to_phys(ctx->next); + dma_addr_t prev_handle = virt_to_phys(ctx->prev); + struct rockchip_ebc_area *area, *next_area; + u32 last_phase = ebc->lut.num_phases - 1; struct drm_device *drm = &ebc->drm; + u32 gray4_size = ctx->gray4_size; + struct device *dev = drm->dev; + LIST_HEAD(areas); + u32 frame; + + for (frame = 0;; frame++) { + /* Swap phase buffers to minimize latency between frames. */ + u8 *phase_buffer = ctx->phase[frame % 2]; + dma_addr_t phase_handle = virt_to_phys(phase_buffer); + bool sync_next = false; + bool sync_prev = false; + + /* Move the queued damage areas to the local list. */ + spin_lock(&ctx->queue_lock); + list_splice_tail_init(&ctx->queue, &areas); + spin_unlock(&ctx->queue_lock); + + list_for_each_entry_safe(area, next_area, &areas, list) { + s32 frame_delta; + u32 phase; + + /* + * Determine when this area can start its refresh. + * If the area is redundant, drop it immediately. + */ + if (area->frame_begin == EBC_FRAME_PENDING && + !rockchip_ebc_schedule_area(&areas, area, frame, + ebc->lut.num_phases)) { + list_del(&area->list); + kfree(area); + continue; + } + + frame_delta = frame - area->frame_begin; + if (frame_delta < 0) + continue; + + /* Copy ctx->final to ctx->next on the first frame. */ + if (frame_delta == 0) { + rockchip_ebc_blit_pixels(ctx, ctx->next, + ctx->final, + &area->clip); + sync_next = true; + } + + /* + * Take advantage of the fact that the last phase in a + * waveform is always zero (neutral polarity). Instead + * of writing the actual phase number, write 0xff (the + * last possible phase number), which is guaranteed to + * be neutral for every waveform. + */ + phase = frame_delta >= last_phase ? 0xff : frame_delta; + rockchip_ebc_blit_phase(ctx, phase_buffer, phase, + &area->clip); + + /* + * Copy ctx->next to ctx->prev after the last phase. + * Technically, this races with the hardware computing + * the last phase, but the last phase is all zeroes + * anyway, regardless of prev/next (see above). + * + * Keeping the area in the list for one extra frame + * also ensures both phase buffers get set to 0xff. + */ + if (frame_delta > last_phase) { + rockchip_ebc_blit_pixels(ctx, ctx->prev, + ctx->next, + &area->clip); + sync_prev = true; + + list_del(&area->list); + kfree(area); + } + } + + if (sync_next) + dma_sync_single_for_device(dev, next_handle, + gray4_size, DMA_TO_DEVICE); + if (sync_prev) + dma_sync_single_for_device(dev, prev_handle, + gray4_size, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, phase_handle, + ctx->phase_size, DMA_TO_DEVICE); + + if (frame) { + if (!wait_for_completion_timeout(&ebc->display_end, + EBC_FRAME_TIMEOUT)) + drm_err(drm, "Frame %d timed out!\n", frame); + } - drm_dbg(drm, "partial refresh\n"); + if (list_empty(&areas)) + break; + + regmap_write(ebc->regmap, EBC_WIN_MST2, + phase_handle); + regmap_write(ebc->regmap, EBC_CONFIG_DONE, + EBC_CONFIG_DONE_REG_CONFIG_DONE); + regmap_write(ebc->regmap, EBC_DSP_START, + ebc->dsp_start | + EBC_DSP_START_DSP_FRM_START); + } } static void rockchip_ebc_refresh(struct rockchip_ebc *ebc, @@ -438,7 +658,8 @@ static int rockchip_ebc_refresh_thread(void *data) rockchip_ebc_refresh(ebc, ctx, false, default_waveform); set_current_state(TASK_IDLE); - schedule(); + if (list_empty(&ctx->queue)) + schedule(); __set_current_state(TASK_RUNNING); } @@ -686,6 +907,7 @@ static const struct drm_crtc_funcs rockchip_ebc_crtc_funcs = { struct ebc_plane_state { struct drm_shadow_plane_state base; + struct list_head areas; }; static inline struct ebc_plane_state * @@ -702,8 +924,13 @@ static inline struct rockchip_ebc *plane_to_ebc(struct drm_plane *plane) static int rockchip_ebc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) { + struct drm_atomic_helper_damage_iter iter; + struct ebc_plane_state *ebc_plane_state; + struct drm_plane_state *old_plane_state; struct drm_plane_state *plane_state; struct drm_crtc_state *crtc_state; + struct rockchip_ebc_area *area; + struct drm_rect clip; int ret; plane_state = drm_atomic_get_new_plane_state(state, plane); @@ -718,19 +945,126 @@ static int rockchip_ebc_plane_atomic_check(struct drm_plane *plane, if (ret) return ret; + ebc_plane_state = to_ebc_plane_state(plane_state); + old_plane_state = drm_atomic_get_old_plane_state(state, plane); + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &clip) { + area = kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return -ENOMEM; + + area->frame_begin = EBC_FRAME_PENDING; + area->clip = clip; + + list_add_tail(&area->list, &ebc_plane_state->areas); + } + return 0; } +static bool rockchip_ebc_blit_fb(const struct rockchip_ebc_ctx *ctx, + const struct drm_rect *dst_clip, + const void *vaddr, + const struct drm_framebuffer *fb, + const struct drm_rect *src_clip) +{ + unsigned int dst_pitch = ctx->gray4_pitch; + unsigned int src_pitch = fb->pitches[0]; + unsigned int x, y; + const void *src; + u8 changed = 0; + void *dst; + + dst = ctx->final + dst_clip->y1 * dst_pitch + dst_clip->x1 / 2; + src = vaddr + src_clip->y1 * src_pitch + src_clip->x1 * fb->format->cpp[0]; + + for (y = src_clip->y1; y < src_clip->y2; y++) { + const u32 *sbuf = src; + u8 *dbuf = dst; + + for (x = src_clip->x1; x < src_clip->x2; x += 2) { + u32 rgb0 = *sbuf++; + u32 rgb1 = *sbuf++; + u8 gray; + + /* Truncate the RGB values to 5 bits each. */ + rgb0 &= 0x00f8f8f8U; rgb1 &= 0x00f8f8f8U; + /* Put the sum 2R+5G+B in bits 24-31. */ + rgb0 *= 0x0020a040U; rgb1 *= 0x0020a040U; + /* Unbias the value for rounding to 4 bits. */ + rgb0 += 0x07000000U; rgb1 += 0x07000000U; + + gray = rgb0 >> 28 | rgb1 >> 28 << 4; + changed |= gray ^ *dbuf; + *dbuf++ = gray; + } + + dst += dst_pitch; + src += src_pitch; + } + + return !!changed; +} + static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { struct rockchip_ebc *ebc = plane_to_ebc(plane); + struct rockchip_ebc_area *area, *next_area; + struct ebc_plane_state *ebc_plane_state; struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + struct rockchip_ebc_ctx *ctx; + int translate_x, translate_y; + struct drm_rect src; + const void *vaddr; plane_state = drm_atomic_get_new_plane_state(state, plane); if (!plane_state->crtc) return; + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); + ctx = to_ebc_crtc_state(crtc_state)->ctx; + + drm_rect_fp_to_int(&src, &plane_state->src); + translate_x = plane_state->dst.x1 - src.x1; + translate_y = plane_state->dst.y1 - src.y1; + + ebc_plane_state = to_ebc_plane_state(plane_state); + vaddr = ebc_plane_state->base.data[0].vaddr; + + list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) { + struct drm_rect *dst_clip = &area->clip; + struct drm_rect src_clip = area->clip; + int adjust; + + /* Convert from plane coordinates to CRTC coordinates. */ + drm_rect_translate(dst_clip, translate_x, translate_y); + + /* Adjust the clips to always process full bytes (2 pixels). */ + adjust = dst_clip->x1 & 1; + dst_clip->x1 -= adjust; + src_clip.x1 -= adjust; + + adjust = dst_clip->x2 & 1; + dst_clip->x2 += adjust; + src_clip.x2 += adjust; + + if (!rockchip_ebc_blit_fb(ctx, dst_clip, vaddr, + plane_state->fb, &src_clip)) { + /* Drop the area if the FB didn't actually change. */ + list_del(&area->list); + kfree(area); + } + } + + if (list_empty(&ebc_plane_state->areas)) + return; + + spin_lock(&ctx->queue_lock); + list_splice_tail_init(&ebc_plane_state->areas, &ctx->queue); + spin_unlock(&ctx->queue_lock); + wake_up_process(ebc->refresh_thread); } @@ -756,6 +1090,8 @@ static void rockchip_ebc_plane_reset(struct drm_plane *plane) return; __drm_gem_reset_shadow_plane(plane, &ebc_plane_state->base); + + INIT_LIST_HEAD(&ebc_plane_state->areas); } static struct drm_plane_state * @@ -772,6 +1108,8 @@ rockchip_ebc_plane_duplicate_state(struct drm_plane *plane) __drm_gem_duplicate_shadow_plane_state(plane, &ebc_plane_state->base); + INIT_LIST_HEAD(&ebc_plane_state->areas); + return &ebc_plane_state->base.base; } @@ -779,6 +1117,10 @@ static void rockchip_ebc_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *plane_state) { struct ebc_plane_state *ebc_plane_state = to_ebc_plane_state(plane_state); + struct rockchip_ebc_area *area, *next_area; + + list_for_each_entry_safe(area, next_area, &ebc_plane_state->areas, list) + kfree(area); __drm_gem_destroy_shadow_plane_state(&ebc_plane_state->base); From patchwork Wed Apr 13 22:19:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560946 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E4BFDC433F5 for ; Wed, 13 Apr 2022 22:20:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238272AbiDMWWp (ORCPT ); Wed, 13 Apr 2022 18:22:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39294 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238062AbiDMWW3 (ORCPT ); Wed, 13 Apr 2022 18:22:29 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B633B240BB; Wed, 13 Apr 2022 15:19:46 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id D15815C033E; Wed, 13 Apr 2022 18:19:45 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 13 Apr 2022 18:19:45 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888385; x=1649974785; bh=yM jDNZk22ts1QSeZuHA3iHwk+1FDAWv58BJ1pfqsy94=; b=whdq+uqQW2un1gyfjq QH/YN4888u9WajBX6vFAnjzIXIm/0H3dnT7DmqEnv87XNVxuQAICx/F0JRuxFrvd jBoSJYdsPUeZ3VtIuzLd7+W5IC2xlLLWIDR30BHtuL6zjv5ydSTpOzE7Czl3L/QH V90CyPj8z5UYaSPfAHCySnGFxjtfxBrHaXUv74TwKHQmynfgrlCWE4JZHynJbUeH gifbwQA52NroLRoKZqU+rCtB6va60KonAfmTE3ohwg36qb3Xsjjt90XwmMRFpdIJ 88yXZ8u57UFZnxNQhEwLraXeT4/bjSA3xsXeV3gyVMvB7ttGj7bse4a9xYVKWaRR hy5g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888385; x=1649974785; bh=yMjDNZk22ts1QSeZuHA3iHwk+1FDAWv58BJ 1pfqsy94=; b=o/8Pde0ihVf9vakWSJ1n68tyevicS+zcAaZm7XMY10IOCxXE/3k po4pyR51AAUrVHwYmXCbHsyrMxJOQfmkwukTuGlIo6QbPXPrceg0K3/VD5R5H1oW A9fkmmqGST3jlFXnqfEtq9CWDhz0FHG/0KtkRZsokjhNbKCxKxlN029jZyTJcPO4 L1XuIOZkPtNTI9GGBy2OtRtu6NaOhDu051UnsDAmJzfP6O2BIARGPw9REoFYlT2Q Rg9GZ3+KiK0YlR5BhYR96LwzxvRzMNvyF3KBEviqET3TYoEbDqL5ZUTi4Zjymd7g IpXOgxYuYM1gt4Zw43gmmnfJkpaRF+sfC8A== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpeehnecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:44 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 12/16] drm/rockchip: ebc: Add support for direct mode Date: Wed, 13 Apr 2022 17:19:12 -0500 Message-Id: <20220413221916.50995-13-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Currently, 3-window mode causes some artifacting. Until the cause is determined, provide an option to use direct mode instead. Direct mode does the waveform lookups in software, so it has much higher CPU usage. This limits the frame rate below the panel's ideal 85 Hz, so it leads to slightly lower brightness accuracy. On the other hand, it doesn't leave random lines all over the screen. Signed-off-by: Samuel Holland --- drivers/gpu/drm/rockchip/rockchip_ebc.c | 97 ++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_ebc.c b/drivers/gpu/drm/rockchip/rockchip_ebc.c index dcd8c8e8208e..93d689ff0933 100644 --- a/drivers/gpu/drm/rockchip/rockchip_ebc.c +++ b/drivers/gpu/drm/rockchip/rockchip_ebc.c @@ -162,6 +162,10 @@ static bool diff_mode = true; module_param(diff_mode, bool, 0644); MODULE_PARM_DESC(diff_mode, "only compute waveforms for changed pixels"); +static bool direct_mode = true; +module_param(direct_mode, bool, 0444); +MODULE_PARM_DESC(direct_mode, "compute waveforms in software (software LUT)"); + static bool skip_reset; module_param(skip_reset, bool, 0444); MODULE_PARM_DESC(skip_reset, "skip the initial display reset"); @@ -370,6 +374,59 @@ static bool rockchip_ebc_schedule_area(struct list_head *areas, return true; } +static void rockchip_ebc_blit_direct(const struct rockchip_ebc_ctx *ctx, + u8 *dst, u8 phase, + const struct drm_epd_lut *lut, + const struct drm_rect *clip) +{ + const u32 *phase_lut = (const u32 *)lut->buf + 16 * phase; + unsigned int dst_pitch = ctx->phase_pitch / 4; + unsigned int src_pitch = ctx->gray4_pitch; + unsigned int x, y; + u8 *dst_line; + u32 src_line; + + dst_line = dst + clip->y1 * dst_pitch + clip->x1 / 4; + src_line = clip->y1 * src_pitch + clip->x1 / 2; + + for (y = clip->y1; y < clip->y2; y++) { + u32 src_offset = src_line; + u8 *dbuf = dst_line; + + for (x = clip->x1; x < clip->x2; x += 4) { + u8 prev0 = ctx->prev[src_offset]; + u8 next0 = ctx->next[src_offset++]; + u8 prev1 = ctx->prev[src_offset]; + u8 next1 = ctx->next[src_offset++]; + + /* + * The LUT is 256 phases * 16 next * 16 previous levels. + * Each value is two bits, so the last dimension neatly + * fits in a 32-bit word. + */ + u8 data = ((phase_lut[next0 & 0xf] >> ((prev0 & 0xf) << 1)) & 0x3) << 0 | + ((phase_lut[next0 >> 4] >> ((prev0 >> 4) << 1)) & 0x3) << 2 | + ((phase_lut[next1 & 0xf] >> ((prev1 & 0xf) << 1)) & 0x3) << 4 | + ((phase_lut[next1 >> 4] >> ((prev1 >> 4) << 1)) & 0x3) << 6; + + /* Diff mode ignores pixels that did not change brightness. */ + if (diff_mode) { + u8 mask = ((next0 ^ prev0) & 0x0f ? 0x03 : 0) | + ((next0 ^ prev0) & 0xf0 ? 0x0c : 0) | + ((next1 ^ prev1) & 0x0f ? 0x30 : 0) | + ((next1 ^ prev1) & 0xf0 ? 0xc0 : 0); + + data &= mask; + } + + *dbuf++ = data; + } + + dst_line += dst_pitch; + src_line += src_pitch; + } +} + static void rockchip_ebc_blit_phase(const struct rockchip_ebc_ctx *ctx, u8 *dst, u8 phase, const struct drm_rect *clip) @@ -472,8 +529,13 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc, * be neutral for every waveform. */ phase = frame_delta >= last_phase ? 0xff : frame_delta; - rockchip_ebc_blit_phase(ctx, phase_buffer, phase, - &area->clip); + if (direct_mode) + rockchip_ebc_blit_direct(ctx, phase_buffer, + phase, &ebc->lut, + &area->clip); + else + rockchip_ebc_blit_phase(ctx, phase_buffer, + phase, &area->clip); /* * Copy ctx->next to ctx->prev after the last phase. @@ -513,7 +575,8 @@ static void rockchip_ebc_partial_refresh(struct rockchip_ebc *ebc, if (list_empty(&areas)) break; - regmap_write(ebc->regmap, EBC_WIN_MST2, + regmap_write(ebc->regmap, + direct_mode ? EBC_WIN_MST0 : EBC_WIN_MST2, phase_handle); regmap_write(ebc->regmap, EBC_CONFIG_DONE, EBC_CONFIG_DONE_REG_CONFIG_DONE); @@ -581,10 +644,12 @@ static void rockchip_ebc_refresh(struct rockchip_ebc *ebc, /* * The hardware has a separate bit for each mode, with some priority * scheme between them. For clarity, only set one bit at a time. + * + * NOTE: In direct mode, no mode bits are set. */ if (global_refresh) { dsp_ctrl |= EBC_DSP_CTRL_DSP_LUT_MODE; - } else { + } else if (!direct_mode) { epd_ctrl |= EBC_EPD_CTRL_DSP_THREE_WIN_MODE; if (diff_mode) dsp_ctrl |= EBC_DSP_CTRL_DSP_DIFF_MODE; @@ -647,8 +712,12 @@ static int rockchip_ebc_refresh_thread(void *data) */ memset(ctx->prev, 0xff, ctx->gray4_size); memset(ctx->next, 0xff, ctx->gray4_size); - memset(ctx->phase[0], 0xff, ctx->phase_size); - memset(ctx->phase[1], 0xff, ctx->phase_size); + /* + * NOTE: In direct mode, the phase buffers are repurposed for + * source driver polarity data, where the no-op value is 0. + */ + memset(ctx->phase[0], direct_mode ? 0 : 0xff, ctx->phase_size); + memset(ctx->phase[1], direct_mode ? 0 : 0xff, ctx->phase_size); /* * LUTs use both the old and the new pixel values as inputs. @@ -1048,12 +1117,22 @@ static void rockchip_ebc_plane_atomic_update(struct drm_plane *plane, /* Convert from plane coordinates to CRTC coordinates. */ drm_rect_translate(dst_clip, translate_x, translate_y); - /* Adjust the clips to always process full bytes (2 pixels). */ - adjust = dst_clip->x1 & 1; + /* + * Adjust the clips to always process full bytes (2 pixels). + * + * NOTE: in direct mode, the minimum block size is 4 pixels. + */ + if (direct_mode) + adjust = dst_clip->x1 & 3; + else + adjust = dst_clip->x1 & 1; dst_clip->x1 -= adjust; src_clip.x1 -= adjust; - adjust = dst_clip->x2 & 1; + if (direct_mode) + adjust = ((dst_clip->x2 + 3) ^ 3) & 3; + else + adjust = dst_clip->x2 & 1; dst_clip->x2 += adjust; src_clip.x2 += adjust; From patchwork Wed Apr 13 22:19:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 560948 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8703EC4332F for ; Wed, 13 Apr 2022 22:20:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238071AbiDMWWj (ORCPT ); Wed, 13 Apr 2022 18:22:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40058 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238201AbiDMWW3 (ORCPT ); Wed, 13 Apr 2022 18:22:29 -0400 Received: from out5-smtp.messagingengine.com (out5-smtp.messagingengine.com [66.111.4.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6289F255B8; Wed, 13 Apr 2022 15:19:52 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id B5DB05C029F; Wed, 13 Apr 2022 18:19:51 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Wed, 13 Apr 2022 18:19:51 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:cc:content-transfer-encoding:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm2; t=1649888391; x=1649974791; bh=r6 8Xnm3H33EQgdbz7LYraAskJdNQ1ivO7aFZEAoIKAI=; b=i1JMYe+VYvdvr43zkv rDuNGZ58Ro3xDHMyKyBN4OnEAlxiHGLPtCA4+O4bdMEUCj14lZE8046cnZCJ9jnD 8DLeXDbxxpqVN2xqdlD4cKBi3N/KFWK+l2jmNrrj3TD2wIAfJcE+PH1IyjO7KMkA 5h+nsE12K6BGbwmLolsUNvj7e1/0ZVxxEcd2LrhvLve6b2Hd2ZGPkb10J8DTfg1N n/4Il/14MsUqiF4LmQ15q2tSfeI+3eEab/HWqd5/OY5Bttbjkf7zkhJtr37YIF+d 1/S+sHJdKWT9rxKu/K2a0xb/s1bpv5XsegcU6zMXBylPARz6LyaWebpxLNBunYxI 0qYQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:sender:subject:subject:to:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1649888391; x=1649974791; bh=r68Xnm3H33EQgdbz7LYraAskJdNQ1ivO7aF ZEAoIKAI=; b=oJmZjkvbs64pHexVyL74MFyo9GHxVwHCiDfNn4xRbkQq4HDfreq QStpQ8heF6cB14ziPQPfc/GPgN0wbD6qAgc8KvyZjnTrDvsTtUFzBqRiKW81EBu1 7K2TUQ8fm84JxB/xtYY2MvqGO3ln2axczz8UPpfY9/kNAb7Zw3hl9HyYGFK1U+lD 2RXkZTz9/V3qRhIRgvqtU/VPh6ZWwm34BSY5LkJn7y9cw5IwT08i7kHCgzmZj8oD QecYDvudLylKTpyEDTqNAIpHTwMelF+BocUpWdFy1MT+BPSQsEJrx1ew9ksbQS0Y akqFdJqTkcpdv7usqoKqzH4oLDWqxd0mNTg== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvvddrudelvddgtdelucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpefurghmuhgv lhcujfholhhlrghnugcuoehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhgqeenucggtf frrghtthgvrhhnpeduhfejfedvhffgfeehtefghfeiiefgfeehgfdvvdevfeegjeehjedv gfejheeuieenucevlhhushhtvghrufhiiigvpedunecurfgrrhgrmhepmhgrihhlfhhroh hmpehsrghmuhgvlhesshhhohhllhgrnhgurdhorhhg X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 13 Apr 2022 18:19:50 -0400 (EDT) From: Samuel Holland To: =?utf-8?q?Heiko_St=C3=BCbner?= , Sandy Huang , dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org, Alistair Francis , =?utf-8?q?Ond=C5=99ej_Jirman?= , Andreas Kemnade , Daniel Vetter , David Airlie , Geert Uytterhoeven , Samuel Holland , Krzysztof Kozlowski , Liang Chen , Maarten Lankhorst , Maxime Ripard , Michael Riesch , Nicolas Frattaroli , Peter Geis , Rob Herring , Sam Ravnborg , Thierry Reding , Thomas Zimmermann , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 15/16] arm64: dts: rockchip: rk356x: Add EBC node Date: Wed, 13 Apr 2022 17:19:15 -0500 Message-Id: <20220413221916.50995-16-samuel@sholland.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220413221916.50995-1-samuel@sholland.org> References: <20220413221916.50995-1-samuel@sholland.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The RK356x SoCs contain an EBC. Add its node. Signed-off-by: Samuel Holland --- arch/arm64/boot/dts/rockchip/rk356x.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi index 7cdef800cb3c..58c26f240af0 100644 --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi @@ -508,6 +508,20 @@ gpu: gpu@fde60000 { status = "disabled"; }; + ebc: ebc@fdec0000 { + compatible = "rockchip,rk3568-ebc"; + reg = <0x0 0xfdec0000 0x0 0x5000>; + interrupts = ; + clocks = <&cru HCLK_EBC>, <&cru DCLK_EBC>; + clock-names = "hclk", "dclk"; + pinctrl-0 = <&ebc_pins>; + pinctrl-names = "default"; + power-domains = <&power RK3568_PD_RGA>; + resets = <&cru SRST_H_EBC>, <&cru SRST_D_EBC>; + reset-names = "hclk", "dclk"; + status = "disabled"; + }; + sdmmc2: mmc@fe000000 { compatible = "rockchip,rk3568-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xfe000000 0x0 0x4000>;