From patchwork Fri Feb 4 13:43:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martinez Canillas X-Patchwork-Id: 12735083 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BA0D4C433EF for ; Fri, 4 Feb 2022 13:44:04 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 497CA10E729; Fri, 4 Feb 2022 13:44:00 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id 1394310E415 for ; Fri, 4 Feb 2022 13:43:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1643982237; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gsCZ0vamCvMedkmFYkO3ZNEjaPZGxukH+BWgq2BQ6do=; b=dkSruGhYgcdBaHymlx2kLQcWhVvwlB5BeWbvMn7a6GxsoXdSTj0oOXcHM2w5JsfJ8fD8Yj ZTisl7WXQx0ga0UZXXHAnUsU85Ic+cZEHX6M06mADZK58wFb93VlVPkj9GmqDzOSDVnLrH OL4DG1b1j/2MsREkCt02Bpb0dY+htbY= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-458-Z3d4KJmdMAWO2luRdsZhew-1; Fri, 04 Feb 2022 08:43:55 -0500 X-MC-Unique: Z3d4KJmdMAWO2luRdsZhew-1 Received: by mail-wr1-f71.google.com with SMTP id e2-20020adfa442000000b001e2dd248341so576075wra.20 for ; Fri, 04 Feb 2022 05:43:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gsCZ0vamCvMedkmFYkO3ZNEjaPZGxukH+BWgq2BQ6do=; b=dqjdnycYZwNMiBmXD7vJt6FzSXGbbkgWqL0y8nApQZGxp+/gaSTX+ANmRG7zvuwhHL jccucR2VxU38GYQ/HnTz2bjzzNB62HT+dIXr9pkdr26k61+Rpc6pHjC78XSjlFNffxgZ J66bPINV10bSgseUPZvOp+OXFxFs9LCDNz4g95diwpZJMnzK04Ip4P/LecdYWcoAC7pq Su0L8GZqCbuKMQC6QmrqA3Wb+21uAB7382YD0ZVpPqlXgDmgfOrUWcqdBcXvooO6eKiE GHQIoCGSEI0f6o97pjymi68poek3IJkHy9Y7cdDiRt7ev6gw8d4hS86E6R22j4D4/pAk 4maw== X-Gm-Message-State: AOAM533VrwHU5MkWfkoJZ7cUKl1dCJIs/TCZj+d0Kh8oknm3VVKRNrC/ 021xGIL3NvXLw0leGSUeCV5aXlyJuuvAnnA0WaetRRTp0Auf6D/1M+k+DbgzQgjjXv6wYRlMmvC TMIjks/J1jCrMxeWKiX0Yrc5CZaH6 X-Received: by 2002:a5d:64a9:: with SMTP id m9mr2454479wrp.661.1643982234670; Fri, 04 Feb 2022 05:43:54 -0800 (PST) X-Google-Smtp-Source: ABdhPJwlXexVQP8cfpmjMdMsb00GdG9twKe+Dfe6NJ6PFq21cUefB7/9Y6OdbGzVDbpRhtxvxM4R3Q== X-Received: by 2002:a5d:64a9:: with SMTP id m9mr2454469wrp.661.1643982234457; Fri, 04 Feb 2022 05:43:54 -0800 (PST) Received: from minerva.home ([92.176.231.205]) by smtp.gmail.com with ESMTPSA id r3sm1871692wrt.102.2022.02.04.05.43.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Feb 2022 05:43:54 -0800 (PST) From: Javier Martinez Canillas To: linux-kernel@vger.kernel.org Subject: [PATCH v2 1/4] drm/format-helper: Add drm_fb_{xrgb8888, gray8}_to_mono_reversed() Date: Fri, 4 Feb 2022 14:43:44 +0100 Message-Id: <20220204134347.1187749-2-javierm@redhat.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220204134347.1187749-1-javierm@redhat.com> References: <20220204134347.1187749-1-javierm@redhat.com> MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=javierm@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-fbdev@vger.kernel.org, David Airlie , Daniel Vetter , Javier Martinez Canillas , dri-devel@lists.freedesktop.org, =?utf-8?q?Noralf_Tr=C3=B8nnes?= , Geert Uytterhoeven , Maxime Ripard , Thomas Zimmermann , Andy Shevchenko , Sam Ravnborg Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Add support to convert XR24 and 8-bit grayscale to reversed monochrome for drivers that control monochromatic panels, that only have 1 bit per pixel. The drm_fb_gray8_to_mono_reversed() helper was based on the function that does the same in the drivers/gpu/drm/tiny/repaper.c driver. Signed-off-by: Javier Martinez Canillas --- (no changes since v1) drivers/gpu/drm/drm_format_helper.c | 80 +++++++++++++++++++++++++++++ include/drm/drm_format_helper.h | 7 +++ 2 files changed, 87 insertions(+) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 0f28dd2bdd72..cdce4b7c25d9 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -584,3 +584,83 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for return -EINVAL; } EXPORT_SYMBOL(drm_fb_blit_toio); + +static void drm_fb_gray8_to_mono_reversed_line(u8 *dst, const u8 *src, size_t pixels) +{ + unsigned int xb, i; + + for (xb = 0; xb < pixels / 8; xb++) { + u8 byte = 0x00; + + for (i = 0; i < 8; i++) { + int x = xb * 8 + i; + + byte >>= 1; + if (src[x] >> 7) + byte |= BIT(7); + } + *dst++ = byte; + } +} + +/** + * drm_fb_gray8_to_mono_reversed - Convert grayscale to reversed monochrome + * @dst: reversed monochrome destination buffer + * @dst_pitch: Number of bytes between two consecutive scanlines within dst + * @src: 8-bit grayscale source buffer + * @clip: Clip rectangle area to copy + * + * DRM doesn't have native monochrome or grayscale support. + * Such drivers can announce the commonly supported XR24 format to userspace + * and use drm_fb_xrgb8888_to_gray8() to convert to grayscale and then this + * helper function to convert to the native format. + */ +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_rect *clip) +{ + + size_t height = drm_rect_height(clip); + size_t width = drm_rect_width(clip); + unsigned int y; + const u8 *gray8 = src; + u8 *mono = dst; + + if (!dst_pitch) + dst_pitch = width; + + for (y = 0; y < height; y++) { + drm_fb_gray8_to_mono_reversed_line(mono, gray8, dst_pitch); + mono += (dst_pitch / 8); + gray8 += dst_pitch; + } +} + +/** + * drm_fb_xrgb8888_to_mono_reversed - Convert XRGB8888 to reversed monochrome + * @dst: reversed monochrome destination buffer + * @dst_pitch: Number of bytes between two consecutive scanlines within dst + * @src: XRGB8888 source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + * + * DRM doesn't have native monochrome support. + * Such drivers can announce the commonly supported XR24 format to userspace + * and use this function to convert to the native format. + * + * This function uses drm_fb_xrgb8888_to_gray8() to convert to grayscale and + * then the result is converted from grayscale to reversed monohrome. + */ +void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_framebuffer *fb, + const struct drm_rect *clip) +{ + if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) + return; + + if (!dst_pitch) + dst_pitch = drm_rect_width(clip); + + drm_fb_xrgb8888_to_gray8(dst, dst_pitch, src, fb, clip); + drm_fb_gray8_to_mono_reversed(dst, dst_pitch, dst, fb, clip); +} +EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed); diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index b30ed5de0a33..85e551a5cbe6 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -43,4 +43,11 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for const void *vmap, const struct drm_framebuffer *fb, const struct drm_rect *rect); +void drm_fb_gray8_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_rect *clip); + +void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const void *src, + const struct drm_framebuffer *fb, + const struct drm_rect *clip); + #endif /* __LINUX_DRM_FORMAT_HELPER_H */ From patchwork Fri Feb 4 13:43:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martinez Canillas X-Patchwork-Id: 12735085 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id AEADBC433EF for ; Fri, 4 Feb 2022 13:44:11 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CEAFF10E593; Fri, 4 Feb 2022 13:44:02 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id A451C10E593 for ; Fri, 4 Feb 2022 13:44:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1643982240; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UE7J0dAD/kdKfJYKCBRD1WsjGQaj92BnekxuYq/R3nQ=; b=Ge+VAEiIWmCE4fHNpxs9E/7OvLla2o7ENR5ysbNG3UobYdiVJ+hq69Mt71g0hB1ovplNX+ yipn2Xx5sj7+Nmc1F/LCV3akdG9Cty50Ot7Z1fMnthx8LT10I1nOFvW3kiAbAK9mlQv8Nw wLd6rx+WKZarwo/vct5VGgPUPzn2Dxo= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-232-EFnOHiRcOxikaGgKRuDSoA-1; Fri, 04 Feb 2022 08:43:58 -0500 X-MC-Unique: EFnOHiRcOxikaGgKRuDSoA-1 Received: by mail-wr1-f72.google.com with SMTP id q4-20020adfbb84000000b001dd3cfddb2dso2048718wrg.11 for ; Fri, 04 Feb 2022 05:43:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=UE7J0dAD/kdKfJYKCBRD1WsjGQaj92BnekxuYq/R3nQ=; b=OcyC4LojMuNpxupbG2nyU28FOrF+kyD9cKsqQOiFBKylCuhsq5ClfxQ8ehvRSILdsC B7RwEkFFvgKNAO3Z+S9V+6g9LVOYkLdEXi380oRJ1K969tSJTsNIBfwRcgX1CghiZO4b KF+lpyT3qDaMrd+33ngk+aipQ9TLCSLFMI1KQ0dBa59l3Gq1vUDCWO7/SEgxkQ8Qf9kk 4tjfiS5jsmB+1y+ik+MmpNi4tT3QZBpwGrONJEvTaVWA1ZBipdVoDbI81/D9CpDpaGpS Scuo5D5Sr2/trpLzqUFjZ6zOgV2vVt8ooMdT2Q6tSw5FPrLYrpabX3adQmYW2gJ8mNLm bvpQ== X-Gm-Message-State: AOAM533CcvT6aYnNy2ol0WTOtJPBXCR8KzaCIEUirGmo6NvBDpHWSytJ Z+VQA4MfAGDJ7gLHy9YsUwDr7fQ9AeGDW/Y7kh/LIakvCYzQ6iD5QSQ94xPDYNIq2CC0NboXd2n s9OBblPBFgc6LzwCpa3EHAIXIVwk5 X-Received: by 2002:a5d:5052:: with SMTP id h18mr2657476wrt.350.1643982237005; Fri, 04 Feb 2022 05:43:57 -0800 (PST) X-Google-Smtp-Source: ABdhPJySZg4Xnu8fPiXOV31ITc+Q3XhQx9CYcNGM8JQfvVrfZdkyaVqkHPNKzZo+9GLeC8ZWgJ70oA== X-Received: by 2002:a5d:5052:: with SMTP id h18mr2657447wrt.350.1643982236592; Fri, 04 Feb 2022 05:43:56 -0800 (PST) Received: from minerva.home ([92.176.231.205]) by smtp.gmail.com with ESMTPSA id r3sm1871692wrt.102.2022.02.04.05.43.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Feb 2022 05:43:56 -0800 (PST) From: Javier Martinez Canillas To: linux-kernel@vger.kernel.org Subject: [PATCH v2 2/4] drm/tiny: Add driver for Solomon SSD130X OLED displays Date: Fri, 4 Feb 2022 14:43:45 +0100 Message-Id: <20220204134347.1187749-3-javierm@redhat.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220204134347.1187749-1-javierm@redhat.com> References: <20220204134347.1187749-1-javierm@redhat.com> MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=javierm@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-pwm@vger.kernel.org, linux-fbdev@vger.kernel.org, David Airlie , Daniel Vetter , Mark Brown , Javier Martinez Canillas , dri-devel@lists.freedesktop.org, Liam Girdwood , =?utf-8?q?Noralf_Tr=C3=B8nnes?= , Geert Uytterhoeven , Maxime Ripard , Thomas Zimmermann , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Thierry Reding , Lee Jones , Andy Shevchenko , Sam Ravnborg Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Add a DRM driver for SSD1305, SSD1306, SSD1307 and SSD1309 Solomon OLED controllers that can be programmed via an I2C interface. This is a port of the ssd1307fb driver that already supports these devices. A Device Tree binding is not added because the DRM driver is compatible with the existing binding for the ssd1307fb driver. Signed-off-by: Javier Martinez Canillas --- (no changes since v1) drivers/gpu/drm/tiny/Kconfig | 12 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/ssd130x.c | 971 +++++++++++++++++++++++++++++++++ 3 files changed, 984 insertions(+) create mode 100644 drivers/gpu/drm/tiny/ssd130x.c diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 712e0004e96e..1b6f5aa41d69 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -67,6 +67,18 @@ config DRM_SIMPLEDRM On x86 BIOS or UEFI systems, you should also select SYSFB_SIMPLEFB to use UEFI and VESA framebuffers. +config DRM_SSD130X + tristate "DRM support for Solomon SSD130X OLED displays" + depends on DRM && I2C + select BACKLIGHT_CLASS_DEVICE + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + DRM driver for the SSD1305, SSD1306, SSD1307 and SSD1309 Solomon + OLED controllers that can be programmed via an I2C interface. + + If M is selected the module will be called ssd130x. + config TINYDRM_HX8357D tristate "DRM support for HX8357D display panels" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 5d5505d40e7b..18c3557dcb71 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs.o obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o obj-$(CONFIG_DRM_GM12U320) += gm12u320.o obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o +obj-$(CONFIG_DRM_SSD130X) += ssd130x.o obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o obj-$(CONFIG_TINYDRM_ILI9163) += ili9163.o obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o diff --git a/drivers/gpu/drm/tiny/ssd130x.c b/drivers/gpu/drm/tiny/ssd130x.c new file mode 100644 index 000000000000..b348768529dc --- /dev/null +++ b/drivers/gpu/drm/tiny/ssd130x.c @@ -0,0 +1,971 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for Solomon SSD130X OLED displays + * + * Copyright 2022 Red Hat Inc. + * + * Based on drivers/video/fbdev/ssd1307fb.c + * Copyright 2012 Free Electrons + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ssd130x" +#define DRIVER_DESC "DRM driver for Solomon SSD130X OLED displays" +#define DRIVER_DATE "20220131" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +#define SSD130X_DATA 0x40 +#define SSD130X_COMMAND 0x80 + +#define SSD130X_SET_ADDRESS_MODE 0x20 +#define SSD130X_SET_ADDRESS_MODE_HORIZONTAL (0x00) +#define SSD130X_SET_ADDRESS_MODE_VERTICAL (0x01) +#define SSD130X_SET_ADDRESS_MODE_PAGE (0x02) +#define SSD130X_SET_COL_RANGE 0x21 +#define SSD130X_SET_PAGE_RANGE 0x22 +#define SSD130X_CONTRAST 0x81 +#define SSD130X_SET_LOOKUP_TABLE 0x91 +#define SSD130X_CHARGE_PUMP 0x8d +#define SSD130X_SEG_REMAP_ON 0xa1 +#define SSD130X_DISPLAY_OFF 0xae +#define SSD130X_SET_MULTIPLEX_RATIO 0xa8 +#define SSD130X_DISPLAY_ON 0xaf +#define SSD130X_START_PAGE_ADDRESS 0xb0 +#define SSD130X_SET_DISPLAY_OFFSET 0xd3 +#define SSD130X_SET_CLOCK_FREQ 0xd5 +#define SSD130X_SET_AREA_COLOR_MODE 0xd8 +#define SSD130X_SET_PRECHARGE_PERIOD 0xd9 +#define SSD130X_SET_COM_PINS_CONFIG 0xda +#define SSD130X_SET_VCOMH 0xdb + +#define MAX_CONTRAST 255 + +struct ssd130x_deviceinfo { + u32 default_vcomh; + u32 default_dclk_div; + u32 default_dclk_frq; + int need_pwm; + int need_chargepump; +}; + +struct ssd130x_device { + struct drm_device drm; + struct drm_simple_display_pipe pipe; + struct drm_display_mode mode; + struct drm_connector connector; + struct i2c_client *client; + + const struct ssd130x_deviceinfo *device_info; + + unsigned area_color_enable : 1; + unsigned com_invdir : 1; + unsigned com_lrremap : 1; + unsigned com_seq : 1; + unsigned lookup_table_set : 1; + unsigned low_power : 1; + unsigned seg_remap : 1; + u32 com_offset; + u32 contrast; + u32 dclk_div; + u32 dclk_frq; + u32 height; + u8 lookup_table[4]; + u32 page_offset; + u32 col_offset; + u32 prechargep1; + u32 prechargep2; + + struct backlight_device *bl_dev; + struct pwm_device *pwm; + struct gpio_desc *reset; + struct regulator *vbat_reg; + u32 vcomh; + u32 width; + /* Cached address ranges */ + u8 col_start; + u8 col_end; + u8 page_start; + u8 page_end; +}; + +struct ssd130x_i2c_msg { + u8 cmd; + u8 data[]; +}; + +static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) +{ + return container_of(drm, struct ssd130x_device, drm); +} + +static struct ssd130x_i2c_msg *ssd130x_alloc_msg(u32 len, u8 cmd) +{ + struct ssd130x_i2c_msg *msg; + + msg = kzalloc(struct_size(msg, data, len), GFP_KERNEL); + if (!msg) + return NULL; + + msg->cmd = cmd; + + return msg; +} + +static int ssd130x_write_msg(struct i2c_client *client, + struct ssd130x_i2c_msg *msg, u32 len) +{ + int ret; + + len += sizeof(*msg); + + ret = i2c_master_send(client, (u8 *)msg, len); + if (ret != len) { + dev_err(&client->dev, "Couldn't send I2C command.\n"); + return ret; + } + + return 0; +} + +static inline int ssd130x_write_value(struct i2c_client *client, u8 value) +{ + struct ssd130x_i2c_msg *msg; + int ret; + + msg = ssd130x_alloc_msg(1, SSD130X_COMMAND); + if (!msg) + return -ENOMEM; + + msg->data[0] = value; + + ret = ssd130x_write_msg(client, msg, 1); + kfree(msg); + + return ret; +} + +static inline int ssd130x_write_cmd(struct i2c_client *client, int count, + /* u8 cmd, u8 value, ... */...) +{ + va_list ap; + u8 value; + int ret; + + va_start(ap, count); + + while (count--) { + value = va_arg(ap, int); + ret = ssd130x_write_value(client, (u8)value); + if (ret) + goto out_end; + } + +out_end: + va_end(ap); + + return ret; +} + +static int ssd130x_set_col_range(struct ssd130x_device *ssd130x, + u8 col_start, u8 cols) +{ + u8 col_end = col_start + cols - 1; + int ret; + + if (col_start == ssd130x->col_start && col_end == ssd130x->col_end) + return 0; + + ret = ssd130x_write_cmd(ssd130x->client, 3, SSD130X_SET_COL_RANGE, + col_start, col_end); + if (ret < 0) + return ret; + + ssd130x->col_start = col_start; + ssd130x->col_end = col_end; + return 0; +} + +static int ssd130x_set_page_range(struct ssd130x_device *ssd130x, + u8 page_start, u8 pages) +{ + u8 page_end = page_start + pages - 1; + int ret; + + if (page_start == ssd130x->page_start && page_end == ssd130x->page_end) + return 0; + + ret = ssd130x_write_cmd(ssd130x->client, 3, SSD130X_SET_PAGE_RANGE, + page_start, page_end); + if (ret < 0) + return ret; + + ssd130x->page_start = page_start; + ssd130x->page_end = page_end; + return 0; +} + +static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x) +{ + struct device *dev = &ssd130x->client->dev; + struct pwm_state pwmstate; + + ssd130x->pwm = pwm_get(dev, NULL); + if (IS_ERR(ssd130x->pwm)) { + dev_err(dev, "Could not get PWM from device tree!\n"); + return PTR_ERR(ssd130x->pwm); + } + + pwm_init_state(ssd130x->pwm, &pwmstate); + pwm_set_relative_duty_cycle(&pwmstate, 50, 100); + pwm_apply_state(ssd130x->pwm, &pwmstate); + + /* Enable the PWM */ + pwm_enable(ssd130x->pwm); + + dev_dbg(dev, "Using PWM%d with a %lluns period.\n", + ssd130x->pwm->pwm, pwm_get_period(ssd130x->pwm)); + + return 0; +} + +static void ssd130x_reset(struct ssd130x_device *ssd130x) +{ + /* Reset the screen */ + gpiod_set_value_cansleep(ssd130x->reset, 1); + udelay(4); + gpiod_set_value_cansleep(ssd130x->reset, 0); + udelay(4); +} + +static int ssd130x_poweron(struct ssd130x_device *ssd130x) +{ + struct device *dev = &ssd130x->client->dev; + int ret; + + if (ssd130x->reset) + ssd130x_reset(ssd130x); + + if (ssd130x->vbat_reg) { + ret = regulator_enable(ssd130x->vbat_reg); + if (ret) { + dev_err(dev, "Failed to enable VBAT: %d\n", ret); + return ret; + } + } + + if (ssd130x->device_info->need_pwm) { + ret = ssd130x_pwm_enable(ssd130x); + if (ret) { + dev_err(dev, "Failed to enable PWM: %d\n", ret); + if (ssd130x->vbat_reg) + regulator_disable(ssd130x->vbat_reg); + return ret; + } + } + + return 0; +} + +static void ssd130x_poweroff(struct ssd130x_device *ssd130x) +{ + if (ssd130x->device_info->need_pwm) { + pwm_disable(ssd130x->pwm); + pwm_put(ssd130x->pwm); + } + + if (ssd130x->vbat_reg) + regulator_disable(ssd130x->vbat_reg); +} + +static int ssd130x_init(struct ssd130x_device *ssd130x) +{ + u32 precharge, dclk, com_invdir, compins, chargepump; + int ret; + + /* Set initial contrast */ + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_CONTRAST, ssd130x->contrast); + if (ret < 0) + return ret; + + /* Set segment re-map */ + if (ssd130x->seg_remap) { + ret = ssd130x_write_cmd(ssd130x->client, 1, SSD130X_SEG_REMAP_ON); + if (ret < 0) + return ret; + } + + /* Set COM direction */ + com_invdir = 0xc0 | ssd130x->com_invdir << 3; + ret = ssd130x_write_cmd(ssd130x->client, 1, com_invdir); + if (ret < 0) + return ret; + + /* Set multiplex ratio value */ + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_MULTIPLEX_RATIO, + ssd130x->height - 1); + if (ret < 0) + return ret; + + /* set display offset value */ + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_DISPLAY_OFFSET, + ssd130x->com_offset); + if (ret < 0) + return ret; + + /* Set clock frequency */ + dclk = ((ssd130x->dclk_div - 1) & 0xf) | (ssd130x->dclk_frq & 0xf) << 4; + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_CLOCK_FREQ, dclk); + if (ret < 0) + return ret; + + /* Set Area Color Mode ON/OFF & Low Power Display Mode */ + if (ssd130x->area_color_enable || ssd130x->low_power) { + u32 mode = ((ssd130x->area_color_enable ? 0x30 : 0) | + (ssd130x->low_power ? 5 : 0)); + + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_AREA_COLOR_MODE, mode); + if (ret < 0) + return ret; + } + + /* Set precharge period in number of ticks from the internal clock */ + precharge = (ssd130x->prechargep1 & 0xf) | (ssd130x->prechargep2 & 0xf) << 4; + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_PRECHARGE_PERIOD, precharge); + if (ret < 0) + return ret; + + /* Set COM pins configuration */ + compins = 0x02 | !ssd130x->com_seq << 4 | ssd130x->com_lrremap << 5; + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_COM_PINS_CONFIG, compins); + if (ret < 0) + return ret; + + + /* Set VCOMH */ + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_VCOMH, ssd130x->vcomh); + if (ret < 0) + return ret; + + /* Turn on the DC-DC Charge Pump */ + chargepump = BIT(4) | (ssd130x->device_info->need_chargepump ? BIT(2) : 0); + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_CHARGE_PUMP, chargepump); + if (ret < 0) + return ret; + + /* Set lookup table */ + if (ssd130x->lookup_table_set) { + int i; + + ret = ssd130x_write_cmd(ssd130x->client, 1, SSD130X_SET_LOOKUP_TABLE); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(ssd130x->lookup_table); ++i) { + u8 val = ssd130x->lookup_table[i]; + + if (val < 31 || val > 63) + dev_warn(&ssd130x->client->dev, + "lookup table index %d value out of range 31 <= %d <= 63\n", + i, val); + ret = ssd130x_write_cmd(ssd130x->client, 1, val); + if (ret < 0) + return ret; + } + } + + /* Switch to horizontal addressing mode */ + ret = ssd130x_write_cmd(ssd130x->client, 2, SSD130X_SET_ADDRESS_MODE, + SSD130X_SET_ADDRESS_MODE_HORIZONTAL); + if (ret < 0) + return ret; + + return 0; +} + +static int ssd130x_update_display(struct ssd130x_device *ssd130x, u8 *buf, + u32 width, u32 height) +{ + struct ssd130x_i2c_msg *msg; + unsigned int line_length = DIV_ROUND_UP(width, 8); + unsigned int pages = DIV_ROUND_UP(height, 8); + u32 array_idx = 0; + int ret, i, j, k; + + msg = ssd130x_alloc_msg(width * pages, SSD130X_DATA); + if (!msg) + return -ENOMEM; + + /* + * The screen is divided in pages, each having a height of 8 + * pixels, and the width of the screen. When sending a byte of + * data to the controller, it gives the 8 bits for the current + * column. I.e, the first byte are the 8 bits of the first + * column, then the 8 bits for the second column, etc. + * + * + * Representation of the screen, assuming it is 5 bits + * wide. Each letter-number combination is a bit that controls + * one pixel. + * + * A0 A1 A2 A3 A4 + * B0 B1 B2 B3 B4 + * C0 C1 C2 C3 C4 + * D0 D1 D2 D3 D4 + * E0 E1 E2 E3 E4 + * F0 F1 F2 F3 F4 + * G0 G1 G2 G3 G4 + * H0 H1 H2 H3 H4 + * + * If you want to update this screen, you need to send 5 bytes: + * (1) A0 B0 C0 D0 E0 F0 G0 H0 + * (2) A1 B1 C1 D1 E1 F1 G1 H1 + * (3) A2 B2 C2 D2 E2 F2 G2 H2 + * (4) A3 B3 C3 D3 E3 F3 G3 H3 + * (5) A4 B4 C4 D4 E4 F4 G4 H4 + */ + + ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset, width); + if (ret < 0) + goto out_free; + + ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset / 8, pages); + if (ret < 0) + goto out_free; + + for (i = 0; i < pages; i++) { + int m = 8; + + /* Last page may be partial */ + if (8 * (i + 1) > ssd130x->height) + m = ssd130x->height % 8; + for (j = 0; j < width; j++) { + u8 data = 0; + + for (k = 0; k < m; k++) { + u8 byte = buf[(8 * i + k) * line_length + + j / 8]; + u8 bit = (byte >> (j % 8)) & 1; + + data |= bit << k; + } + msg->data[array_idx++] = data; + } + } + + ret = ssd130x_write_msg(ssd130x->client, msg, width * pages); + +out_free: + kfree(msg); + return ret; +} + +static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u32 width, u32 height) +{ + u8 *buf = NULL; + + buf = kcalloc(width, height, GFP_KERNEL); + if (!buf) + return; + + ssd130x_update_display(ssd130x, buf, width, height); + + kfree(buf); +} + +static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf_map *map, + struct drm_rect *rect) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); + void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ + int ret = 0; + u8 *buf = NULL; + + buf = kcalloc(fb->width, fb->height, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + drm_fb_xrgb8888_to_mono_reversed(buf, 0, vmap, fb, rect); + + ssd130x_update_display(ssd130x, buf, fb->width, fb->height); + + kfree(buf); + + return ret; +} + +static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, + const struct drm_display_mode *mode) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); + + if (mode->hdisplay != ssd130x->mode.hdisplay && + mode->vdisplay != ssd130x->mode.vdisplay) + return MODE_ONE_SIZE; + else if (mode->hdisplay != ssd130x->mode.hdisplay) + return MODE_ONE_WIDTH; + else if (mode->vdisplay != ssd130x->mode.vdisplay) + return MODE_ONE_HEIGHT; + + return MODE_OK; +} + +static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); + struct drm_device *drm = &ssd130x->drm; + int idx, ret; + + ret = ssd130x_poweron(ssd130x); + if (ret) + return; + + ret = ssd130x_init(ssd130x); + if (ret) + goto poweroff; + + if (!drm_dev_enter(drm, &idx)) + goto poweroff; + + ssd130x_clear_screen(ssd130x, ssd130x->width, ssd130x->height); + + ssd130x_write_cmd(ssd130x->client, 1, SSD130X_DISPLAY_ON); + + backlight_enable(ssd130x->bl_dev); + + drm_dev_exit(idx); + + return; +poweroff: + ssd130x_poweroff(ssd130x); +} + +static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); + struct drm_device *drm = &ssd130x->drm; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + + ssd130x_clear_screen(ssd130x, ssd130x->width, ssd130x->height); + + backlight_disable(ssd130x->bl_dev); + + ssd130x_write_cmd(ssd130x->client, 1, SSD130X_DISPLAY_OFF); + + ssd130x_poweroff(ssd130x); + + drm_dev_exit(idx); +} + +static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_plane_state) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); + struct drm_plane_state *plane_state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_device *drm = &ssd130x->drm; + struct drm_rect src_clip, dst_clip; + int idx; + + if (!fb) + return; + + if (!pipe->crtc.state->active) + 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(drm, &idx)) + return; + + ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip); + + drm_dev_exit(idx); +} + +static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = { + .mode_valid = ssd130x_display_pipe_mode_valid, + .enable = ssd130x_display_pipe_enable, + .disable = ssd130x_display_pipe_disable, + .update = ssd130x_display_pipe_update, + DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, +}; + +static int ssd130x_connector_get_modes(struct drm_connector *connector) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev); + struct drm_display_mode *mode = &ssd130x->mode; + struct device *dev = &ssd130x->client->dev; + + mode = drm_mode_duplicate(connector->dev, &ssd130x->mode); + if (!mode) { + dev_err(dev, "Failed to duplicated mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay); + + return 1; +} + +static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = { + .get_modes = ssd130x_connector_get_modes, +}; + +static const struct drm_connector_funcs ssd130x_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const uint32_t ssd130x_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +DEFINE_DRM_GEM_FOPS(ssd130x_fops); + +static const struct drm_driver ssd130x_drm_driver = { + DRM_GEM_SHMEM_DRIVER_OPS, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops = &ssd130x_fops, +}; + +static int ssd130x_update_bl(struct backlight_device *bdev) +{ + struct ssd130x_device *ssd130x = bl_get_data(bdev); + int brightness = backlight_get_brightness(bdev); + int ret; + + ssd130x->contrast = brightness; + + ret = ssd130x_write_cmd(ssd130x->client, 1, SSD130X_CONTRAST); + if (ret < 0) + return ret; + + ret = ssd130x_write_cmd(ssd130x->client, 1, ssd130x->contrast); + if (ret < 0) + return ret; + + return 0; +} + +static const struct backlight_ops ssd130xfb_bl_ops = { + .update_status = ssd130x_update_bl, +}; + +static void ssd130x_parse_properties(struct ssd130x_device *ssd130x) +{ + struct device *dev = &ssd130x->client->dev; + + if (device_property_read_u32(dev, "solomon,width", &ssd130x->width)) + ssd130x->width = 96; + + if (device_property_read_u32(dev, "solomon,height", &ssd130x->height)) + ssd130x->height = 16; + + if (device_property_read_u32(dev, "solomon,page-offset", &ssd130x->page_offset)) + ssd130x->page_offset = 1; + + if (device_property_read_u32(dev, "solomon,col-offset", &ssd130x->col_offset)) + ssd130x->col_offset = 0; + + if (device_property_read_u32(dev, "solomon,com-offset", &ssd130x->com_offset)) + ssd130x->com_offset = 0; + + if (device_property_read_u32(dev, "solomon,prechargep1", &ssd130x->prechargep1)) + ssd130x->prechargep1 = 2; + + if (device_property_read_u32(dev, "solomon,prechargep2", &ssd130x->prechargep2)) + ssd130x->prechargep2 = 2; + + if (!device_property_read_u8_array(dev, "solomon,lookup-table", + ssd130x->lookup_table, + ARRAY_SIZE(ssd130x->lookup_table))) + ssd130x->lookup_table_set = 1; + + ssd130x->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap"); + ssd130x->com_seq = device_property_read_bool(dev, "solomon,com-seq"); + ssd130x->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap"); + ssd130x->com_invdir = device_property_read_bool(dev, "solomon,com-invdir"); + ssd130x->area_color_enable = + device_property_read_bool(dev, "solomon,area-color-enable"); + ssd130x->low_power = device_property_read_bool(dev, "solomon,low-power"); + + ssd130x->contrast = 127; + ssd130x->vcomh = ssd130x->device_info->default_vcomh; + + /* Setup display timing */ + if (device_property_read_u32(dev, "solomon,dclk-div", &ssd130x->dclk_div)) + ssd130x->dclk_div = ssd130x->device_info->default_dclk_div; + if (device_property_read_u32(dev, "solomon,dclk-frq", &ssd130x->dclk_frq)) + ssd130x->dclk_frq = ssd130x->device_info->default_dclk_frq; +} + +static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) +{ + struct drm_display_mode *mode = &ssd130x->mode; + struct device *dev = &ssd130x->client->dev; + struct drm_device *drm = &ssd130x->drm; + unsigned long max_width, max_height; + int ret; + + ret = drmm_mode_config_init(drm); + if (ret) { + dev_err(dev, "DRM mode config init failed: %d\n", ret); + return ret; + } + + mode->type = DRM_MODE_TYPE_DRIVER; + mode->clock = 1; + mode->hdisplay = mode->htotal = ssd130x->width; + mode->hsync_start = mode->hsync_end = ssd130x->width; + mode->vdisplay = mode->vtotal = ssd130x->height; + mode->vsync_start = mode->vsync_end = ssd130x->height; + mode->width_mm = 27; + mode->height_mm = 27; + + max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH); + max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT); + + drm->mode_config.min_width = mode->hdisplay; + drm->mode_config.max_width = max_width; + drm->mode_config.min_height = mode->vdisplay; + drm->mode_config.max_height = max_height; + drm->mode_config.preferred_depth = 32; + drm->mode_config.funcs = &ssd130x_mode_config_funcs; + + ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) { + dev_err(dev, "DRM connector init failed: %d\n", ret); + return ret; + } + + drm_connector_helper_add(&ssd130x->connector, &ssd130x_connector_helper_funcs); + + ret = drm_simple_display_pipe_init(drm, &ssd130x->pipe, &ssd130x_pipe_funcs, + ssd130x_formats, ARRAY_SIZE(ssd130x_formats), + NULL, &ssd130x->connector); + if (ret) { + dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret); + return ret; + } + + drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane); + + drm_mode_config_reset(drm); + + return 0; +} + +static int ssd130x_get_resources(struct ssd130x_device *ssd130x) +{ + struct device *dev = &ssd130x->client->dev; + int ret; + + ssd130x->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ssd130x->reset)) { + ret = PTR_ERR(ssd130x->reset); + dev_err(dev, "Failed to get reset gpio: %d\n", ret); + return ret; + } + + ssd130x->vbat_reg = devm_regulator_get_optional(dev, "vbat"); + if (IS_ERR(ssd130x->vbat_reg)) { + ret = PTR_ERR(ssd130x->vbat_reg); + if (ret == -ENODEV) { + ssd130x->vbat_reg = NULL; + } else { + dev_err(dev, "Failed to get VBAT regulator: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int ssd130x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ssd130x_device *ssd130x; + struct backlight_device *bl; + struct drm_device *drm; + int ret; + + ssd130x = devm_drm_dev_alloc(dev, &ssd130x_drm_driver, + struct ssd130x_device, drm); + if (IS_ERR(ssd130x)) { + ret = PTR_ERR(ssd130x); + dev_err(dev, "Failed to allocate DRM device: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, ssd130x); + + drm = &ssd130x->drm; + + ssd130x->client = client; + + ssd130x->device_info = device_get_match_data(dev); + + ssd130x_parse_properties(ssd130x); + + ret = ssd130x_get_resources(ssd130x); + if (ret) + return ret; + + bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x, + &ssd130xfb_bl_ops, NULL); + if (IS_ERR(bl)) { + ret = PTR_ERR(bl); + dev_err(dev, "Unable to register backlight device: %d\n", ret); + return ret; + } + + bl->props.brightness = ssd130x->contrast; + bl->props.max_brightness = MAX_CONTRAST; + ssd130x->bl_dev = bl; + + ret = ssd130x_init_modeset(ssd130x); + if (ret) + return ret; + + ret = drm_dev_register(drm, 0); + if (ret) { + dev_err(dev, "DRM device register failed: %d\n", ret); + return ret; + } + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} + +static int ssd130x_remove(struct i2c_client *client) +{ + struct ssd130x_device *ssd130x = i2c_get_clientdata(client); + + drm_dev_unplug(&ssd130x->drm); + + return 0; +} + +static void ssd130x_shutdown(struct i2c_client *client) +{ + struct ssd130x_device *ssd130x = i2c_get_clientdata(client); + + drm_atomic_helper_shutdown(&ssd130x->drm); +} + +static struct ssd130x_deviceinfo ssd130x_ssd1305_deviceinfo = { + .default_vcomh = 0x34, + .default_dclk_div = 1, + .default_dclk_frq = 7, +}; + +static struct ssd130x_deviceinfo ssd130x_ssd1306_deviceinfo = { + .default_vcomh = 0x20, + .default_dclk_div = 1, + .default_dclk_frq = 8, + .need_chargepump = 1, +}; + +static struct ssd130x_deviceinfo ssd130x_ssd1307_deviceinfo = { + .default_vcomh = 0x20, + .default_dclk_div = 2, + .default_dclk_frq = 12, + .need_pwm = 1, +}; + +static struct ssd130x_deviceinfo ssd130x_ssd1309_deviceinfo = { + .default_vcomh = 0x34, + .default_dclk_div = 1, + .default_dclk_frq = 10, +}; + +static const struct of_device_id ssd130x_of_match[] = { + { + .compatible = "solomon,ssd1305fb-i2c", + .data = (void *)&ssd130x_ssd1305_deviceinfo, + }, + { + .compatible = "solomon,ssd1306fb-i2c", + .data = (void *)&ssd130x_ssd1306_deviceinfo, + }, + { + .compatible = "solomon,ssd1307fb-i2c", + .data = (void *)&ssd130x_ssd1307_deviceinfo, + }, + { + .compatible = "solomon,ssd1309fb-i2c", + .data = (void *)&ssd130x_ssd1309_deviceinfo, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ssd130x_of_match); + + +static struct i2c_driver ssd130x_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = ssd130x_of_match, + }, + .probe_new = ssd130x_probe, + .remove = ssd130x_remove, + .shutdown = ssd130x_shutdown, +}; + +module_i2c_driver(ssd130x_i2c_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Javier Martinez Canillas "); +MODULE_LICENSE("GPL v2"); From patchwork Fri Feb 4 13:43:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martinez Canillas X-Patchwork-Id: 12735084 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 906F3C433F5 for ; Fri, 4 Feb 2022 13:44:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5E56A10E72D; Fri, 4 Feb 2022 13:44:02 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id C362510E72D for ; Fri, 4 Feb 2022 13:44:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1643982240; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=X1G5ufPFCDyg3xao5Sw4eo8sGKGFiB4HM9GwjLkAtDY=; b=gPpUDvlRfrqKEwgs1pocLtwg1DFuRlSHvvcXYhMW5wChfUNU7906x3YC8L/7d3NzqmFHur lGNzsW2Q8RPyZKfGOJv2PgNUH0ItY3nNwc3dnf06lMPwusKnpw3L8RZ6pvhAaUdc8cYUKv UbA2nr/eQWIMBhf9h3D6sjVdvafgCTA= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-624--wi1YP0VPkCsUXU3xiSX8A-1; Fri, 04 Feb 2022 08:43:59 -0500 X-MC-Unique: -wi1YP0VPkCsUXU3xiSX8A-1 Received: by mail-wr1-f71.google.com with SMTP id v28-20020adfa1dc000000b001dd1cb24081so2035267wrv.10 for ; Fri, 04 Feb 2022 05:43:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=X1G5ufPFCDyg3xao5Sw4eo8sGKGFiB4HM9GwjLkAtDY=; b=qJQRPLFh4KVfNSc2/ARIeF30BKNT5cHWMvQ7ebpdAY//zeTYMlApZBZQO6tIIFk2Kt 5D5kTf0o01i2WYi/NJq0Oo6ArldeVF9E+kLtt4+J8DpSxfMBafN+J7k4RfrGJVOO6SHs 8bTA4EI1Tb4sYDlMoRkIEPnL/yAkcoQKDxKeo3+8E2qnmvCHnrhY3MNwDF+ngK/lkNA+ h/nyAWPxJ2pX0rSnkR74puugtA/Bc5AMQBYFmatCNUqAN9ltlqmV1cQfijEDJCS0HOyi //xd+QRzFnzu05FIn6nDmSYtSsR9Q5AnxKlAtOZ2/q/nEAL/yWu3op+wS9smoZjb9aKy VJZA== X-Gm-Message-State: AOAM530WQcBvvsee4Oiz/XSnu2gkzkx/ACTu42R8CGrri7KL9WGz4z7c Bdn1pUzTTV4flJRJssPUkpmPzFQO0TGl2WXlEdVQ4x2dMy3hbYBNm83lpsN+VSo+OsvtT2zH+XJ xTZz+mnLepPUgOv6FDx3PrSN5bSPR X-Received: by 2002:adf:e54e:: with SMTP id z14mr2605811wrm.490.1643982238275; Fri, 04 Feb 2022 05:43:58 -0800 (PST) X-Google-Smtp-Source: ABdhPJw+QSl2du9O4I/8+CrVvIgvDAYN5ik3pP+2eWE6GqWbaie7alsXVW5weyc4o2n22Zxaig+5Rg== X-Received: by 2002:adf:e54e:: with SMTP id z14mr2605798wrm.490.1643982238073; Fri, 04 Feb 2022 05:43:58 -0800 (PST) Received: from minerva.home ([92.176.231.205]) by smtp.gmail.com with ESMTPSA id r3sm1871692wrt.102.2022.02.04.05.43.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Feb 2022 05:43:57 -0800 (PST) From: Javier Martinez Canillas To: linux-kernel@vger.kernel.org Subject: [PATCH v2 3/4] MAINTAINERS: Add entry for Solomon SSD130X OLED displays DRM driver Date: Fri, 4 Feb 2022 14:43:46 +0100 Message-Id: <20220204134347.1187749-4-javierm@redhat.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220204134347.1187749-1-javierm@redhat.com> References: <20220204134347.1187749-1-javierm@redhat.com> MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=javierm@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-fbdev@vger.kernel.org, David Airlie , Daniel Vetter , Javier Martinez Canillas , dri-devel@lists.freedesktop.org, =?utf-8?q?Noralf_Tr=C3=B8nnes?= , Geert Uytterhoeven , Maxime Ripard , Thomas Zimmermann , Andy Shevchenko , Sam Ravnborg Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To make sure that tools like the get_maintainer.pl script will suggest to Cc me if patches are posted for this driver. Also include the Device Tree binding for the old ssd1307fb fbdev driver since the new DRM driver was made compatible with the existing binding. Signed-off-by: Javier Martinez Canillas Acked-by: Sam Ravnborg --- (no changes since v1) MAINTAINERS | 7 +++++++ drivers/gpu/drm/drm_format_helper.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index d03ad8da1f36..9061488a4113 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6102,6 +6102,13 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/repaper.txt F: drivers/gpu/drm/tiny/repaper.c +DRM DRIVER FOR SOLOMON SSD130X OLED DISPLAYS +M: Javier Martinez Canillas +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml +F: drivers/gpu/drm/tiny/ssd130x.c + DRM DRIVER FOR QEMU'S CIRRUS DEVICE M: Dave Airlie M: Gerd Hoffmann diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index cdce4b7c25d9..c3c1372fb771 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -661,6 +661,6 @@ void drm_fb_xrgb8888_to_mono_reversed(void *dst, unsigned int dst_pitch, const v dst_pitch = drm_rect_width(clip); drm_fb_xrgb8888_to_gray8(dst, dst_pitch, src, fb, clip); - drm_fb_gray8_to_mono_reversed(dst, dst_pitch, dst, fb, clip); + drm_fb_gray8_to_mono_reversed(dst, dst_pitch, dst, clip); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono_reversed); From patchwork Fri Feb 4 13:43:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Javier Martinez Canillas X-Patchwork-Id: 12735086 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 872B7C433F5 for ; Fri, 4 Feb 2022 13:44:13 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 2C84F10F843; Fri, 4 Feb 2022 13:44:06 +0000 (UTC) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by gabe.freedesktop.org (Postfix) with ESMTPS id EA41E10E788 for ; Fri, 4 Feb 2022 13:44:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1643982242; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FGn+B4gE3kkARtCZwbp34WsdN+cmoVNQCk7A1PY1HEE=; b=CR8TQmSBECelYH4OH0UfI+9FGPbdgVTAaC/Dloro0zaUewwHltcHBukeHYYcOQZh8cFRC9 Ty0CNBo5JEO/SiEu5vOHlEwtOoxeLfd3e5o9vyFq+i2VYDh66iWwHPafkHmaVVkLTss9Zy EKP1NvFZZHeKfCBH69H1kqkVOKZ65vU= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-230-kXUnkFENPIqvlRRHN6Qbag-1; Fri, 04 Feb 2022 08:44:01 -0500 X-MC-Unique: kXUnkFENPIqvlRRHN6Qbag-1 Received: by mail-wr1-f72.google.com with SMTP id g6-20020adfbc86000000b001a2d62be244so2024525wrh.23 for ; Fri, 04 Feb 2022 05:44:00 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FGn+B4gE3kkARtCZwbp34WsdN+cmoVNQCk7A1PY1HEE=; b=s4LdjqmPRiRXopNtuMX7A/4jHrXlwg/sK1QtRp0QU4rtGZz/2z7S3HmBgZR4eJRtOY ljrjJISoUISHX9lWBXwQfTgyvfaDPSjgygKVe1MPPHdTmBho/GvhCy21Xd0pjOlBteAd IfxPHY8PN5BapcZe20HwCBEFo100APjDbL1Fz6ZTNJH1GUU/sZmSLRtbnhB5JZNECeKf SauH7WkH+REfMGZVrfrBHW1zQoFKWLsmzBgfAgn2NJh7S+rCrZ5cqJa72ADuZOq+V0ml Lmq03BUpXjGsV19+RG+RgNtHexWFbMvO6gGgssncHlAx7gEiqMTt1IIX4ZAcCMcLltgq QKqA== X-Gm-Message-State: AOAM532b/UeD+8nID9EOfUqj48+igFLjJXDJe0SjO9DmgkeVrEBLi8Ql 9AGmMJb/pOvOhW3E+Px9TbpbYOfKe3n752sbrWoXZ4nXtbrKK2be6tsgrCmH5DlOf3OCfq6OR+y lDqYjyX7m1X4PcGxRtM/VGtlhNDV8 X-Received: by 2002:adf:db4d:: with SMTP id f13mr2526497wrj.329.1643982239875; Fri, 04 Feb 2022 05:43:59 -0800 (PST) X-Google-Smtp-Source: ABdhPJxIH15SYdGMCJPbF9406dm+rwJsqjp0GPK+4c7vxU052qmuPNJJZFqWRduDtlttxDXm3iBi5w== X-Received: by 2002:adf:db4d:: with SMTP id f13mr2526479wrj.329.1643982239695; Fri, 04 Feb 2022 05:43:59 -0800 (PST) Received: from minerva.home ([92.176.231.205]) by smtp.gmail.com with ESMTPSA id r3sm1871692wrt.102.2022.02.04.05.43.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Feb 2022 05:43:59 -0800 (PST) From: Javier Martinez Canillas To: linux-kernel@vger.kernel.org Subject: [PATCH v2 4/4] dt-bindings: display: ssd1307fb: Add myself as binding co-maintainer Date: Fri, 4 Feb 2022 14:43:47 +0100 Message-Id: <20220204134347.1187749-5-javierm@redhat.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220204134347.1187749-1-javierm@redhat.com> References: <20220204134347.1187749-1-javierm@redhat.com> MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=javierm@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-fbdev@vger.kernel.org, David Airlie , Daniel Vetter , Javier Martinez Canillas , dri-devel@lists.freedesktop.org, Rob Herring , =?utf-8?q?Noralf_Tr=C3=B8nnes?= , Geert Uytterhoeven , Maxime Ripard , Thomas Zimmermann , Andy Shevchenko , Sam Ravnborg Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" The ssd130x DRM driver also makes use of this Device Tree binding to allow existing users of the fbdev driver to migrate without the need to change their Device Trees. Add myself as another maintainer of the binding, to make sure that I will be on Cc when patches are proposed for it. Suggested-by: Sam Ravnborg Signed-off-by: Javier Martinez Canillas Acked-by: Rob Herring --- (no changes since v1) Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml index 2ed2a7d0ca2f..9baafd0c42dd 100644 --- a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml +++ b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml @@ -8,6 +8,7 @@ title: Solomon SSD1307 OLED Controller Framebuffer maintainers: - Maxime Ripard + - Javier Martinez Canillas properties: compatible: