From patchwork Wed Jul 20 14:27:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 591995 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 73ECBC433EF for ; Wed, 20 Jul 2022 14:27:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232916AbiGTO1h (ORCPT ); Wed, 20 Jul 2022 10:27:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41770 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229827AbiGTO1h (ORCPT ); Wed, 20 Jul 2022 10:27:37 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.220.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B60AB3ED68 for ; Wed, 20 Jul 2022 07:27:36 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 6CE3A2007E; Wed, 20 Jul 2022 14:27:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1658327255; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FRGx8deRD7rv9jPWgDUQa6mugDHCQ6/GgyWx9UYzCwQ=; b=QMdEtnKbdiApyIevjOpXQFoxK2ZCjLuBRP0L3Atv1dUyaLoCgEfR1Vp7MbZYDoLu7rtSkd 3wOdU9Bvyu+31wduIc9MieEbYqgCV9wJJFH3fRYZtyknjY2hYGSleGtu7LzX2CdeZZWmAb Ne5hDgEIVlszV59vq8cxUFvALuJFppk= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1658327255; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FRGx8deRD7rv9jPWgDUQa6mugDHCQ6/GgyWx9UYzCwQ=; b=qARdayyDGwkRoOLCoWcibAcpdJouSqvJG7aL4aA1oUXL/y24GgRQR/Jq1jiZJ3X2ME97Cm pu7qf2aNFWvoqKAw== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 1B30913AAD; Wed, 20 Jul 2022 14:27:35 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id GJ3WBdcQ2GLfHgAAMHmgww (envelope-from ); Wed, 20 Jul 2022 14:27:35 +0000 From: Thomas Zimmermann To: javierm@redhat.com, airlied@linux.ie, daniel@ffwll.ch, deller@gmx.de, maxime@cerno.tech, sam@ravnborg.org, msuchanek@suse.de, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, geert@linux-m68k.org, mark.cave-ayland@ilande.co.uk Cc: linux-fbdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, dri-devel@lists.freedesktop.org, Thomas Zimmermann Subject: [PATCH v2 04/10] drm/simpledrm: Compute framebuffer stride if not set Date: Wed, 20 Jul 2022 16:27:26 +0200 Message-Id: <20220720142732.32041-5-tzimmermann@suse.de> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220720142732.32041-1-tzimmermann@suse.de> References: <20220720142732.32041-1-tzimmermann@suse.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Compute the framebuffer's scanline stride length if not given by the simplefb data. Signed-off-by: Thomas Zimmermann Acked-by: Javier Martinez Canillas --- drivers/gpu/drm/tiny/simpledrm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 7de477835d44..cff9e7f71f80 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -743,6 +743,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, drm_err(dev, "no simplefb configuration found\n"); return ERR_PTR(-ENODEV); } + if (!stride) + stride = format->cpp[0] * width; + sdev->mode = simpledrm_mode(width, height); sdev->format = format; sdev->pitch = stride; From patchwork Wed Jul 20 14:27:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 591993 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 198C3C43334 for ; Wed, 20 Jul 2022 14:27:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234701AbiGTO1n (ORCPT ); Wed, 20 Jul 2022 10:27:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41812 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234483AbiGTO1k (ORCPT ); Wed, 20 Jul 2022 10:27:40 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [IPv6:2001:67c:2178:6::1d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 74CF73ED75 for ; Wed, 20 Jul 2022 07:27:37 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 1DEAE20151; Wed, 20 Jul 2022 14:27:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1658327256; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HcM1TyenBIZW16ak+Xu3LhPRX5q3T7h/+L0HPlZ4uu8=; b=B8u1dwz1Fua/vbKf6SUVc5xMZdMCE1stKPcVKDEi7lfKepxFfZl6tCBEjewSMznz51W8dw I6GHdW1gQSAMmXIRL/sVQAxAOw5sxh83Z//gHmJ1PiMBMrnqUEAp4UeT3xJHWeTulSoqal P/IoYeF8I3+5tbXGP/l/QUWa6VYUsTQ= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1658327256; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HcM1TyenBIZW16ak+Xu3LhPRX5q3T7h/+L0HPlZ4uu8=; b=5WRX5S6LfvnRAHZUdzLioN2xfuvj8VHQ88LCC3oesI7eEuFBam0pedKMEVsgrfKDscYeir D4HaNNUvw0GZvSBw== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id C394D13ABB; Wed, 20 Jul 2022 14:27:35 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 8CvnLtcQ2GLfHgAAMHmgww (envelope-from ); Wed, 20 Jul 2022 14:27:35 +0000 From: Thomas Zimmermann To: javierm@redhat.com, airlied@linux.ie, daniel@ffwll.ch, deller@gmx.de, maxime@cerno.tech, sam@ravnborg.org, msuchanek@suse.de, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, geert@linux-m68k.org, mark.cave-ayland@ilande.co.uk Cc: linux-fbdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, dri-devel@lists.freedesktop.org, Thomas Zimmermann Subject: [PATCH v2 06/10] drm/simpledrm: Move some functionality into fwfb helper library Date: Wed, 20 Jul 2022 16:27:28 +0200 Message-Id: <20220720142732.32041-7-tzimmermann@suse.de> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220720142732.32041-1-tzimmermann@suse.de> References: <20220720142732.32041-1-tzimmermann@suse.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Move some of simpledrm's functionality into a helper library. Other drivers for firmware-provided framebuffers will also need functions to handle fixed modes and color formats, or update the back buffer. Signed-off-by: Thomas Zimmermann --- Documentation/gpu/drm-kms-helpers.rst | 12 + MAINTAINERS | 2 + drivers/gpu/drm/Kconfig | 6 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_fwfb_helper.c | 301 ++++++++++++++++++++++++++ drivers/gpu/drm/tiny/Kconfig | 1 + drivers/gpu/drm/tiny/simpledrm.c | 163 ++------------ include/drm/drm_fwfb_helper.h | 51 +++++ 8 files changed, 398 insertions(+), 141 deletions(-) create mode 100644 drivers/gpu/drm/drm_fwfb_helper.c create mode 100644 include/drm/drm_fwfb_helper.h diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 2d473bc64c9f..0a8815b4243e 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -421,6 +421,18 @@ OF/DT Helpers .. kernel-doc:: drivers/gpu/drm/drm_of.c :export: +Firmware Framebuffer Helper Reference +===================================== + +.. kernel-doc:: drivers/gpu/drm/drm_fwfb_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_fwfb_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_fwfb_helper.c + :export: + Legacy Plane Helper Reference ============================= diff --git a/MAINTAINERS b/MAINTAINERS index 0f9366144d31..138680415e79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6572,9 +6572,11 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/drm_aperture.c +F: drivers/gpu/drm/drm_fwfb_helper.c F: drivers/gpu/drm/tiny/simpledrm.c F: drivers/video/aperture.c F: include/drm/drm_aperture.h +F: include/drm/drm_fwfb_helper.h F: include/linux/aperture.h DRM DRIVER FOR SIS VIDEO CARDS diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 1c91e1e861a5..47a82705e052 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -78,6 +78,12 @@ config DRM_KMS_HELPER help CRTC helpers for KMS drivers. +config DRM_FWFB_HELPER + bool + depends on DRM_KMS_HELPER + help + Helpers for firmware-provided framebuffers. + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 25016dcab55e..be51018a3cbf 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -67,8 +67,9 @@ drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o \ drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ drm_format_helper.o drm_self_refresh_helper.o drm_rect.o -drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o +drm_kms_helper-$(CONFIG_DRM_FWFB_HELPER) += drm_fwfb_helper.o +drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # diff --git a/drivers/gpu/drm/drm_fwfb_helper.c b/drivers/gpu/drm/drm_fwfb_helper.c new file mode 100644 index 000000000000..522294f459c8 --- /dev/null +++ b/drivers/gpu/drm/drm_fwfb_helper.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * The Firmware Framebuffer library FWFB provides helpers for devices with + * fixed-mode backing storage. It helps drivers to export a display mode of + * te correct size and copy updates to the backing storage. + */ + +/* + * Assume a monitor resolution of 96 dpi to + * get a somewhat reasonable screen size. + */ +#define RES_MM(d) \ + (((d) * 254ul) / (96ul * 10ul)) + +#define DRM_FWFB_MODE(hd, vd) \ + DRM_SIMPLE_MODE(hd, vd, RES_MM(hd), RES_MM(vd)) + +static struct drm_display_mode drm_fwfb_mode(unsigned int width, unsigned int height) +{ + struct drm_display_mode mode = { DRM_FWFB_MODE(width, height) }; + + mode.clock = mode.hdisplay * mode.vdisplay * 60 / 1000 /* kHz */; + drm_mode_set_name(&mode); + + return mode; +} + +/** + * drm_fwfb_init - Initializes an fwfb buffer + * @fwfb: fwfb buffer + * @screen_base: Address of the backing buffer in kernel address space + * @width: Number of pixels per scanline + * @height: Number of scanlines + * @format: Color format + * @pitch: Distance between two consecutive scanlines in bytes + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drm_fwfb_init(struct drm_fwfb *fwfb, struct iosys_map *screen_base, + unsigned int width, unsigned int height, + const struct drm_format_info *format, unsigned int pitch) +{ + fwfb->screen_base = *screen_base; + fwfb->mode = drm_fwfb_mode(width, height); + fwfb->format = format; + fwfb->pitch = pitch; + + return 0; +} +EXPORT_SYMBOL(drm_fwfb_init); + +static bool is_listed_fourcc(const uint32_t *fourccs, size_t nfourccs, uint32_t fourcc) +{ + const uint32_t *fourccs_end = fourccs + nfourccs; + + while (fourccs < fourccs_end) { + if (*fourccs == fourcc) + return true; + ++fourccs; + } + return false; +} + +/** + * drm_fwfb_extra_formats - Filters a list of supported color formats against + * the device's native formats + * @dev: DRM device + * @native_fourccs: 4CC codes of natively supported color formats + * @native_nfourccs: The number of entries in @native_fourccs + * @extra_fourccs: 4CC codes of additionally supported color formats + * @extra_nfourccs: The number of entries in @extra_fourccs + * @fourccs_out: Returns 4CC codes of supported color formats + * @nfourccs_out: The number of available entries in @fourccs_out + * + * This function create a list of supported color format from the device's + * native formats and the driver's emulated formats. The returned list can + * be handed over to drm_universal_plane_init() et al. + * + * Returns: + * The number of color formats returned in @fourccs_out. + */ +size_t drm_fwfb_extra_formats(struct drm_device *dev, + const uint32_t *native_fourccs, size_t native_nfourccs, + const uint32_t *extra_fourccs, size_t extra_nfourccs, + uint32_t *fourccs_out, size_t nfourccs_out) +{ + uint32_t *fourccs = fourccs_out; + const uint32_t *fourccs_end = fourccs_out + nfourccs_out; + bool found_native = false; + size_t nfourccs, i; + + /* native formats go first */ + + nfourccs = min_t(size_t, native_nfourccs, nfourccs_out); + + for (i = 0; i < nfourccs; ++i) { + uint32_t fourcc = native_fourccs[i]; + + drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc); + + if (!found_native) + found_native = is_listed_fourcc(extra_fourccs, extra_nfourccs, fourcc); + *fourccs = fourcc; + ++fourccs; + } + + /* + * The plane's atomic_update helper converts the framebuffer's color format + * to the native format when copying them to device memory. + * + * If there is not a single format supported by both, device and + * plane, the native formats are likely not supported by the conversion + * helpers. Therefore *only* support the native formats and add a + * conversion helper ASAP. + */ + if (!found_native) { + drm_warn(dev, "format conversion helpers required to add extra formats\n"); + goto out; + } + + /* extra formats go second */ + + nfourccs = min_t(size_t, extra_nfourccs, fourccs_end - fourccs); + + for (i = 0; i < nfourccs; ++i) { + uint32_t fourcc = extra_fourccs[i]; + + if (is_listed_fourcc(native_fourccs, native_nfourccs, fourcc)) + continue; /* native formats already went first */ + *fourccs = fourcc; + ++fourccs; + } + +out: + return fourccs - fourccs_out; +} +EXPORT_SYMBOL(drm_fwfb_extra_formats); + +/* + * Plane + */ + +/** + * drm_fwfb_plane_helper_atomic_update - Helper for implementing atomic plane updates + * @fwfb: fwfb buffer + * @plane: the plane to update + * @old_state: the old state + * + * This function updates the plane's damaged areas in the fwfb buffer. + */ +void drm_fwfb_plane_helper_atomic_update(struct drm_fwfb *fwfb, struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct iosys_map dst = fwfb->screen_base; + struct drm_plane_state *plane_state = plane->state; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + void *vmap = shadow_plane_state->data[0].vaddr; /* TODO: Use mapping abstraction */ + struct drm_framebuffer *fb = plane_state->fb; + void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ + unsigned int dst_pitch = fwfb->pitch; + const struct drm_format_info *dst_format = fwfb->format; + struct drm_rect src_clip, dst_clip; + int idx; + + if (!fb) + return; + + if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip)) + return; + + dst_clip = plane_state->dst; + if (!drm_rect_intersect(&dst_clip, &src_clip)) + return; + + if (!drm_dev_enter(dev, &idx)) + return; + + dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); + drm_fb_blit_toio(dst_vmap, dst_pitch, dst_format->format, vmap, fb, &src_clip); + + drm_dev_exit(idx); +} +EXPORT_SYMBOL(drm_fwfb_plane_helper_atomic_update); + +/** + * drm_fwfb_plane_helper_atomic_disable - Helper for disabling planes + * @fwfb: fwfb buffer + * @plane: the plane to disable + * @old_state: the old state + * + * This function clears the plane's fwfb buffer to zero. + */ +void drm_fwfb_plane_helper_atomic_disable(struct drm_fwfb *fwfb, struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct iosys_map dst = fwfb->screen_base; + struct drm_plane_state *plane_state = plane->state; + void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ + unsigned int dst_pitch = fwfb->pitch; + const struct drm_format_info *dst_format = fwfb->format; + struct drm_rect dst_clip; + unsigned long lines, linepixels, i; + int idx; + + drm_rect_init(&dst_clip, plane_state->src_x >> 16, plane_state->src_y >> 16, + plane_state->src_w >> 16, plane_state->src_h >> 16); + lines = drm_rect_height(&dst_clip); + linepixels = drm_rect_width(&dst_clip); + + if (!drm_dev_enter(dev, &idx)) + return; + + /* Clear buffer to black if disabled */ + dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); + for (i = 0; i < lines; ++i) { + memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); + dst_vmap += dst_pitch; + } + + drm_dev_exit(idx); +} +EXPORT_SYMBOL(drm_fwfb_plane_helper_atomic_disable); + +/* + * CRTC + */ + +/** + * drm_fwfb_crtc_helper_mode_valid - Validates a display mode + * @fwfb: fwfb buffer + * @crtc: the crtc + * @mode: the mode to validate + */ +enum drm_mode_status drm_fwfb_crtc_helper_mode_valid(struct drm_fwfb *fwfb, struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + + if (mode->hdisplay != fwfb->mode.hdisplay && mode->vdisplay != fwfb->mode.vdisplay) + return MODE_ONE_SIZE; + else if (mode->hdisplay != fwfb->mode.hdisplay) + return MODE_ONE_WIDTH; + else if (mode->vdisplay != fwfb->mode.vdisplay) + return MODE_ONE_HEIGHT; + + return MODE_OK; +} +EXPORT_SYMBOL(drm_fwfb_crtc_helper_mode_valid); + +/* + * Connector + */ + +/** + * drm_fwfb_connector_helper_get_modes - Creates a list of display modes for a connector + * @fwfb: fwfb buffer + * @connector: the connector + * + * This function creates a list of display modes for a connector. + * + * Returns: + * The number of created modes. + */ +int drm_fwfb_connector_helper_get_modes(struct drm_fwfb *fwfb, struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &fwfb->mode); + if (!mode) + return 0; + + if (mode->name[0] == '\0') + drm_mode_set_name(mode); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + if (mode->width_mm) + connector->display_info.width_mm = mode->width_mm; + if (mode->height_mm) + connector->display_info.height_mm = mode->height_mm; + + return 1; +} +EXPORT_SYMBOL(drm_fwfb_connector_helper_get_modes); diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 027cd87c3d0d..f3d6e4713dc6 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -70,6 +70,7 @@ config DRM_SIMPLEDRM tristate "Simple framebuffer driver" depends on DRM && MMU select APERTURE_HELPERS + select DRM_FWFB_HELPER select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER help diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 727ab9d86bcc..e2bf2f8abc74 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -30,16 +31,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -/* - * Assume a monitor resolution of 96 dpi to - * get a somewhat reasonable screen size. - */ -#define RES_MM(d) \ - (((d) * 254ul) / (96ul * 10ul)) - -#define SIMPLEDRM_MODE(hd, vd) \ - DRM_SIMPLE_MODE(hd, vd, RES_MM(hd), RES_MM(vd)) - /* * Helpers for simplefb */ @@ -211,13 +202,8 @@ struct simpledrm_device { struct regulator **regulators; #endif - /* simplefb settings */ - struct drm_display_mode mode; - const struct drm_format_info *format; - unsigned int pitch; - - /* memory management */ - void __iomem *screen_base; + /* firmware framebuffer */ + struct drm_fwfb fwfb; /* modesetting */ uint32_t formats[8]; @@ -504,50 +490,17 @@ static int simpledrm_primary_plane_helper_atomic_check(struct drm_plane *plane, static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *old_state) { - struct drm_plane_state *plane_state = plane->state; - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); - void *vmap = shadow_plane_state->data[0].vaddr; /* TODO: Use mapping abstraction */ - struct drm_framebuffer *fb = plane_state->fb; - struct drm_device *dev = plane->dev; - struct simpledrm_device *sdev = simpledrm_device_of_dev(dev); - void __iomem *dst = sdev->screen_base; - struct drm_rect src_clip, dst_clip; - int idx; - - if (!fb) - return; - - if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip)) - return; - - dst_clip = plane_state->dst; - if (!drm_rect_intersect(&dst_clip, &src_clip)) - return; - - if (!drm_dev_enter(dev, &idx)) - return; - - dst += drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_clip); - drm_fb_blit_toio(dst, sdev->pitch, sdev->format->format, vmap, fb, &src_clip); - - drm_dev_exit(idx); + struct simpledrm_device *sdev = simpledrm_device_of_dev(plane->dev); + + drm_fwfb_plane_helper_atomic_update(&sdev->fwfb, plane, old_state); } static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *old_state) { - struct drm_device *dev = plane->dev; - struct simpledrm_device *sdev = simpledrm_device_of_dev(dev); - int idx; - - if (!drm_dev_enter(dev, &idx)) - return; - - /* Clear screen to black if disabled */ - memset_io(sdev->screen_base, 0, sdev->pitch * sdev->mode.vdisplay); + struct simpledrm_device *sdev = simpledrm_device_of_dev(plane->dev); - drm_dev_exit(idx); + drm_fwfb_plane_helper_atomic_disable(&sdev->fwfb, plane, old_state); } static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = { @@ -569,15 +522,7 @@ static enum drm_mode_status simpledrm_crtc_helper_mode_valid(struct drm_crtc *cr { struct simpledrm_device *sdev = simpledrm_device_of_dev(crtc->dev); - if (mode->hdisplay != sdev->mode.hdisplay && - mode->vdisplay != sdev->mode.vdisplay) - return MODE_ONE_SIZE; - else if (mode->hdisplay != sdev->mode.hdisplay) - return MODE_ONE_WIDTH; - else if (mode->vdisplay != sdev->mode.vdisplay) - return MODE_ONE_HEIGHT; - - return MODE_OK; + return drm_fwfb_crtc_helper_mode_valid(&sdev->fwfb, crtc, mode); } static int simpledrm_crtc_helper_atomic_check(struct drm_crtc *crtc, @@ -634,24 +579,8 @@ static const struct drm_encoder_funcs simpledrm_encoder_funcs = { static int simpledrm_connector_helper_get_modes(struct drm_connector *connector) { struct simpledrm_device *sdev = simpledrm_device_of_dev(connector->dev); - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, &sdev->mode); - if (!mode) - return 0; - - if (mode->name[0] == '\0') - drm_mode_set_name(mode); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - drm_mode_probed_add(connector, mode); - if (mode->width_mm) - connector->display_info.width_mm = mode->width_mm; - if (mode->height_mm) - connector->display_info.height_mm = mode->height_mm; - - return 1; + return drm_fwfb_connector_helper_get_modes(&sdev->fwfb, connector); } static const struct drm_connector_helper_funcs simpledrm_connector_helper_funcs = { @@ -676,56 +605,6 @@ static const struct drm_mode_config_funcs simpledrm_mode_config_funcs = { * Init / Cleanup */ -static struct drm_display_mode simpledrm_mode(unsigned int width, - unsigned int height) -{ - struct drm_display_mode mode = { SIMPLEDRM_MODE(width, height) }; - - mode.clock = mode.hdisplay * mode.vdisplay * 60 / 1000 /* kHz */; - drm_mode_set_name(&mode); - - return mode; -} - -static const uint32_t *simpledrm_device_formats(struct simpledrm_device *sdev, - size_t *nformats_out) -{ - struct drm_device *dev = &sdev->dev; - size_t i; - - if (sdev->nformats) - goto out; /* don't rebuild list on recurring calls */ - - /* native format goes first */ - sdev->formats[0] = sdev->format->format; - sdev->nformats = 1; - - /* default formats go second */ - for (i = 0; i < ARRAY_SIZE(simpledrm_primary_plane_formats); ++i) { - if (simpledrm_primary_plane_formats[i] == sdev->format->format) - continue; /* native format already went first */ - sdev->formats[sdev->nformats] = simpledrm_primary_plane_formats[i]; - sdev->nformats++; - } - - /* - * TODO: The simpledrm driver converts framebuffers to the native - * format when copying them to device memory. If there are more - * formats listed than supported by the driver, the native format - * is not supported by the conversion helpers. Therefore *only* - * support the native format and add a conversion helper ASAP. - */ - if (drm_WARN_ONCE(dev, i != sdev->nformats, - "format conversion helpers required for %p4cc", - &sdev->format->format)) { - sdev->nformats = 1; - } - -out: - *nformats_out = sdev->nformats; - return sdev->formats; -} - static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev) { @@ -737,12 +616,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, const struct drm_format_info *format; struct resource *res, *mem; void __iomem *screen_base; + struct iosys_map screen_base_vmap; + struct drm_fwfb *fwfb; struct drm_plane *primary_plane; struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; unsigned long max_width, max_height; - const uint32_t *formats; size_t nformats; int ret; @@ -796,11 +676,6 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, if (!stride) stride = format->cpp[0] * width; - sdev->mode = simpledrm_mode(width, height); - sdev->format = format; - sdev->pitch = stride; - - drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sdev->mode)); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d byte\n", &format->format, width, height, stride); @@ -832,12 +707,17 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); if (!screen_base) return ERR_PTR(-ENOMEM); - sdev->screen_base = screen_base; + iosys_map_set_vaddr_iomem(&screen_base_vmap, screen_base); /* * Modesetting */ + fwfb = &sdev->fwfb; + ret = drm_fwfb_init(fwfb, &screen_base_vmap, width, height, format, stride); + if (ret) + return ERR_PTR(ret); + ret = drmm_mode_config_init(dev); if (ret) return ERR_PTR(ret); @@ -854,11 +734,14 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, /* Primary plane */ - formats = simpledrm_device_formats(sdev, &nformats); + nformats = drm_fwfb_extra_formats(dev, &format->format, 1, + simpledrm_primary_plane_formats, + ARRAY_SIZE(simpledrm_primary_plane_formats), + sdev->formats, ARRAY_SIZE(sdev->formats)); primary_plane = &sdev->primary_plane; ret = drm_universal_plane_init(dev, primary_plane, 0, &simpledrm_primary_plane_funcs, - formats, nformats, + sdev->formats, nformats, simpledrm_primary_plane_format_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) diff --git a/include/drm/drm_fwfb_helper.h b/include/drm/drm_fwfb_helper.h new file mode 100644 index 000000000000..2ad674e6564f --- /dev/null +++ b/include/drm/drm_fwfb_helper.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRM_FWFB_HELPER_H__ +#define __DRM_FWFB_HELPER_H__ + +#include + +#include + +/** + * struct drm_fwfb - Represents fixed-mode backing storage + */ +struct drm_fwfb { + /** + * @screen_base: Address of the screen memory + */ + struct iosys_map screen_base; + /** + * @mode: Display mode + */ + struct drm_display_mode mode; + /** + * @format: Color format + */ + const struct drm_format_info *format; + /** + * @pitch: Distance between two consecutive scanlines in bytes + */ + unsigned int pitch; +}; + +int drm_fwfb_init(struct drm_fwfb *fwfb, struct iosys_map *screen_base, + unsigned int width, unsigned int height, + const struct drm_format_info *format, unsigned int pitch); + +size_t drm_fwfb_extra_formats(struct drm_device *dev, + const uint32_t *native_fourccs, size_t native_nfourccs, + const uint32_t *extra_fourccs, size_t extra_nfourccs, + uint32_t *fourccs_out, size_t nfourccs_out); + +void drm_fwfb_plane_helper_atomic_update(struct drm_fwfb *fwfb, struct drm_plane *plane, + struct drm_atomic_state *state); +void drm_fwfb_plane_helper_atomic_disable(struct drm_fwfb *fwfb, struct drm_plane *plane, + struct drm_atomic_state *state); + +enum drm_mode_status drm_fwfb_crtc_helper_mode_valid(struct drm_fwfb *fwfb, struct drm_crtc *crtc, + const struct drm_display_mode *mode); + +int drm_fwfb_connector_helper_get_modes(struct drm_fwfb *fwfb, struct drm_connector *connector); + +#endif From patchwork Wed Jul 20 14:27:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 591992 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 DB797CCA487 for ; Wed, 20 Jul 2022 14:27:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230285AbiGTO1q (ORCPT ); Wed, 20 Jul 2022 10:27:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41854 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234574AbiGTO1m (ORCPT ); Wed, 20 Jul 2022 10:27:42 -0400 Received: from smtp-out1.suse.de (smtp-out1.suse.de [IPv6:2001:67c:2178:6::1c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A00383E75F for ; Wed, 20 Jul 2022 07:27:41 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id C2C8D3501E; Wed, 20 Jul 2022 14:27:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1658327256; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0EZDvfPE66GG3fviAlJzOKesYJSQCp8hvUpf+BMjfa0=; b=wRlPxUr78aAPd5XcbN05MRwvmg3ATHnqkXw9n6h1QUtAI9wiRBHUZ3GW/wulujij5VQPyd vhk0HkxQtLGBopN+MtBnergAo5NwnccdoFfnbMkzKnS6161Yep/Fsmhmea95cXqXub4Y5L od3ymMEBs9HlmhVk/oy0qCxE9gNLnQg= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1658327256; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0EZDvfPE66GG3fviAlJzOKesYJSQCp8hvUpf+BMjfa0=; b=brKm/zFDOgRveUCDbAxRBGSJXOAFCX1vpSFhGkCjuT6mg8bo1FCcllhe+uzIuXTuyro7PW zIvqXRIIAItyntCw== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 7957413ABB; Wed, 20 Jul 2022 14:27:36 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id oAS8HNgQ2GLfHgAAMHmgww (envelope-from ); Wed, 20 Jul 2022 14:27:36 +0000 From: Thomas Zimmermann To: javierm@redhat.com, airlied@linux.ie, daniel@ffwll.ch, deller@gmx.de, maxime@cerno.tech, sam@ravnborg.org, msuchanek@suse.de, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, geert@linux-m68k.org, mark.cave-ayland@ilande.co.uk Cc: linux-fbdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, dri-devel@lists.freedesktop.org, Thomas Zimmermann Subject: [PATCH v2 08/10] drm/ofdrm: Add CRTC state Date: Wed, 20 Jul 2022 16:27:30 +0200 Message-Id: <20220720142732.32041-9-tzimmermann@suse.de> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220720142732.32041-1-tzimmermann@suse.de> References: <20220720142732.32041-1-tzimmermann@suse.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Add a dedicated CRTC state to ofdrm to later store information for palette updates. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas --- drivers/gpu/drm/tiny/ofdrm.c | 62 ++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index 6c062b48d354..ad673c9b5502 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -277,6 +277,21 @@ static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev, * Modesetting */ +struct ofdrm_crtc_state { + struct drm_crtc_state base; +}; + +static struct ofdrm_crtc_state *to_ofdrm_crtc_state(struct drm_crtc_state *base) +{ + return container_of(base, struct ofdrm_crtc_state, base); +} + +static void ofdrm_crtc_state_destroy(struct ofdrm_crtc_state *ofdrm_crtc_state) +{ + __drm_atomic_helper_crtc_destroy_state(&ofdrm_crtc_state->base); + kfree(ofdrm_crtc_state); +} + /* * Support all formats of OF display and maybe more; in order * of preference. The display's update function will do any @@ -395,13 +410,54 @@ static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = { .atomic_disable = ofdrm_crtc_helper_atomic_disable, }; +static void ofdrm_crtc_reset(struct drm_crtc *crtc) +{ + struct ofdrm_crtc_state *ofdrm_crtc_state; + + if (crtc->state) { + ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc->state)); + crtc->state = NULL; /* must be set to NULL here */ + } + + ofdrm_crtc_state = kzalloc(sizeof(*ofdrm_crtc_state), GFP_KERNEL); + if (!ofdrm_crtc_state) + return; + __drm_atomic_helper_crtc_reset(crtc, &ofdrm_crtc_state->base); +} + +static struct drm_crtc_state *ofdrm_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state = crtc->state; + struct ofdrm_crtc_state *ofdrm_crtc_state; + struct ofdrm_crtc_state *new_ofdrm_crtc_state; + + if (!crtc_state) + return NULL; + + ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state); + + new_ofdrm_crtc_state = kzalloc(sizeof(*new_ofdrm_crtc_state), GFP_KERNEL); + if (!new_ofdrm_crtc_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &new_ofdrm_crtc_state->base); + + return &new_ofdrm_crtc_state->base; +} + +static void ofdrm_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc_state)); +} + static const struct drm_crtc_funcs ofdrm_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .reset = ofdrm_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = ofdrm_crtc_atomic_duplicate_state, + .atomic_destroy_state = ofdrm_crtc_atomic_destroy_state, }; static int ofdrm_connector_helper_get_modes(struct drm_connector *connector) From patchwork Wed Jul 20 14:27:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 591991 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 35854CCA485 for ; Wed, 20 Jul 2022 14:27:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234574AbiGTO1q (ORCPT ); Wed, 20 Jul 2022 10:27:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41864 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234279AbiGTO1m (ORCPT ); Wed, 20 Jul 2022 10:27:42 -0400 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.220.28]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F2FFD3ED68 for ; Wed, 20 Jul 2022 07:27:41 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 2974533B4B; Wed, 20 Jul 2022 14:27:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1658327257; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QYYN3Ovi5RWR1IrEvWTkZ8r4xqCUz9Gl6//yUK5ESrQ=; b=PL64OI9MOx+Ug1dYO36x8kBB5KbsdvpYjacrpVBz76wME7UXhQl5PyY4fvOeQtxqkaFkxq 6QP+ciQj22q0EyMAYl+PWFP+xlxH2e4CIprj/7e9nIeQ2C+jllX+Mgv+3bftjVKkUxAkve rekP0SnfUuPJkkd+4ons/VcwosDWXGY= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1658327257; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QYYN3Ovi5RWR1IrEvWTkZ8r4xqCUz9Gl6//yUK5ESrQ=; b=ccim5dWbukDSTwM/hzLMdJoddzuK22KUBjjQuyIkOAl3UdjeOtOnOFhlfNo3j1mA6AKeBU 1agv7KyYVBjjOkDA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id C7C3C13AAD; Wed, 20 Jul 2022 14:27:36 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id aDvwL9gQ2GLfHgAAMHmgww (envelope-from ); Wed, 20 Jul 2022 14:27:36 +0000 From: Thomas Zimmermann To: javierm@redhat.com, airlied@linux.ie, daniel@ffwll.ch, deller@gmx.de, maxime@cerno.tech, sam@ravnborg.org, msuchanek@suse.de, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, geert@linux-m68k.org, mark.cave-ayland@ilande.co.uk Cc: linux-fbdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, dri-devel@lists.freedesktop.org, Thomas Zimmermann Subject: [PATCH v2 09/10] drm/ofdrm: Add per-model device function Date: Wed, 20 Jul 2022 16:27:31 +0200 Message-Id: <20220720142732.32041-10-tzimmermann@suse.de> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220720142732.32041-1-tzimmermann@suse.de> References: <20220720142732.32041-1-tzimmermann@suse.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Add a per-model device-function structure in preparation of adding color-management support. Detection of the individual models has been taken from fbdev's offb. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas --- drivers/gpu/drm/tiny/ofdrm.c | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index ad673c9b5502..62136b8ab975 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -29,6 +29,18 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 +enum ofdrm_model { + OFDRM_MODEL_UNKNOWN, + OFDRM_MODEL_MACH64, /* ATI Mach64 */ + OFDRM_MODEL_RAGE128, /* ATI Rage128 */ + OFDRM_MODEL_RAGE_M3A, /* ATI Rage Mobility M3 Head A */ + OFDRM_MODEL_RAGE_M3B, /* ATI Rage Mobility M3 Head B */ + OFDRM_MODEL_RADEON, /* ATI Radeon */ + OFDRM_MODEL_GXT2000, /* IBM GXT2000 */ + OFDRM_MODEL_AVIVO, /* ATI R5xx */ + OFDRM_MODEL_QEMU, /* QEMU VGA */ +}; + /* * Helpers for display nodes */ @@ -150,14 +162,62 @@ static u64 display_get_address_of(struct drm_device *dev, struct device_node *of return address; } +static bool is_avivo(__be32 vendor, __be32 device) +{ + /* This will match most R5xx */ + return (vendor == 0x1002) && + ((device >= 0x7100 && device < 0x7800) || (device >= 0x9400)); +} + +static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node) +{ + enum ofdrm_model model = OFDRM_MODEL_UNKNOWN; + + if (of_node_name_prefix(of_node, "ATY,Rage128")) { + model = OFDRM_MODEL_RAGE128; + } else if (of_node_name_prefix(of_node, "ATY,RageM3pA") || + of_node_name_prefix(of_node, "ATY,RageM3p12A")) { + model = OFDRM_MODEL_RAGE_M3A; + } else if (of_node_name_prefix(of_node, "ATY,RageM3pB")) { + model = OFDRM_MODEL_RAGE_M3B; + } else if (of_node_name_prefix(of_node, "ATY,Rage6")) { + model = OFDRM_MODEL_RADEON; + } else if (of_node_name_prefix(of_node, "ATY,")) { + return OFDRM_MODEL_MACH64; + } else if (of_device_is_compatible(of_node, "pci1014,b7") || + of_device_is_compatible(of_node, "pci1014,21c")) { + model = OFDRM_MODEL_GXT2000; + } else if (of_node_name_prefix(of_node, "vga,Display-")) { + struct device_node *of_parent; + const __be32 *vendor_p, *device_p; + + /* Look for AVIVO initialized by SLOF */ + of_parent = of_get_parent(of_node); + vendor_p = of_get_property(of_parent, "vendor-id", NULL); + device_p = of_get_property(of_parent, "device-id", NULL); + if (vendor_p && device_p && is_avivo(*vendor_p, *device_p)) + model = OFDRM_MODEL_AVIVO; + of_node_put(of_parent); + } else if (of_device_is_compatible(of_node, "qemu,std-vga")) { + model = OFDRM_MODEL_QEMU; + } + + return model; +} + /* * Open Firmware display device */ +struct ofdrm_device_funcs { +}; + struct ofdrm_device { struct drm_device dev; struct platform_device *pdev; + const struct ofdrm_device_funcs *funcs; + /* firmware framebuffer */ struct drm_fwfb fwfb; @@ -489,12 +549,40 @@ static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = { * Init / Cleanup */ +static const struct ofdrm_device_funcs ofdrm_unknown_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_mach64_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_rage128_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_rage_m3a_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_rage_m3b_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_radeon_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_gxt2000_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_avivo_device_funcs = { +}; + +static const struct ofdrm_device_funcs ofdrm_qemu_device_funcs = { +}; + static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, struct platform_device *pdev) { struct device_node *of_node = pdev->dev.of_node; struct ofdrm_device *odev; struct drm_device *dev; + enum ofdrm_model model; int width, height, linebytes; const struct drm_format_info *format; u64 address; @@ -525,6 +613,39 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, * OF display-node settings */ + model = display_get_model_of(dev, of_node); + drm_dbg(dev, "detected model %d\n", model); + + switch (model) { + case OFDRM_MODEL_UNKNOWN: + odev->funcs = &ofdrm_unknown_device_funcs; + break; + case OFDRM_MODEL_MACH64: + odev->funcs = &ofdrm_mach64_device_funcs; + break; + case OFDRM_MODEL_RAGE128: + odev->funcs = &ofdrm_rage128_device_funcs; + break; + case OFDRM_MODEL_RAGE_M3A: + odev->funcs = &ofdrm_rage_m3a_device_funcs; + break; + case OFDRM_MODEL_RAGE_M3B: + odev->funcs = &ofdrm_rage_m3b_device_funcs; + break; + case OFDRM_MODEL_RADEON: + odev->funcs = &ofdrm_radeon_device_funcs; + break; + case OFDRM_MODEL_GXT2000: + odev->funcs = &ofdrm_gxt2000_device_funcs; + break; + case OFDRM_MODEL_AVIVO: + odev->funcs = &ofdrm_avivo_device_funcs; + break; + case OFDRM_MODEL_QEMU: + odev->funcs = &ofdrm_qemu_device_funcs; + break; + } + width = display_get_width_of(dev, of_node); if (width < 0) return ERR_PTR(width); From patchwork Wed Jul 20 14:27:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zimmermann X-Patchwork-Id: 591990 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 5318EC43334 for ; Wed, 20 Jul 2022 14:27:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234510AbiGTO1r (ORCPT ); Wed, 20 Jul 2022 10:27:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41874 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234711AbiGTO1o (ORCPT ); Wed, 20 Jul 2022 10:27:44 -0400 Received: from smtp-out1.suse.de (smtp-out1.suse.de [IPv6:2001:67c:2178:6::1c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 546813ED75 for ; Wed, 20 Jul 2022 07:27:42 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 8272835021; Wed, 20 Jul 2022 14:27:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1658327257; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oeQDMoXrJcaQf78V1izqW+LNkllTNRER/MG1oqngfiQ=; b=DIDOCowvtbRhptyfG+k4evpB8z+R3IWPE2+fU+Ybq4xoT47xdrLh6HiD+p62MvG033os2p XHn8lJM7cip6YJqWiyq/cVRQHoOyrH1WtZ3QEk0USi4SlxxgBiN6cCEUkjRzaYMUbu95tA YhFlSZDR8XYNl5UDYKbRS8yK3WWJLh4= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1658327257; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oeQDMoXrJcaQf78V1izqW+LNkllTNRER/MG1oqngfiQ=; b=d0AC5CW+ErKZ/sWukp0EWM25l7LsYMjcr5zgdXun3ZQaFdM+7mjX7zrajV7FJDZBFlBisi kz5pq3H7albweNAA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 2E1CD13ABB; Wed, 20 Jul 2022 14:27:37 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id qO5oCtkQ2GLfHgAAMHmgww (envelope-from ); Wed, 20 Jul 2022 14:27:37 +0000 From: Thomas Zimmermann To: javierm@redhat.com, airlied@linux.ie, daniel@ffwll.ch, deller@gmx.de, maxime@cerno.tech, sam@ravnborg.org, msuchanek@suse.de, mpe@ellerman.id.au, benh@kernel.crashing.org, paulus@samba.org, geert@linux-m68k.org, mark.cave-ayland@ilande.co.uk Cc: linux-fbdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org, dri-devel@lists.freedesktop.org, Thomas Zimmermann Subject: [PATCH v2 10/10] drm/ofdrm: Support color management Date: Wed, 20 Jul 2022 16:27:32 +0200 Message-Id: <20220720142732.32041-11-tzimmermann@suse.de> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220720142732.32041-1-tzimmermann@suse.de> References: <20220720142732.32041-1-tzimmermann@suse.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org Support the CRTC's color-management property and implement each model's palette support. The OF hardware has different methods of setting the palette. The respective code has been taken from fbdev's offb and refactored into per-model device functions. The device functions integrate this functionality into the overall modesetting. As palette handling is a CRTC property that depends on the primary plane's color format, the plane's atomic_check helper now updates the format field in ofdrm's custom CRTC state. The CRTC's atomic_flush helper updates the palette for the format as needed. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas --- drivers/gpu/drm/tiny/ofdrm.c | 434 ++++++++++++++++++++++++++++++++++- 1 file changed, 433 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index 62136b8ab975..2964eb9951c5 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,44 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 +#if !defined(CONFIG_PPC) +static inline void out_8(void __iomem *addr, int val) +{ } +static inline void out_le32(void __iomem *addr, int val) +{ } +static inline unsigned int in_le32(const void __iomem *addr) +{ + return 0; +} +#endif + +#define OFDRM_GAMMA_LUT_SIZE 256 + +/* Definitions used by the Avivo palette */ +#define AVIVO_DC_LUT_RW_SELECT 0x6480 +#define AVIVO_DC_LUT_RW_MODE 0x6484 +#define AVIVO_DC_LUT_RW_INDEX 0x6488 +#define AVIVO_DC_LUT_SEQ_COLOR 0x648c +#define AVIVO_DC_LUT_PWL_DATA 0x6490 +#define AVIVO_DC_LUT_30_COLOR 0x6494 +#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 +#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c +#define AVIVO_DC_LUT_AUTOFILL 0x64a0 +#define AVIVO_DC_LUTA_CONTROL 0x64c0 +#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 +#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 +#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc +#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 +#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 +#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 +#define AVIVO_DC_LUTB_CONTROL 0x6cc0 +#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 +#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 +#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc +#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 +#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 +#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 + enum ofdrm_model { OFDRM_MODEL_UNKNOWN, OFDRM_MODEL_MACH64, /* ATI Mach64 */ @@ -209,7 +248,14 @@ static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct devi * Open Firmware display device */ +struct ofdrm_device; + struct ofdrm_device_funcs { + void __iomem *(*cmap_ioremap)(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_bas); + void (*cmap_write)(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b); }; struct ofdrm_device { @@ -221,6 +267,9 @@ struct ofdrm_device { /* firmware framebuffer */ struct drm_fwfb fwfb; + /* colormap */ + void __iomem *cmap_base; + /* modesetting */ uint32_t formats[8]; struct drm_plane primary_plane; @@ -333,12 +382,322 @@ static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev, return max_res; } +/* + * Colormap / Palette + */ + +static void __iomem *get_cmap_address_of(struct ofdrm_device *odev, struct device_node *of_node, + int bar_no, unsigned long offset, unsigned long size) +{ + struct drm_device *dev = &odev->dev; + const __be32 *addr_p; + u64 max_size, address; + unsigned int flags; + void __iomem *mem; + + addr_p = of_get_pci_address(of_node, bar_no, &max_size, &flags); + if (!addr_p) + addr_p = of_get_address(of_node, bar_no, &max_size, &flags); + if (!addr_p) + return ERR_PTR(-ENODEV); + + if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + return ERR_PTR(-ENODEV); + + if ((offset + size) >= max_size) + return ERR_PTR(-ENODEV); + + address = of_translate_address(of_node, addr_p); + if (address == OF_BAD_ADDR) + return ERR_PTR(-ENODEV); + + mem = devm_ioremap(dev->dev, address + offset, size); + if (!mem) + return ERR_PTR(-ENOMEM); + + return mem; +} + +static void __iomem *ofdrm_mach64_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + struct drm_device *dev = &odev->dev; + u64 address; + void __iomem *cmap_base; + + address = fb_base & 0xff000000ul; + address += 0x7ff000; + + cmap_base = devm_ioremap(dev->dev, address, 0x1000); + if (!cmap_base) + return ERR_PTR(-ENOMEM); + + return cmap_base; +} + +static void ofdrm_mach64_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *addr = odev->cmap_base + 0xcc0; + void __iomem *data = odev->cmap_base + 0xcc0 + 1; + + writeb(index, addr); + writeb(r, data); + writeb(g, data); + writeb(b, data); +} + +static void __iomem *ofdrm_rage128_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); +} + +static void ofdrm_rage128_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *addr = odev->cmap_base + 0xb0; + void __iomem *data = odev->cmap_base + 0xb4; + int color = (r << 16) | (g << 8) | b; + + out_8(addr, index); + out_le32(data, color); +} + +static void __iomem *ofdrm_rage_m3a_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); +} + +static void ofdrm_rage_m3a_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *dac_ctl = odev->cmap_base + 0x58; + void __iomem *addr = odev->cmap_base + 0xb0; + void __iomem *data = odev->cmap_base + 0xb4; + int color = (r << 16) | (g << 8) | b; + unsigned int val; + + /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ + val = in_le32(dac_ctl); + val &= ~0x20; + out_le32(dac_ctl, val); + + /* Set color at palette index */ + out_8(addr, index); + out_le32(data, color); +} + +static void __iomem *ofdrm_rage_m3b_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff); +} + +static void ofdrm_rage_m3b_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *dac_ctl = odev->cmap_base + 0x58; + void __iomem *addr = odev->cmap_base + 0xb0; + void __iomem *data = odev->cmap_base + 0xb4; + int color = (r << 16) | (g << 8) | b; + unsigned int val; + + /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ + val = in_le32(dac_ctl); + val |= 0x20; + out_le32(dac_ctl, val); + + /* Set color at palette index */ + out_8(addr, index); + out_le32(data, color); +} + +static void __iomem *ofdrm_radeon_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + return get_cmap_address_of(odev, of_node, 1, 0, 0x1fff); +} + +static void __iomem *ofdrm_gxt2000_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + return get_cmap_address_of(odev, of_node, 0, 0x6000, 0x1000); +} + +static void ofdrm_gxt2000_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *data = ((unsigned int __iomem *)odev->cmap_base) + index; + int color = (r << 16) | (g << 8) | b; + + out_le32(data, color); +} + +static void __iomem *ofdrm_avivo_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ + struct device_node *of_parent; + void __iomem *cmap_base; + + of_parent = of_get_parent(of_node); + cmap_base = get_cmap_address_of(odev, of_parent, 0, 0, 0x10000); + of_node_put(of_parent); + + return cmap_base; +} + +static void ofdrm_avivo_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *lutsel = odev->cmap_base + AVIVO_DC_LUT_RW_SELECT; + void __iomem *addr = odev->cmap_base + AVIVO_DC_LUT_RW_INDEX; + void __iomem *data = odev->cmap_base + AVIVO_DC_LUT_30_COLOR; + u32 color = (r << 22) | (g << 12) | (b << 2); + + /* Write to both LUTs for now */ + + writel(1, lutsel); + writeb(index, addr); + writel(color, data); + + writel(0, lutsel); + writeb(index, addr); + writel(color, data); +} + +static void __iomem *ofdrm_qemu_cmap_ioremap(struct ofdrm_device *odev, + struct device_node *of_node, + u64 fb_base) +{ +#ifdef __BIG_ENDIAN + static const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 }; +#else + static const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 }; +#endif + + struct drm_device *dev = &odev->dev; + u64 address; + void __iomem *cmap_base; + + address = of_translate_address(of_node, io_of_addr); + if (address == OF_BAD_ADDR) + return ERR_PTR(-ENODEV); + + cmap_base = devm_ioremap(dev->dev, address + 0x3c8, 2); + if (!cmap_base) + return ERR_PTR(-ENOMEM); + + return cmap_base; +} + +static void ofdrm_qemu_cmap_write(struct ofdrm_device *odev, unsigned char index, + unsigned char r, unsigned char g, unsigned char b) +{ + void __iomem *addr = odev->cmap_base; + void __iomem *data = odev->cmap_base + 1; + + writeb(index, addr); + writeb(r, data); + writeb(g, data); + writeb(b, data); +} + +static void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev, + const struct drm_format_info *format) +{ + struct drm_device *dev = &odev->dev; + int i; + + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from 0 to 255 */ + for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { + unsigned char r = i * 8 + i / 4; + unsigned char g = i * 4 + i / 16; + unsigned char b = i * 8 + i / 4; + + odev->funcs->cmap_write(odev, i, r, g, b); + } + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { + unsigned char r = 0; + unsigned char g = i * 4 + i / 16; + unsigned char b = 0; + + odev->funcs->cmap_write(odev, i, r, g, b); + } + break; + case DRM_FORMAT_XRGB8888: + for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) + odev->funcs->cmap_write(odev, i, i, i, i); + break; + default: + drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; + } +} + +static void ofdrm_device_set_gamma(struct ofdrm_device *odev, + const struct drm_format_info *format, + struct drm_color_lut *lut) +{ + struct drm_device *dev = &odev->dev; + int i; + + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ + for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { + unsigned char r = lut[i * 8 + i / 4].red >> 8; + unsigned char g = lut[i * 4 + i / 16].green >> 8; + unsigned char b = lut[i * 8 + i / 4].blue >> 8; + + odev->funcs->cmap_write(odev, i, r, g, b); + } + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { + unsigned char r = 0; + unsigned char g = lut[i * 4 + i / 16].green >> 8; + unsigned char b = 0; + + odev->funcs->cmap_write(odev, i, r, g, b); + } + break; + case DRM_FORMAT_XRGB8888: + for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) { + unsigned char r = lut[i].red >> 8; + unsigned char g = lut[i].green >> 8; + unsigned char b = lut[i].blue >> 8; + + odev->funcs->cmap_write(odev, i, r, g, b); + } + break; + default: + drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; + } +} + /* * Modesetting */ struct ofdrm_crtc_state { struct drm_crtc_state base; + + /* Primary-plane format; required for color mgmt. */ + const struct drm_format_info *format; }; static struct ofdrm_crtc_state *to_ofdrm_crtc_state(struct drm_crtc_state *base) @@ -376,10 +735,12 @@ static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *new_state) { struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; struct drm_crtc_state *new_crtc_state; + struct ofdrm_crtc_state *new_ofdrm_crtc_state; int ret; - if (!new_plane_state->fb) + if (!new_fb) return 0; new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); @@ -391,6 +752,14 @@ static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, if (ret) return ret; + if (!new_plane_state->visible) + return 0; + + new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); + + new_ofdrm_crtc_state = to_ofdrm_crtc_state(new_crtc_state); + new_ofdrm_crtc_state->format = new_fb->format; + return 0; } @@ -435,6 +804,9 @@ static enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc, static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { + static const size_t gamma_lut_length = OFDRM_GAMMA_LUT_SIZE * sizeof(struct drm_color_lut); + + struct drm_device *dev = crtc->dev; struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); int ret; @@ -442,9 +814,35 @@ static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc, if (ret) return ret; + if (new_crtc_state->color_mgmt_changed) { + struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; + + if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { + drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); + return -EINVAL; + } + } + return drm_atomic_add_affected_planes(new_state, crtc); } +static void ofdrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *old_state) +{ + struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev); + struct drm_crtc_state *crtc_state = crtc->state; + struct ofdrm_crtc_state *ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state); + + if (crtc_state->enable && crtc_state->color_mgmt_changed) { + const struct drm_format_info *format = ofdrm_crtc_state->format; + + if (crtc_state->gamma_lut) + ofdrm_device_set_gamma(odev, format, crtc_state->gamma_lut->data); + else + ofdrm_device_set_gamma_linear(odev, format); + } +} + static void ofdrm_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *old_state) { @@ -466,6 +864,7 @@ static void ofdrm_crtc_helper_atomic_disable(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = { .mode_valid = ofdrm_crtc_helper_mode_valid, .atomic_check = ofdrm_crtc_helper_atomic_check, + .atomic_flush = ofdrm_crtc_helper_atomic_flush, .atomic_enable = ofdrm_crtc_helper_atomic_enable, .atomic_disable = ofdrm_crtc_helper_atomic_disable, }; @@ -501,6 +900,7 @@ static struct drm_crtc_state *ofdrm_crtc_atomic_duplicate_state(struct drm_crtc return NULL; __drm_atomic_helper_crtc_duplicate_state(crtc, &new_ofdrm_crtc_state->base); + new_ofdrm_crtc_state->format = ofdrm_crtc_state->format; return &new_ofdrm_crtc_state->base; } @@ -553,27 +953,43 @@ static const struct ofdrm_device_funcs ofdrm_unknown_device_funcs = { }; static const struct ofdrm_device_funcs ofdrm_mach64_device_funcs = { + .cmap_ioremap = ofdrm_mach64_cmap_ioremap, + .cmap_write = ofdrm_mach64_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_rage128_device_funcs = { + .cmap_ioremap = ofdrm_rage128_cmap_ioremap, + .cmap_write = ofdrm_rage128_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_rage_m3a_device_funcs = { + .cmap_ioremap = ofdrm_rage_m3a_cmap_ioremap, + .cmap_write = ofdrm_rage_m3a_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_rage_m3b_device_funcs = { + .cmap_ioremap = ofdrm_rage_m3b_cmap_ioremap, + .cmap_write = ofdrm_rage_m3b_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_radeon_device_funcs = { + .cmap_ioremap = ofdrm_radeon_cmap_ioremap, + .cmap_write = ofdrm_rage128_cmap_write, /* same as Rage128 */ }; static const struct ofdrm_device_funcs ofdrm_gxt2000_device_funcs = { + .cmap_ioremap = ofdrm_gxt2000_cmap_ioremap, + .cmap_write = ofdrm_gxt2000_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_avivo_device_funcs = { + .cmap_ioremap = ofdrm_avivo_cmap_ioremap, + .cmap_write = ofdrm_avivo_cmap_write, }; static const struct ofdrm_device_funcs ofdrm_qemu_device_funcs = { + .cmap_ioremap = ofdrm_qemu_cmap_ioremap, + .cmap_write = ofdrm_qemu_cmap_write, }; static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, @@ -716,6 +1132,17 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, return ERR_PTR(-ENOMEM); iosys_map_set_vaddr_iomem(&screen_base_vmap, screen_base); + if (odev->funcs->cmap_ioremap) { + void __iomem *cmap_base = odev->funcs->cmap_ioremap(odev, of_node, fb_base); + + if (IS_ERR(cmap_base)) { + /* Don't fail; continue without colormap */ + ret = PTR_ERR(cmap_base); + } else { + odev->cmap_base = cmap_base; + } + } + /* * Firmware framebuffer */ @@ -769,6 +1196,11 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, return ERR_PTR(ret); drm_crtc_helper_add(crtc, &ofdrm_crtc_helper_funcs); + if (odev->cmap_base) { + drm_mode_crtc_set_gamma_size(crtc, OFDRM_GAMMA_LUT_SIZE); + drm_crtc_enable_color_mgmt(crtc, 0, false, OFDRM_GAMMA_LUT_SIZE); + } + /* Encoder */ encoder = &odev->encoder;