* [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI
@ 2025-10-24 10:56 Marcus Folkesson
2025-10-24 10:56 ` [PATCH 1/6] drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device Marcus Folkesson
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
This series splits up the driver and finally implements
support for ST7571/ST7561 connected to a SPI bus.
I've not tested the SPI interface myself as I lack HW, but the
implementation should be okay from what I've read in the datasheet.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
Marcus Folkesson (6):
drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device
drm/sitronix/st7571-i2c: add 'struct device' to st7571_device
drm/sitronix/st7571-i2c: move common structures to st7571.h
drm/sitronix/st7571-i2c: make probe independent of hw interface
drm/sitronix/st7571: split up the driver into a common and an i2c part
drm/sitronix/st7571-spi: add support for SPI interface
MAINTAINERS | 3 +
drivers/gpu/drm/sitronix/Kconfig | 38 +-
drivers/gpu/drm/sitronix/Makefile | 2 +
drivers/gpu/drm/sitronix/st7571-i2c.c | 1002 ++-------------------------------
drivers/gpu/drm/sitronix/st7571-spi.c | 75 +++
drivers/gpu/drm/sitronix/st7571.c | 918 ++++++++++++++++++++++++++++++
drivers/gpu/drm/sitronix/st7571.h | 91 +++
7 files changed, 1160 insertions(+), 969 deletions(-)
---
base-commit: 7e73cefd2bede5408d1aeb6145261b62d85d23be
change-id: 20251024-st7571-split-c734b06106a4
Best regards,
--
Marcus Folkesson <marcus.folkesson@gmail.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-24 10:56 ` [PATCH 2/6] drm/sitronix/st7571-i2c: add 'struct device' to st7571_device Marcus Folkesson
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Rename st7571_device.dev to st7571_device.drm in preparation to
introduce a 'struct device' member to this structure.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/gpu/drm/sitronix/st7571-i2c.c | 60 +++++++++++++++++------------------
1 file changed, 30 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index 4e73c8b415d677dab5b421666b56f4bb3697b982..71814a3eb93b7adf554da082a0237da371e5f5b5 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -112,7 +112,7 @@ struct st7571_panel_format {
};
struct st7571_device {
- struct drm_device dev;
+ struct drm_device drm;
struct drm_plane primary_plane;
struct drm_crtc crtc;
@@ -166,9 +166,9 @@ struct st7571_device {
u8 *row;
};
-static inline struct st7571_device *drm_to_st7571(struct drm_device *dev)
+static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
{
- return container_of(dev, struct st7571_device, dev);
+ return container_of(drm, struct st7571_device, drm);
}
static int st7571_regmap_write(void *context, const void *data, size_t count)
@@ -467,7 +467,7 @@ static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_atomic_helper_damage_iter iter;
- struct drm_device *dev = plane->dev;
+ struct drm_device *drm = plane->dev;
struct drm_rect damage;
struct st7571_device *st7571 = drm_to_st7571(plane->dev);
int ret, idx;
@@ -479,7 +479,7 @@ static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
if (ret)
return;
- if (!drm_dev_enter(dev, &idx))
+ if (!drm_dev_enter(drm, &idx))
goto out_drm_gem_fb_end_cpu_access;
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
@@ -501,11 +501,11 @@ static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
- struct drm_device *dev = plane->dev;
+ struct drm_device *drm = plane->dev;
struct st7571_device *st7571 = drm_to_st7571(plane->dev);
int idx;
- if (!drm_dev_enter(dev, &idx))
+ if (!drm_dev_enter(drm, &idx))
return;
st7571_fb_clear_screen(st7571);
@@ -621,20 +621,20 @@ static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
static int st7571_mode_config_init(struct st7571_device *st7571)
{
- struct drm_device *dev = &st7571->dev;
+ struct drm_device *drm = &st7571->drm;
const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
int ret;
- ret = drmm_mode_config_init(dev);
+ ret = drmm_mode_config_init(drm);
if (ret)
return ret;
- dev->mode_config.min_width = constraints->min_ncols;
- dev->mode_config.min_height = constraints->min_nlines;
- dev->mode_config.max_width = constraints->max_ncols;
- dev->mode_config.max_height = constraints->max_nlines;
- dev->mode_config.preferred_depth = 24;
- dev->mode_config.funcs = &st7571_mode_config_funcs;
+ drm->mode_config.min_width = constraints->min_ncols;
+ drm->mode_config.min_height = constraints->min_nlines;
+ drm->mode_config.max_width = constraints->max_ncols;
+ drm->mode_config.max_height = constraints->max_nlines;
+ drm->mode_config.preferred_depth = 24;
+ drm->mode_config.funcs = &st7571_mode_config_funcs;
return 0;
}
@@ -643,10 +643,10 @@ static int st7571_plane_init(struct st7571_device *st7571,
const struct st7571_panel_format *pformat)
{
struct drm_plane *primary_plane = &st7571->primary_plane;
- struct drm_device *dev = &st7571->dev;
+ struct drm_device *drm = &st7571->drm;
int ret;
- ret = drm_universal_plane_init(dev, primary_plane, 0,
+ ret = drm_universal_plane_init(drm, primary_plane, 0,
&st7571_primary_plane_funcs,
pformat->formats,
pformat->nformats,
@@ -665,10 +665,10 @@ static int st7571_crtc_init(struct st7571_device *st7571)
{
struct drm_plane *primary_plane = &st7571->primary_plane;
struct drm_crtc *crtc = &st7571->crtc;
- struct drm_device *dev = &st7571->dev;
+ struct drm_device *drm = &st7571->drm;
int ret;
- ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
+ ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
&st7571_crtc_funcs, NULL);
if (ret)
return ret;
@@ -682,10 +682,10 @@ static int st7571_encoder_init(struct st7571_device *st7571)
{
struct drm_encoder *encoder = &st7571->encoder;
struct drm_crtc *crtc = &st7571->crtc;
- struct drm_device *dev = &st7571->dev;
+ struct drm_device *drm = &st7571->drm;
int ret;
- ret = drm_encoder_init(dev, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
+ ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
if (ret)
return ret;
@@ -700,10 +700,10 @@ static int st7571_connector_init(struct st7571_device *st7571)
{
struct drm_connector *connector = &st7571->connector;
struct drm_encoder *encoder = &st7571->encoder;
- struct drm_device *dev = &st7571->dev;
+ struct drm_device *drm = &st7571->drm;
int ret;
- ret = drm_connector_init(dev, connector, &st7571_connector_funcs,
+ ret = drm_connector_init(drm, connector, &st7571_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
if (ret)
return ret;
@@ -934,15 +934,15 @@ static int st7571_lcd_init(struct st7571_device *st7571)
static int st7571_probe(struct i2c_client *client)
{
struct st7571_device *st7571;
- struct drm_device *dev;
+ struct drm_device *drm;
int ret;
st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
- struct st7571_device, dev);
+ struct st7571_device, drm);
if (IS_ERR(st7571))
return PTR_ERR(st7571);
- dev = &st7571->dev;
+ drm = &st7571->drm;
st7571->client = client;
i2c_set_clientdata(client, st7571);
st7571->pdata = device_get_match_data(&client->dev);
@@ -1010,14 +1010,14 @@ static int st7571_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, ret,
"Failed to initialize connector\n");
- drm_mode_config_reset(dev);
+ drm_mode_config_reset(drm);
- ret = drm_dev_register(dev, 0);
+ ret = drm_dev_register(drm, 0);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to register DRM device\n");
- drm_client_setup(dev, NULL);
+ drm_client_setup(drm, NULL);
return 0;
}
@@ -1025,7 +1025,7 @@ static void st7571_remove(struct i2c_client *client)
{
struct st7571_device *st7571 = i2c_get_clientdata(client);
- drm_dev_unplug(&st7571->dev);
+ drm_dev_unplug(&st7571->drm);
}
static const struct st7571_panel_data st7567_config = {
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/6] drm/sitronix/st7571-i2c: add 'struct device' to st7571_device
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
2025-10-24 10:56 ` [PATCH 1/6] drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-24 10:56 ` [PATCH 3/6] drm/sitronix/st7571-i2c: move common structures to st7571.h Marcus Folkesson
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Keep a copy of the device structure instead of referring to i2c_client.
This is a preparation step to separate the generic part from all i2c
stuff.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/gpu/drm/sitronix/st7571-i2c.c | 30 ++++++++++++++++--------------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index 71814a3eb93b7adf554da082a0237da371e5f5b5..2b52919d7dd434bb16aa66274eae8730649f62f1 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -113,6 +113,7 @@ struct st7571_panel_format {
struct st7571_device {
struct drm_device drm;
+ struct device *dev;
struct drm_plane primary_plane;
struct drm_crtc crtc;
@@ -741,7 +742,7 @@ static const struct regmap_config st7571_regmap_config = {
static int st7571_validate_parameters(struct st7571_device *st7571)
{
- struct device *dev = st7571->dev.dev;
+ struct device *dev = st7571->dev;
const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
if (st7571->width_mm == 0) {
@@ -781,7 +782,7 @@ static int st7571_validate_parameters(struct st7571_device *st7571)
static int st7567_parse_dt(struct st7571_device *st7567)
{
- struct device *dev = &st7567->client->dev;
+ struct device *dev = st7567->dev;
struct device_node *np = dev->of_node;
struct display_timing dt;
int ret;
@@ -808,7 +809,7 @@ static int st7567_parse_dt(struct st7571_device *st7567)
static int st7571_parse_dt(struct st7571_device *st7571)
{
- struct device *dev = &st7571->client->dev;
+ struct device *dev = st7571->dev;
struct device_node *np = dev->of_node;
struct display_timing dt;
int ret;
@@ -943,9 +944,10 @@ static int st7571_probe(struct i2c_client *client)
return PTR_ERR(st7571);
drm = &st7571->drm;
+ st7571->dev = &client->dev;
st7571->client = client;
i2c_set_clientdata(client, st7571);
- st7571->pdata = device_get_match_data(&client->dev);
+ st7571->pdata = device_get_match_data(st7571->dev);
ret = st7571->pdata->parse_dt(st7571);
if (ret)
@@ -966,20 +968,20 @@ static int st7571_probe(struct i2c_client *client)
if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
st7571->ignore_nak = true;
- st7571->regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
+ st7571->regmap = devm_regmap_init(st7571->dev, &st7571_regmap_bus,
client, &st7571_regmap_config);
if (IS_ERR(st7571->regmap)) {
- return dev_err_probe(&client->dev, PTR_ERR(st7571->regmap),
+ return dev_err_probe(st7571->dev, PTR_ERR(st7571->regmap),
"Failed to initialize regmap\n");
}
- st7571->hwbuf = devm_kzalloc(&client->dev,
+ st7571->hwbuf = devm_kzalloc(st7571->dev,
(st7571->nlines * st7571->ncols * st7571->bpp) / 8,
GFP_KERNEL);
if (!st7571->hwbuf)
return -ENOMEM;
- st7571->row = devm_kzalloc(&client->dev,
+ st7571->row = devm_kzalloc(st7571->dev,
(st7571->ncols * st7571->bpp),
GFP_KERNEL);
if (!st7571->row)
@@ -987,34 +989,34 @@ static int st7571_probe(struct i2c_client *client)
ret = st7571_mode_config_init(st7571);
if (ret)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to initialize mode config\n");
ret = st7571_plane_init(st7571, st7571->pformat);
if (ret)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to initialize primary plane\n");
ret = st7571_crtc_init(st7571);
if (ret < 0)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to initialize CRTC\n");
ret = st7571_encoder_init(st7571);
if (ret < 0)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to initialize encoder\n");
ret = st7571_connector_init(st7571);
if (ret < 0)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to initialize connector\n");
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
- return dev_err_probe(&client->dev, ret,
+ return dev_err_probe(st7571->dev, ret,
"Failed to register DRM device\n");
drm_client_setup(drm, NULL);
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/6] drm/sitronix/st7571-i2c: move common structures to st7571.h
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
2025-10-24 10:56 ` [PATCH 1/6] drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device Marcus Folkesson
2025-10-24 10:56 ` [PATCH 2/6] drm/sitronix/st7571-i2c: add 'struct device' to st7571_device Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-24 10:56 ` [PATCH 4/6] drm/sitronix/st7571-i2c: make probe independent of hw interface Marcus Folkesson
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Move all structures that will be common for all interfaces (SPI/I2C) to
a separate header file.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
MAINTAINERS | 1 +
drivers/gpu/drm/sitronix/st7571-i2c.c | 91 +---------------------------
drivers/gpu/drm/sitronix/st7571.h | 108 ++++++++++++++++++++++++++++++++++
3 files changed, 111 insertions(+), 89 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index cc8a7f3f1dabaf402b21f767ecb093a31ade248f..2814faae61eceecae1bccaaf92010e22dca81376 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8083,6 +8083,7 @@ S: Maintained
F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml
F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml
F: drivers/gpu/drm/sitronix/st7571-i2c.c
+F: drivers/gpu/drm/sitronix/st7571.h
DRM DRIVER FOR SITRONIX ST7701 PANELS
M: Jagan Teki <jagan@amarulasolutions.com>
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index 2b52919d7dd434bb16aa66274eae8730649f62f1..af27658a5e152534d445bc623893eee6b3ca00d5 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -35,6 +35,8 @@
#include <video/display_timing.h>
#include <video/of_display_timing.h>
+#include "st7571.h"
+
#define ST7571_COMMAND_MODE (0x00)
#define ST7571_DATA_MODE (0x40)
@@ -78,95 +80,6 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-enum st7571_color_mode {
- ST7571_COLOR_MODE_GRAY = 0,
- ST7571_COLOR_MODE_BLACKWHITE = 1,
-};
-
-struct st7571_device;
-
-struct st7571_panel_constraints {
- u32 min_nlines;
- u32 max_nlines;
- u32 min_ncols;
- u32 max_ncols;
- bool support_grayscale;
-};
-
-struct st7571_panel_data {
- int (*init)(struct st7571_device *st7571);
- int (*parse_dt)(struct st7571_device *st7571);
- struct st7571_panel_constraints constraints;
-};
-
-struct st7571_panel_format {
- void (*prepare_buffer)(struct st7571_device *st7571,
- const struct iosys_map *vmap,
- struct drm_framebuffer *fb,
- struct drm_rect *rect,
- struct drm_format_conv_state *fmtcnv_state);
- int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
- enum st7571_color_mode mode;
- const u8 nformats;
- const u32 formats[];
-};
-
-struct st7571_device {
- struct drm_device drm;
- struct device *dev;
-
- struct drm_plane primary_plane;
- struct drm_crtc crtc;
- struct drm_encoder encoder;
- struct drm_connector connector;
-
- struct drm_display_mode mode;
-
- const struct st7571_panel_format *pformat;
- const struct st7571_panel_data *pdata;
- struct i2c_client *client;
- struct gpio_desc *reset;
- struct regmap *regmap;
-
- /*
- * Depending on the hardware design, the acknowledge signal may be hard to
- * recognize as a valid logic "0" level.
- * Therefor, ignore NAK if possible to stay compatible with most hardware designs
- * and off-the-shelf panels out there.
- *
- * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
- *
- * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
- * I2C interface compatible.
- * Separating acknowledge-output from serial data
- * input is advantageous for chip-on-glass (COG) applications. In COG
- * applications, the ITO resistance and the pull-up resistor will form a
- * voltage divider, which affects acknowledge-signal level. Larger ITO
- * resistance will raise the acknowledged-signal level and system cannot
- * recognize this level as a valid logic “0” level. By separating SDA_IN from
- * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
- * For applications which check acknowledge-bit, it is necessary to minimize
- * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
- *
- */
- bool ignore_nak;
-
- bool grayscale;
- bool inverted;
- u32 height_mm;
- u32 width_mm;
- u32 startline;
- u32 nlines;
- u32 ncols;
- u32 bpp;
-
- /* Intermediate buffer in LCD friendly format */
- u8 *hwbuf;
-
- /* Row of (transformed) pixels ready to be written to the display */
- u8 *row;
-};
-
static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
{
return container_of(drm, struct st7571_device, drm);
diff --git a/drivers/gpu/drm/sitronix/st7571.h b/drivers/gpu/drm/sitronix/st7571.h
new file mode 100644
index 0000000000000000000000000000000000000000..c6fd6f1d3aa33d6b43330ce8f2cb2d3f2321b29b
--- /dev/null
+++ b/drivers/gpu/drm/sitronix/st7571.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Header file for:
+ * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
+ *
+ * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#ifndef __ST7571_H__
+#define __ST7571_H__
+
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+
+#include <linux/regmap.h>
+
+enum st7571_color_mode {
+ ST7571_COLOR_MODE_GRAY = 0,
+ ST7571_COLOR_MODE_BLACKWHITE = 1,
+};
+
+struct st7571_device;
+
+struct st7571_panel_constraints {
+ u32 min_nlines;
+ u32 max_nlines;
+ u32 min_ncols;
+ u32 max_ncols;
+ bool support_grayscale;
+};
+
+struct st7571_panel_data {
+ int (*init)(struct st7571_device *st7571);
+ int (*parse_dt)(struct st7571_device *st7571);
+ struct st7571_panel_constraints constraints;
+};
+
+struct st7571_panel_format {
+ void (*prepare_buffer)(struct st7571_device *st7571,
+ const struct iosys_map *vmap,
+ struct drm_framebuffer *fb,
+ struct drm_rect *rect,
+ struct drm_format_conv_state *fmtcnv_state);
+ int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
+ enum st7571_color_mode mode;
+ const u8 nformats;
+ const u32 formats[];
+};
+
+struct st7571_device {
+ struct drm_device drm;
+ struct device *dev;
+
+ struct drm_plane primary_plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct drm_display_mode mode;
+
+ const struct st7571_panel_format *pformat;
+ const struct st7571_panel_data *pdata;
+ struct i2c_client *client;
+ struct gpio_desc *reset;
+ struct regmap *regmap;
+
+ /*
+ * Depending on the hardware design, the acknowledge signal may be hard to
+ * recognize as a valid logic "0" level.
+ * Therefor, ignore NAK if possible to stay compatible with most hardware designs
+ * and off-the-shelf panels out there.
+ *
+ * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
+ *
+ * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
+ * I2C interface compatible.
+ * Separating acknowledge-output from serial data
+ * input is advantageous for chip-on-glass (COG) applications. In COG
+ * applications, the ITO resistance and the pull-up resistor will form a
+ * voltage divider, which affects acknowledge-signal level. Larger ITO
+ * resistance will raise the acknowledged-signal level and system cannot
+ * recognize this level as a valid logic “0” level. By separating SDA_IN from
+ * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
+ * For applications which check acknowledge-bit, it is necessary to minimize
+ * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
+ *
+ */
+ bool ignore_nak;
+
+ bool grayscale;
+ bool inverted;
+ u32 height_mm;
+ u32 width_mm;
+ u32 startline;
+ u32 nlines;
+ u32 ncols;
+ u32 bpp;
+
+ /* Intermediate buffer in LCD friendly format */
+ u8 *hwbuf;
+
+ /* Row of (transformed) pixels ready to be written to the display */
+ u8 *row;
+};
+
+#endif /* __ST7571_H__ */
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/6] drm/sitronix/st7571-i2c: make probe independent of hw interface
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
` (2 preceding siblings ...)
2025-10-24 10:56 ` [PATCH 3/6] drm/sitronix/st7571-i2c: move common structures to st7571.h Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-24 10:56 ` [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part Marcus Folkesson
2025-10-24 10:56 ` [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface Marcus Folkesson
5 siblings, 0 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Create a interface independent layer for the probe function. This to
make it possible to add support for other interfaces.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/gpu/drm/sitronix/st7571-i2c.c | 165 +++++++++++++++++++++++-----------
drivers/gpu/drm/sitronix/st7571.h | 25 +-----
2 files changed, 113 insertions(+), 77 deletions(-)
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index af27658a5e152534d445bc623893eee6b3ca00d5..f994ace407390dba30c0968ca99f437382badfab 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -80,6 +80,33 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
+struct st7571_i2c_transport {
+ struct i2c_client *client;
+
+ /*
+ * Depending on the hardware design, the acknowledge signal may be hard to
+ * recognize as a valid logic "0" level.
+ * Therefor, ignore NAK if possible to stay compatible with most hardware designs
+ * and off-the-shelf panels out there.
+ *
+ * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
+ *
+ * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
+ * I2C interface compatible.
+ * Separating acknowledge-output from serial data
+ * input is advantageous for chip-on-glass (COG) applications. In COG
+ * applications, the ITO resistance and the pull-up resistor will form a
+ * voltage divider, which affects acknowledge-signal level. Larger ITO
+ * resistance will raise the acknowledged-signal level and system cannot
+ * recognize this level as a valid logic “0” level. By separating SDA_IN from
+ * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
+ * For applications which check acknowledge-bit, it is necessary to minimize
+ * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
+ *
+ */
+ bool ignore_nak;
+};
+
static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
{
return container_of(drm, struct st7571_device, drm);
@@ -87,18 +114,17 @@ static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
static int st7571_regmap_write(void *context, const void *data, size_t count)
{
- struct i2c_client *client = context;
- struct st7571_device *st7571 = i2c_get_clientdata(client);
+ struct st7571_i2c_transport *t = context;
int ret;
struct i2c_msg msg = {
- .addr = st7571->client->addr,
- .flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0,
+ .addr = t->client->addr,
+ .flags = t->ignore_nak ? I2C_M_IGNORE_NAK : 0,
.len = count,
.buf = (u8 *)data
};
- ret = i2c_transfer(st7571->client->adapter, &msg, 1);
+ ret = i2c_transfer(t->client->adapter, &msg, 1);
/*
* Unfortunately, there is no way to check if the transfer failed because of
@@ -106,7 +132,7 @@ static int st7571_regmap_write(void *context, const void *data, size_t count)
*
* However, if the transfer fails and ignore_nak is set, we know it is an error.
*/
- if (ret < 0 && st7571->ignore_nak)
+ if (ret < 0 && t->ignore_nak)
return ret;
return 0;
@@ -845,102 +871,135 @@ static int st7571_lcd_init(struct st7571_device *st7571)
return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
}
-static int st7571_probe(struct i2c_client *client)
+static struct st7571_device *st7571_probe(struct device *dev,
+ struct regmap *regmap)
{
struct st7571_device *st7571;
struct drm_device *drm;
int ret;
- st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
+ st7571 = devm_drm_dev_alloc(dev, &st7571_driver,
struct st7571_device, drm);
if (IS_ERR(st7571))
- return PTR_ERR(st7571);
+ return st7571;
drm = &st7571->drm;
- st7571->dev = &client->dev;
- st7571->client = client;
- i2c_set_clientdata(client, st7571);
+ st7571->dev = dev;
st7571->pdata = device_get_match_data(st7571->dev);
ret = st7571->pdata->parse_dt(st7571);
if (ret)
- return ret;
+ return ERR_PTR(ret);
ret = st7571_validate_parameters(st7571);
if (ret)
- return ret;
+ return ERR_PTR(ret);
st7571->mode = st7571_mode(st7571);
+ st7571->regmap = regmap;
- /*
- * The hardware design could make it hard to detect a NAK on the I2C bus.
- * If the adapter does not support protocol mangling do
- * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
- * cruft in the logs.
- */
- if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
- st7571->ignore_nak = true;
-
- st7571->regmap = devm_regmap_init(st7571->dev, &st7571_regmap_bus,
- client, &st7571_regmap_config);
- if (IS_ERR(st7571->regmap)) {
- return dev_err_probe(st7571->dev, PTR_ERR(st7571->regmap),
- "Failed to initialize regmap\n");
- }
st7571->hwbuf = devm_kzalloc(st7571->dev,
(st7571->nlines * st7571->ncols * st7571->bpp) / 8,
GFP_KERNEL);
if (!st7571->hwbuf)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
st7571->row = devm_kzalloc(st7571->dev,
(st7571->ncols * st7571->bpp),
GFP_KERNEL);
if (!st7571->row)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
ret = st7571_mode_config_init(st7571);
- if (ret)
- return dev_err_probe(st7571->dev, ret,
- "Failed to initialize mode config\n");
+ if (ret) {
+ dev_err(st7571->dev, "Failed to initialize mode config\n");
+ return ERR_PTR(ret);
+ }
ret = st7571_plane_init(st7571, st7571->pformat);
- if (ret)
- return dev_err_probe(st7571->dev, ret,
- "Failed to initialize primary plane\n");
+ if (ret) {
+ dev_err(st7571->dev, "Failed to initialize primary plane\n");
+ return ERR_PTR(ret);
+ }
ret = st7571_crtc_init(st7571);
- if (ret < 0)
- return dev_err_probe(st7571->dev, ret,
- "Failed to initialize CRTC\n");
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize CRTC\n");
+ return ERR_PTR(ret);
+ }
ret = st7571_encoder_init(st7571);
- if (ret < 0)
- return dev_err_probe(st7571->dev, ret,
- "Failed to initialize encoder\n");
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize encoder\n");
+ return ERR_PTR(ret);
+ }
ret = st7571_connector_init(st7571);
- if (ret < 0)
- return dev_err_probe(st7571->dev, ret,
- "Failed to initialize connector\n");
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize connector\n");
+ return ERR_PTR(ret);
+ }
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
- if (ret)
- return dev_err_probe(st7571->dev, ret,
- "Failed to register DRM device\n");
+ if (ret) {
+ dev_err(st7571->dev, "Failed to register DRM device\n");
+ return ERR_PTR(ret);
+ }
drm_client_setup(drm, NULL);
+ return st7571;
+}
+
+static void st7571_remove(struct st7571_device *st7571)
+{
+ drm_dev_unplug(&st7571->drm);
+}
+
+static int st7571_i2c_probe(struct i2c_client *client)
+{
+ struct st7571_device *st7571;
+ struct st7571_i2c_transport *t;
+ struct regmap *regmap;
+
+ t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+
+ t->client = client;
+
+ /*
+ * The hardware design could make it hard to detect a NAK on the I2C bus.
+ * If the adapter does not support protocol mangling do
+ * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
+ * cruft in the logs.
+ */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
+ t->ignore_nak = true;
+
+ regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
+ t, &st7571_regmap_config);
+ if (IS_ERR(regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to initialize regmap\n");
+ }
+
+ st7571 = st7571_probe(&client->dev, regmap);
+ if (IS_ERR(st7571))
+ return dev_err_probe(&client->dev, PTR_ERR(st7571),
+ "Failed to initialize regmap\n");
+
+ i2c_set_clientdata(client, st7571);
return 0;
}
-static void st7571_remove(struct i2c_client *client)
+static void st7571_i2c_remove(struct i2c_client *client)
{
struct st7571_device *st7571 = i2c_get_clientdata(client);
- drm_dev_unplug(&st7571->drm);
+ st7571_remove(st7571);
}
static const struct st7571_panel_data st7567_config = {
@@ -986,8 +1045,8 @@ static struct i2c_driver st7571_i2c_driver = {
.name = "st7571",
.of_match_table = st7571_of_match,
},
- .probe = st7571_probe,
- .remove = st7571_remove,
+ .probe = st7571_i2c_probe,
+ .remove = st7571_i2c_remove,
.id_table = st7571_id,
};
diff --git a/drivers/gpu/drm/sitronix/st7571.h b/drivers/gpu/drm/sitronix/st7571.h
index c6fd6f1d3aa33d6b43330ce8f2cb2d3f2321b29b..f62c57ddb99ebe82f63048cc2ccf75a81518d717 100644
--- a/drivers/gpu/drm/sitronix/st7571.h
+++ b/drivers/gpu/drm/sitronix/st7571.h
@@ -13,6 +13,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_format_helper.h>
#include <linux/regmap.h>
@@ -62,33 +63,9 @@ struct st7571_device {
const struct st7571_panel_format *pformat;
const struct st7571_panel_data *pdata;
- struct i2c_client *client;
struct gpio_desc *reset;
struct regmap *regmap;
- /*
- * Depending on the hardware design, the acknowledge signal may be hard to
- * recognize as a valid logic "0" level.
- * Therefor, ignore NAK if possible to stay compatible with most hardware designs
- * and off-the-shelf panels out there.
- *
- * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
- *
- * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
- * I2C interface compatible.
- * Separating acknowledge-output from serial data
- * input is advantageous for chip-on-glass (COG) applications. In COG
- * applications, the ITO resistance and the pull-up resistor will form a
- * voltage divider, which affects acknowledge-signal level. Larger ITO
- * resistance will raise the acknowledged-signal level and system cannot
- * recognize this level as a valid logic “0” level. By separating SDA_IN from
- * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
- * For applications which check acknowledge-bit, it is necessary to minimize
- * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
- *
- */
- bool ignore_nak;
-
bool grayscale;
bool inverted;
u32 height_mm;
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
` (3 preceding siblings ...)
2025-10-24 10:56 ` [PATCH 4/6] drm/sitronix/st7571-i2c: make probe independent of hw interface Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-25 3:56 ` kernel test robot
2025-10-24 10:56 ` [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface Marcus Folkesson
5 siblings, 1 reply; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Split up the driver to make it possible to add support for hw interfaces
other than I2C.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
MAINTAINERS | 1 +
drivers/gpu/drm/sitronix/Kconfig | 26 +-
drivers/gpu/drm/sitronix/Makefile | 1 +
drivers/gpu/drm/sitronix/st7571-i2c.c | 924 +---------------------------------
drivers/gpu/drm/sitronix/st7571.c | 918 +++++++++++++++++++++++++++++++++
drivers/gpu/drm/sitronix/st7571.h | 6 +
6 files changed, 959 insertions(+), 917 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2814faae61eceecae1bccaaf92010e22dca81376..66e9ffb757c8bb19dbb894eb51f88f589ee83af6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8083,6 +8083,7 @@ S: Maintained
F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml
F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml
F: drivers/gpu/drm/sitronix/st7571-i2c.c
+F: drivers/gpu/drm/sitronix/st7571.c
F: drivers/gpu/drm/sitronix/st7571.h
DRM DRIVER FOR SITRONIX ST7701 PANELS
diff --git a/drivers/gpu/drm/sitronix/Kconfig b/drivers/gpu/drm/sitronix/Kconfig
index 6de7d92d9b74c72746915b945869dba91f161d2b..0676a86661caa4ab81f226ff2e990a3e2526f74d 100644
--- a/drivers/gpu/drm/sitronix/Kconfig
+++ b/drivers/gpu/drm/sitronix/Kconfig
@@ -1,13 +1,29 @@
-config DRM_ST7571_I2C
- tristate "DRM support for Sitronix ST7571 display panels (I2C)"
- depends on DRM && I2C && MMU
+config DRM_ST7571
+ tristate "DRM support for Sitronix ST7567/ST7571 display panels"
+ depends on DRM && MMU
select DRM_CLIENT_SELECTION
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
- select REGMAP_I2C
select VIDEOMODE_HELPERS
help
- DRM driver for Sitronix ST7571 panels controlled over I2C.
+ Sitronix ST7571 is a driver and controller for 4-level gray
+ scale and monochrome dot matrix LCD panels.
+
+ DRM driver for Sitronix ST7567/ST7571 panels.
+ This is only the core driver, a driver for the appropriate bus
+ transport in your chip also must be selected.
+
+ if M is selected the module will be called st7571.
+
+config DRM_ST7571_I2C
+ tristate "DRM support for Sitronix ST7567/ST7571 display panels (I2C)"
+ depends on DRM_ST7571 && I2C
+ select REGMAP_I2C
+ help
+ Sitronix ST7571 is a driver and controller for 4-level gray
+ scale and monochrome dot matrix LCD panels.
+
+ DRM driver for Sitronix ST7565/ST7571 panels connected via I2C bus.
if M is selected the module will be called st7571-i2c.
diff --git a/drivers/gpu/drm/sitronix/Makefile b/drivers/gpu/drm/sitronix/Makefile
index bd139e5a6995fa026cc635b3c29782473d1efad7..8073bb776ff94de750f350b636fd9db3d54fdd46 100644
--- a/drivers/gpu/drm/sitronix/Makefile
+++ b/drivers/gpu/drm/sitronix/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_DRM_ST7571) += st7571.o
obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o
obj-$(CONFIG_DRM_ST7586) += st7586.o
obj-$(CONFIG_DRM_ST7735R) += st7735r.o
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index f994ace407390dba30c0968ca99f437382badfab..038a7c2b6f26169f40305a382dace7031d9efc43 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -1,85 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
+ * Driver for Sitronix ST7571 connected via I2C bus.
*
* Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
*/
-#include <linux/bitfield.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
-#include <drm/clients/drm_client_setup.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_connector.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_damage_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_encoder.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_fbdev_shmem.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_gem_shmem_helper.h>
-#include <drm/drm_modeset_helper_vtables.h>
-#include <drm/drm_module.h>
-#include <drm/drm_plane.h>
-#include <drm/drm_probe_helper.h>
-
-#include <video/display_timing.h>
-#include <video/of_display_timing.h>
-
#include "st7571.h"
-#define ST7571_COMMAND_MODE (0x00)
-#define ST7571_DATA_MODE (0x40)
-
-/* Normal mode command set */
-#define ST7571_DISPLAY_OFF (0xae)
-#define ST7571_DISPLAY_ON (0xaf)
-#define ST7571_OSC_ON (0xab)
-#define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
-#define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
-#define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x)))
-#define ST7571_SET_COM0_MSB (0x44)
-#define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
-#define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c)))
-#define ST7571_SET_CONTRAST_MSB (0x81)
-#define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d)))
-#define ST7571_SET_DISPLAY_DUTY_MSB (0x48)
-#define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
-#define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
-#define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m)))
-#define ST7571_SET_MODE_MSB (0x38)
-#define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
-#define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
-#define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
-#define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
-#define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
-#define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l)))
-#define ST7571_SET_START_LINE_MSB (0x40)
-
-/* Extension command set 3 */
-#define ST7571_COMMAND_SET_3 (0x7b)
-#define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
-#define ST7571_COMMAND_SET_NORMAL (0x00)
-
-/* ST7567 commands */
-#define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m)))
-
-#define ST7571_PAGE_HEIGHT 8
-
-#define DRIVER_NAME "st7571"
-#define DRIVER_DESC "ST7571 DRM driver"
-#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 0
-
struct st7571_i2c_transport {
struct i2c_client *client;
@@ -107,12 +38,7 @@ struct st7571_i2c_transport {
bool ignore_nak;
};
-static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
-{
- return container_of(drm, struct st7571_device, drm);
-}
-
-static int st7571_regmap_write(void *context, const void *data, size_t count)
+static int st7571_i2c_regmap_write(void *context, const void *data, size_t count)
{
struct st7571_i2c_transport *t = context;
int ret;
@@ -139,825 +65,23 @@ static int st7571_regmap_write(void *context, const void *data, size_t count)
}
/* The st7571 driver does not read registers but regmap expects a .read */
-static int st7571_regmap_read(void *context, const void *reg_buf,
- size_t reg_size, void *val_buf, size_t val_size)
+static int st7571_i2c_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
{
return -EOPNOTSUPP;
}
-static int st7571_send_command_list(struct st7571_device *st7571,
- const u8 *cmd_list, size_t len)
-{
- int ret;
-
- for (int i = 0; i < len; i++) {
- ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
- if (ret < 0)
- return ret;
- }
-
- return ret;
-}
-
-static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp)
-{
- int xrest = x % 8;
- u8 result = 0;
- u8 row_len = 16 * bpp;
-
- /*
- * Transforms an (x, y) pixel coordinate into a vertical 8-bit
- * column from the framebuffer. It calculates the corresponding byte in the
- * framebuffer, extracts the bit at the given x position across 8 consecutive
- * rows, and packs those bits into a single byte.
- *
- * Return an 8-bit value representing a vertical column of pixels.
- */
- x = x / 8;
- y = (y / 8) * 8;
-
- for (int i = 0; i < 8; i++) {
- int row_idx = y + i;
- u8 byte = p[row_idx * row_len + x];
- u8 bit = (byte >> xrest) & 1;
-
- result |= (bit << i);
- }
-
- return result;
-}
-
-static int st7571_set_position(struct st7571_device *st7571, int x, int y)
-{
- u8 cmd_list[] = {
- ST7571_SET_COLUMN_LSB(x),
- ST7571_SET_COLUMN_MSB(x),
- ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
- };
-
- return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
-}
-
-static int st7571_fb_clear_screen(struct st7571_device *st7571)
-{
- u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
- char pixelvalue = 0x00;
-
- st7571_set_position(st7571, 0, 0);
- for (int i = 0; i < npixels; i++)
- regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
-
- return 0;
-}
-
-static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
- const struct iosys_map *vmap,
- struct drm_framebuffer *fb,
- struct drm_rect *rect,
- struct drm_format_conv_state *fmtcnv_state)
-{
- unsigned int dst_pitch;
- struct iosys_map dst;
- u32 size;
-
- switch (fb->format->format) {
- case DRM_FORMAT_XRGB8888:
- dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
- iosys_map_set_vaddr(&dst, st7571->hwbuf);
-
- drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
- break;
-
- case DRM_FORMAT_R1:
- size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
- memcpy(st7571->hwbuf, vmap->vaddr, size);
- break;
- }
-}
-
-static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
- const struct iosys_map *vmap,
- struct drm_framebuffer *fb,
- struct drm_rect *rect,
- struct drm_format_conv_state *fmtcnv_state)
-{
- u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
- unsigned int dst_pitch;
- struct iosys_map dst;
-
- switch (fb->format->format) {
- case DRM_FORMAT_XRGB8888:
- dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4);
- iosys_map_set_vaddr(&dst, st7571->hwbuf);
-
- drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
- break;
-
- case DRM_FORMAT_R1:
- size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
- memcpy(st7571->hwbuf, vmap->vaddr, size);
- break;
-
- case DRM_FORMAT_R2:
- size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
- memcpy(st7571->hwbuf, vmap->vaddr, size);
- break;
- }
-}
-
-static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
-{
- struct st7571_device *st7571 = drm_to_st7571(fb->dev);
- char *row = st7571->row;
-
- /* Align y to display page boundaries */
- rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
- rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
-
- for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
- for (int x = rect->x1; x < rect->x2; x++)
- row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1);
-
- st7571_set_position(st7571, rect->x1, y);
-
- /* TODO: Investige why we can't write multiple bytes at once */
- for (int x = rect->x1; x < rect->x2; x++)
- regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
- }
-
- return 0;
-}
-
-static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
-{
- struct st7571_device *st7571 = drm_to_st7571(fb->dev);
- u32 format = fb->format->format;
- char *row = st7571->row;
- int x1;
- int x2;
-
- /* Align y to display page boundaries */
- rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
- rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
-
- switch (format) {
- case DRM_FORMAT_R1:
- x1 = rect->x1 * 1;
- x2 = rect->x2 * 1;
- break;
- case DRM_FORMAT_R2:
- fallthrough;
- case DRM_FORMAT_XRGB8888:
- x1 = rect->x1 * 2;
- x2 = rect->x2 * 2;
- break;
- }
-
- for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
- for (int x = x1; x < x2; x++)
- row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2);
-
- st7571_set_position(st7571, rect->x1, y);
-
- /* TODO: Investige why we can't write multiple bytes at once */
- for (int x = x1; x < x2; x++) {
- regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
-
- /*
- * As the display supports grayscale, all pixels must be written as two bits
- * even if the format is monochrome.
- *
- * The bit values maps to the following grayscale:
- * 0 0 = Black
- * 0 1 = Dark gray
- * 1 0 = Light gray
- * 1 1 = White
- *
- * For monochrome formats, write the same value twice to get
- * either a black or white pixel.
- */
- if (format == DRM_FORMAT_R1)
- regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
- }
- }
-
- return 0;
-}
-
-static int st7571_connector_get_modes(struct drm_connector *conn)
-{
- struct st7571_device *st7571 = drm_to_st7571(conn->dev);
-
- return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
-}
-
-static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
- .get_modes = st7571_connector_get_modes,
-};
-
-static const struct st7571_panel_format st7571_monochrome = {
- .prepare_buffer = st7571_prepare_buffer_monochrome,
- .update_rect = st7571_fb_update_rect_monochrome,
- .mode = ST7571_COLOR_MODE_BLACKWHITE,
- .formats = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_R1,
- },
- .nformats = 2,
-};
-
-static const struct st7571_panel_format st7571_grayscale = {
- .prepare_buffer = st7571_prepare_buffer_grayscale,
- .update_rect = st7571_fb_update_rect_grayscale,
- .mode = ST7571_COLOR_MODE_GRAY,
- .formats = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_R1,
- DRM_FORMAT_R2,
- },
- .nformats = 3,
-};
-
-static const u64 st7571_primary_plane_fmtmods[] = {
- DRM_FORMAT_MOD_LINEAR,
- DRM_FORMAT_MOD_INVALID
-};
-
-static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
- struct drm_atomic_state *state)
-{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_crtc *new_crtc = new_plane_state->crtc;
- struct drm_crtc_state *new_crtc_state = NULL;
-
- if (new_crtc)
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
-
- return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
- DRM_PLANE_NO_SCALING,
- DRM_PLANE_NO_SCALING,
- false, false);
-}
-
-static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
- struct drm_atomic_state *state)
-{
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
- struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_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 damage;
- struct st7571_device *st7571 = drm_to_st7571(plane->dev);
- int ret, idx;
-
- if (!fb)
- return; /* no framebuffer; plane is disabled */
-
- ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
- if (ret)
- return;
-
- if (!drm_dev_enter(drm, &idx))
- goto out_drm_gem_fb_end_cpu_access;
-
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
- drm_atomic_for_each_plane_damage(&iter, &damage) {
- st7571->pformat->prepare_buffer(st7571,
- &shadow_plane_state->data[0],
- fb, &damage,
- &shadow_plane_state->fmtcnv_state);
-
- st7571->pformat->update_rect(fb, &damage);
- }
-
- drm_dev_exit(idx);
-
-out_drm_gem_fb_end_cpu_access:
- drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-}
-
-static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
- struct drm_atomic_state *state)
-{
- struct drm_device *drm = plane->dev;
- struct st7571_device *st7571 = drm_to_st7571(plane->dev);
- int idx;
-
- if (!drm_dev_enter(drm, &idx))
- return;
-
- st7571_fb_clear_screen(st7571);
- drm_dev_exit(idx);
-}
-
-static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
- DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
- .atomic_check = st7571_primary_plane_helper_atomic_check,
- .atomic_update = st7571_primary_plane_helper_atomic_update,
- .atomic_disable = st7571_primary_plane_helper_atomic_disable,
-};
-
-static const struct drm_plane_funcs st7571_primary_plane_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .destroy = drm_plane_cleanup,
- DRM_GEM_SHADOW_PLANE_FUNCS,
-};
-
-/*
- * CRTC
- */
-
-static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
- const struct drm_display_mode *mode)
-{
- struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
-
- return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
-}
-
-static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
- .atomic_check = drm_crtc_helper_atomic_check,
- .mode_valid = st7571_crtc_mode_valid,
-};
-
-static const struct drm_crtc_funcs st7571_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
- .destroy = drm_crtc_cleanup,
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
-};
-
-/*
- * Encoder
- */
-
-static void st7571_encoder_atomic_enable(struct drm_encoder *encoder,
- struct drm_atomic_state *state)
-{
- struct drm_device *drm = encoder->dev;
- struct st7571_device *st7571 = drm_to_st7571(drm);
- u8 command = ST7571_DISPLAY_ON;
- int ret;
-
- ret = st7571->pdata->init(st7571);
- if (ret)
- return;
-
- st7571_send_command_list(st7571, &command, 1);
-}
-
-static void st7571_encoder_atomic_disable(struct drm_encoder *encoder,
- struct drm_atomic_state *state)
-{
- struct drm_device *drm = encoder->dev;
- struct st7571_device *st7571 = drm_to_st7571(drm);
- u8 command = ST7571_DISPLAY_OFF;
-
- st7571_send_command_list(st7571, &command, 1);
-}
-
-static const struct drm_encoder_funcs st7571_encoder_funcs = {
- .destroy = drm_encoder_cleanup,
-
-};
-
-static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
- .atomic_enable = st7571_encoder_atomic_enable,
- .atomic_disable = st7571_encoder_atomic_disable,
-};
-
-/*
- * Connector
- */
-
-static const struct drm_connector_funcs st7571_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = drm_connector_cleanup,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const struct drm_mode_config_funcs st7571_mode_config_funcs = {
- .fb_create = drm_gem_fb_create_with_dirty,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
+static const struct regmap_bus st7571_i2c_regmap_bus = {
+ .read = st7571_i2c_regmap_read,
+ .write = st7571_i2c_regmap_write,
};
-static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
-{
- struct drm_display_mode mode = {
- DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
- st7571->width_mm, st7571->height_mm),
- };
-
- return mode;
-}
-
-static int st7571_mode_config_init(struct st7571_device *st7571)
-{
- struct drm_device *drm = &st7571->drm;
- const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
- int ret;
-
- ret = drmm_mode_config_init(drm);
- if (ret)
- return ret;
-
- drm->mode_config.min_width = constraints->min_ncols;
- drm->mode_config.min_height = constraints->min_nlines;
- drm->mode_config.max_width = constraints->max_ncols;
- drm->mode_config.max_height = constraints->max_nlines;
- drm->mode_config.preferred_depth = 24;
- drm->mode_config.funcs = &st7571_mode_config_funcs;
-
- return 0;
-}
-
-static int st7571_plane_init(struct st7571_device *st7571,
- const struct st7571_panel_format *pformat)
-{
- struct drm_plane *primary_plane = &st7571->primary_plane;
- struct drm_device *drm = &st7571->drm;
- int ret;
-
- ret = drm_universal_plane_init(drm, primary_plane, 0,
- &st7571_primary_plane_funcs,
- pformat->formats,
- pformat->nformats,
- st7571_primary_plane_fmtmods,
- DRM_PLANE_TYPE_PRIMARY, NULL);
- if (ret)
- return ret;
-
- drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
- drm_plane_enable_fb_damage_clips(primary_plane);
-
- return 0;
-}
-
-static int st7571_crtc_init(struct st7571_device *st7571)
-{
- struct drm_plane *primary_plane = &st7571->primary_plane;
- struct drm_crtc *crtc = &st7571->crtc;
- struct drm_device *drm = &st7571->drm;
- int ret;
-
- ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
- &st7571_crtc_funcs, NULL);
- if (ret)
- return ret;
-
- drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
-
- return 0;
-}
-
-static int st7571_encoder_init(struct st7571_device *st7571)
-{
- struct drm_encoder *encoder = &st7571->encoder;
- struct drm_crtc *crtc = &st7571->crtc;
- struct drm_device *drm = &st7571->drm;
- int ret;
-
- ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
- if (ret)
- return ret;
-
- drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
-
- encoder->possible_crtcs = drm_crtc_mask(crtc);
-
- return 0;
-}
-
-static int st7571_connector_init(struct st7571_device *st7571)
-{
- struct drm_connector *connector = &st7571->connector;
- struct drm_encoder *encoder = &st7571->encoder;
- struct drm_device *drm = &st7571->drm;
- int ret;
-
- ret = drm_connector_init(drm, connector, &st7571_connector_funcs,
- DRM_MODE_CONNECTOR_Unknown);
- if (ret)
- return ret;
-
- drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
-
- return drm_connector_attach_encoder(connector, encoder);
-}
-
-DEFINE_DRM_GEM_FOPS(st7571_fops);
-
-static const struct drm_driver st7571_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
-
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
-
- .fops = &st7571_fops,
- DRM_GEM_SHMEM_DRIVER_OPS,
- DRM_FBDEV_SHMEM_DRIVER_OPS,
-};
-
-static const struct regmap_bus st7571_regmap_bus = {
- .read = st7571_regmap_read,
- .write = st7571_regmap_write,
-};
-
-static const struct regmap_config st7571_regmap_config = {
+static const struct regmap_config st7571_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.use_single_write = true,
};
-static int st7571_validate_parameters(struct st7571_device *st7571)
-{
- struct device *dev = st7571->dev;
- const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
-
- if (st7571->width_mm == 0) {
- dev_err(dev, "Invalid panel width\n");
- return -EINVAL;
- }
-
- if (st7571->height_mm == 0) {
- dev_err(dev, "Invalid panel height\n");
- return -EINVAL;
- }
-
- if (st7571->nlines < constraints->min_nlines ||
- st7571->nlines > constraints->max_nlines) {
- dev_err(dev, "Invalid timing configuration.\n");
- return -EINVAL;
- }
-
- if (st7571->startline + st7571->nlines > constraints->max_nlines) {
- dev_err(dev, "Invalid timing configuration.\n");
- return -EINVAL;
- }
-
- if (st7571->ncols < constraints->min_ncols ||
- st7571->ncols > constraints->max_ncols) {
- dev_err(dev, "Invalid timing configuration.\n");
- return -EINVAL;
- }
-
- if (st7571->grayscale && !constraints->support_grayscale) {
- dev_err(dev, "Grayscale not supported\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int st7567_parse_dt(struct st7571_device *st7567)
-{
- struct device *dev = st7567->dev;
- struct device_node *np = dev->of_node;
- struct display_timing dt;
- int ret;
-
- ret = of_get_display_timing(np, "panel-timing", &dt);
- if (ret) {
- dev_err(dev, "Failed to get display timing from DT\n");
- return ret;
- }
-
- of_property_read_u32(np, "width-mm", &st7567->width_mm);
- of_property_read_u32(np, "height-mm", &st7567->height_mm);
- st7567->inverted = of_property_read_bool(np, "sitronix,inverted");
-
- st7567->pformat = &st7571_monochrome;
- st7567->bpp = 1;
-
- st7567->startline = dt.vfront_porch.typ;
- st7567->nlines = dt.vactive.typ;
- st7567->ncols = dt.hactive.typ;
-
- return 0;
-}
-
-static int st7571_parse_dt(struct st7571_device *st7571)
-{
- struct device *dev = st7571->dev;
- struct device_node *np = dev->of_node;
- struct display_timing dt;
- int ret;
-
- ret = of_get_display_timing(np, "panel-timing", &dt);
- if (ret) {
- dev_err(dev, "Failed to get display timing from DT\n");
- return ret;
- }
-
- of_property_read_u32(np, "width-mm", &st7571->width_mm);
- of_property_read_u32(np, "height-mm", &st7571->height_mm);
- st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
- st7571->inverted = of_property_read_bool(np, "sitronix,inverted");
-
- if (st7571->grayscale) {
- st7571->pformat = &st7571_grayscale;
- st7571->bpp = 2;
- } else {
- st7571->pformat = &st7571_monochrome;
- st7571->bpp = 1;
- }
-
- st7571->startline = dt.vfront_porch.typ;
- st7571->nlines = dt.vactive.typ;
- st7571->ncols = dt.hactive.typ;
-
- st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(st7571->reset))
- return dev_err_probe(dev, PTR_ERR(st7571->reset),
- "Failed to get reset gpio\n");
-
-
- return 0;
-}
-
-static void st7571_reset(struct st7571_device *st7571)
-{
- gpiod_set_value_cansleep(st7571->reset, 1);
- fsleep(20);
- gpiod_set_value_cansleep(st7571->reset, 0);
-}
-
-static int st7567_lcd_init(struct st7571_device *st7567)
-{
- /*
- * Most of the initialization sequence is taken directly from the
- * referential initial code in the ST7567 datasheet.
- */
- u8 commands[] = {
- ST7571_DISPLAY_OFF,
-
- ST7567_SET_LCD_BIAS(1),
-
- ST7571_SET_SEG_SCAN_DIR(0),
- ST7571_SET_COM_SCAN_DIR(1),
-
- ST7571_SET_REGULATOR_REG(4),
- ST7571_SET_CONTRAST_MSB,
- ST7571_SET_CONTRAST_LSB(0x20),
-
- ST7571_SET_START_LINE_MSB,
- ST7571_SET_START_LINE_LSB(st7567->startline),
-
- ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
- ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
- ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
-
- ST7571_SET_REVERSE(st7567->inverted ? 1 : 0),
- ST7571_SET_ENTIRE_DISPLAY_ON(0),
- };
-
- return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands));
-}
-
-static int st7571_lcd_init(struct st7571_device *st7571)
-{
- /*
- * Most of the initialization sequence is taken directly from the
- * referential initial code in the ST7571 datasheet.
- */
- u8 commands[] = {
- ST7571_DISPLAY_OFF,
-
- ST7571_SET_MODE_MSB,
- ST7571_SET_MODE_LSB(0x2e),
-
- ST7571_SET_SEG_SCAN_DIR(0),
- ST7571_SET_COM_SCAN_DIR(1),
-
- ST7571_SET_COM0_MSB,
- ST7571_SET_COM0_LSB(0x00),
-
- ST7571_SET_START_LINE_MSB,
- ST7571_SET_START_LINE_LSB(st7571->startline),
-
- ST7571_OSC_ON,
- ST7571_SET_REGULATOR_REG(5),
- ST7571_SET_CONTRAST_MSB,
- ST7571_SET_CONTRAST_LSB(0x33),
- ST7571_SET_LCD_BIAS(0x04),
- ST7571_SET_DISPLAY_DUTY_MSB,
- ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
-
- ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
- ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
- ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
-
- ST7571_COMMAND_SET_3,
- ST7571_SET_COLOR_MODE(st7571->pformat->mode),
- ST7571_COMMAND_SET_NORMAL,
-
- ST7571_SET_REVERSE(st7571->inverted ? 1 : 0),
- ST7571_SET_ENTIRE_DISPLAY_ON(0),
- };
-
- /* Perform a reset before initializing the controller */
- st7571_reset(st7571);
-
- return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
-}
-
-static struct st7571_device *st7571_probe(struct device *dev,
- struct regmap *regmap)
-{
- struct st7571_device *st7571;
- struct drm_device *drm;
- int ret;
-
- st7571 = devm_drm_dev_alloc(dev, &st7571_driver,
- struct st7571_device, drm);
- if (IS_ERR(st7571))
- return st7571;
-
- drm = &st7571->drm;
- st7571->dev = dev;
- st7571->pdata = device_get_match_data(st7571->dev);
-
- ret = st7571->pdata->parse_dt(st7571);
- if (ret)
- return ERR_PTR(ret);
-
- ret = st7571_validate_parameters(st7571);
- if (ret)
- return ERR_PTR(ret);
-
- st7571->mode = st7571_mode(st7571);
- st7571->regmap = regmap;
-
-
- st7571->hwbuf = devm_kzalloc(st7571->dev,
- (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
- GFP_KERNEL);
- if (!st7571->hwbuf)
- return ERR_PTR(-ENOMEM);
-
- st7571->row = devm_kzalloc(st7571->dev,
- (st7571->ncols * st7571->bpp),
- GFP_KERNEL);
- if (!st7571->row)
- return ERR_PTR(-ENOMEM);
-
- ret = st7571_mode_config_init(st7571);
- if (ret) {
- dev_err(st7571->dev, "Failed to initialize mode config\n");
- return ERR_PTR(ret);
- }
-
- ret = st7571_plane_init(st7571, st7571->pformat);
- if (ret) {
- dev_err(st7571->dev, "Failed to initialize primary plane\n");
- return ERR_PTR(ret);
- }
-
- ret = st7571_crtc_init(st7571);
- if (ret < 0) {
- dev_err(st7571->dev, "Failed to initialize CRTC\n");
- return ERR_PTR(ret);
- }
-
- ret = st7571_encoder_init(st7571);
- if (ret < 0) {
- dev_err(st7571->dev, "Failed to initialize encoder\n");
- return ERR_PTR(ret);
- }
-
- ret = st7571_connector_init(st7571);
- if (ret < 0) {
- dev_err(st7571->dev, "Failed to initialize connector\n");
- return ERR_PTR(ret);
- }
-
- drm_mode_config_reset(drm);
-
- ret = drm_dev_register(drm, 0);
- if (ret) {
- dev_err(st7571->dev, "Failed to register DRM device\n");
- return ERR_PTR(ret);
- }
-
- drm_client_setup(drm, NULL);
- return st7571;
-}
-
-static void st7571_remove(struct st7571_device *st7571)
-{
- drm_dev_unplug(&st7571->drm);
-}
-
static int st7571_i2c_probe(struct i2c_client *client)
{
struct st7571_device *st7571;
@@ -979,8 +103,8 @@ static int st7571_i2c_probe(struct i2c_client *client)
if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
t->ignore_nak = true;
- regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
- t, &st7571_regmap_config);
+ regmap = devm_regmap_init(&client->dev, &st7571_i2c_regmap_bus,
+ t, &st7571_i2c_regmap_config);
if (IS_ERR(regmap)) {
return dev_err_probe(&client->dev, PTR_ERR(regmap),
"Failed to initialize regmap\n");
@@ -1002,30 +126,6 @@ static void st7571_i2c_remove(struct i2c_client *client)
st7571_remove(st7571);
}
-static const struct st7571_panel_data st7567_config = {
- .init = st7567_lcd_init,
- .parse_dt = st7567_parse_dt,
- .constraints = {
- .min_nlines = 1,
- .max_nlines = 64,
- .min_ncols = 128,
- .max_ncols = 128,
- .support_grayscale = false,
- },
-};
-
-static const struct st7571_panel_data st7571_config = {
- .init = st7571_lcd_init,
- .parse_dt = st7571_parse_dt,
- .constraints = {
- .min_nlines = 1,
- .max_nlines = 128,
- .min_ncols = 128,
- .max_ncols = 128,
- .support_grayscale = true,
- },
-};
-
static const struct of_device_id st7571_of_match[] = {
{ .compatible = "sitronix,st7567", .data = &st7567_config },
{ .compatible = "sitronix,st7571", .data = &st7571_config },
@@ -1042,7 +142,7 @@ MODULE_DEVICE_TABLE(i2c, st7571_id);
static struct i2c_driver st7571_i2c_driver = {
.driver = {
- .name = "st7571",
+ .name = "st7571-i2c",
.of_match_table = st7571_of_match,
},
.probe = st7571_i2c_probe,
@@ -1053,5 +153,5 @@ static struct i2c_driver st7571_i2c_driver = {
module_i2c_driver(st7571_i2c_driver);
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
-MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
+MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (I2C)");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fd575d972a2e3b6654af7ed0a4ab5c4323e7a33
--- /dev/null
+++ b/drivers/gpu/drm/sitronix/st7571.c
@@ -0,0 +1,918 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
+ *
+ * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_shmem.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_module.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+
+#include "st7571.h"
+
+#define ST7571_COMMAND_MODE (0x00)
+#define ST7571_DATA_MODE (0x40)
+
+/* Normal mode command set */
+#define ST7571_DISPLAY_OFF (0xae)
+#define ST7571_DISPLAY_ON (0xaf)
+#define ST7571_OSC_ON (0xab)
+#define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
+#define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
+#define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x)))
+#define ST7571_SET_COM0_MSB (0x44)
+#define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
+#define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c)))
+#define ST7571_SET_CONTRAST_MSB (0x81)
+#define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d)))
+#define ST7571_SET_DISPLAY_DUTY_MSB (0x48)
+#define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
+#define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
+#define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m)))
+#define ST7571_SET_MODE_MSB (0x38)
+#define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
+#define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
+#define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
+#define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
+#define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
+#define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l)))
+#define ST7571_SET_START_LINE_MSB (0x40)
+
+/* Extension command set 3 */
+#define ST7571_COMMAND_SET_3 (0x7b)
+#define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
+#define ST7571_COMMAND_SET_NORMAL (0x00)
+
+/* ST7567 commands */
+#define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m)))
+
+#define ST7571_PAGE_HEIGHT 8
+
+#define DRIVER_NAME "st7571"
+#define DRIVER_DESC "ST7571 DRM driver"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
+{
+ return container_of(drm, struct st7571_device, drm);
+}
+
+static int st7571_send_command_list(struct st7571_device *st7571,
+ const u8 *cmd_list, size_t len)
+{
+ int ret;
+
+ for (int i = 0; i < len; i++) {
+ ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp)
+{
+ int xrest = x % 8;
+ u8 result = 0;
+ u8 row_len = 16 * bpp;
+
+ /*
+ * Transforms an (x, y) pixel coordinate into a vertical 8-bit
+ * column from the framebuffer. It calculates the corresponding byte in the
+ * framebuffer, extracts the bit at the given x position across 8 consecutive
+ * rows, and packs those bits into a single byte.
+ *
+ * Return an 8-bit value representing a vertical column of pixels.
+ */
+ x = x / 8;
+ y = (y / 8) * 8;
+
+ for (int i = 0; i < 8; i++) {
+ int row_idx = y + i;
+ u8 byte = p[row_idx * row_len + x];
+ u8 bit = (byte >> xrest) & 1;
+
+ result |= (bit << i);
+ }
+
+ return result;
+}
+
+static int st7571_set_position(struct st7571_device *st7571, int x, int y)
+{
+ u8 cmd_list[] = {
+ ST7571_SET_COLUMN_LSB(x),
+ ST7571_SET_COLUMN_MSB(x),
+ ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
+ };
+
+ return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
+}
+
+static int st7571_fb_clear_screen(struct st7571_device *st7571)
+{
+ u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
+ char pixelvalue = 0x00;
+
+ st7571_set_position(st7571, 0, 0);
+ for (int i = 0; i < npixels; i++)
+ regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
+
+ return 0;
+}
+
+static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
+ const struct iosys_map *vmap,
+ struct drm_framebuffer *fb,
+ struct drm_rect *rect,
+ struct drm_format_conv_state *fmtcnv_state)
+{
+ unsigned int dst_pitch;
+ struct iosys_map dst;
+ u32 size;
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_XRGB8888:
+ dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
+ iosys_map_set_vaddr(&dst, st7571->hwbuf);
+
+ drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+ break;
+
+ case DRM_FORMAT_R1:
+ size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+ memcpy(st7571->hwbuf, vmap->vaddr, size);
+ break;
+ }
+}
+
+static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
+ const struct iosys_map *vmap,
+ struct drm_framebuffer *fb,
+ struct drm_rect *rect,
+ struct drm_format_conv_state *fmtcnv_state)
+{
+ u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+ unsigned int dst_pitch;
+ struct iosys_map dst;
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_XRGB8888:
+ dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4);
+ iosys_map_set_vaddr(&dst, st7571->hwbuf);
+
+ drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+ break;
+
+ case DRM_FORMAT_R1:
+ size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+ memcpy(st7571->hwbuf, vmap->vaddr, size);
+ break;
+
+ case DRM_FORMAT_R2:
+ size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
+ memcpy(st7571->hwbuf, vmap->vaddr, size);
+ break;
+ }
+}
+
+static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
+{
+ struct st7571_device *st7571 = drm_to_st7571(fb->dev);
+ char *row = st7571->row;
+
+ /* Align y to display page boundaries */
+ rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
+ rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
+
+ for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
+ for (int x = rect->x1; x < rect->x2; x++)
+ row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1);
+
+ st7571_set_position(st7571, rect->x1, y);
+
+ /* TODO: Investige why we can't write multiple bytes at once */
+ for (int x = rect->x1; x < rect->x2; x++)
+ regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+ }
+
+ return 0;
+}
+
+static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
+{
+ struct st7571_device *st7571 = drm_to_st7571(fb->dev);
+ u32 format = fb->format->format;
+ char *row = st7571->row;
+ int x1;
+ int x2;
+
+ /* Align y to display page boundaries */
+ rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
+ rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
+
+ switch (format) {
+ case DRM_FORMAT_R1:
+ x1 = rect->x1 * 1;
+ x2 = rect->x2 * 1;
+ break;
+ case DRM_FORMAT_R2:
+ fallthrough;
+ case DRM_FORMAT_XRGB8888:
+ x1 = rect->x1 * 2;
+ x2 = rect->x2 * 2;
+ break;
+ }
+
+ for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
+ for (int x = x1; x < x2; x++)
+ row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2);
+
+ st7571_set_position(st7571, rect->x1, y);
+
+ /* TODO: Investige why we can't write multiple bytes at once */
+ for (int x = x1; x < x2; x++) {
+ regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+
+ /*
+ * As the display supports grayscale, all pixels must be written as two bits
+ * even if the format is monochrome.
+ *
+ * The bit values maps to the following grayscale:
+ * 0 0 = Black
+ * 0 1 = Dark gray
+ * 1 0 = Light gray
+ * 1 1 = White
+ *
+ * For monochrome formats, write the same value twice to get
+ * either a black or white pixel.
+ */
+ if (format == DRM_FORMAT_R1)
+ regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+ }
+ }
+
+ return 0;
+}
+
+static int st7571_connector_get_modes(struct drm_connector *conn)
+{
+ struct st7571_device *st7571 = drm_to_st7571(conn->dev);
+
+ return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
+}
+
+static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
+ .get_modes = st7571_connector_get_modes,
+};
+
+static const struct st7571_panel_format st7571_monochrome = {
+ .prepare_buffer = st7571_prepare_buffer_monochrome,
+ .update_rect = st7571_fb_update_rect_monochrome,
+ .mode = ST7571_COLOR_MODE_BLACKWHITE,
+ .formats = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_R1,
+ },
+ .nformats = 2,
+};
+
+static const struct st7571_panel_format st7571_grayscale = {
+ .prepare_buffer = st7571_prepare_buffer_grayscale,
+ .update_rect = st7571_fb_update_rect_grayscale,
+ .mode = ST7571_COLOR_MODE_GRAY,
+ .formats = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_R1,
+ DRM_FORMAT_R2,
+ },
+ .nformats = 3,
+};
+
+static const u64 st7571_primary_plane_fmtmods[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
+static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *new_crtc = new_plane_state->crtc;
+ struct drm_crtc_state *new_crtc_state = NULL;
+
+ if (new_crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
+
+ return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+}
+
+static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_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 damage;
+ struct st7571_device *st7571 = drm_to_st7571(plane->dev);
+ int ret, idx;
+
+ if (!fb)
+ return; /* no framebuffer; plane is disabled */
+
+ ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
+ if (ret)
+ return;
+
+ if (!drm_dev_enter(drm, &idx))
+ goto out_drm_gem_fb_end_cpu_access;
+
+ drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_for_each_plane_damage(&iter, &damage) {
+ st7571->pformat->prepare_buffer(st7571,
+ &shadow_plane_state->data[0],
+ fb, &damage,
+ &shadow_plane_state->fmtcnv_state);
+
+ st7571->pformat->update_rect(fb, &damage);
+ }
+
+ drm_dev_exit(idx);
+
+out_drm_gem_fb_end_cpu_access:
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+}
+
+static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *drm = plane->dev;
+ struct st7571_device *st7571 = drm_to_st7571(plane->dev);
+ int idx;
+
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+ st7571_fb_clear_screen(st7571);
+ drm_dev_exit(idx);
+}
+
+static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = st7571_primary_plane_helper_atomic_check,
+ .atomic_update = st7571_primary_plane_helper_atomic_update,
+ .atomic_disable = st7571_primary_plane_helper_atomic_disable,
+};
+
+static const struct drm_plane_funcs st7571_primary_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+};
+
+/*
+ * CRTC
+ */
+
+static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
+
+ return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
+}
+
+static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
+ .atomic_check = drm_crtc_helper_atomic_check,
+ .mode_valid = st7571_crtc_mode_valid,
+};
+
+static const struct drm_crtc_funcs st7571_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * Encoder
+ */
+
+static void st7571_encoder_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *drm = encoder->dev;
+ struct st7571_device *st7571 = drm_to_st7571(drm);
+ u8 command = ST7571_DISPLAY_ON;
+ int ret;
+
+ ret = st7571->pdata->init(st7571);
+ if (ret)
+ return;
+
+ st7571_send_command_list(st7571, &command, 1);
+}
+
+static void st7571_encoder_atomic_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *drm = encoder->dev;
+ struct st7571_device *st7571 = drm_to_st7571(drm);
+ u8 command = ST7571_DISPLAY_OFF;
+
+ st7571_send_command_list(st7571, &command, 1);
+}
+
+static const struct drm_encoder_funcs st7571_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+
+};
+
+static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
+ .atomic_enable = st7571_encoder_atomic_enable,
+ .atomic_disable = st7571_encoder_atomic_disable,
+};
+
+/*
+ * Connector
+ */
+
+static const struct drm_connector_funcs st7571_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_mode_config_funcs st7571_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create_with_dirty,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
+{
+ struct drm_display_mode mode = {
+ DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
+ st7571->width_mm, st7571->height_mm),
+ };
+
+ return mode;
+}
+
+static int st7571_mode_config_init(struct st7571_device *st7571)
+{
+ struct drm_device *drm = &st7571->drm;
+ const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
+ int ret;
+
+ ret = drmm_mode_config_init(drm);
+ if (ret)
+ return ret;
+
+ drm->mode_config.min_width = constraints->min_ncols;
+ drm->mode_config.min_height = constraints->min_nlines;
+ drm->mode_config.max_width = constraints->max_ncols;
+ drm->mode_config.max_height = constraints->max_nlines;
+ drm->mode_config.preferred_depth = 24;
+ drm->mode_config.funcs = &st7571_mode_config_funcs;
+
+ return 0;
+}
+
+static int st7571_plane_init(struct st7571_device *st7571,
+ const struct st7571_panel_format *pformat)
+{
+ struct drm_plane *primary_plane = &st7571->primary_plane;
+ struct drm_device *drm = &st7571->drm;
+ int ret;
+
+ ret = drm_universal_plane_init(drm, primary_plane, 0,
+ &st7571_primary_plane_funcs,
+ pformat->formats,
+ pformat->nformats,
+ st7571_primary_plane_fmtmods,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+
+ drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
+ drm_plane_enable_fb_damage_clips(primary_plane);
+
+ return 0;
+}
+
+static int st7571_crtc_init(struct st7571_device *st7571)
+{
+ struct drm_plane *primary_plane = &st7571->primary_plane;
+ struct drm_crtc *crtc = &st7571->crtc;
+ struct drm_device *drm = &st7571->drm;
+ int ret;
+
+ ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+ &st7571_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
+
+ return 0;
+}
+
+static int st7571_encoder_init(struct st7571_device *st7571)
+{
+ struct drm_encoder *encoder = &st7571->encoder;
+ struct drm_crtc *crtc = &st7571->crtc;
+ struct drm_device *drm = &st7571->drm;
+ int ret;
+
+ ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ return 0;
+}
+
+static int st7571_connector_init(struct st7571_device *st7571)
+{
+ struct drm_connector *connector = &st7571->connector;
+ struct drm_encoder *encoder = &st7571->encoder;
+ struct drm_device *drm = &st7571->drm;
+ int ret;
+
+ ret = drm_connector_init(drm, connector, &st7571_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ if (ret)
+ return ret;
+
+ drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
+
+ return drm_connector_attach_encoder(connector, encoder);
+}
+
+DEFINE_DRM_GEM_FOPS(st7571_fops);
+
+static const struct drm_driver st7571_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+
+ .fops = &st7571_fops,
+ DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_FBDEV_SHMEM_DRIVER_OPS,
+};
+
+static int st7571_validate_parameters(struct st7571_device *st7571)
+{
+ struct device *dev = st7571->dev;
+ const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
+
+ if (st7571->width_mm == 0) {
+ dev_err(dev, "Invalid panel width\n");
+ return -EINVAL;
+ }
+
+ if (st7571->height_mm == 0) {
+ dev_err(dev, "Invalid panel height\n");
+ return -EINVAL;
+ }
+
+ if (st7571->nlines < constraints->min_nlines ||
+ st7571->nlines > constraints->max_nlines) {
+ dev_err(dev, "Invalid timing configuration.\n");
+ return -EINVAL;
+ }
+
+ if (st7571->startline + st7571->nlines > constraints->max_nlines) {
+ dev_err(dev, "Invalid timing configuration.\n");
+ return -EINVAL;
+ }
+
+ if (st7571->ncols < constraints->min_ncols ||
+ st7571->ncols > constraints->max_ncols) {
+ dev_err(dev, "Invalid timing configuration.\n");
+ return -EINVAL;
+ }
+
+ if (st7571->grayscale && !constraints->support_grayscale) {
+ dev_err(dev, "Grayscale not supported\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st7567_parse_dt(struct st7571_device *st7567)
+{
+ struct device *dev = st7567->dev;
+ struct device_node *np = dev->of_node;
+ struct display_timing dt;
+ int ret;
+
+ ret = of_get_display_timing(np, "panel-timing", &dt);
+ if (ret) {
+ dev_err(dev, "Failed to get display timing from DT\n");
+ return ret;
+ }
+
+ of_property_read_u32(np, "width-mm", &st7567->width_mm);
+ of_property_read_u32(np, "height-mm", &st7567->height_mm);
+ st7567->inverted = of_property_read_bool(np, "sitronix,inverted");
+
+ st7567->pformat = &st7571_monochrome;
+ st7567->bpp = 1;
+
+ st7567->startline = dt.vfront_porch.typ;
+ st7567->nlines = dt.vactive.typ;
+ st7567->ncols = dt.hactive.typ;
+
+ return 0;
+}
+
+static int st7571_parse_dt(struct st7571_device *st7571)
+{
+ struct device *dev = st7571->dev;
+ struct device_node *np = dev->of_node;
+ struct display_timing dt;
+ int ret;
+
+ ret = of_get_display_timing(np, "panel-timing", &dt);
+ if (ret) {
+ dev_err(dev, "Failed to get display timing from DT\n");
+ return ret;
+ }
+
+ of_property_read_u32(np, "width-mm", &st7571->width_mm);
+ of_property_read_u32(np, "height-mm", &st7571->height_mm);
+ st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
+ st7571->inverted = of_property_read_bool(np, "sitronix,inverted");
+
+ if (st7571->grayscale) {
+ st7571->pformat = &st7571_grayscale;
+ st7571->bpp = 2;
+ } else {
+ st7571->pformat = &st7571_monochrome;
+ st7571->bpp = 1;
+ }
+
+ st7571->startline = dt.vfront_porch.typ;
+ st7571->nlines = dt.vactive.typ;
+ st7571->ncols = dt.hactive.typ;
+
+ st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(st7571->reset))
+ return dev_err_probe(dev, PTR_ERR(st7571->reset),
+ "Failed to get reset gpio\n");
+
+ return 0;
+}
+
+static void st7571_reset(struct st7571_device *st7571)
+{
+ gpiod_set_value_cansleep(st7571->reset, 1);
+ fsleep(20);
+ gpiod_set_value_cansleep(st7571->reset, 0);
+}
+
+static int st7567_lcd_init(struct st7571_device *st7567)
+{
+ /*
+ * Most of the initialization sequence is taken directly from the
+ * referential initial code in the ST7567 datasheet.
+ */
+ u8 commands[] = {
+ ST7571_DISPLAY_OFF,
+
+ ST7567_SET_LCD_BIAS(1),
+
+ ST7571_SET_SEG_SCAN_DIR(0),
+ ST7571_SET_COM_SCAN_DIR(1),
+
+ ST7571_SET_REGULATOR_REG(4),
+ ST7571_SET_CONTRAST_MSB,
+ ST7571_SET_CONTRAST_LSB(0x20),
+
+ ST7571_SET_START_LINE_MSB,
+ ST7571_SET_START_LINE_LSB(st7567->startline),
+
+ ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
+ ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
+ ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
+
+ ST7571_SET_REVERSE(st7567->inverted ? 1 : 0),
+ ST7571_SET_ENTIRE_DISPLAY_ON(0),
+ };
+
+ return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands));
+}
+
+static int st7571_lcd_init(struct st7571_device *st7571)
+{
+ /*
+ * Most of the initialization sequence is taken directly from the
+ * referential initial code in the ST7571 datasheet.
+ */
+ u8 commands[] = {
+ ST7571_DISPLAY_OFF,
+
+ ST7571_SET_MODE_MSB,
+ ST7571_SET_MODE_LSB(0x2e),
+
+ ST7571_SET_SEG_SCAN_DIR(0),
+ ST7571_SET_COM_SCAN_DIR(1),
+
+ ST7571_SET_COM0_MSB,
+ ST7571_SET_COM0_LSB(0x00),
+
+ ST7571_SET_START_LINE_MSB,
+ ST7571_SET_START_LINE_LSB(st7571->startline),
+
+ ST7571_OSC_ON,
+ ST7571_SET_REGULATOR_REG(5),
+ ST7571_SET_CONTRAST_MSB,
+ ST7571_SET_CONTRAST_LSB(0x33),
+ ST7571_SET_LCD_BIAS(0x04),
+ ST7571_SET_DISPLAY_DUTY_MSB,
+ ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
+
+ ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
+ ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
+ ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
+
+ ST7571_COMMAND_SET_3,
+ ST7571_SET_COLOR_MODE(st7571->pformat->mode),
+ ST7571_COMMAND_SET_NORMAL,
+
+ ST7571_SET_REVERSE(st7571->inverted ? 1 : 0),
+ ST7571_SET_ENTIRE_DISPLAY_ON(0),
+ };
+
+ /* Perform a reset before initializing the controller */
+ st7571_reset(st7571);
+
+ return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
+}
+
+struct st7571_device *st7571_probe(struct device *dev,
+ struct regmap *regmap)
+{
+ struct st7571_device *st7571;
+ struct drm_device *drm;
+ int ret;
+
+ st7571 = devm_drm_dev_alloc(dev, &st7571_driver,
+ struct st7571_device, drm);
+ if (IS_ERR(st7571))
+ return st7571;
+
+ drm = &st7571->drm;
+ st7571->dev = dev;
+ st7571->pdata = device_get_match_data(st7571->dev);
+
+ ret = st7571->pdata->parse_dt(st7571);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = st7571_validate_parameters(st7571);
+ if (ret)
+ return ERR_PTR(ret);
+
+ st7571->mode = st7571_mode(st7571);
+ st7571->regmap = regmap;
+
+ st7571->hwbuf = devm_kzalloc(st7571->dev,
+ (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
+ GFP_KERNEL);
+ if (!st7571->hwbuf)
+ return ERR_PTR(-ENOMEM);
+
+ st7571->row = devm_kzalloc(st7571->dev,
+ (st7571->ncols * st7571->bpp),
+ GFP_KERNEL);
+ if (!st7571->row)
+ return ERR_PTR(-ENOMEM);
+
+ ret = st7571_mode_config_init(st7571);
+ if (ret) {
+ dev_err(st7571->dev, "Failed to initialize mode config\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = st7571_plane_init(st7571, st7571->pformat);
+ if (ret) {
+ dev_err(st7571->dev, "Failed to initialize primary plane\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = st7571_crtc_init(st7571);
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize CRTC\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = st7571_encoder_init(st7571);
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize encoder\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = st7571_connector_init(st7571);
+ if (ret < 0) {
+ dev_err(st7571->dev, "Failed to initialize connector\n");
+ return ERR_PTR(ret);
+ }
+
+ drm_mode_config_reset(drm);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret) {
+ dev_err(st7571->dev, "Failed to register DRM device\n");
+ return ERR_PTR(ret);
+ }
+
+ drm_client_setup(drm, NULL);
+ return st7571;
+}
+EXPORT_SYMBOL_GPL(st7571_probe);
+
+void st7571_remove(struct st7571_device *st7571)
+{
+ drm_dev_unplug(&st7571->drm);
+}
+EXPORT_SYMBOL_GPL(st7571_remove);
+
+const struct st7571_panel_data st7567_config = {
+ .init = st7567_lcd_init,
+ .parse_dt = st7567_parse_dt,
+ .constraints = {
+ .min_nlines = 1,
+ .max_nlines = 64,
+ .min_ncols = 128,
+ .max_ncols = 128,
+ .support_grayscale = false,
+ },
+};
+EXPORT_SYMBOL_NS_GPL(st7567_config, "DRM_ST7571");
+
+const struct st7571_panel_data st7571_config = {
+ .init = st7571_lcd_init,
+ .parse_dt = st7571_parse_dt,
+ .constraints = {
+ .min_nlines = 1,
+ .max_nlines = 128,
+ .min_ncols = 128,
+ .max_ncols = 128,
+ .support_grayscale = true,
+ },
+};
+EXPORT_SYMBOL_NS_GPL(st7571_config, "DRM_ST7571");
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sitronix/st7571.h b/drivers/gpu/drm/sitronix/st7571.h
index f62c57ddb99ebe82f63048cc2ccf75a81518d717..af264f2e2ea47e7813067733e7051db9529ca1f1 100644
--- a/drivers/gpu/drm/sitronix/st7571.h
+++ b/drivers/gpu/drm/sitronix/st7571.h
@@ -82,4 +82,10 @@ struct st7571_device {
u8 *row;
};
+extern const struct st7571_panel_data st7567_config;
+extern const struct st7571_panel_data st7571_config;
+
+struct st7571_device *st7571_probe(struct device *dev, struct regmap *regmap);
+void st7571_remove(struct st7571_device *st7571);
+
#endif /* __ST7571_H__ */
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
` (4 preceding siblings ...)
2025-10-24 10:56 ` [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part Marcus Folkesson
@ 2025-10-24 10:56 ` Marcus Folkesson
2025-10-25 1:23 ` kernel test robot
2025-10-25 6:42 ` kernel test robot
5 siblings, 2 replies; 11+ messages in thread
From: Marcus Folkesson @ 2025-10-24 10:56 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter
Cc: dri-devel, linux-kernel, Marcus Folkesson
Add support for ST7561/ST7571 connected to SPI bus.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
MAINTAINERS | 1 +
drivers/gpu/drm/sitronix/Kconfig | 12 ++++++
drivers/gpu/drm/sitronix/Makefile | 1 +
drivers/gpu/drm/sitronix/st7571-spi.c | 75 +++++++++++++++++++++++++++++++++++
4 files changed, 89 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 66e9ffb757c8bb19dbb894eb51f88f589ee83af6..c89e521cafa1d50fd94bfe7a6868c531aec1f494 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8083,6 +8083,7 @@ S: Maintained
F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml
F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml
F: drivers/gpu/drm/sitronix/st7571-i2c.c
+F: drivers/gpu/drm/sitronix/st7571-spi.c
F: drivers/gpu/drm/sitronix/st7571.c
F: drivers/gpu/drm/sitronix/st7571.h
diff --git a/drivers/gpu/drm/sitronix/Kconfig b/drivers/gpu/drm/sitronix/Kconfig
index 0676a86661caa4ab81f226ff2e990a3e2526f74d..2204250ab4136ac2d3f18f295dc7413a6e17ed45 100644
--- a/drivers/gpu/drm/sitronix/Kconfig
+++ b/drivers/gpu/drm/sitronix/Kconfig
@@ -27,6 +27,18 @@ config DRM_ST7571_I2C
if M is selected the module will be called st7571-i2c.
+config DRM_ST7571_SPI
+ tristate "DRM support for Sitronix ST7567/ST7571 display panels (SPI)"
+ depends on DRM_ST7571 && SPI
+ select REGMAP_SPI
+ help
+ Sitronix ST7571 is a driver and controller for 4-level gray
+ scale and monochrome dot matrix LCD panels.
+
+ DRM driver for Sitronix ST7565/ST7571 panels connected via SPI bus.
+
+ if M is selected the module will be called st7571-spi.
+
config DRM_ST7586
tristate "DRM support for Sitronix ST7586 display panels"
depends on DRM && SPI
diff --git a/drivers/gpu/drm/sitronix/Makefile b/drivers/gpu/drm/sitronix/Makefile
index 8073bb776ff94de750f350b636fd9db3d54fdd46..c631e3359c3dc21ab8522b8f0cfe6e9bf0dbc011 100644
--- a/drivers/gpu/drm/sitronix/Makefile
+++ b/drivers/gpu/drm/sitronix/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_DRM_ST7571) += st7571.o
obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o
+obj-$(CONFIG_DRM_ST7571_SPI) += st7571-spi.o
obj-$(CONFIG_DRM_ST7586) += st7586.o
obj-$(CONFIG_DRM_ST7735R) += st7735r.o
diff --git a/drivers/gpu/drm/sitronix/st7571-spi.c b/drivers/gpu/drm/sitronix/st7571-spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..104737ca860ce44ff411dbdd36c3a7b32a81733f
--- /dev/null
+++ b/drivers/gpu/drm/sitronix/st7571-spi.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Sitronix ST7571 connected via SPI bus.
+ *
+ * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "st7571.h"
+
+static const struct regmap_config st7571_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .can_multi_write = true,
+};
+
+static int st7571_spi_probe(struct spi_device *spi)
+{
+ struct st7571_device *st7571;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &st7571_spi_regmap_config);
+ if (IS_ERR(regmap)) {
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to initialize regmap\n");
+ }
+
+ st7571 = st7571_probe(&spi->dev, regmap);
+ if (IS_ERR(st7571))
+ return dev_err_probe(&spi->dev, PTR_ERR(st7571),
+ "Failed to initialize regmap\n");
+
+ spi_set_drvdata(spi, st7571);
+ return 0;
+}
+
+static void st7571_spi_remove(struct spi_device *spi)
+{
+ struct st7571_device *st7571 = spi_get_drvdata(spi);
+
+ st7571_remove(st7571);
+}
+
+static const struct of_device_id st7571_of_match[] = {
+ { .compatible = "sitronix,st7567", .data = &st7567_config },
+ { .compatible = "sitronix,st7571", .data = &st7571_config },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st7571_of_match);
+
+static const struct spi_device_id st7571_spi_id[] = {
+ { "st7567", 0 },
+ { "st7571", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, st7571_spi_id);
+
+static struct spi_driver st7571_spi_driver = {
+ .driver = {
+ .name = "st7571-spi",
+ .of_match_table = st7571_of_match,
+ },
+ .probe = st7571_spi_probe,
+ .remove = st7571_spi_remove,
+ .id_table = st7571_spi_id,
+};
+
+module_spi_driver(st7571_spi_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (SPI)");
+MODULE_LICENSE("GPL");
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface
2025-10-24 10:56 ` [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface Marcus Folkesson
@ 2025-10-25 1:23 ` kernel test robot
2025-10-25 6:42 ` kernel test robot
1 sibling, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-10-25 1:23 UTC (permalink / raw)
To: Marcus Folkesson, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: oe-kbuild-all, dri-devel, linux-kernel, Marcus Folkesson
Hi Marcus,
kernel test robot noticed the following build errors:
[auto build test ERROR on 7e73cefd2bede5408d1aeb6145261b62d85d23be]
url: https://github.com/intel-lab-lkp/linux/commits/Marcus-Folkesson/drm-sitronix-st7571-i2c-rename-struct-drm_device-in-st7571_device/20251024-192347
base: 7e73cefd2bede5408d1aeb6145261b62d85d23be
patch link: https://lore.kernel.org/r/20251024-st7571-split-v1-6-d3092b98130f%40gmail.com
patch subject: [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface
config: i386-randconfig-014-20251025 (https://download.01.org/0day-ci/archive/20251025/202510250945.hB9LcnfV-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251025/202510250945.hB9LcnfV-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510250945.hB9LcnfV-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: module st7571-spi uses symbol st7567_config from namespace DRM_ST7571, but does not import it.
>> ERROR: modpost: module st7571-spi uses symbol st7571_config from namespace DRM_ST7571, but does not import it.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part
2025-10-24 10:56 ` [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part Marcus Folkesson
@ 2025-10-25 3:56 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-10-25 3:56 UTC (permalink / raw)
To: Marcus Folkesson, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: llvm, oe-kbuild-all, dri-devel, linux-kernel, Marcus Folkesson
Hi Marcus,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 7e73cefd2bede5408d1aeb6145261b62d85d23be]
url: https://github.com/intel-lab-lkp/linux/commits/Marcus-Folkesson/drm-sitronix-st7571-i2c-rename-struct-drm_device-in-st7571_device/20251024-192347
base: 7e73cefd2bede5408d1aeb6145261b62d85d23be
patch link: https://lore.kernel.org/r/20251024-st7571-split-v1-5-d3092b98130f%40gmail.com
patch subject: [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part
config: x86_64-randconfig-005-20251025 (https://download.01.org/0day-ci/archive/20251025/202510251116.5wpReHRY-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251025/202510251116.5wpReHRY-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510251116.5wpReHRY-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: module st7571-i2c uses symbol st7567_config from namespace DRM_ST7571, but does not import it.
>> WARNING: modpost: module st7571-i2c uses symbol st7571_config from namespace DRM_ST7571, but does not import it.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface
2025-10-24 10:56 ` [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface Marcus Folkesson
2025-10-25 1:23 ` kernel test robot
@ 2025-10-25 6:42 ` kernel test robot
1 sibling, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-10-25 6:42 UTC (permalink / raw)
To: Marcus Folkesson, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: llvm, oe-kbuild-all, dri-devel, linux-kernel, Marcus Folkesson
Hi Marcus,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 7e73cefd2bede5408d1aeb6145261b62d85d23be]
url: https://github.com/intel-lab-lkp/linux/commits/Marcus-Folkesson/drm-sitronix-st7571-i2c-rename-struct-drm_device-in-st7571_device/20251024-192347
base: 7e73cefd2bede5408d1aeb6145261b62d85d23be
patch link: https://lore.kernel.org/r/20251024-st7571-split-v1-6-d3092b98130f%40gmail.com
patch subject: [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface
config: x86_64-randconfig-005-20251025 (https://download.01.org/0day-ci/archive/20251025/202510251430.rl42LCVH-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251025/202510251430.rl42LCVH-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510251430.rl42LCVH-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
WARNING: modpost: module st7571-i2c uses symbol st7567_config from namespace DRM_ST7571, but does not import it.
WARNING: modpost: module st7571-i2c uses symbol st7571_config from namespace DRM_ST7571, but does not import it.
>> WARNING: modpost: module st7571-spi uses symbol st7567_config from namespace DRM_ST7571, but does not import it.
>> WARNING: modpost: module st7571-spi uses symbol st7571_config from namespace DRM_ST7571, but does not import it.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part
@ 2025-10-30 13:55 kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-10-30 13:55 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp, Dan Carpenter
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20251024-st7571-split-v1-5-d3092b98130f@gmail.com>
References: <20251024-st7571-split-v1-5-d3092b98130f@gmail.com>
TO: Marcus Folkesson <marcus.folkesson@gmail.com>
TO: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
TO: Maxime Ripard <mripard@kernel.org>
TO: Thomas Zimmermann <tzimmermann@suse.de>
TO: David Airlie <airlied@gmail.com>
TO: Simona Vetter <simona@ffwll.ch>
CC: dri-devel@lists.freedesktop.org
CC: linux-kernel@vger.kernel.org
CC: Marcus Folkesson <marcus.folkesson@gmail.com>
Hi Marcus,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 7e73cefd2bede5408d1aeb6145261b62d85d23be]
url: https://github.com/intel-lab-lkp/linux/commits/Marcus-Folkesson/drm-sitronix-st7571-i2c-rename-struct-drm_device-in-st7571_device/20251024-192347
base: 7e73cefd2bede5408d1aeb6145261b62d85d23be
patch link: https://lore.kernel.org/r/20251024-st7571-split-v1-5-d3092b98130f%40gmail.com
patch subject: [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part
:::::: branch date: 6 days ago
:::::: commit date: 6 days ago
config: um-randconfig-r073-20251025 (https://download.01.org/0day-ci/archive/20251030/202510302121.g9BHuXb7-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project e1ae12640102fd2b05bc567243580f90acb1135f)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Closes: https://lore.kernel.org/r/202510302121.g9BHuXb7-lkp@intel.com/
smatch warnings:
drivers/gpu/drm/sitronix/st7571.c:99 st7571_send_command_list() error: uninitialized symbol 'ret'.
drivers/gpu/drm/sitronix/st7571.c:257 st7571_fb_update_rect_grayscale() error: uninitialized symbol 'x1'.
drivers/gpu/drm/sitronix/st7571.c:257 st7571_fb_update_rect_grayscale() error: uninitialized symbol 'x2'.
vim +/ret +99 drivers/gpu/drm/sitronix/st7571.c
b6a4754f3a5418 Marcus Folkesson 2025-10-24 87
b6a4754f3a5418 Marcus Folkesson 2025-10-24 88 static int st7571_send_command_list(struct st7571_device *st7571,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 89 const u8 *cmd_list, size_t len)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 90 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 91 int ret;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 92
b6a4754f3a5418 Marcus Folkesson 2025-10-24 93 for (int i = 0; i < len; i++) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 94 ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 95 if (ret < 0)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 96 return ret;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 97 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 98
b6a4754f3a5418 Marcus Folkesson 2025-10-24 @99 return ret;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 100 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 101
b6a4754f3a5418 Marcus Folkesson 2025-10-24 102 static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 103 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 104 int xrest = x % 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 105 u8 result = 0;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 106 u8 row_len = 16 * bpp;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 107
b6a4754f3a5418 Marcus Folkesson 2025-10-24 108 /*
b6a4754f3a5418 Marcus Folkesson 2025-10-24 109 * Transforms an (x, y) pixel coordinate into a vertical 8-bit
b6a4754f3a5418 Marcus Folkesson 2025-10-24 110 * column from the framebuffer. It calculates the corresponding byte in the
b6a4754f3a5418 Marcus Folkesson 2025-10-24 111 * framebuffer, extracts the bit at the given x position across 8 consecutive
b6a4754f3a5418 Marcus Folkesson 2025-10-24 112 * rows, and packs those bits into a single byte.
b6a4754f3a5418 Marcus Folkesson 2025-10-24 113 *
b6a4754f3a5418 Marcus Folkesson 2025-10-24 114 * Return an 8-bit value representing a vertical column of pixels.
b6a4754f3a5418 Marcus Folkesson 2025-10-24 115 */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 116 x = x / 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 117 y = (y / 8) * 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 118
b6a4754f3a5418 Marcus Folkesson 2025-10-24 119 for (int i = 0; i < 8; i++) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 120 int row_idx = y + i;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 121 u8 byte = p[row_idx * row_len + x];
b6a4754f3a5418 Marcus Folkesson 2025-10-24 122 u8 bit = (byte >> xrest) & 1;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 123
b6a4754f3a5418 Marcus Folkesson 2025-10-24 124 result |= (bit << i);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 125 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 126
b6a4754f3a5418 Marcus Folkesson 2025-10-24 127 return result;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 128 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 129
b6a4754f3a5418 Marcus Folkesson 2025-10-24 130 static int st7571_set_position(struct st7571_device *st7571, int x, int y)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 131 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 132 u8 cmd_list[] = {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 133 ST7571_SET_COLUMN_LSB(x),
b6a4754f3a5418 Marcus Folkesson 2025-10-24 134 ST7571_SET_COLUMN_MSB(x),
b6a4754f3a5418 Marcus Folkesson 2025-10-24 135 ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
b6a4754f3a5418 Marcus Folkesson 2025-10-24 136 };
b6a4754f3a5418 Marcus Folkesson 2025-10-24 137
b6a4754f3a5418 Marcus Folkesson 2025-10-24 138 return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
b6a4754f3a5418 Marcus Folkesson 2025-10-24 139 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 140
b6a4754f3a5418 Marcus Folkesson 2025-10-24 141 static int st7571_fb_clear_screen(struct st7571_device *st7571)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 142 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 143 u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 144 char pixelvalue = 0x00;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 145
b6a4754f3a5418 Marcus Folkesson 2025-10-24 146 st7571_set_position(st7571, 0, 0);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 147 for (int i = 0; i < npixels; i++)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 148 regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 149
b6a4754f3a5418 Marcus Folkesson 2025-10-24 150 return 0;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 151 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 152
b6a4754f3a5418 Marcus Folkesson 2025-10-24 153 static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 154 const struct iosys_map *vmap,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 155 struct drm_framebuffer *fb,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 156 struct drm_rect *rect,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 157 struct drm_format_conv_state *fmtcnv_state)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 158 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 159 unsigned int dst_pitch;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 160 struct iosys_map dst;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 161 u32 size;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 162
b6a4754f3a5418 Marcus Folkesson 2025-10-24 163 switch (fb->format->format) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 164 case DRM_FORMAT_XRGB8888:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 165 dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 166 iosys_map_set_vaddr(&dst, st7571->hwbuf);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 167
b6a4754f3a5418 Marcus Folkesson 2025-10-24 168 drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 169 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 170
b6a4754f3a5418 Marcus Folkesson 2025-10-24 171 case DRM_FORMAT_R1:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 172 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 173 memcpy(st7571->hwbuf, vmap->vaddr, size);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 174 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 175 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 176 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 177
b6a4754f3a5418 Marcus Folkesson 2025-10-24 178 static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 179 const struct iosys_map *vmap,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 180 struct drm_framebuffer *fb,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 181 struct drm_rect *rect,
b6a4754f3a5418 Marcus Folkesson 2025-10-24 182 struct drm_format_conv_state *fmtcnv_state)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 183 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 184 u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 185 unsigned int dst_pitch;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 186 struct iosys_map dst;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 187
b6a4754f3a5418 Marcus Folkesson 2025-10-24 188 switch (fb->format->format) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 189 case DRM_FORMAT_XRGB8888:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 190 dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 191 iosys_map_set_vaddr(&dst, st7571->hwbuf);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 192
b6a4754f3a5418 Marcus Folkesson 2025-10-24 193 drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 194 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 195
b6a4754f3a5418 Marcus Folkesson 2025-10-24 196 case DRM_FORMAT_R1:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 197 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 198 memcpy(st7571->hwbuf, vmap->vaddr, size);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 199 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 200
b6a4754f3a5418 Marcus Folkesson 2025-10-24 201 case DRM_FORMAT_R2:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 202 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 203 memcpy(st7571->hwbuf, vmap->vaddr, size);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 204 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 205 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 206 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 207
b6a4754f3a5418 Marcus Folkesson 2025-10-24 208 static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 209 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 210 struct st7571_device *st7571 = drm_to_st7571(fb->dev);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 211 char *row = st7571->row;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 212
b6a4754f3a5418 Marcus Folkesson 2025-10-24 213 /* Align y to display page boundaries */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 214 rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 215 rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 216
b6a4754f3a5418 Marcus Folkesson 2025-10-24 217 for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 218 for (int x = rect->x1; x < rect->x2; x++)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 219 row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 220
b6a4754f3a5418 Marcus Folkesson 2025-10-24 221 st7571_set_position(st7571, rect->x1, y);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 222
b6a4754f3a5418 Marcus Folkesson 2025-10-24 223 /* TODO: Investige why we can't write multiple bytes at once */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 224 for (int x = rect->x1; x < rect->x2; x++)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 225 regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 226 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 227
b6a4754f3a5418 Marcus Folkesson 2025-10-24 228 return 0;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 229 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 230
b6a4754f3a5418 Marcus Folkesson 2025-10-24 231 static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 232 {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 233 struct st7571_device *st7571 = drm_to_st7571(fb->dev);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 234 u32 format = fb->format->format;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 235 char *row = st7571->row;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 236 int x1;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 237 int x2;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 238
b6a4754f3a5418 Marcus Folkesson 2025-10-24 239 /* Align y to display page boundaries */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 240 rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 241 rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 242
b6a4754f3a5418 Marcus Folkesson 2025-10-24 243 switch (format) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 244 case DRM_FORMAT_R1:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 245 x1 = rect->x1 * 1;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 246 x2 = rect->x2 * 1;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 247 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 248 case DRM_FORMAT_R2:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 249 fallthrough;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 250 case DRM_FORMAT_XRGB8888:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 251 x1 = rect->x1 * 2;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 252 x2 = rect->x2 * 2;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 253 break;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 254 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 255
b6a4754f3a5418 Marcus Folkesson 2025-10-24 256 for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 @257 for (int x = x1; x < x2; x++)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 258 row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 259
b6a4754f3a5418 Marcus Folkesson 2025-10-24 260 st7571_set_position(st7571, rect->x1, y);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 261
b6a4754f3a5418 Marcus Folkesson 2025-10-24 262 /* TODO: Investige why we can't write multiple bytes at once */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 263 for (int x = x1; x < x2; x++) {
b6a4754f3a5418 Marcus Folkesson 2025-10-24 264 regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 265
b6a4754f3a5418 Marcus Folkesson 2025-10-24 266 /*
b6a4754f3a5418 Marcus Folkesson 2025-10-24 267 * As the display supports grayscale, all pixels must be written as two bits
b6a4754f3a5418 Marcus Folkesson 2025-10-24 268 * even if the format is monochrome.
b6a4754f3a5418 Marcus Folkesson 2025-10-24 269 *
b6a4754f3a5418 Marcus Folkesson 2025-10-24 270 * The bit values maps to the following grayscale:
b6a4754f3a5418 Marcus Folkesson 2025-10-24 271 * 0 0 = Black
b6a4754f3a5418 Marcus Folkesson 2025-10-24 272 * 0 1 = Dark gray
b6a4754f3a5418 Marcus Folkesson 2025-10-24 273 * 1 0 = Light gray
b6a4754f3a5418 Marcus Folkesson 2025-10-24 274 * 1 1 = White
b6a4754f3a5418 Marcus Folkesson 2025-10-24 275 *
b6a4754f3a5418 Marcus Folkesson 2025-10-24 276 * For monochrome formats, write the same value twice to get
b6a4754f3a5418 Marcus Folkesson 2025-10-24 277 * either a black or white pixel.
b6a4754f3a5418 Marcus Folkesson 2025-10-24 278 */
b6a4754f3a5418 Marcus Folkesson 2025-10-24 279 if (format == DRM_FORMAT_R1)
b6a4754f3a5418 Marcus Folkesson 2025-10-24 280 regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
b6a4754f3a5418 Marcus Folkesson 2025-10-24 281 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 282 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 283
b6a4754f3a5418 Marcus Folkesson 2025-10-24 284 return 0;
b6a4754f3a5418 Marcus Folkesson 2025-10-24 285 }
b6a4754f3a5418 Marcus Folkesson 2025-10-24 286
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-10-30 13:55 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-24 10:56 [PATCH 0/6] drm/sitronix/st7571: split up driver to support both I2C and SPI Marcus Folkesson
2025-10-24 10:56 ` [PATCH 1/6] drm/sitronix/st7571-i2c: rename 'struct drm_device' in st7571_device Marcus Folkesson
2025-10-24 10:56 ` [PATCH 2/6] drm/sitronix/st7571-i2c: add 'struct device' to st7571_device Marcus Folkesson
2025-10-24 10:56 ` [PATCH 3/6] drm/sitronix/st7571-i2c: move common structures to st7571.h Marcus Folkesson
2025-10-24 10:56 ` [PATCH 4/6] drm/sitronix/st7571-i2c: make probe independent of hw interface Marcus Folkesson
2025-10-24 10:56 ` [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part Marcus Folkesson
2025-10-25 3:56 ` kernel test robot
2025-10-24 10:56 ` [PATCH 6/6] drm/sitronix/st7571-spi: add support for SPI interface Marcus Folkesson
2025-10-25 1:23 ` kernel test robot
2025-10-25 6:42 ` kernel test robot
-- strict thread matches above, loose matches on Subject: below --
2025-10-30 13:55 [PATCH 5/6] drm/sitronix/st7571: split up the driver into a common and an i2c part kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.