* [PATCH v2 4/4] staging: fbtft: remove fb_ssd1351 driver
From: Amit Barzilai @ 2026-06-22 15:25 UTC (permalink / raw)
To: javierm, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt, andy, gregkh, deller
Cc: azuddinadam, chintanlike, dri-devel, devicetree, linux-kernel,
linux-fbdev, linux-staging, Amit Barzilai
In-Reply-To: <20260622152506.78627-1-amit.barzilai22@gmail.com>
The SSD1351 support was added to the ssd130x DRM driver. To avoid
confusion and irrelevant updates, the staging fb_ssd1351 driver is
removed.
Signed-off-by: Amit Barzilai <amit.barzilai22@gmail.com>
---
drivers/staging/fbtft/Kconfig | 5 -
drivers/staging/fbtft/Makefile | 1 -
drivers/staging/fbtft/fb_ssd1351.c | 240 -----------------------------
3 files changed, 246 deletions(-)
delete mode 100644 drivers/staging/fbtft/fb_ssd1351.c
diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig
index 92943564cb91..c8ea1a0f3fb0 100644
--- a/drivers/staging/fbtft/Kconfig
+++ b/drivers/staging/fbtft/Kconfig
@@ -133,11 +133,6 @@ config FB_TFT_SSD1331
help
Framebuffer support for SSD1331
-config FB_TFT_SSD1351
- tristate "FB driver for the SSD1351 LCD Controller"
- help
- Framebuffer support for SSD1351
-
config FB_TFT_ST7735R
tristate "FB driver for the ST7735R LCD Controller"
help
diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile
index e9cdf0f0a7da..c230bf008fc7 100644
--- a/drivers/staging/fbtft/Makefile
+++ b/drivers/staging/fbtft/Makefile
@@ -28,7 +28,6 @@ obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1305.o
obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o
obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1325.o
obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o
-obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o
obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o
obj-$(CONFIG_FB_TFT_ST7789V) += fb_st7789v.o
obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o
diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c
deleted file mode 100644
index 6736b09b2f45..000000000000
--- a/drivers/staging/fbtft/fb_ssd1351.c
+++ /dev/null
@@ -1,240 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/backlight.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/spi/spi.h>
-#include <linux/delay.h>
-#include <linux/string_choices.h>
-
-#include "fbtft.h"
-
-#define DRVNAME "fb_ssd1351"
-#define WIDTH 128
-#define HEIGHT 128
-#define GAMMA_NUM 1
-#define GAMMA_LEN 63
-#define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2 2 " \
- "2 2 2 2 2 2 2" \
-
-static void register_onboard_backlight(struct fbtft_par *par);
-
-static int init_display(struct fbtft_par *par)
-{
- if (par->pdata &&
- par->pdata->display.backlight == FBTFT_ONBOARD_BACKLIGHT) {
- /* module uses onboard GPIO for panel power */
- par->fbtftops.register_backlight = register_onboard_backlight;
- }
-
- par->fbtftops.reset(par);
-
- write_reg(par, 0xfd, 0x12); /* Command Lock */
- write_reg(par, 0xfd, 0xb1); /* Command Lock */
- write_reg(par, 0xae); /* Display Off */
- write_reg(par, 0xb3, 0xf1); /* Front Clock Div */
- write_reg(par, 0xca, 0x7f); /* Set Mux Ratio */
- write_reg(par, 0x15, 0x00, 0x7f); /* Set Column Address */
- write_reg(par, 0x75, 0x00, 0x7f); /* Set Row Address */
- write_reg(par, 0xa1, 0x00); /* Set Display Start Line */
- write_reg(par, 0xa2, 0x00); /* Set Display Offset */
- write_reg(par, 0xb5, 0x00); /* Set GPIO */
- write_reg(par, 0xab, 0x01); /* Set Function Selection */
- write_reg(par, 0xb1, 0x32); /* Set Phase Length */
- write_reg(par, 0xb4, 0xa0, 0xb5, 0x55); /* Set Segment Low Voltage */
- write_reg(par, 0xbb, 0x17); /* Set Precharge Voltage */
- write_reg(par, 0xbe, 0x05); /* Set VComH Voltage */
- write_reg(par, 0xc1, 0xc8, 0x80, 0xc8); /* Set Contrast */
- write_reg(par, 0xc7, 0x0f); /* Set Master Contrast */
- write_reg(par, 0xb6, 0x01); /* Set Second Precharge Period */
- write_reg(par, 0xa6); /* Set Display Mode Reset */
- write_reg(par, 0xaf); /* Set Sleep Mode Display On */
-
- return 0;
-}
-
-static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
-{
- write_reg(par, 0x15, xs, xe);
- write_reg(par, 0x75, ys, ye);
- write_reg(par, 0x5c);
-}
-
-static int set_var(struct fbtft_par *par)
-{
- unsigned int remap;
-
- if (par->fbtftops.init_display != init_display) {
- /* don't risk messing up register A0h */
- return 0;
- }
-
- remap = 0x60 | (par->bgr << 2); /* Set Colour Depth */
-
- switch (par->info->var.rotate) {
- case 0:
- write_reg(par, 0xA0, remap | 0x00 | BIT(4));
- break;
- case 270:
- write_reg(par, 0xA0, remap | 0x03 | BIT(4));
- break;
- case 180:
- write_reg(par, 0xA0, remap | 0x02);
- break;
- case 90:
- write_reg(par, 0xA0, remap | 0x01);
- break;
- }
-
- return 0;
-}
-
-/*
- * Grayscale Lookup Table
- * GS1 - GS63
- * The driver Gamma curve contains the relative values between the entries
- * in the Lookup table.
- *
- * From datasheet:
- * 8.8 Gray Scale Decoder
- *
- * there are total 180 Gamma Settings (Setting 0 to Setting 180)
- * available for the Gray Scale table.
- *
- * The gray scale is defined in incremental way, with reference
- * to the length of previous table entry:
- * Setting of GS1 has to be >= 0
- * Setting of GS2 has to be > Setting of GS1 +1
- * Setting of GS3 has to be > Setting of GS2 +1
- * :
- * Setting of GS63 has to be > Setting of GS62 +1
- *
- */
-static int set_gamma(struct fbtft_par *par, u32 *curves)
-{
- unsigned long tmp[GAMMA_NUM * GAMMA_LEN];
- int i, acc = 0;
-
- for (i = 0; i < 63; i++) {
- if (i > 0 && curves[i] < 2) {
- dev_err(par->info->device,
- "Illegal value in Grayscale Lookup Table at index %d : %d. Must be greater than 1\n",
- i, curves[i]);
- return -EINVAL;
- }
- acc += curves[i];
- tmp[i] = acc;
- if (acc > 180) {
- dev_err(par->info->device,
- "Illegal value(s) in Grayscale Lookup Table. At index=%d : %d, the accumulated value has exceeded 180\n",
- i, acc);
- return -EINVAL;
- }
- }
-
- write_reg(par, 0xB8,
- tmp[0], tmp[1], tmp[2], tmp[3],
- tmp[4], tmp[5], tmp[6], tmp[7],
- tmp[8], tmp[9], tmp[10], tmp[11],
- tmp[12], tmp[13], tmp[14], tmp[15],
- tmp[16], tmp[17], tmp[18], tmp[19],
- tmp[20], tmp[21], tmp[22], tmp[23],
- tmp[24], tmp[25], tmp[26], tmp[27],
- tmp[28], tmp[29], tmp[30], tmp[31],
- tmp[32], tmp[33], tmp[34], tmp[35],
- tmp[36], tmp[37], tmp[38], tmp[39],
- tmp[40], tmp[41], tmp[42], tmp[43],
- tmp[44], tmp[45], tmp[46], tmp[47],
- tmp[48], tmp[49], tmp[50], tmp[51],
- tmp[52], tmp[53], tmp[54], tmp[55],
- tmp[56], tmp[57], tmp[58], tmp[59],
- tmp[60], tmp[61], tmp[62]);
-
- return 0;
-}
-
-static int blank(struct fbtft_par *par, bool on)
-{
- fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n",
- __func__, str_true_false(on));
- if (on)
- write_reg(par, 0xAE);
- else
- write_reg(par, 0xAF);
- return 0;
-}
-
-static struct fbtft_display display = {
- .regwidth = 8,
- .width = WIDTH,
- .height = HEIGHT,
- .gamma_num = GAMMA_NUM,
- .gamma_len = GAMMA_LEN,
- .gamma = DEFAULT_GAMMA,
- .fbtftops = {
- .init_display = init_display,
- .set_addr_win = set_addr_win,
- .set_var = set_var,
- .set_gamma = set_gamma,
- .blank = blank,
- },
-};
-
-static int update_onboard_backlight(struct backlight_device *bd)
-{
- struct fbtft_par *par = bl_get_data(bd);
- bool on;
-
- fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s: power=%d\n", __func__, bd->props.power);
-
- on = !backlight_is_blank(bd);
- /* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */
- write_reg(par, 0xB5, on ? 0x03 : 0x02);
-
- return 0;
-}
-
-static const struct backlight_ops bl_ops = {
- .update_status = update_onboard_backlight,
-};
-
-static void register_onboard_backlight(struct fbtft_par *par)
-{
- struct backlight_device *bd;
- struct backlight_properties bl_props = { 0, };
-
- bl_props.type = BACKLIGHT_RAW;
- bl_props.power = BACKLIGHT_POWER_OFF;
-
- bd = backlight_device_register(dev_driver_string(par->info->device),
- par->info->device, par, &bl_ops,
- &bl_props);
- if (IS_ERR(bd)) {
- dev_err(par->info->device,
- "cannot register backlight device (%ld)\n",
- PTR_ERR(bd));
- return;
- }
- par->info->bl_dev = bd;
-
- if (!par->fbtftops.unregister_backlight)
- par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
-}
-
-FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1351", &display);
-
-MODULE_ALIAS("spi:" DRVNAME);
-MODULE_ALIAS("platform:" DRVNAME);
-MODULE_ALIAS("spi:ssd1351");
-MODULE_ALIAS("platform:ssd1351");
-
-MODULE_DESCRIPTION("SSD1351 OLED Driver");
-MODULE_AUTHOR("James Davies");
-MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related
* [PATCH v2 3/4] drm/ssd130x: Add SSD135X_FAMILY and SSD1351 support
From: Amit Barzilai @ 2026-06-22 15:25 UTC (permalink / raw)
To: javierm, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt, andy, gregkh, deller
Cc: azuddinadam, chintanlike, dri-devel, devicetree, linux-kernel,
linux-fbdev, linux-staging, Amit Barzilai
In-Reply-To: <20260622152506.78627-1-amit.barzilai22@gmail.com>
The Solomon SSD1351 is a 128x128 RGB color OLED controller. It shares
the SSD133X data path: a column/row addressing window followed by a bulk
RGB565 pixel write. Add it as a new SSD135X_FAMILY rather than a separate
driver, reusing the SSD133X plane, CRTC and blit/clear helpers.
The only data-path difference is that the SSD1351 requires an explicit
Write RAM command (0x5c) after the address window is programmed, before
pixel data is accepted, whereas the SSD133X enters data mode implicitly.
This is emitted from a shared ssd133x_write_pixels() helper so both the
damage-update and clear-screen paths cover it.
The SSD1351 also needs its own init sequence (ssd135x_init), dispatched
via ssd135x_encoder_atomic_enable, and a longer post-reset settle delay.
The re-map byte is fixed at 0 degrees, 65k color, COM split, BGR
sub-pixel order; rotation is not supported.
The SSD1351 is SPI-only, so only the SPI transport match tables gain an
entry; no new config symbol is needed.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Amit Barzilai <amit.barzilai22@gmail.com>
---
drivers/gpu/drm/solomon/ssd130x-spi.c | 7 +
drivers/gpu/drm/solomon/ssd130x.c | 214 +++++++++++++++++++++-----
drivers/gpu/drm/solomon/ssd130x.h | 5 +-
3 files changed, 189 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c
index b52f5fd592a1..6e0dd6e5a88d 100644
--- a/drivers/gpu/drm/solomon/ssd130x-spi.c
+++ b/drivers/gpu/drm/solomon/ssd130x-spi.c
@@ -146,6 +146,11 @@ static const struct of_device_id ssd130x_of_match[] = {
.compatible = "solomon,ssd1331",
.data = &ssd130x_variants[SSD1331_ID],
},
+ /* ssd135x family */
+ {
+ .compatible = "solomon,ssd1351",
+ .data = &ssd130x_variants[SSD1351_ID],
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ssd130x_of_match);
@@ -171,6 +176,8 @@ static const struct spi_device_id ssd130x_spi_id[] = {
{ "ssd1327", SSD1327_ID },
/* ssd133x family */
{ "ssd1331", SSD1331_ID },
+ /* ssd135x family */
+ { "ssd1351", SSD1351_ID },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, ssd130x_spi_id);
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 2b0a8218f529..e5a9428f91b8 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -146,6 +146,33 @@
#define SSD133X_COLOR_DEPTH_256 0x0
#define SSD133X_COLOR_DEPTH_65K 0x1
+/* ssd135x commands */
+#define SSD135X_SET_COL_RANGE 0x15
+#define SSD135X_WRITE_RAM 0x5c
+#define SSD135X_SET_ROW_RANGE 0x75
+#define SSD135X_SET_DISPLAY_START 0xa1
+#define SSD135X_SET_DISPLAY_OFFSET 0xa2
+#define SSD135X_SET_DISPLAY_NORMAL 0xa6
+#define SSD135X_SET_FUNCTION 0xab
+#define SSD135X_SET_PHASE_LENGTH 0xb1
+#define SSD135X_SET_CLOCK_FREQ 0xb3
+#define SSD135X_SET_VSL 0xb4
+#define SSD135X_SET_GPIO 0xb5
+#define SSD135X_SET_PRECHARGE2 0xb6
+#define SSD135X_SET_PRECHARGE 0xbb
+#define SSD135X_SET_VCOMH_VOLTAGE 0xbe
+#define SSD135X_SET_CONTRAST 0xc1
+#define SSD135X_SET_CONTRAST_MASTER 0xc7
+#define SSD135X_SET_MUX_RATIO 0xca
+#define SSD135X_SET_COMMAND_LOCK 0xfd
+
+/* ssd135x remap byte (data of SSD13XX_SET_SEG_REMAP) */
+#define SSD135X_SET_REMAP_COLUMN BIT(1)
+#define SSD135X_SET_REMAP_COLOR_BGR BIT(2)
+#define SSD135X_SET_REMAP_COM_SCAN BIT(4)
+#define SSD135X_SET_REMAP_COM_SPLIT BIT(5)
+#define SSD135X_SET_REMAP_65K BIT(6)
+
#define MAX_CONTRAST 255
const struct ssd130x_deviceinfo ssd130x_variants[] = {
@@ -214,6 +241,13 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
.default_height = 64,
.format_rgb565 = 1,
.family_id = SSD133X_FAMILY,
+ },
+ /* ssd135x family */
+ [SSD1351_ID] = {
+ .default_width = 128,
+ .default_height = 128,
+ .format_rgb565 = 1,
+ .family_id = SSD135X_FAMILY,
}
};
EXPORT_SYMBOL_NS_GPL(ssd130x_variants, "DRM_SSD130X");
@@ -248,47 +282,16 @@ static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)
/*
* Helper to write data (SSD13XX_DATA) to the device.
*/
-static int ssd130x_write_data(struct ssd130x_device *ssd130x, u8 *values, int count)
+static int ssd130x_write_data(struct ssd130x_device *ssd130x, const u8 *values, int count)
{
return regmap_bulk_write(ssd130x->regmap, SSD13XX_DATA, values, count);
}
-/*
- * Helper to write command (SSD13XX_COMMAND). The fist variadic argument
- * is the command to write and the following are the command options.
- *
- * Note that the ssd13xx protocol requires each command and option to be
- * written as a SSD13XX_COMMAND device register value. That is why a call
- * to regmap_write(..., SSD13XX_COMMAND, ...) is done for each argument.
- */
-static int ssd130x_write_cmd(struct ssd130x_device *ssd130x, int count,
- /* u8 cmd, u8 option, ... */...)
-{
- va_list ap;
- u8 value;
- int ret;
-
- va_start(ap, count);
-
- do {
- value = va_arg(ap, int);
- ret = regmap_write(ssd130x->regmap, SSD13XX_COMMAND, value);
- if (ret)
- goto out_end;
- } while (--count);
-
-out_end:
- va_end(ap);
-
- return ret;
-}
-
/*
* Write a command byte sequence from a buffer.
*
- * Like ssd130x_write_cmd() but takes a pre-built byte array instead of
- * variadic arguments, handy when the command is already in an array or
- * when the caller wants to use sizeof() for the length.
+ * The first byte is the command opcode and the following bytes are its
+ * options/parameters.
*/
static int ssd130x_write_cmds(struct ssd130x_device *ssd130x, const u8 *cmd,
size_t len)
@@ -296,6 +299,22 @@ static int ssd130x_write_cmds(struct ssd130x_device *ssd130x, const u8 *cmd,
unsigned int i;
int ret;
+ /*
+ * The SSD135X family latches command parameters with D/C# HIGH (i.e.
+ * clocked in as data), unlike the other families where the opcode and
+ * all of its parameters are sent as commands (D/C# LOW). Send the
+ * opcode as a command and any following parameter bytes as data.
+ */
+ if (ssd130x->device_info->family_id == SSD135X_FAMILY) {
+ if (len == 0)
+ return 0;
+ ret = regmap_write(ssd130x->regmap, SSD13XX_COMMAND, cmd[0]);
+ if (ret || len == 1)
+ return ret;
+
+ return ssd130x_write_data(ssd130x, cmd + 1, len - 1);
+ }
+
for (i = 0; i < len; i++) {
ret = regmap_write(ssd130x->regmap, SSD13XX_COMMAND, cmd[i]);
if (ret)
@@ -305,6 +324,28 @@ static int ssd130x_write_cmds(struct ssd130x_device *ssd130x, const u8 *cmd,
return 0;
}
+/*
+ * Variadic wrapper around ssd130x_write_cmds(). The first variadic argument is
+ * the command opcode and the following ones are its options/parameters.
+ */
+static int ssd130x_write_cmd(struct ssd130x_device *ssd130x, int count,
+ /* u8 cmd, u8 option, ... */...)
+{
+ u8 buf[8];
+ va_list ap;
+ int i;
+
+ if (count > ARRAY_SIZE(buf))
+ return -EINVAL;
+
+ va_start(ap, count);
+ for (i = 0; i < count; i++)
+ buf[i] = va_arg(ap, int);
+ va_end(ap);
+
+ return ssd130x_write_cmds(ssd130x, buf, count);
+}
+
/*
* Run a packed command sequence. The format is a flat byte array where each
* entry starts with a length byte followed by that many command bytes. A
@@ -628,6 +669,49 @@ static int ssd133x_init(struct ssd130x_device *ssd130x)
return ssd130x_run_cmd_seq(ssd130x, cmds);
}
+static int ssd135x_init(struct ssd130x_device *ssd130x)
+{
+ /*
+ * Horizontal address increment, COM split, reversed COM scan direction,
+ * BGR sub-pixel order and 65k (RGB565) color depth. Rotation is not
+ * supported, so the remap byte is fixed.
+ */
+ u8 remap = SSD135X_SET_REMAP_65K | SSD135X_SET_REMAP_COM_SPLIT |
+ SSD135X_SET_REMAP_COLOR_BGR | SSD135X_SET_REMAP_COM_SCAN;
+ const u8 cmds[] = {
+ 2, SSD135X_SET_COMMAND_LOCK, 0x12,
+ 2, SSD135X_SET_COMMAND_LOCK, 0xb1,
+ 1, SSD13XX_DISPLAY_OFF,
+ 2, SSD135X_SET_CLOCK_FREQ, 0xf1,
+ 2, SSD135X_SET_MUX_RATIO, ssd130x->height - 1,
+ 3, SSD135X_SET_COL_RANGE, 0x00, ssd130x->width - 1,
+ 3, SSD135X_SET_ROW_RANGE, 0x00, ssd130x->height - 1,
+ 2, SSD135X_SET_DISPLAY_START, 0x00,
+ 2, SSD135X_SET_DISPLAY_OFFSET, 0x00,
+ 2, SSD135X_SET_GPIO, 0x00,
+ 2, SSD135X_SET_FUNCTION, 0x01,
+ 2, SSD135X_SET_PHASE_LENGTH, 0x32,
+ 4, SSD135X_SET_VSL, 0xa0, 0xb5, 0x55,
+ 2, SSD135X_SET_PRECHARGE, 0x17,
+ 2, SSD135X_SET_VCOMH_VOLTAGE, 0x05,
+ 4, SSD135X_SET_CONTRAST, 0xc8, 0x80, 0xc8,
+ 2, SSD135X_SET_CONTRAST_MASTER, 0x0f,
+ 2, SSD135X_SET_PRECHARGE2, 0x01,
+ 1, SSD135X_SET_DISPLAY_NORMAL,
+ 2, SSD13XX_SET_SEG_REMAP, remap,
+ 0,
+ };
+
+ /*
+ * ssd130x_power_on() issues a short reset pulse, but the SSD1351 is not
+ * ready to accept commands immediately afterwards. Give the controller
+ * time to settle before sending the init sequence.
+ */
+ msleep(120);
+
+ return ssd130x_run_cmd_seq(ssd130x, cmds);
+}
+
static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
struct drm_rect *rect, u8 *buf,
u8 *data_array)
@@ -790,6 +874,25 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
return ret;
}
+/*
+ * Write a run of pixel data to the controller's display RAM. The SSD135X
+ * family requires an explicit Write RAM command once the address window has
+ * been set, before any pixel data is accepted; the SSD133X family enters data
+ * mode implicitly after the column/row range is programmed.
+ */
+static int ssd133x_write_pixels(struct ssd130x_device *ssd130x,
+ u8 *data_array, unsigned int count)
+{
+ if (ssd130x->device_info->family_id == SSD135X_FAMILY) {
+ int ret = ssd130x_write_cmd(ssd130x, 1, SSD135X_WRITE_RAM);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return ssd130x_write_data(ssd130x, data_array, count);
+}
+
static int ssd133x_update_rect(struct ssd130x_device *ssd130x,
struct drm_rect *rect, u8 *data_array,
unsigned int pitch)
@@ -832,7 +935,7 @@ static int ssd133x_update_rect(struct ssd130x_device *ssd130x,
return ret;
/* Write out update in one go since horizontal addressing mode is used */
- ret = ssd130x_write_data(ssd130x, data_array, pitch * rows);
+ ret = ssd133x_write_pixels(ssd130x, data_array, pitch * rows);
return ret;
}
@@ -917,7 +1020,7 @@ static void ssd133x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
memset(data_array, 0, pitch * ssd130x->height);
/* Write out update in one go since horizontal addressing mode is used */
- ssd130x_write_data(ssd130x, data_array, pitch * ssd130x->height);
+ ssd133x_write_pixels(ssd130x, data_array, pitch * ssd130x->height);
}
static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
@@ -1380,6 +1483,12 @@ static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[]
.atomic_check = ssd133x_primary_plane_atomic_check,
.atomic_update = ssd133x_primary_plane_atomic_update,
.atomic_disable = ssd133x_primary_plane_atomic_disable,
+ },
+ [SSD135X_FAMILY] = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = ssd133x_primary_plane_atomic_check,
+ .atomic_update = ssd133x_primary_plane_atomic_update,
+ .atomic_disable = ssd133x_primary_plane_atomic_disable,
}
};
@@ -1534,6 +1643,10 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = {
.mode_valid = ssd130x_crtc_mode_valid,
.atomic_check = ssd133x_crtc_atomic_check,
},
+ [SSD135X_FAMILY] = {
+ .mode_valid = ssd130x_crtc_mode_valid,
+ .atomic_check = ssd133x_crtc_atomic_check,
+ },
};
static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
@@ -1621,6 +1734,31 @@ static void ssd133x_encoder_atomic_enable(struct drm_encoder *encoder,
ssd130x_power_off(ssd130x);
}
+static void ssd135x_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 = ssd135x_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)
{
@@ -1646,6 +1784,10 @@ static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs[] = {
[SSD133X_FAMILY] = {
.atomic_enable = ssd133x_encoder_atomic_enable,
.atomic_disable = ssd130x_encoder_atomic_disable,
+ },
+ [SSD135X_FAMILY] = {
+ .atomic_enable = ssd135x_encoder_atomic_enable,
+ .atomic_disable = ssd130x_encoder_atomic_disable,
}
};
diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h
index b0b487c06e04..da89d4455270 100644
--- a/drivers/gpu/drm/solomon/ssd130x.h
+++ b/drivers/gpu/drm/solomon/ssd130x.h
@@ -26,7 +26,8 @@
enum ssd130x_family_ids {
SSD130X_FAMILY,
SSD132X_FAMILY,
- SSD133X_FAMILY
+ SSD133X_FAMILY,
+ SSD135X_FAMILY
};
enum ssd130x_variants {
@@ -42,6 +43,8 @@ enum ssd130x_variants {
SSD1327_ID,
/* ssd133x family */
SSD1331_ID,
+ /* ssd135x family */
+ SSD1351_ID,
NR_SSD130X_VARIANTS
};
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/4] drm/ssd130x: Add RGB565 support to SSD133X family
From: Amit Barzilai @ 2026-06-22 15:25 UTC (permalink / raw)
To: javierm, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt, andy, gregkh, deller
Cc: azuddinadam, chintanlike, dri-devel, devicetree, linux-kernel,
linux-fbdev, linux-staging, Amit Barzilai
In-Reply-To: <20260622152506.78627-1-amit.barzilai22@gmail.com>
SSD133X screens were getting 8bpp (RGB332) instead of the 16bpp
(RGB565) that they support. This change adds a boolean to the
deviceinfo struct selecting whether the variant is driven at
DRM_FORMAT_RGB565.
Changed SSD133X to now utilize 65k color (RGB565).
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Amit Barzilai <amit.barzilai22@gmail.com>
---
drivers/gpu/drm/solomon/ssd130x.c | 55 +++++++++++++++++++++++++------
drivers/gpu/drm/solomon/ssd130x.h | 7 ++++
2 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 04da4f2f7d08..2b0a8218f529 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -140,6 +140,12 @@
#define SSD133X_SET_PRECHARGE_VOLTAGE 0xbb
#define SSD133X_SET_VCOMH_VOLTAGE 0xbe
+/* ssd133x remap byte (data of SSD13XX_SET_SEG_REMAP) */
+#define SSD133X_SET_REMAP_COM_SPLIT BIT(5)
+#define SSD133X_SET_REMAP_COLOR_DEPTH_MASK GENMASK(7, 6)
+#define SSD133X_COLOR_DEPTH_256 0x0
+#define SSD133X_COLOR_DEPTH_65K 0x1
+
#define MAX_CONTRAST 255
const struct ssd130x_deviceinfo ssd130x_variants[] = {
@@ -206,6 +212,7 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
[SSD1331_ID] = {
.default_width = 96,
.default_height = 64,
+ .format_rgb565 = 1,
.family_id = SSD133X_FAMILY,
}
};
@@ -584,6 +591,10 @@ static int ssd132x_init(struct ssd130x_device *ssd130x)
static int ssd133x_init(struct ssd130x_device *ssd130x)
{
+ u8 remap = SSD133X_SET_REMAP_COM_SPLIT |
+ FIELD_PREP(SSD133X_SET_REMAP_COLOR_DEPTH_MASK,
+ ssd130x->device_info->format_rgb565 ?
+ SSD133X_COLOR_DEPTH_65K : SSD133X_COLOR_DEPTH_256);
const u8 cmds[] = {
2, SSD133X_CONTRAST_A, 0x91,
2, SSD133X_CONTRAST_B, 0x50,
@@ -595,9 +606,9 @@ static int ssd133x_init(struct ssd130x_device *ssd130x)
* Horizontal Address Increment
* Normal order SA,SB,SC (e.g. RGB)
* COM Split Odd Even
- * 256 color format
+ * 256 or 65k color format, depending on the variant
*/
- 2, SSD13XX_SET_SEG_REMAP, 0x20,
+ 2, SSD13XX_SET_SEG_REMAP, remap,
2, SSD133X_SET_DISPLAY_START, 0x00,
2, SSD133X_SET_DISPLAY_OFFSET, 0x00,
1, SSD133X_SET_DISPLAY_NORMAL,
@@ -794,14 +805,20 @@ static int ssd133x_update_rect(struct ssd130x_device *ssd130x,
* COM0 to COM[N - 1] are the rows and SEG0 to SEG[M - 1] are
* the columns.
*
- * Each Segment has a 8-bit pixel and each Common output has a
- * row of pixels. When using the (default) horizontal address
- * increment mode, each byte of data sent to the controller has
- * a Segment (e.g: SEG0).
+ * Each Segment holds one pixel and each Common output has a row
+ * of pixels. A pixel is 8 bits (one byte) in the 256 color
+ * (RGB332) format or 16 bits (two bytes) in the 65k color
+ * (RGB565) format. When using the (default) horizontal address
+ * increment mode, the pixel data is sent Segment by Segment
+ * (e.g: SEG0 first).
*
* When using the 256 color depth format, each pixel contains 3
* sub-pixels for color A, B and C. These have 3 bit, 3 bit and
* 2 bits respectively.
+ *
+ * When using the 65k color depth format, each pixel contains 3
+ * sub-pixels for color A, B and C. These have 5 bit, 6 bit and
+ * 5 bits respectively.
*/
/* Set column start and end */
@@ -872,9 +889,24 @@ static void ssd132x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
ssd130x_write_data(ssd130x, data_array, columns * height);
}
+/*
+ * The SSD133X family can drive the panel in either RGB332 (1 byte per pixel)
+ * or RGB565 (2 bytes per pixel). The format is a per-variant policy choice
+ * selected through ssd130x_deviceinfo::format_rgb565, not a capability probe.
+ * Centralize the choice here so that the buffer sizing (allocation, clear and
+ * blit pitch) can never disagree.
+ */
+static const struct drm_format_info *ssd133x_format_info(struct ssd130x_device *ssd130x)
+{
+ if (ssd130x->device_info->format_rgb565)
+ return drm_format_info(DRM_FORMAT_RGB565);
+
+ return drm_format_info(DRM_FORMAT_RGB332);
+}
+
static void ssd133x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
{
- const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
+ const struct drm_format_info *fi = ssd133x_format_info(ssd130x);
unsigned int pitch;
if (!fi)
@@ -945,7 +977,7 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
struct drm_format_conv_state *fmtcnv_state)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
- const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
+ const struct drm_format_info *fi = ssd133x_format_info(ssd130x);
unsigned int dst_pitch;
struct iosys_map dst;
int ret = 0;
@@ -956,7 +988,10 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
dst_pitch = drm_format_info_min_pitch(fi, 0, drm_rect_width(rect));
iosys_map_set_vaddr(&dst, data_array);
- drm_fb_xrgb8888_to_rgb332(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+ if (ssd130x->device_info->format_rgb565)
+ drm_fb_xrgb8888_to_rgb565be(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+ else
+ drm_fb_xrgb8888_to_rgb332(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch);
@@ -1414,7 +1449,7 @@ static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc,
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct ssd130x_crtc_state *ssd130x_state = to_ssd130x_crtc_state(crtc_state);
- const struct drm_format_info *fi = drm_format_info(DRM_FORMAT_RGB332);
+ const struct drm_format_info *fi = ssd133x_format_info(ssd130x);
unsigned int pitch;
int ret;
diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h
index a4554018bb2a..b0b487c06e04 100644
--- a/drivers/gpu/drm/solomon/ssd130x.h
+++ b/drivers/gpu/drm/solomon/ssd130x.h
@@ -54,6 +54,13 @@ struct ssd130x_deviceinfo {
bool need_pwm;
bool need_chargepump;
bool page_mode_only;
+ /*
+ * Per-variant output format selector for the SSD133X data path. The
+ * hardware can drive the panel in RGB332 (1 byte/pixel) or RGB565
+ * (2 bytes/pixel); this is a policy choice per variant, not a
+ * capability probe. When set, the variant is driven at RGB565.
+ */
+ bool format_rgb565;
enum ssd130x_family_ids family_id;
};
--
2.54.0
^ permalink raw reply related
* [PATCH v2 1/4] dt-bindings: display: Add Solomon SSD1351 OLED controller
From: Amit Barzilai @ 2026-06-22 15:25 UTC (permalink / raw)
To: javierm, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt, andy, gregkh, deller
Cc: azuddinadam, chintanlike, dri-devel, devicetree, linux-kernel,
linux-fbdev, linux-staging, Amit Barzilai
In-Reply-To: <20260622152506.78627-1-amit.barzilai22@gmail.com>
Add a device tree binding for the Solomon SSD1351, a 128x128 65k-color
RGB OLED display controller driven over a 4-wire SPI bus. The binding
builds on the shared solomon,ssd-common.yaml properties already used by
the other Solomon display controllers.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Amit Barzilai <amit.barzilai22@gmail.com>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
---
.../bindings/display/solomon,ssd1351.yaml | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/solomon,ssd1351.yaml
diff --git a/Documentation/devicetree/bindings/display/solomon,ssd1351.yaml b/Documentation/devicetree/bindings/display/solomon,ssd1351.yaml
new file mode 100644
index 000000000000..80850c2ab5b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/solomon,ssd1351.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/solomon,ssd1351.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Solomon SSD1351 OLED Display Controller
+
+maintainers:
+ - Amit Barzilai <amit.barzilai22@gmail.com>
+ - Javier Martinez Canillas <javierm@redhat.com>
+
+allOf:
+ - $ref: solomon,ssd-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - solomon,ssd1351
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ oled@0 {
+ compatible = "solomon,ssd1351";
+ reg = <0x0>;
+ reset-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
+ dc-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>;
+ spi-max-frequency = <10000000>;
+ };
+ };
--
2.54.0
^ permalink raw reply related
* [PATCH v2 0/4] drm/ssd130x: Add support for the Solomon SSD1351 OLED controller
From: Amit Barzilai @ 2026-06-22 15:25 UTC (permalink / raw)
To: javierm, maarten.lankhorst, mripard, tzimmermann, airlied, simona,
robh, krzk+dt, conor+dt, andy, gregkh, deller
Cc: azuddinadam, chintanlike, dri-devel, devicetree, linux-kernel,
linux-fbdev, linux-staging, Amit Barzilai
This series adds support for the Solomon SSD1351, a 128x128 65k-color
RGB OLED controller, to the ssd130x DRM driver, and removes the legacy
fbtft fb_ssd1351 driver it supersedes.
v1 [1] was a self-contained ssd1351.c driver. Following Javier's review,
the SSD1351 is instead folded into ssd130x as a new color family, which
also brings 65k color to the existing SSD1331. The work is split as:
- Patch 1 adds the device tree binding. It was previously posted
standalone as a v2 [2]; it is folded into this series here, as Conor
asked, so the binding lands together with the driver and the fbtft
removal.
- Patch 2 changes the SSD133X family to drive RGB565 instead of
RGB332, via a per-variant flag in deviceinfo. The SSD1331 is the
only current member and gains 65k color from this.
- Patch 3 adds the SSD1351 as a new SSD135X_FAMILY, reusing the
SSD133X plane/CRTC and blit/clear helpers. The only data-path
difference is the explicit Write RAM command (0x5c) the SSD1351
needs before pixel data; it also gets its own init sequence and a
longer post-reset settle delay.
- Patch 4 removes the now-redundant staging fbtft fb_ssd1351 driver.
Testing:
- The SSD1351 (patches 1 and 3) is tested on hardware.
- The SSD1331 RGB565 change (patch 2) is compile-tested only; I do not
currently have a working SSD1331 panel. Javier has kindly offered to
test it on his SSD1331.
Dependency:
The SSD1351 reuses ssd133x_update_rect(), which programs the column
and row *end* address as a relative offset rather than an absolute
coordinate. This breaks partial updates that do not start at (0,0). A
separate fix is posted at [3]; until it lands, the SSD1351 shows the
same partial-redraw artifacts. This series applies independently of
that fix, but the two are best merged together.
Based on drm-misc-next.
[1] standalone v1 driver:
https://lore.kernel.org/dri-devel/20260615181253.97551-1-amit.barzilai22@gmail.com
[2] standalone v2 binding:
https://lore.kernel.org/dri-devel/20260615175620.88828-1-amit.barzilai22@gmail.com
[3] ssd132x/ssd133x update_rect end-address fix:
https://lore.kernel.org/dri-devel/20260622122604.32500-1-amit.barzilai22@gmail.com
---
Changes since v1:
- Fold the SSD1351 into ssd130x as a new SSD135X family instead of a
standalone ssd1351.c driver (per Javier).
- Add RGB565 to the SSD133X family, so the SSD1331 also gains 65k color.
- Drop native 256k color (no matching DRM fourcc) and the 0/180
rotation support, to keep the series focused; both can return later.
- Binding: drop solomon,width / solomon,height (deducible from the
compatible) and the rotation property (no consumer), per Krzysztof;
use dt-bindings/gpio/gpio.h flag defines in the example.
- Remove the staging fbtft fb_ssd1351 driver in the same series (per
Conor).
Amit Barzilai (4):
dt-bindings: display: Add Solomon SSD1351 OLED controller
drm/ssd130x: Add RGB565 support to SSD133X family
drm/ssd130x: Add SSD135X_FAMILY and SSD1351 support
staging: fbtft: remove fb_ssd1351 driver
.../bindings/display/solomon,ssd1351.yaml | 42 +++
drivers/gpu/drm/solomon/ssd130x-spi.c | 7 +
drivers/gpu/drm/solomon/ssd130x.c | 269 +++++++++++++++---
drivers/gpu/drm/solomon/ssd130x.h | 12 +-
drivers/staging/fbtft/Kconfig | 5 -
drivers/staging/fbtft/Makefile | 1 -
drivers/staging/fbtft/fb_ssd1351.c | 240 ----------------
7 files changed, 283 insertions(+), 293 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/solomon,ssd1351.yaml
delete mode 100644 drivers/staging/fbtft/fb_ssd1351.c
--
2.54.0
^ permalink raw reply
* Re: [PATCH] staging: fbtft: fix parenthesis alignment in fb_tinylcd.c
From: Dan Carpenter @ 2026-06-22 9:05 UTC (permalink / raw)
To: Aditya Chari
Cc: andy, gregkh, dri-devel, linux-fbdev, linux-staging, linux-kernel
In-Reply-To: <20260621062945.42519-1-adi25charis@gmail.com>
On Sun, Jun 21, 2026 at 11:59:45AM +0530, Aditya Chari wrote:
> Fix a checkpatch.pl
> CHECK:PARENTHESIS_ALIGNMENT warning by aligning the wrapped
> argument list of write_reg() with the line above it.
>
> Signed-off-by: Aditya Chari <adi25charis@gmail.com>
> ---
> drivers/staging/fbtft/fb_tinylcd.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c
> index afa8f1c74..d58b12472 100644
> --- a/drivers/staging/fbtft/fb_tinylcd.c
> +++ b/drivers/staging/fbtft/fb_tinylcd.c
> @@ -38,7 +38,7 @@ static int init_display(struct fbtft_par *par)
> write_reg(par, 0xE5, 0x00);
> write_reg(par, 0xF0, 0x36, 0xA5, 0x53);
> write_reg(par, 0xE0, 0x00, 0x35, 0x33, 0x00, 0x00, 0x00,
> - 0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
> + 0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
The original is deliberate. Just leave it as-is, please.
regards,
dan carpenter
^ permalink raw reply
* [PATCH] fbdev: fix use-after-free of fb_display[i].mode in store_modes()
From: Ian Bridges @ 2026-06-22 7:45 UTC (permalink / raw)
To: Simona Vetter, Helge Deller, linux-fbdev, dri-devel, linux-kernel
store_modes() replaces a framebuffer's modelist and frees the old entries
with fb_destroy_modelist(). fb_display[i].mode is a back-pointer into a
modelist entry, and it is not cleared when a console is detached from the
framebuffer (unbinding fbcon clears con2fb_map[] but leaves
fb_display[i].mode set).
store_modes() relies on fb_new_modelist() -> fbcon_new_modelist() to move
the back-pointers to the new list, but that only re-points consoles still
mapped to the framebuffer. A console that is no longer mapped to it is
skipped, so once fb_destroy_modelist() frees the old list that console's
fb_display[i].mode dangles. A later FBIOPUT_VSCREENINFO with
FB_ACTIVATE_INV_MODE makes fbcon_mode_deleted() read it through
fb_mode_is_equal(), a use-after-free.
Commit a1f305893074 ("fbcon: Set fb_display[i]->mode to NULL when the
mode is released") fixed the same use-after-free for the framebuffer
unregister path. Do the same in store_modes() by clearing the stale
pointers with fbcon_delete_modelist() before freeing the old list.
Reported-by: syzbot+81c7c6b52649fd07299d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=81c7c6b52649fd07299d
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ian Bridges <icb@fastmail.org>
---
This patch contains a proposed fix for a crash reported by syzbot in
fb_mode_is_equal().
The file names and offsets in this description are from commit
8cd9520d35a6c38db6567e97dd93b1f11f185dc6 on the for-next branch of
git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev.git
I also have a deterministic reproducer that triggers the crash under virtme-ng
with a KASAN kernel and two framebuffers (vfb as fb0, bochs-drm as fb1). It
performs six ioctl/sysfs operations, listed at the end of "The Bug". The free
is driven by a write to /sys/class/graphics/fb0/modes, which needs write access
to that file. The reproducer was written with the help of a coding agent
(Claude Code).
This crash has the same KASAN signature as the one fixed by commit
a1f305893074 ("fbcon: Set fb_display[i]->mode to NULL when the mode is
released"), but reaches it through a different free path. It is still reported
on kernels that contain that commit.
The Bug
fb_display[i].mode is a back-pointer into a struct fb_modelist entry on
info->modelist. var_to_display() (fbcon.c:989) sets it when a console is
attached to a framebuffer. fbcon_mode_deleted() reads it through
fb_mode_is_equal() (fbcon.c:2750, modedb.c:931).
store_modes() is the .store handler for /sys/class/graphics/fbN/modes
(fbsysfs.c:94). It splices the current modelist into a local old_list, builds
the new list, and on success frees old_list with fb_destroy_modelist()
(fbsysfs.c:115). To keep the back-pointers valid it relies on
fb_new_modelist() -> fbcon_new_modelist() (fbmem.c:737), but that only re-points
consoles still mapped to this framebuffer (fbcon_info_from_console(i) != info,
fbcon.c:3038).
fb_display[i].mode is not cleared when a console is detached. Unbinding fbcon
runs fbcon_release_all() (fbcon.c:1248), which sets con2fb_map[i] = -1 but
leaves fb_display[i].mode pointing into the former framebuffer's modelist. Such
a console is skipped by fbcon_new_modelist(), so once fb_destroy_modelist()
frees old_list its fb_display[i].mode dangles.
The minimal reproduction is six operations on two framebuffers (fb0, fb1). All
six are required (verified against source and by removing each in turn). fbcon
starts bound with the consoles mapped to fb0. The operations all act on one
unused console i (no allocated vc_data). An allocated console would be
re-initialised, and its fb_display[i].mode reset, by the takeover in step 5.
Pre-allocating all consoles does not reproduce.
1. FBIOPUT_CON2FBMAP, the console -> fb1
set_con2fb_map() reaches var_to_display(), which points fb_display[i].mode
at an fb1 modelist entry (var_to_display() uses the console index, so it
runs even though the console has no vc_data)
2. FBIOPUT_CON2FBMAP, the console -> fb0
fb_display[i].mode now points at an fb0 modelist entry M
3. echo 0 > /sys/class/vtconsole/vtcon1/bind
fbcon_release_all() sets con2fb_map[i] = -1 and leaves fb_display[i].mode
pointing at M
4. write a videomode array to /sys/class/graphics/fb0/modes
The buffer holds raw struct fb_videomode entries. At least one must be
valid so fb_new_modelist() keeps it and store_modes() takes the success
path that frees old_list (an empty or invalid set takes the failure path
that restores it, and nothing is freed). fbcon_new_modelist() skips the
console (con2fb_map[i] == -1), then fb_destroy_modelist() frees M, so
fb_display[i].mode now dangles
5. FBIOPUT_CON2FBMAP, the console -> fb1
fbcon is unbound, so set_con2fb_map() takes the do_fbcon_takeover() branch
(fbcon.c:931). do_fbcon_takeover() sets con2fb_map[] = info_idx for the
whole range (fbcon.c:619), so the console is now mapped to fb1. The mode is
reset only via fbcon_init() during the takeover, which runs for allocated
consoles only, so this unused console keeps its dangling fb_display[i].mode
6. FBIOPUT_VSCREENINFO on fb1 with FB_ACTIVATE_INV_MODE and a mode different
from fb1's current var (otherwise fb_set_var() returns early without
calling fbcon_mode_deleted(), to avoid deleting the current var's
videomode)
fb_set_var() (fbmem.c:240,248) -> fbcon_mode_deleted() walks fb1's consoles
and reads the dangling fb_display[i].mode via fb_mode_is_equal(), a
use-after-free
The free is in store_modes() on fb0, and the read in fbcon_mode_deleted() on
fb1.
The Proposed Fix
A framebuffer's modelist is freed in two places, do_unregister_framebuffer()
(fbmem.c:548) and store_modes() (fbsysfs.c:115). Commit a1f305893074 added the
helper fbcon_delete_modelist() to clear the stale fb_display[i].mode pointers,
but called it from the first site only (fbmem.c:547). The store_modes() site
was raised as a potential issue during review of that commit [1] but
assumed safe.
Fix store_modes() the same way by clearing the stale pointers with
fbcon_delete_modelist() before freeing old_list.
The fix was verified with the reproducer. The unpatched kernel reports the
slab-use-after-free read, freed by fb_destroy_modelist() from store_modes().
The patched kernel produces no use-after-free.
The bug predates git history. Both the fb_display[i].mode back-pointer and the
store_modes() swap-and-free are present in the initial 2.6.12-rc2 import
(1da177e4c3f4), so there is no Fixes tag, matching a1f305893074. The fix calls
fbcon_delete_modelist(), which a1f305893074 added, so a stable backport needs
that commit first.
[1] https://lkml.org/lkml/2025/9/23/786
drivers/video/fbdev/core/fbsysfs.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c
index baa2bae0fb5b..c761dcf21daf 100644
--- a/drivers/video/fbdev/core/fbsysfs.c
+++ b/drivers/video/fbdev/core/fbsysfs.c
@@ -111,8 +111,10 @@ static ssize_t store_modes(struct device *device,
if (fb_new_modelist(fb_info)) {
fb_destroy_modelist(&fb_info->modelist);
list_splice(&old_list, &fb_info->modelist);
- } else
+ } else {
+ fbcon_delete_modelist(&old_list);
fb_destroy_modelist(&old_list);
+ }
unlock_fb_info(fb_info);
console_unlock();
--
2.47.3
^ permalink raw reply related
* [PATCH] video: hpfb: Unregister DIO driver on init failure
From: Haoxiang Li @ 2026-06-22 6:49 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Haoxiang Li
hpfb_init() registers the DIO driver via dio_register_driver().
If a later error occurs, the function returns directly without
unregistering the DIO driver. Unregister the DIO driver before
returning from these error paths.
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
drivers/video/fbdev/hpfb.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbdev/hpfb.c b/drivers/video/fbdev/hpfb.c
index a1144b150982..1405712e5b4f 100644
--- a/drivers/video/fbdev/hpfb.c
+++ b/drivers/video/fbdev/hpfb.c
@@ -407,10 +407,13 @@ static int __init hpfb_init(void)
err = copy_from_kernel_nofault(&i, (unsigned char *)INTFBVADDR + DIO_IDOFF, 1);
if (!err && (i == DIO_ID_FBUFFER) && topcat_sid_ok(sid = DIO_SECID(INTFBVADDR))) {
- if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat"))
+ if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat")) {
+ dio_unregister_driver(&hpfb_driver);
return -EBUSY;
+ }
printk(KERN_INFO "Internal Topcat found (secondary id %02x)\n", sid);
if (hpfb_init_one(INTFBPADDR, INTFBVADDR)) {
+ dio_unregister_driver(&hpfb_driver);
return -ENOMEM;
}
}
--
2.25.1
^ permalink raw reply related
* [PATCH] video: fbdev: pm2fb: unwind WC setup on probe failure
From: Haoxiang Li @ 2026-06-21 7:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, linux-kernel, Haoxiang Li
Add arch_phys_wc_del() on error path to keep the
write-combining setup balanced when later probe
steps fail.
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
drivers/video/fbdev/pm2fb.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c
index f34429829b7d..82408503c281 100644
--- a/drivers/video/fbdev/pm2fb.c
+++ b/drivers/video/fbdev/pm2fb.c
@@ -1711,6 +1711,7 @@ static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err_exit_both:
kfree(info->pixmap.addr);
err_exit_pixmap:
+ arch_phys_wc_del(default_par->wc_cookie);
iounmap(info->screen_base);
release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
err_exit_mmio:
--
2.25.1
^ permalink raw reply related
* [PATCH] staging: fbtft: fix parenthesis alignment in fb_tinylcd.c
From: Aditya Chari @ 2026-06-21 6:29 UTC (permalink / raw)
To: andy, gregkh
Cc: dri-devel, linux-fbdev, linux-staging, linux-kernel, Aditya Chari
Fix a checkpatch.pl
CHECK:PARENTHESIS_ALIGNMENT warning by aligning the wrapped
argument list of write_reg() with the line above it.
Signed-off-by: Aditya Chari <adi25charis@gmail.com>
---
drivers/staging/fbtft/fb_tinylcd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c
index afa8f1c74..d58b12472 100644
--- a/drivers/staging/fbtft/fb_tinylcd.c
+++ b/drivers/staging/fbtft/fb_tinylcd.c
@@ -38,7 +38,7 @@ static int init_display(struct fbtft_par *par)
write_reg(par, 0xE5, 0x00);
write_reg(par, 0xF0, 0x36, 0xA5, 0x53);
write_reg(par, 0xE0, 0x00, 0x35, 0x33, 0x00, 0x00, 0x00,
- 0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
+ 0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
fsleep(250);
--
2.53.0
^ permalink raw reply related
* [staging] staging: sm750fb: fix remaining CamelCase issues
From: Arnav Kapoor @ 2026-06-21 4:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Sudip Mukherjee, Teddy Wang
Cc: linux-staging, linux-fbdev, linux-kernel, Arnav Kapoor
Rename the remaining CamelCase variables and constants to follow
kernel coding style:
- powerMode → power_mode
- setAllEngOff → set_all_eng_off
- resetMemory → reset_memory
- sm750_doubleTFT → SM750_DOUBLE_TFT
- sm750_dualTFT → SM750_DUAL_TFT
Signed-off-by: Arnav Kapoor <kapoorarnav43@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 125ad1062..f034d3278 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -859,9 +859,9 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
sm750_dev->init_parm.chip_clk = 0;
sm750_dev->init_parm.mem_clk = 0;
sm750_dev->init_parm.master_clk = 0;
- sm750_dev->init_parm.powerMode = 0;
- sm750_dev->init_parm.setAllEngOff = 0;
- sm750_dev->init_parm.resetMemory = 1;
+ sm750_dev->init_parm.power_mode = 0;
+ sm750_dev->init_parm.set_all_eng_off = 0;
+ sm750_dev->init_parm.reset_memory = 1;
/* defaultly turn g_hwcursor on for both view */
g_hwcursor = 3;
@@ -880,9 +880,9 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
} else if (!strncmp(opt, "nocrt", strlen("nocrt"))) {
sm750_dev->nocrt = 1;
} else if (!strncmp(opt, "36bit", strlen("36bit"))) {
- sm750_dev->pnltype = sm750_doubleTFT;
+ sm750_dev->pnltype = SM750_DOUBLE_TFT;
} else if (!strncmp(opt, "18bit", strlen("18bit"))) {
- sm750_dev->pnltype = sm750_dualTFT;
+ sm750_dev->pnltype = SM750_DUAL_TFT;
} else if (!strncmp(opt, "24bit", strlen("24bit"))) {
sm750_dev->pnltype = sm750_24TFT;
} else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) {
--
2.53.0
^ permalink raw reply related
* [staging] staging: sm750fb: rename pvMem to vram and pvReg to reg
From: Arnav Kapoor @ 2026-06-21 4:50 UTC (permalink / raw)
To: Greg Kroah-Hartman, Sudip Mukherjee, Teddy Wang
Cc: linux-staging, linux-fbdev, linux-kernel, Arnav Kapoor
Fix CamelCase issues reported by checkpatch.
Signed-off-by: arnavk23 <kapoorarnav43@gmail.com>
Signed-off-by: Arnav Kapoor <kapoorarnav43@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 19c3da654..125ad1062 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -622,26 +622,26 @@ static int sm750fb_set_drv(struct lynxfb_par *par)
output->paths = sm750_pnc;
crtc->channel = sm750_primary;
crtc->o_screen = 0;
- crtc->v_screen = sm750_dev->pvMem;
+ crtc->v_screen = sm750_dev->vram;
break;
case sm750_simul_sec:
output->paths = sm750_pnc;
crtc->channel = sm750_secondary;
crtc->o_screen = 0;
- crtc->v_screen = sm750_dev->pvMem;
+ crtc->v_screen = sm750_dev->vram;
break;
case sm750_dual_normal:
if (par->index == 0) {
output->paths = sm750_panel;
crtc->channel = sm750_primary;
crtc->o_screen = 0;
- crtc->v_screen = sm750_dev->pvMem;
+ crtc->v_screen = sm750_dev->vram;
} else {
output->paths = sm750_crt;
crtc->channel = sm750_secondary;
/* not consider of padding stuffs for o_screen,need fix */
crtc->o_screen = sm750_dev->vidmem_size >> 1;
- crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;
+ crtc->v_screen = sm750_dev->vram + crtc->o_screen;
}
break;
case sm750_dual_swap:
@@ -649,7 +649,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par)
output->paths = sm750_panel;
crtc->channel = sm750_secondary;
crtc->o_screen = 0;
- crtc->v_screen = sm750_dev->pvMem;
+ crtc->v_screen = sm750_dev->vram;
} else {
output->paths = sm750_crt;
crtc->channel = sm750_primary;
@@ -657,7 +657,7 @@ static int sm750fb_set_drv(struct lynxfb_par *par)
* need fix
*/
crtc->o_screen = sm750_dev->vidmem_size >> 1;
- crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;
+ crtc->v_screen = sm750_dev->vram + crtc->o_screen;
}
break;
default:
@@ -755,13 +755,13 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index)
* must be set after crtc member initialized
*/
crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
- crtc->cursor.mmio = sm750_dev->pvReg +
+ crtc->cursor.mmio = sm750_dev->reg +
0x800f0 + (int)crtc->channel * 0x140;
crtc->cursor.max_h = 64;
crtc->cursor.max_w = 64;
crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
- crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
+ crtc->cursor.vstart = sm750_dev->vram + crtc->cursor.offset;
memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
if (!g_hwcursor)
@@ -1028,7 +1028,7 @@ static int lynxfb_pci_probe(struct pci_dev *pdev,
sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,
sm750_dev->vidmem_size);
- memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);
+ memset_io(sm750_dev->vram, 0, sm750_dev->vidmem_size);
pci_set_drvdata(pdev, sm750_dev);
@@ -1059,8 +1059,8 @@ static void lynxfb_pci_remove(struct pci_dev *pdev)
sm750fb_framebuffer_release(sm750_dev);
arch_phys_wc_del(sm750_dev->mtrr.vram);
- iounmap(sm750_dev->pvReg);
- iounmap(sm750_dev->pvMem);
+ iounmap(sm750_dev->reg);
+ iounmap(sm750_dev->vram);
pci_release_region(pdev, 1);
kfree(g_settings);
}
--
2.53.0
^ permalink raw reply related
* [PATCH] staging: sm750fb: make g_fbmode array const
From: Arnav Kapoor @ 2026-06-21 4:44 UTC (permalink / raw)
To: Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman,
open list:STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER,
open list:STAGING SUBSYSTEM, open list
Cc: Arnav Kapoor,
open list:STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER,
open list:STAGING SUBSYSTEM, open list
From: arnavk23 <kapoorarnav43@gmail.com>
checkpatch complains that a static const char * array should
probably be static const char * const.
Signed-off-by: Arnav Kapoor <kapoorarnav43@gmail.com>
Signed-off-by: arnavk23 <kapoorarnav43@gmail.com>
---
drivers/staging/sm750fb/sm750.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 9f3e3d37e..19c3da654 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -33,7 +33,7 @@
static int g_hwcursor = 1;
static int g_noaccel;
static int g_nomtrr;
-static const char *g_fbmode[] = {NULL, NULL};
+static const char * const g_fbmode[] = {NULL, NULL};
static const char *g_def_fbmode = "1024x768-32@60";
static char *g_settings;
static int g_dualview;
--
2.53.0
^ permalink raw reply related
* [syzbot] Monthly fbdev report (Jun 2026)
From: syzbot @ 2026-06-20 12:32 UTC (permalink / raw)
To: deller, dri-devel, linux-fbdev, linux-kernel, syzkaller-bugs
Hello fbdev maintainers/developers,
This is a 31-day syzbot report for the fbdev subsystem.
All related reports/information can be found at:
https://syzkaller.appspot.com/upstream/s/fbdev
During the period, 1 new issues were detected and 0 were fixed.
In total, 5 issues are still open and 29 have already been fixed.
Some of the still happening issues:
Ref Crashes Repro Title
<1> 5429 Yes KASAN: vmalloc-out-of-bounds Write in imageblit (6)
https://syzkaller.appspot.com/bug?extid=5a40432dfe8f86ee657a
<2> 1600 Yes KASAN: slab-out-of-bounds Read in fbcon_prepare_logo
https://syzkaller.appspot.com/bug?extid=0c815b25cdb3678e7083
<3> 271 No KASAN: vmalloc-out-of-bounds Write in fillrect
https://syzkaller.appspot.com/bug?extid=7a63ce155648954e749b
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
To disable reminders for individual bugs, reply with the following command:
#syz set <Ref> no-reminders
To change bug's subsystems, reply with:
#syz set <Ref> subsystems: new-subsystem
You may send multiple commands in a single email message.
^ permalink raw reply
* Re: [PATCH v3] staging: sm750fb: rename pv_reg to io_base
From: neha arora @ 2026-06-20 7:43 UTC (permalink / raw)
To: Dan Carpenter
Cc: sudipm.mukherjee, teddy.wang, gregkh, linux-fbdev, linux-staging,
linux-kernel
In-Reply-To: <CAOWJOpt2CW=-zybOrPr2pwfPVQFcd+j3YoNP+rMyAar_2kJtbw@mail.gmail.com>
Hi everyone,
After further investigation, and feedback from maintainers, I have
concluded that porting DRM to the SM750, will cause a huge performance
regression as DRM has no interface for 2D acceleration, making the CPU
do graphics, which is slow, the only way to expose 2D acceleration is
custom IOCTLs, which is not preferable.
So, it is best to keep using the fbdev stack for the SM750.
Regards,
Onish
On Wed, Jun 17, 2026 at 8:30 AM neha arora <neharora23587@gmail.com> wrote:
>
> Hi Dan,
>
> Please disregard my previous patch submission.
>
> I am currently working on a complete rewrite of the sm750fb staging
> driver into a modern, KMS-based DRM driver (sm750drm). Because this
> is a complete architectural overhaul, I am building it outside the
> staging tree structure.
>
> Once the sm750drm driver framework is stable and ready for a RFC
> (Request for Comments), I will submit the entire new subsystem patch series
> directly to the dri-devel mailing list and CC you.
>
> Thank you for your review and your time!
>
> Regards,
> Onish
>
>
> On Mon, Jun 15, 2026 at 12:34 PM Dan Carpenter <error27@gmail.com> wrote:
> >
> > On Sun, Jun 14, 2026 at 12:45:05AM +0530, neha arora wrote:
> > > Hi everyone,
> > >
> > > Just following up on this patch to ensure it didn't get lost in the queue.
> > > Please let me know if any changes or a V4 are needed.
> > >
> >
> > It doesn't apply to linux-next. Did you work against the lastest
> > devel-next tree?
> >
> > regards,
> > dan carpenter
> >
^ permalink raw reply
* Re: [PATCH] fbcon: Avoid OOB font access if console rotation fails
From: Greg KH @ 2026-06-18 10:35 UTC (permalink / raw)
To: rdhan
Cc: Andrew Morton, Antonino Daplas, stable, dri-devel, linux-fbdev,
linux-kernel, Thomas Zimmermann, Helge Deller
In-Reply-To: <20260618-prep-base-v5-15-209-v1-1-cfcf596dca7a@smu.edu.sg>
On Thu, Jun 18, 2026 at 02:30:17PM +0800, HAN Ruidong via B4 Relay wrote:
> From: HAN Ruidong <rdhan@smu.edu.sg>
>
> [ Upstream commit e4ef723d8975a2694cc90733a6b888a5e2841842 ]
>
> Clear the font buffer if the reallocation during console rotation fails
> in fbcon_rotate_font(). The putcs implementations for the rotated buffer
> will return early in this case. See [1] for an example.
>
> Currently, fbcon_rotate_font() keeps the old buffer, which is too small
> for the rotated font. Printing to the rotated console with a high-enough
> character code will overflow the font buffer.
>
> v2:
> - fix typos in commit message
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> Fixes: 6cc50e1c5b57 ("[PATCH] fbcon: Console Rotation - Add support to rotate font bitmap")
> Cc: stable@vger.kernel.org # v2.6.15+
> Link: https://elixir.bootlin.com/linux/v6.19/source/drivers/video/fbdev/core/fbcon_ccw.c#L144 # [1]
> Signed-off-by: Helge Deller <deller@gmx.de>
> [ renamed `par` to `ops` to match the v5.15 local pointer name ]
> Signed-off-by: HAN Ruidong <rdhan@smu.edu.sg>
> ---
> drivers/video/fbdev/core/fbcon_rotate.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
What stable tree(s) is this for?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH] firmware: sysfb: Mark CONFIG_SYSFB_SIMPLEFB as deprecated
From: Thomas Zimmermann @ 2026-06-18 7:21 UTC (permalink / raw)
To: Julian Braha, javierm, sima, airlied; +Cc: dri-devel, linux-fbdev, linux-kernel
In-Reply-To: <7da7d892-3b3c-4f83-b3b0-93eafe14e15e@gmail.com>
Hi
Am 17.06.26 um 18:45 schrieb Julian Braha:
> Hi Thomas,
>
> On 6/17/26 09:17, Thomas Zimmermann wrote:
>> Mark CONFIG_SYSFB_SIMPLEFB as deprecated. Enabling it allows to
>> config SYSFB_SIMPLEFB
>> - bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
>> + bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)"
> In v2, I think it also makes sense to add the 'transitional' attribute
> to the Kconfig entry to help phase it out.
>
> Also see this patch message for an explanation of 'transitional':
> https://lore.kernel.org/all/20250923213422.1105654-2-kees@kernel.org/
Thanks for this pointer; I wasn't aware of this feature. This will be
really useful to not accidentally leave users without display output.
I've played with the option, but it does not work are purely as shown in
the example. I think it's because EFIDRM and VESADRM are existing
options. So they already have a value.
What I did instead was to add new internal config options. They depend
on the now-transitional SYSFB_SIMPLEFB and select the correct drivers.
Once SYSFB_SIMPLEFB goes away, these options can go away as well.
Best regards
Thomas
>
> - Julian Braha
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply
* [PATCH] fbcon: Avoid OOB font access if console rotation fails
From: HAN Ruidong via B4 Relay @ 2026-06-18 6:30 UTC (permalink / raw)
To: Andrew Morton, Antonino Daplas, stable
Cc: dri-devel, linux-fbdev, linux-kernel, Thomas Zimmermann,
Helge Deller, HAN Ruidong
From: HAN Ruidong <rdhan@smu.edu.sg>
[ Upstream commit e4ef723d8975a2694cc90733a6b888a5e2841842 ]
Clear the font buffer if the reallocation during console rotation fails
in fbcon_rotate_font(). The putcs implementations for the rotated buffer
will return early in this case. See [1] for an example.
Currently, fbcon_rotate_font() keeps the old buffer, which is too small
for the rotated font. Printing to the rotated console with a high-enough
character code will overflow the font buffer.
v2:
- fix typos in commit message
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 6cc50e1c5b57 ("[PATCH] fbcon: Console Rotation - Add support to rotate font bitmap")
Cc: stable@vger.kernel.org # v2.6.15+
Link: https://elixir.bootlin.com/linux/v6.19/source/drivers/video/fbdev/core/fbcon_ccw.c#L144 # [1]
Signed-off-by: Helge Deller <deller@gmx.de>
[ renamed `par` to `ops` to match the v5.15 local pointer name ]
Signed-off-by: HAN Ruidong <rdhan@smu.edu.sg>
---
drivers/video/fbdev/core/fbcon_rotate.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbdev/core/fbcon_rotate.c b/drivers/video/fbdev/core/fbcon_rotate.c
index ec3c883400f7..4a06e71ae443 100644
--- a/drivers/video/fbdev/core/fbcon_rotate.c
+++ b/drivers/video/fbdev/core/fbcon_rotate.c
@@ -46,6 +46,10 @@ static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc)
info->fbops->fb_sync(info);
if (ops->fd_size < d_cellsize * len) {
+ kfree(ops->fontbuffer);
+ ops->fontbuffer = NULL;
+ ops->fd_size = 0;
+
dst = kmalloc_array(len, d_cellsize, GFP_KERNEL);
if (dst == NULL) {
@@ -54,7 +58,6 @@ static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc)
}
ops->fd_size = d_cellsize * len;
- kfree(ops->fontbuffer);
ops->fontbuffer = dst;
}
---
base-commit: dc027a595035729e290c0adffae363a653acde7c
change-id: 20260618-prep-base-v5-15-209-596d47c98637
Best regards,
--
HAN Ruidong <rdhan@smu.edu.sg>
^ permalink raw reply related
* Re: [PATCH] firmware: sysfb: Mark CONFIG_SYSFB_SIMPLEFB as deprecated
From: Julian Braha @ 2026-06-17 16:45 UTC (permalink / raw)
To: Thomas Zimmermann, javierm, sima, airlied
Cc: dri-devel, linux-fbdev, linux-kernel
In-Reply-To: <20260617081810.218168-1-tzimmermann@suse.de>
Hi Thomas,
On 6/17/26 09:17, Thomas Zimmermann wrote:
> Mark CONFIG_SYSFB_SIMPLEFB as deprecated. Enabling it allows to
> config SYSFB_SIMPLEFB
> - bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
> + bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)"
In v2, I think it also makes sense to add the 'transitional' attribute
to the Kconfig entry to help phase it out.
Also see this patch message for an explanation of 'transitional':
https://lore.kernel.org/all/20250923213422.1105654-2-kees@kernel.org/
- Julian Braha
^ permalink raw reply
* Re: [PATCH v5 02/14] mfd: lm3533: Remove driver specific regmap wrappers
From: Andy Shevchenko @ 2026-06-17 10:32 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Helge Deller,
Johan Hovold, dri-devel, linux-leds, devicetree, linux-kernel,
linux-iio, linux-fbdev
In-Reply-To: <20260617080031.99156-3-clamor95@gmail.com>
On Wed, Jun 17, 2026 at 11:00:19AM +0300, Svyatoslav Ryhel wrote:
> Remove driver-specific regmap wrappers in favor of using regmap helpers
> directly.
OK, let's go with this variant.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Some side notes below for the record.
...
> struct lm3533_led *led = to_lm3533_led(led_cdev);
> unsigned enable;
Oh, besides using the old way of declaring unsigned int, it most likely
just needs to be kstrtobool().
> u8 reg;
> - u8 mask;
> - u8 val;
> int ret;
>
> if (kstrtouint(buf, 0, &enable))
> return -EINVAL;
We should unshadow error codes (it may return more than -EINVAL).
> reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
> - mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK;
>
> - if (enable)
> - val = mask;
> - else
> - val = 0;
> -
...
> - if (kstrtou8(buf, 0, &val))
> + if (kstrtou32(buf, 0, &val))
> return -EINVAL;
Like in the previous case we should unshadow error codes.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v1] fbdev: vga16fb: Drop unused assignment of platform_device_id driver data
From: Uwe Kleine-König (The Capable Hub) @ 2026-06-17 9:44 UTC (permalink / raw)
To: Helge Deller
Cc: Ard Biesheuvel, Javier Garcia, Vivek BalachandharTN,
Thomas Zimmermann, linux-fbdev, dri-devel, linux-kernel
The driver explicitly sets the .driver_data member of struct
platform_device_id to zero without relying on that value. Drop these
unused assignments.
While touching this array unify spacing and usage of commas and use
named initializers for .name.
Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
---
drivers/video/fbdev/vga16fb.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c
index 22085d3668e8..cdd6b8de0ceb 100644
--- a/drivers/video/fbdev/vga16fb.c
+++ b/drivers/video/fbdev/vga16fb.c
@@ -1421,8 +1421,8 @@ static void vga16fb_remove(struct platform_device *dev)
}
static const struct platform_device_id vga16fb_driver_id_table[] = {
- {"ega-framebuffer", 0},
- {"vga-framebuffer", 0},
+ { .name = "ega-framebuffer" },
+ { .name = "vga-framebuffer" },
{ }
};
MODULE_DEVICE_TABLE(platform, vga16fb_driver_id_table);
base-commit: 4fa3f5fabb30bf00d7475d5a33459ea83d639bf9
--
2.47.3
^ permalink raw reply related
* Re: [PATCH] firmware: sysfb: Mark CONFIG_SYSFB_SIMPLEFB as deprecated
From: Thomas Zimmermann @ 2026-06-17 8:57 UTC (permalink / raw)
To: Javier Martinez Canillas, sima, airlied
Cc: dri-devel, linux-fbdev, linux-kernel
In-Reply-To: <877bnxzh9s.fsf@ocarina.mail-host-address-is-not-set>
Hi
Am 17.06.26 um 10:46 schrieb Javier Martinez Canillas:
> Thomas Zimmermann <tzimmermann@suse.de> writes:
>
> Hello Thomas,
>
> Thanks for the patch.
>
>> Mark CONFIG_SYSFB_SIMPLEFB as deprecated. Enabling it allows to
>> run simpledrm and simplefb on EFI/VESA framebuffers. Doing this
>> is discouraged in favor of using efidrm and vesadrm.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Thanks. The sashiko bot detected some inconsistencies in the docs, so
there will be a v2 of this patch.
>
> Do you know what the policy is for removal? For example,
> how many Linux releases should we wait for this to go away?
IDK. I'd wait for 2 or 3 releases after this lands and then remove it.
It's a small change with a clear upgrade patch after all.
Best regards
Thomas
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply
* Re: [PATCH] firmware: sysfb: Mark CONFIG_SYSFB_SIMPLEFB as deprecated
From: Javier Martinez Canillas @ 2026-06-17 8:46 UTC (permalink / raw)
To: Thomas Zimmermann, sima, airlied
Cc: dri-devel, linux-fbdev, linux-kernel, Thomas Zimmermann
In-Reply-To: <20260617081810.218168-1-tzimmermann@suse.de>
Thomas Zimmermann <tzimmermann@suse.de> writes:
Hello Thomas,
Thanks for the patch.
> Mark CONFIG_SYSFB_SIMPLEFB as deprecated. Enabling it allows to
> run simpledrm and simplefb on EFI/VESA framebuffers. Doing this
> is discouraged in favor of using efidrm and vesadrm.
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Do you know what the policy is for removal? For example,
how many Linux releases should we wait for this to go away?
--
Best regards,
Javier Martinez Canillas
Core Platforms
Red Hat
^ permalink raw reply
* [PATCH] firmware: sysfb: Mark CONFIG_SYSFB_SIMPLEFB as deprecated
From: Thomas Zimmermann @ 2026-06-17 8:17 UTC (permalink / raw)
To: javierm, sima, airlied
Cc: dri-devel, linux-fbdev, linux-kernel, Thomas Zimmermann
Mark CONFIG_SYSFB_SIMPLEFB as deprecated. Enabling it allows to
run simpledrm and simplefb on EFI/VESA framebuffers. Doing this
is discouraged in favor of using efidrm and vesadrm.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/firmware/Kconfig | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bbd2155d8483..b4e705abdc8f 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -184,32 +184,23 @@ config SYSFB
select SCREEN_INFO
config SYSFB_SIMPLEFB
- bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
+ bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)"
depends on X86 || EFI
select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
- user-guidance and debugging. Historically, x86 used the VESA BIOS
- Extensions and EFI-framebuffers for this, which are mostly limited
- to x86 BIOS or EFI systems.
- This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
- framebuffers so the new generic system-framebuffer drivers can be
- used instead. If the framebuffer is not compatible with the generic
- modes, it is advertised as fallback platform framebuffer so legacy
- drivers like efifb, vesafb and uvesafb can pick it up.
- If this option is not selected, all system framebuffers are always
- marked as fallback platform framebuffers as usual.
-
- Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will
- not be able to pick up generic system framebuffers if this option
- is selected. You are highly encouraged to enable simplefb as
- replacement if you select this option. simplefb can correctly deal
- with generic system framebuffers. But you should still keep vesafb
- and others enabled as fallback if a system framebuffer is
- incompatible with simplefb.
-
- If unsure, say Y.
+ user-guidance and debugging.
+
+ This option, if enabled, marks VBE/EFI framebuffers as system
+ framebuffers so the generic simpledrm driver can be used. If the
+ framebuffer is not compatible with the generic modes, it is
+ advertised as fallback platform framebuffer so regular drivers like
+ efidrm, vesadrm can pick it up.
+
+ This option is deprecated and will be removed in the near future. If
+ unsure, say N and select efidrm, vesadrm instead. The dedicated DRM
+ drivers provide the same functionality plus additional features.
config TH1520_AON_PROTOCOL
tristate "Always-On firmware protocol"
--
2.54.0
^ permalink raw reply related
* [PATCH v5 14/14] video: leds: backlight: lm3533: Support getting LED sources from DT
From: Svyatoslav Ryhel @ 2026-06-17 8:00 UTC (permalink / raw)
To: Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Helge Deller,
Svyatoslav Ryhel
Cc: Johan Hovold, dri-devel, linux-leds, devicetree, linux-kernel,
linux-iio, linux-fbdev
In-Reply-To: <20260617080031.99156-1-clamor95@gmail.com>
Add Control Bank to HVLED/LVLED muxing support based on the led-sources
defined in the device tree.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/leds/leds-lm3533.c | 60 +++++++++++++++++++++++++++++
drivers/video/backlight/lm3533_bl.c | 45 ++++++++++++++++++++++
2 files changed, 105 insertions(+)
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index ed810c23f30f..9e07953814fd 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -27,6 +27,11 @@
#define LM3533_ALS_CHANNEL_LV_MIN 1
#define LM3533_ALS_CHANNEL_LV_MAX 2
+#define LM3533_REG_OUTPUT_CONF1 0x10
+#define OUTPUT_CONF1_SHIFT 2
+#define OUTPUT_LVLED_MASK 0x3
+#define LM3533_REG_OUTPUT_CONF2 0x11
+#define OUTPUT_CONF2_SHIFT 6
#define LM3533_REG_CTRLBANK_BCONF_BASE 0x1b
#define LM3533_REG_PATTERN_ENABLE 0x28
#define LM3533_REG_PATTERN_LOW_TIME_BASE 0x71
@@ -55,6 +60,9 @@ struct lm3533_led {
u32 max_current;
u32 pwm;
+ int num_leds;
+ u32 leds[LM3533_LVCTRLBANK_MAX];
+
bool have_als;
};
@@ -623,8 +631,36 @@ static const struct attribute_group *lm3533_led_attribute_groups[] = {
static int lm3533_led_setup(struct lm3533_led *led)
{
+ u32 output_cfg_shift = 0;
+ u32 output_cfg_val = 0;
+ u32 output_cfg_mask = 0;
int ret;
+ if (led->num_leds) {
+ for (int i = 0; i < led->num_leds; i++) {
+ if (led->leds[i] >= LM3533_LVCTRLBANK_MAX)
+ continue;
+
+ output_cfg_shift = led->leds[i] * 2;
+ output_cfg_val |= led->id << output_cfg_shift;
+ output_cfg_mask |= OUTPUT_LVLED_MASK << output_cfg_shift;
+ }
+
+ /* LVLED1, LVLED2 and LVLED3 */
+ ret = regmap_update_bits(led->regmap, LM3533_REG_OUTPUT_CONF1,
+ output_cfg_mask << OUTPUT_CONF1_SHIFT,
+ output_cfg_val << OUTPUT_CONF1_SHIFT);
+ if (ret)
+ return ret;
+
+ /* LVLED4 and LVLED5 */
+ ret = regmap_update_bits(led->regmap, LM3533_REG_OUTPUT_CONF2,
+ output_cfg_mask >> OUTPUT_CONF2_SHIFT,
+ output_cfg_val >> OUTPUT_CONF2_SHIFT);
+ if (ret)
+ return ret;
+ }
+
ret = lm3533_ctrlbank_set_max_current(&led->cb, led->max_current);
if (ret)
return ret;
@@ -699,6 +735,30 @@ static int lm3533_led_probe(struct platform_device *pdev)
device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &led->pwm);
+ /*
+ * If led-sources property is not set then either this Control Bank uses
+ * its default LVLED or is not linked to any LVLED at all.
+ */
+ led->num_leds = device_property_count_u32(&pdev->dev, "led-sources");
+ if (led->num_leds > LM3533_LVCTRLBANK_MAX) {
+ dev_err(&pdev->dev, "num of LED sources exceeds max %d: %d\n",
+ LM3533_LVCTRLBANK_MAX, led->num_leds);
+ ret = -EINVAL;
+ goto err_deregister;
+ }
+
+ if (led->num_leds < 0)
+ led->num_leds = 0;
+
+ if (led->num_leds > 0) {
+ ret = device_property_read_u32_array(&pdev->dev, "led-sources",
+ led->leds, led->num_leds);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get led-sources\n");
+ goto err_deregister;
+ }
+ }
+
ret = lm3533_led_setup(led);
if (ret)
goto err_deregister;
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
index c99fc68cb669..b3e5b3042d34 100644
--- a/drivers/video/backlight/lm3533_bl.c
+++ b/drivers/video/backlight/lm3533_bl.c
@@ -7,6 +7,7 @@
* Author: Johan Hovold <jhovold@gmail.com>
*/
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
@@ -22,6 +23,7 @@
#define LM3533_HVCTRLBANK_COUNT 2
#define LM3533_BL_MAX_BRIGHTNESS 255
+#define LM3533_REG_OUTPUT_CONF1 0x10
#define LM3533_REG_CTRLBANK_AB_BCONF 0x1a
#define CTRLBANK_AB_BCONF_ALS(n) BIT(2 * (n))
#define CTRLBANK_AB_BCONF_MODE(n) BIT(2 * (n) + 1)
@@ -36,6 +38,9 @@ struct lm3533_bl {
u32 max_current;
u32 pwm;
+ int num_leds;
+ u32 led_strings[LM3533_HVCTRLBANK_COUNT];
+
bool have_als;
bool linear;
};
@@ -237,6 +242,8 @@ static const struct attribute_group *lm3533_bl_attribute_groups[] = {
static int lm3533_bl_setup(struct lm3533_bl *bl)
{
int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+ u32 output_cfg_val = 0;
+ u32 output_cfg_mask = 0;
int ret;
ret = regmap_assign_bits(bl->regmap, LM3533_REG_CTRLBANK_AB_BCONF,
@@ -244,6 +251,21 @@ static int lm3533_bl_setup(struct lm3533_bl *bl)
if (ret)
return ret;
+ if (bl->num_leds) {
+ for (int i = 0; i < bl->num_leds; i++) {
+ if (bl->led_strings[i] >= LM3533_HVCTRLBANK_COUNT)
+ continue;
+
+ output_cfg_val |= ctrlbank << bl->led_strings[i];
+ output_cfg_mask |= BIT(bl->led_strings[i]);
+ }
+
+ ret = regmap_update_bits(bl->regmap, LM3533_REG_OUTPUT_CONF1,
+ output_cfg_mask, output_cfg_val);
+ if (ret)
+ return ret;
+ }
+
ret = lm3533_ctrlbank_set_max_current(&bl->cb, bl->max_current);
if (ret)
return ret;
@@ -321,6 +343,29 @@ static int lm3533_bl_probe(struct platform_device *pdev)
device_property_read_u32(&pdev->dev, "ti,pwm-config-mask", &bl->pwm);
+ /*
+ * If led-sources property is not set then either this Control Bank uses
+ * its default HVLED or is not linked to any HVLED at all.
+ */
+ bl->num_leds = device_property_count_u32(&pdev->dev, "led-sources");
+ if (bl->num_leds > LM3533_HVCTRLBANK_COUNT) {
+ dev_err(&pdev->dev, "num of LED sources %d exceeds max %d\n",
+ bl->num_leds, LM3533_HVCTRLBANK_COUNT);
+ return -EINVAL;
+ }
+
+ if (bl->num_leds < 0)
+ bl->num_leds = 0;
+
+ if (bl->num_leds > 0) {
+ ret = device_property_read_u32_array(&pdev->dev, "led-sources",
+ bl->led_strings,
+ bl->num_leds);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to get led-sources\n");
+ }
+
ret = lm3533_bl_setup(bl);
if (ret)
return ret;
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox