From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.160.169 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793307527; b=iER3zovfWWtRyPIguwEMCaFpT6VAhsFb6xLJPFRy9c3c2hvh8eDvS5ax4C6NwwPEEcNV zpKTtaLvyut/MAi2pZf6kOPaKHJ4BAEFlg8hV22jHHYTHmGBuxq31YyDV2v3HMN+0mPgu T40OMka3YuUDcgcTlejKEtSx0ggze5+Lg52MxLyx+aC49oKKJ0SA13UxCkwn8ZLdViAH2 TeoXAsV2YYBqtlgv6KZhKTOYNhqnDWrnD74BSLnFxj4Jjh1scAyY+uBwVCGDua4SlzLX/ 7st2I31MeeLSKO1thr/IlMAWZe4BQwCbs8XnFb0lIZTmCPGIwrPu3ulLU08QkD14/MA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793307527; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:To:Cc:Subject:Date:Message-ID:X-Mailer:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding; bh=v0nueKfM1lSAPftYVoSLUkDbPpn2D3P/t5rVcRmFMj8=; b=25q8OQfYILhga+7PxgifZJCE4KKZHlZ63AG6R3lDbaJLrl5Wx8gpUf4543Czq8+sE5Sh TrGDQ6+kkq94r51Bz/4tyob07Gf65jNuaWDN8SXZm3ahHRR38dVWMynqv+SNS8Gb35dMK 18/hYm3RWWxWo1SjT6clRdSWN5R19t9JFCC/3klmnIFltBatBa3XxL7mTktIPBs9w7+Il jpDZD1816aRh8nvvIYBC0f0uavi5QtHYQYjoIqQJFuS8VK/6oBFrYaaXhLFeMn/5rsNUh m35TswiCvCDodyaWIa/4ZYucZzhZNCSoFY4fq2CZy+n8xhPVVD+K9X2SjhQ422iEM3Q== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=wjY5abZa; arc=none smtp.remote-ip=209.85.160.169 Received: from mail-qt1-f169.google.com (mail-qt1-f169.google.com [209.85.160.169]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1188D606A31F for ; Thu, 29 Oct 2026 20:58:46 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 1188D606A31F Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=wjY5abZa Received: by mail-qt1-f169.google.com with SMTP id d75a77b69052e-53fdd2fb171so27968811cf.2 for ; Thu, 29 Oct 2026 13:58:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793307526; x=1793912326; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=v0nueKfM1lSAPftYVoSLUkDbPpn2D3P/t5rVcRmFMj8=; b=wjY5abZa3Fc6uC7MHphitEjJ+L2paYKZq/ttLYUiqlIauZ+j9+cojs+jSZ+lkvNmX1 qKVRV/ii+86jRW1iVhhXHYSDamXcBxpAWdzXz/0IDwME45OdKsTOgTC1gAmRsa+ClabZ YxB8zS1KkazIypvCIJ4lFtaCnGP24PxwKNf0rcckgetbvgim5XOek5R2c2otzeOg4n7h uZE80AGX9+kJ1YRGFzBvXFEqB7ihuxRx8UZ/7kEN5/v4XttVfyH/UiUcIVqF16Ijf358 YJ1p3t8EDCNyqMrfPBZYq9t3clbFmtYAlSCvAVX8hXsQEbVSxwRTopwy4fpDHMPG/VJy W9Gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793307526; x=1793912326; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=v0nueKfM1lSAPftYVoSLUkDbPpn2D3P/t5rVcRmFMj8=; b=D1yw/NqAExLyKT4QM/3CCqwSXlzgaOAF9TLjeAmsxdX26Ox8svhyzaCbQpQoCiuFLq EAf6VFIuW2PPs2zvsl5QtYSgTSBR1XAFM1AydEkTk/eDr78OYVk8K/hjV1cTWl8q7RBH U0p9iKnnWAoegHtWUuso04+juneUxaocX2cxu1jq/lA8k+zj7Tc3oQiL6BG/Ceoa6nra mCDnDCCraxaVrsHLIV0VS5GY7OU62x4lU2w0oamlutJOp8khcCn6X2/da+g5kPWQvfuK afc1D2zm/JpdwN6S2kPi4UC2ba2/f8fl90yMIG7g5MA1KQD8GuMwJ8FuWdSCjA3cXf8x ZBYA== X-Forwarded-Encrypted: i=1; AKwUvBzgQNdEhElu9OHTb81UmJGdrM+RZXu8JryLzu7I5gkqL+o/HcAO8kLxmrK1a2f1INLOtxLHGY0McNpd@vger.kernel.org X-Gm-Message-State: AFq9FYLakchJd4SRYgwV2WXZi3TcmbggrTUfcP4nwius9DWfvgCvSuSq j5xM8B4BbysrVzUhCgTlGKJojxkwBjeRdtdR6aMO8fpNhQF1mCOy4AkV X-Gm-Gg: AYBFou1mmYvVv8VjEdds4CeK0S261m09aGGcMEGkqKVMVcfHjL5PE2A+dGJgXFPNSCB EvnN6aAD57Cd+Bi+9f9pZTP7rOz9+bplcIhZxFk1Cponz0YLdbrmr1tN7bR45uXW1DRMQUyrmFj xpWqm/edWSXeJw0Jzfk8kLp/W206JYctuwMdL8lAN8vsbEUPeEdMjH7ppQrFlu1++XBo2c+fTOr /vySJWzSFrA9cgZEedi3SOJJDycA0E4+hkspOqLS1ZGuqSDjZQlNAKDh4sbeTuH2oWuWf5AUP4r COMe2m5aPGwDulUc/mIkybL8ErV04nv4pr7jE0xyP9Q3sY84Z4Oq/zleZsn/ch9w5R5XOx/1l8q 5CTaGl7P90EZwduNJR3pmt5/XL5Xm3mFPJT7YxVYEkiOEhsee5Z5coxaIzhQrjtzGoopVQwYK9S OnFAzFfn+yOBt3w97cSR42QP8B02Q6lp5ZfD9YShiTQxl1UXbygi9V2loFP8aNUMTE2bN21Jlpi oPPkMZDYoks/DeUMdvqHzRGw+M7mBxqwd+Y7Nho6dV15aTVbbsiSRcj+FhroaeB5pLx3N+fJ495 wpvcj8tOB6Th0V7pkbYvNtcGU3T5FotsiTD48PWPrKIamwA= X-Received: by 2002:a05:622a:488:b0:535:dd0a:c09e with SMTP id d75a77b69052e-542b85e3f81mr3752831cf.74.1793307525763; Thu, 29 Oct 2026 13:58:45 -0700 (PDT) Received: from holowork.tail3176d.ts.net ([71.104.86.121]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-91ca31032c9sm6006136d6.21.2026.10.29.13.58.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 13:58:43 -0700 (PDT) From: Fabio Piparo To: Javier Martinez Canillas Cc: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Amit Barzilai , Mario Limonciello , dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Fabio Piparo Subject: [PATCH 2/2] drm/ssd130x: Add SSD1322_FAMILY and move the SSD1322 to it Date: Thu, 29 Oct 2026 16:57:51 -0400 Message-ID: <20261029205751.3513474-3-holofermes@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261029205751.3513474-1-holofermes@gmail.com> References: <20261029205751.3513474-1-holofermes@gmail.com> X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The SSD1322 has been in the match tables since commit fdd591e00a9c ("drm/ssd130x: Add support for the SSD132x OLED controller family") as a member of the ssd132x family, which was brought up on an SSD1327. The two controllers share the 4-bit grayscale RAM layout and many opcodes, but the ssd132x paths cannot produce an image on an SSD1322: - A command's parameters travel on the data path, as on the SSD1351, so every multi-byte command in ssd132x_init() is read as a run of single opcodes. - Some of the opcodes differ: 0xa4 is "entire display off" on the SSD1322 and normal display mode is 0xa6, the multiplex ratio command is 0xca, the contrast command 0xc1 and the precharge voltage command 0xbb. - A column address covers four Segments instead of two, the RAM only accepts pixel data after a Write RAM command (0x5c), and with nibble remap the leftmost pixel of a pair is the high nibble. Give the SSD1322 its own family, shaped like ssd135x: an init table, a Write RAM before the pixel data, a contrast command with its value in the data phase, and its own plane update, encoder enable and backlight callbacks. The init sequence follows the Newhaven NHD-3.12-25664UCW2 datasheet, including its analog settings for the module (external VSL, precharge, VCOMH), with three changes: the display clock and the column offset come from the device tree, the contrast is the backlight brightness and is set after the sequence, and dual COM line mode, which the datasheet only allows up to 64 rows, is turned off for taller panels. Assisted-by: LLM Signed-off-by: Fabio Piparo --- drivers/gpu/drm/solomon/ssd130x-i2c.c | 3 +- drivers/gpu/drm/solomon/ssd130x-spi.c | 6 +- drivers/gpu/drm/solomon/ssd130x.c | 277 +++++++++++++++++++++++++- drivers/gpu/drm/solomon/ssd130x.h | 4 +- 4 files changed, 281 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/solomon/ssd130x-i2c.c b/drivers/gpu/drm/solomon/ssd130x-i2c.c index 941a2eb44c5..5b85d000c57 100644 --- a/drivers/gpu/drm/solomon/ssd130x-i2c.c +++ b/drivers/gpu/drm/solomon/ssd130x-i2c.c @@ -92,11 +92,12 @@ static const struct of_device_id ssd130x_of_match[] = { .compatible = "solomon,ssd1309fb-i2c", .data = &ssd130x_variants[SSD1309_ID], }, - /* ssd132x family */ + /* ssd1322 family */ { .compatible = "solomon,ssd1322", .data = &ssd130x_variants[SSD1322_ID], }, + /* ssd132x family */ { .compatible = "solomon,ssd1325", .data = &ssd130x_variants[SSD1325_ID], diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c index c41064aa822..dba35c2e881 100644 --- a/drivers/gpu/drm/solomon/ssd130x-spi.c +++ b/drivers/gpu/drm/solomon/ssd130x-spi.c @@ -128,11 +128,12 @@ static const struct of_device_id ssd130x_of_match[] = { .compatible = "solomon,ssd1309", .data = &ssd130x_variants[SSD1309_ID], }, - /* ssd132x family */ + /* ssd1322 family */ { .compatible = "solomon,ssd1322", .data = &ssd130x_variants[SSD1322_ID], }, + /* ssd132x family */ { .compatible = "solomon,ssd1325", .data = &ssd130x_variants[SSD1325_ID], @@ -170,8 +171,9 @@ static const struct spi_device_id ssd130x_spi_id[] = { { .name = "ssd1306", .driver_data = SSD1306_ID }, { .name = "ssd1307", .driver_data = SSD1307_ID }, { .name = "ssd1309", .driver_data = SSD1309_ID }, - /* ssd132x family */ + /* ssd1322 family */ { .name = "ssd1322", .driver_data = SSD1322_ID }, + /* ssd132x family */ { .name = "ssd1325", .driver_data = SSD1325_ID }, { .name = "ssd1327", .driver_data = SSD1327_ID }, /* ssd133x family */ diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index ee43cf0da87..7ea92d72326 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #define SSD130X_PAGE_HEIGHT 8 #define SSD132X_SEGMENT_WIDTH 2 +#define SSD1322_SEGMENT_WIDTH 4 /* ssd13xx commands */ #define SSD13XX_CONTRAST 0x81 @@ -182,6 +184,38 @@ #define SSD135X_SET_REMAP_COM_SPLIT BIT(5) #define SSD135X_SET_REMAP_65K BIT(6) +/* ssd1322 commands */ +#define SSD1322_WRITE_RAM 0x5c +#define SSD1322_SET_REMAP 0xa0 +#define SSD1322_SET_DISPLAY_START 0xa1 +#define SSD1322_SET_DISPLAY_OFFSET 0xa2 +#define SSD1322_SET_DISPLAY_NORMAL 0xa6 +#define SSD1322_EXIT_PARTIAL_DISPLAY 0xa9 +#define SSD1322_SET_FUNCTION_SELECT_A 0xab +#define SSD1322_SET_PHASE_LENGTH 0xb1 +#define SSD1322_SET_CLOCK_FREQ 0xb3 +#define SSD1322_SET_DISPLAY_ENHANCE_A 0xb4 +#define SSD1322_SET_GPIO 0xb5 +#define SSD1322_SET_PRECHARGE_PERIOD 0xb6 +#define SSD1322_SELECT_DEFAULT_TABLE 0xb9 +#define SSD1322_SET_PRECHARGE_VOLTAGE 0xbb +#define SSD1322_SET_VCOMH_VOLTAGE 0xbe +#define SSD1322_SET_CONTRAST_CURRENT 0xc1 +#define SSD1322_SET_MASTER_CONTRAST 0xc7 +#define SSD1322_SET_MULTIPLEX_RATIO 0xca +#define SSD1322_SET_DISPLAY_ENHANCE_B 0xd1 +#define SSD1322_SET_COMMAND_LOCK 0xfd + +/* ssd1322 remap first byte (data of SSD1322_SET_REMAP) */ +#define SSD1322_SET_REMAP_NIBBLE BIT(2) +#define SSD1322_SET_REMAP_COM_SCAN BIT(4) +/* ssd1322 remap second byte (data of SSD1322_SET_REMAP) */ +#define SSD1322_SET_REMAP_B_RESERVED BIT(0) +#define SSD1322_SET_REMAP_DUAL_COM BIT(4) + +#define SSD1322_CLOCK_DIV_MAX 1024 +#define SSD1322_DUAL_COM_MAX_HEIGHT 64 + #define MAX_CONTRAST 255 const struct ssd130x_deviceinfo ssd130x_variants[] = { @@ -229,11 +263,6 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = { .family_id = SSD130X_FAMILY, }, /* ssd132x family */ - [SSD1322_ID] = { - .default_width = 480, - .default_height = 128, - .family_id = SSD132X_FAMILY, - }, [SSD1325_ID] = { .default_width = 128, .default_height = 80, @@ -257,6 +286,15 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = { .family_id = SSD135X_FAMILY, .cmd_params_are_data = true, }, + /* ssd1322 family */ + [SSD1322_ID] = { + .default_width = 480, + .default_height = 128, + .default_dclk_div = 2, + .default_dclk_frq = 9, + .family_id = SSD1322_FAMILY, + .cmd_params_are_data = true, + }, }; EXPORT_SYMBOL_NS_GPL(ssd130x_variants, "DRM_SSD130X"); @@ -774,6 +812,66 @@ static int ssd135x_init(struct ssd130x_device *ssd130x) return ssd135x_set_contrast(ssd130x, ssd130x->contrast); } +static int ssd1322_set_contrast(struct ssd130x_device *ssd130x, u32 brightness) +{ + return ssd130x_write_cmd(ssd130x, 2, SSD1322_SET_CONTRAST_CURRENT, brightness); +} + +static int ssd1322_init(struct ssd130x_device *ssd130x) +{ + unsigned int columns = DIV_ROUND_UP(ssd130x->width, SSD1322_SEGMENT_WIDTH); + u8 dclk = SSD130X_SET_CLOCK_DIV_SET(ilog2(ssd130x->dclk_div)) | + SSD130X_SET_CLOCK_FREQ_SET(ssd130x->dclk_frq); + /* + * Horizontal address increment, nibble remap and reversed COM scan + * direction. Rotation is not supported, so the remap bytes are fixed. + */ + const u8 remap = SSD1322_SET_REMAP_NIBBLE | SSD1322_SET_REMAP_COM_SCAN; + const u8 remap_b = SSD1322_SET_REMAP_B_RESERVED | + (ssd130x->height <= SSD1322_DUAL_COM_MAX_HEIGHT ? + SSD1322_SET_REMAP_DUAL_COM : 0); + const u8 cmds[] = { + /* Unlock the controller */ + 2, SSD1322_SET_COMMAND_LOCK, 0x12, + 1, SSD13XX_DISPLAY_OFF, + 3, SSD132X_SET_COL_RANGE, ssd130x->col_offset, + ssd130x->col_offset + columns - 1, + 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1, + 2, SSD1322_SET_CLOCK_FREQ, dclk, + 2, SSD1322_SET_MULTIPLEX_RATIO, ssd130x->height - 1, + 2, SSD1322_SET_DISPLAY_OFFSET, 0x00, + 2, SSD1322_SET_DISPLAY_START, 0x00, + 3, SSD1322_SET_REMAP, remap, remap_b, + 2, SSD1322_SET_GPIO, 0x00, + 2, SSD1322_SET_FUNCTION_SELECT_A, 0x01, + 3, SSD1322_SET_DISPLAY_ENHANCE_A, 0xa0, 0xfd, + 2, SSD1322_SET_MASTER_CONTRAST, 0x0f, + 1, SSD1322_SELECT_DEFAULT_TABLE, + 2, SSD1322_SET_PHASE_LENGTH, 0xe2, + 3, SSD1322_SET_DISPLAY_ENHANCE_B, 0xa2, 0x20, + 2, SSD1322_SET_PRECHARGE_VOLTAGE, 0x1f, + 2, SSD1322_SET_PRECHARGE_PERIOD, 0x08, + 2, SSD1322_SET_VCOMH_VOLTAGE, 0x07, + 1, SSD1322_SET_DISPLAY_NORMAL, + 1, SSD1322_EXIT_PARTIAL_DISPLAY, + 0, + }; + int ret; + + if (!is_power_of_2(ssd130x->dclk_div) || + ssd130x->dclk_div > SSD1322_CLOCK_DIV_MAX) { + drm_err(&ssd130x->drm, "Invalid display clock divisor %u\n", + ssd130x->dclk_div); + return -EINVAL; + } + + ret = ssd130x_run_cmd_seq(ssd130x, cmds); + if (ret < 0) + return ret; + + return ssd1322_set_contrast(ssd130x, ssd130x->contrast); +} + static int ssd130x_update_rect(struct ssd130x_device *ssd130x, struct drm_rect *rect, u8 *buf, u8 *data_array) @@ -1020,6 +1118,35 @@ static int ssd135x_update_rect(struct ssd130x_device *ssd130x, return ssd130x_write_data(ssd130x, data_array, pitch * rows); } +static int ssd1322_update_rect(struct ssd130x_device *ssd130x, + struct drm_rect *rect, u8 *data_array, + unsigned int pitch) +{ + unsigned int col = ssd130x->col_offset + rect->x1 / SSD1322_SEGMENT_WIDTH; + unsigned int columns = DIV_ROUND_UP(drm_rect_width(rect), SSD1322_SEGMENT_WIDTH); + unsigned int rows = drm_rect_height(rect); + struct drm_device *drm = &ssd130x->drm; + int ret; + + drm_WARN_ONCE(drm, rect->x1 % SSD1322_SEGMENT_WIDTH != 0, + "x must be aligned to screen segment\n"); + + ret = ssd132x_set_col_range(ssd130x, col, columns); + if (ret < 0) + return ret; + + ret = ssd132x_set_row_range(ssd130x, rect->y1, rows); + if (ret < 0) + return ret; + + ret = ssd130x_write_cmd(ssd130x, 1, SSD1322_WRITE_RAM); + if (ret < 0) + return ret; + + /* Write out update in one go since horizontal addressing mode is used */ + return ssd130x_write_data(ssd130x, data_array, pitch * rows); +} + static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array) { unsigned int pages = DIV_ROUND_UP(ssd130x->height, SSD130X_PAGE_HEIGHT); @@ -1212,6 +1339,45 @@ static int ssd135x_fb_blit_rect(struct drm_framebuffer *fb, return 0; } +static int ssd1322_fb_blit_rect(struct drm_framebuffer *fb, + const struct iosys_map *vmap, + struct drm_rect *rect, u8 *buf, + u8 *data_array, + struct drm_format_conv_state *fmtcnv_state) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); + unsigned int width, height, pitch; + unsigned int i, j; + u32 array_idx = 0; + struct iosys_map dst; + + /* Align x to display segment boundaries */ + rect->x1 = round_down(rect->x1, SSD1322_SEGMENT_WIDTH); + rect->x2 = min_t(unsigned int, round_up(rect->x2, SSD1322_SEGMENT_WIDTH), + ssd130x->width); + + width = drm_rect_width(rect); + height = drm_rect_height(rect); + pitch = DIV_ROUND_UP(width, 2); + + iosys_map_set_vaddr(&dst, buf); + drm_fb_xrgb8888_to_gray8(&dst, &width, vmap, fb, rect, fmtcnv_state); + + for (i = 0; i < height; i++) { + /* Process pair of pixels and combine them into a single byte */ + for (j = 0; j < width; j += 2) { + u8 n1 = buf[i * width + j]; + u8 n2 = j + 1 < width ? buf[i * width + j + 1] : 0; + + data_array[array_idx++] = (n1 & 0xf0) | (n2 >> 4); + } + } + + ssd1322_update_rect(ssd130x, rect, data_array, pitch); + + return 0; +} + static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { @@ -1493,6 +1659,47 @@ static void ssd135x_primary_plane_atomic_update(struct drm_plane *plane, drm_dev_exit(idx); } +static void ssd1322_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_commit *state) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); + struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state); + struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_atomic_helper_damage_iter iter; + struct drm_device *drm = plane->dev; + struct drm_rect dst_clip; + struct drm_rect damage; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE)) + goto out_drm_dev_exit; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + dst_clip = plane_state->dst; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + ssd1322_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip, + ssd130x_plane_state->buffer, + ssd130x_crtc_state->data_array, + &shadow_plane_state->fmtcnv_state); + } + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + +out_drm_dev_exit: + drm_dev_exit(idx); +} + static void ssd130x_primary_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_commit *state) { @@ -1590,6 +1797,15 @@ static void ssd135x_primary_plane_atomic_disable(struct drm_plane *plane, drm_dev_exit(idx); } +static void ssd1322_primary_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_commit *state) +{ + /* + * Keep this empty function to avoid calling + * atomic_update when disabling the plane. + */ +} + /* Called during init to allocate the plane's atomic state. */ static struct drm_plane_state *ssd130x_primary_plane_create_state(struct drm_plane *plane) { @@ -1665,6 +1881,12 @@ static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[] .atomic_update = ssd135x_primary_plane_atomic_update, .atomic_disable = ssd135x_primary_plane_atomic_disable, }, + [SSD1322_FAMILY] = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = ssd132x_primary_plane_atomic_check, + .atomic_update = ssd1322_primary_plane_atomic_update, + .atomic_disable = ssd1322_primary_plane_atomic_disable, + }, }; static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { @@ -1825,6 +2047,10 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = { .mode_valid = ssd130x_crtc_mode_valid, .atomic_check = ssd133x_crtc_atomic_check, }, + [SSD1322_FAMILY] = { + .mode_valid = ssd130x_crtc_mode_valid, + .atomic_check = ssd132x_crtc_atomic_check, + }, }; static const struct drm_crtc_funcs ssd130x_crtc_funcs = { @@ -1937,6 +2163,31 @@ static void ssd135x_encoder_atomic_enable(struct drm_encoder *encoder, ssd130x_power_off(ssd130x); } +static void ssd1322_encoder_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_commit *state) +{ + struct drm_device *drm = encoder->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + int ret; + + ret = ssd130x_power_on(ssd130x); + if (ret) + return; + + ret = ssd1322_init(ssd130x); + if (ret) + goto power_off; + + ssd130x_write_cmd(ssd130x, 1, SSD13XX_DISPLAY_ON); + + backlight_enable(ssd130x->bl_dev); + + return; + +power_off: + ssd130x_power_off(ssd130x); +} + static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_commit *state) { @@ -1967,6 +2218,10 @@ static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs[] = { .atomic_enable = ssd135x_encoder_atomic_enable, .atomic_disable = ssd130x_encoder_atomic_disable, }, + [SSD1322_FAMILY] = { + .atomic_enable = ssd1322_encoder_atomic_enable, + .atomic_disable = ssd130x_encoder_atomic_disable, + }, }; static const struct drm_encoder_funcs ssd130x_encoder_funcs = { @@ -2052,6 +2307,15 @@ static int ssd135x_update_bl(struct backlight_device *bdev) return ssd135x_set_contrast(ssd130x, ssd130x->contrast); } +static int ssd1322_update_bl(struct backlight_device *bdev) +{ + struct ssd130x_device *ssd130x = bl_get_data(bdev); + + ssd130x->contrast = backlight_get_brightness(bdev); + + return ssd1322_set_contrast(ssd130x, ssd130x->contrast); +} + static const struct backlight_ops ssd130xfb_bl_ops[] = { [SSD130X_FAMILY] = { .update_status = ssd130x_update_bl, @@ -2065,6 +2329,9 @@ static const struct backlight_ops ssd130xfb_bl_ops[] = { [SSD135X_FAMILY] = { .update_status = ssd135x_update_bl, }, + [SSD1322_FAMILY] = { + .update_status = ssd1322_update_bl, + }, }; static void ssd130x_parse_properties(struct ssd130x_device *ssd130x) diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h index fd81262f2ce..b49bbf36228 100644 --- a/drivers/gpu/drm/solomon/ssd130x.h +++ b/drivers/gpu/drm/solomon/ssd130x.h @@ -28,6 +28,7 @@ enum ssd130x_family_ids { SSD132X_FAMILY, SSD133X_FAMILY, SSD135X_FAMILY, + SSD1322_FAMILY, }; enum ssd130x_variants { @@ -37,8 +38,9 @@ enum ssd130x_variants { SSD1306_ID, SSD1307_ID, SSD1309_ID, - /* ssd132x family */ + /* ssd1322 family */ SSD1322_ID, + /* ssd132x family */ SSD1325_ID, SSD1327_ID, /* ssd133x family */ -- 2.53.0