Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/sitronix/st7571: split up the driver into a common and an i2c part

Split up the driver to make it possible to add support for hw interfaces
other than I2C.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
Link: https://patch.msgid.link/20251215-st7571-split-v3-5-d5f3205c3138@gmail.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>

authored by

Marcus Folkesson and committed by
Javier Martinez Canillas
b362de16 b0c20d82

+960 -917
+1
MAINTAINERS
··· 8201 8201 F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml 8202 8202 F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml 8203 8203 F: drivers/gpu/drm/sitronix/st7571-i2c.c 8204 + F: drivers/gpu/drm/sitronix/st7571.c 8204 8205 F: drivers/gpu/drm/sitronix/st7571.h 8205 8206 8206 8207 DRM DRIVER FOR SITRONIX ST7701 PANELS
+21 -5
drivers/gpu/drm/sitronix/Kconfig
··· 1 - config DRM_ST7571_I2C 2 - tristate "DRM support for Sitronix ST7571 display panels (I2C)" 3 - depends on DRM && I2C && MMU 1 + config DRM_ST7571 2 + tristate "DRM support for Sitronix ST7567/ST7571 display panels" 3 + depends on DRM && MMU 4 4 select DRM_CLIENT_SELECTION 5 5 select DRM_GEM_SHMEM_HELPER 6 6 select DRM_KMS_HELPER 7 - select REGMAP_I2C 8 7 select VIDEOMODE_HELPERS 9 8 help 10 - DRM driver for Sitronix ST7571 panels controlled over I2C. 9 + Sitronix ST7571 is a driver and controller for 4-level gray 10 + scale and monochrome dot matrix LCD panels. 11 + 12 + DRM driver for Sitronix ST7567/ST7571 panels. 13 + This is only the core driver, a driver for the appropriate bus 14 + transport in your chip also must be selected. 15 + 16 + if M is selected the module will be called st7571. 17 + 18 + config DRM_ST7571_I2C 19 + tristate "DRM support for Sitronix ST7567/ST7571 display panels (I2C)" 20 + depends on DRM_ST7571 && I2C 21 + select REGMAP 22 + help 23 + Sitronix ST7571 is a driver and controller for 4-level gray 24 + scale and monochrome dot matrix LCD panels. 25 + 26 + DRM driver for Sitronix ST7565/ST7571 panels connected via I2C bus. 11 27 12 28 if M is selected the module will be called st7571-i2c. 13 29
+1
drivers/gpu/drm/sitronix/Makefile
··· 1 + obj-$(CONFIG_DRM_ST7571) += st7571.o 1 2 obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o 2 3 obj-$(CONFIG_DRM_ST7586) += st7586.o 3 4 obj-$(CONFIG_DRM_ST7735R) += st7735r.o
+13 -912
drivers/gpu/drm/sitronix/st7571-i2c.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller 3 + * Driver for Sitronix ST7571 connected via I2C bus. 4 4 * 5 5 * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 6 6 */ 7 7 8 - #include <linux/bitfield.h> 9 - #include <linux/delay.h> 10 - #include <linux/gpio/consumer.h> 11 8 #include <linux/i2c.h> 12 9 #include <linux/module.h> 13 10 #include <linux/regmap.h> 14 11 15 - #include <drm/clients/drm_client_setup.h> 16 - #include <drm/drm_atomic.h> 17 - #include <drm/drm_atomic_helper.h> 18 - #include <drm/drm_connector.h> 19 - #include <drm/drm_crtc_helper.h> 20 - #include <drm/drm_damage_helper.h> 21 - #include <drm/drm_drv.h> 22 - #include <drm/drm_encoder.h> 23 - #include <drm/drm_fb_helper.h> 24 - #include <drm/drm_fbdev_shmem.h> 25 - #include <drm/drm_fourcc.h> 26 - #include <drm/drm_framebuffer.h> 27 - #include <drm/drm_gem_atomic_helper.h> 28 - #include <drm/drm_gem_framebuffer_helper.h> 29 - #include <drm/drm_gem_shmem_helper.h> 30 - #include <drm/drm_modeset_helper_vtables.h> 31 - #include <drm/drm_module.h> 32 - #include <drm/drm_plane.h> 33 - #include <drm/drm_probe_helper.h> 34 - 35 - #include <video/display_timing.h> 36 - #include <video/of_display_timing.h> 37 - 38 12 #include "st7571.h" 39 - 40 - #define ST7571_COMMAND_MODE (0x00) 41 - #define ST7571_DATA_MODE (0x40) 42 - 43 - /* Normal mode command set */ 44 - #define ST7571_DISPLAY_OFF (0xae) 45 - #define ST7571_DISPLAY_ON (0xaf) 46 - #define ST7571_OSC_ON (0xab) 47 - #define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c))) 48 - #define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4)) 49 - #define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x))) 50 - #define ST7571_SET_COM0_MSB (0x44) 51 - #define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d))) 52 - #define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c))) 53 - #define ST7571_SET_CONTRAST_MSB (0x81) 54 - #define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d))) 55 - #define ST7571_SET_DISPLAY_DUTY_MSB (0x48) 56 - #define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p))) 57 - #define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b))) 58 - #define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m))) 59 - #define ST7571_SET_MODE_MSB (0x38) 60 - #define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p))) 61 - #define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p))) 62 - #define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r))) 63 - #define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r))) 64 - #define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d))) 65 - #define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l))) 66 - #define ST7571_SET_START_LINE_MSB (0x40) 67 - 68 - /* Extension command set 3 */ 69 - #define ST7571_COMMAND_SET_3 (0x7b) 70 - #define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c))) 71 - #define ST7571_COMMAND_SET_NORMAL (0x00) 72 - 73 - /* ST7567 commands */ 74 - #define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m))) 75 - 76 - #define ST7571_PAGE_HEIGHT 8 77 - 78 - #define DRIVER_NAME "st7571" 79 - #define DRIVER_DESC "ST7571 DRM driver" 80 - #define DRIVER_MAJOR 1 81 - #define DRIVER_MINOR 0 82 13 83 14 struct st7571_i2c_transport { 84 15 struct i2c_client *client; ··· 38 107 bool ignore_nak; 39 108 }; 40 109 41 - static inline struct st7571_device *drm_to_st7571(struct drm_device *drm) 42 - { 43 - return container_of(drm, struct st7571_device, drm); 44 - } 45 - 46 - static int st7571_regmap_write(void *context, const void *data, size_t count) 110 + static int st7571_i2c_regmap_write(void *context, const void *data, size_t count) 47 111 { 48 112 struct st7571_i2c_transport *t = context; 49 113 int ret; ··· 65 139 } 66 140 67 141 /* The st7571 driver does not read registers but regmap expects a .read */ 68 - static int st7571_regmap_read(void *context, const void *reg_buf, 69 - size_t reg_size, void *val_buf, size_t val_size) 142 + static int st7571_i2c_regmap_read(void *context, const void *reg_buf, 143 + size_t reg_size, void *val_buf, size_t val_size) 70 144 { 71 145 return -EOPNOTSUPP; 72 146 } 73 147 74 - static int st7571_send_command_list(struct st7571_device *st7571, 75 - const u8 *cmd_list, size_t len) 76 - { 77 - int ret; 78 - 79 - for (int i = 0; i < len; i++) { 80 - ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]); 81 - if (ret < 0) 82 - return ret; 83 - } 84 - 85 - return ret; 86 - } 87 - 88 - static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp) 89 - { 90 - int xrest = x % 8; 91 - u8 result = 0; 92 - u8 row_len = 16 * bpp; 93 - 94 - /* 95 - * Transforms an (x, y) pixel coordinate into a vertical 8-bit 96 - * column from the framebuffer. It calculates the corresponding byte in the 97 - * framebuffer, extracts the bit at the given x position across 8 consecutive 98 - * rows, and packs those bits into a single byte. 99 - * 100 - * Return an 8-bit value representing a vertical column of pixels. 101 - */ 102 - x = x / 8; 103 - y = (y / 8) * 8; 104 - 105 - for (int i = 0; i < 8; i++) { 106 - int row_idx = y + i; 107 - u8 byte = p[row_idx * row_len + x]; 108 - u8 bit = (byte >> xrest) & 1; 109 - 110 - result |= (bit << i); 111 - } 112 - 113 - return result; 114 - } 115 - 116 - static int st7571_set_position(struct st7571_device *st7571, int x, int y) 117 - { 118 - u8 cmd_list[] = { 119 - ST7571_SET_COLUMN_LSB(x), 120 - ST7571_SET_COLUMN_MSB(x), 121 - ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT), 122 - }; 123 - 124 - return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list)); 125 - } 126 - 127 - static int st7571_fb_clear_screen(struct st7571_device *st7571) 128 - { 129 - u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp; 130 - char pixelvalue = 0x00; 131 - 132 - st7571_set_position(st7571, 0, 0); 133 - for (int i = 0; i < npixels; i++) 134 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1); 135 - 136 - return 0; 137 - } 138 - 139 - static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571, 140 - const struct iosys_map *vmap, 141 - struct drm_framebuffer *fb, 142 - struct drm_rect *rect, 143 - struct drm_format_conv_state *fmtcnv_state) 144 - { 145 - unsigned int dst_pitch; 146 - struct iosys_map dst; 147 - u32 size; 148 - 149 - switch (fb->format->format) { 150 - case DRM_FORMAT_XRGB8888: 151 - dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 152 - iosys_map_set_vaddr(&dst, st7571->hwbuf); 153 - 154 - drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 155 - break; 156 - 157 - case DRM_FORMAT_R1: 158 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 159 - memcpy(st7571->hwbuf, vmap->vaddr, size); 160 - break; 161 - } 162 - } 163 - 164 - static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571, 165 - const struct iosys_map *vmap, 166 - struct drm_framebuffer *fb, 167 - struct drm_rect *rect, 168 - struct drm_format_conv_state *fmtcnv_state) 169 - { 170 - u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 171 - unsigned int dst_pitch; 172 - struct iosys_map dst; 173 - 174 - switch (fb->format->format) { 175 - case DRM_FORMAT_XRGB8888: 176 - dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4); 177 - iosys_map_set_vaddr(&dst, st7571->hwbuf); 178 - 179 - drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 180 - break; 181 - 182 - case DRM_FORMAT_R1: 183 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 184 - memcpy(st7571->hwbuf, vmap->vaddr, size); 185 - break; 186 - 187 - case DRM_FORMAT_R2: 188 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4; 189 - memcpy(st7571->hwbuf, vmap->vaddr, size); 190 - break; 191 - } 192 - } 193 - 194 - static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect) 195 - { 196 - struct st7571_device *st7571 = drm_to_st7571(fb->dev); 197 - char *row = st7571->row; 198 - 199 - /* Align y to display page boundaries */ 200 - rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 201 - rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 202 - 203 - for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 204 - for (int x = rect->x1; x < rect->x2; x++) 205 - row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1); 206 - 207 - st7571_set_position(st7571, rect->x1, y); 208 - 209 - /* TODO: Investige why we can't write multiple bytes at once */ 210 - for (int x = rect->x1; x < rect->x2; x++) 211 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 212 - } 213 - 214 - return 0; 215 - } 216 - 217 - static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect) 218 - { 219 - struct st7571_device *st7571 = drm_to_st7571(fb->dev); 220 - u32 format = fb->format->format; 221 - char *row = st7571->row; 222 - int x1; 223 - int x2; 224 - 225 - /* Align y to display page boundaries */ 226 - rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 227 - rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 228 - 229 - switch (format) { 230 - case DRM_FORMAT_R1: 231 - x1 = rect->x1 * 1; 232 - x2 = rect->x2 * 1; 233 - break; 234 - case DRM_FORMAT_R2: 235 - fallthrough; 236 - case DRM_FORMAT_XRGB8888: 237 - x1 = rect->x1 * 2; 238 - x2 = rect->x2 * 2; 239 - break; 240 - } 241 - 242 - for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 243 - for (int x = x1; x < x2; x++) 244 - row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2); 245 - 246 - st7571_set_position(st7571, rect->x1, y); 247 - 248 - /* TODO: Investige why we can't write multiple bytes at once */ 249 - for (int x = x1; x < x2; x++) { 250 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 251 - 252 - /* 253 - * As the display supports grayscale, all pixels must be written as two bits 254 - * even if the format is monochrome. 255 - * 256 - * The bit values maps to the following grayscale: 257 - * 0 0 = Black 258 - * 0 1 = Dark gray 259 - * 1 0 = Light gray 260 - * 1 1 = White 261 - * 262 - * For monochrome formats, write the same value twice to get 263 - * either a black or white pixel. 264 - */ 265 - if (format == DRM_FORMAT_R1) 266 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 267 - } 268 - } 269 - 270 - return 0; 271 - } 272 - 273 - static int st7571_connector_get_modes(struct drm_connector *conn) 274 - { 275 - struct st7571_device *st7571 = drm_to_st7571(conn->dev); 276 - 277 - return drm_connector_helper_get_modes_fixed(conn, &st7571->mode); 278 - } 279 - 280 - static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = { 281 - .get_modes = st7571_connector_get_modes, 148 + static const struct regmap_bus st7571_i2c_regmap_bus = { 149 + .read = st7571_i2c_regmap_read, 150 + .write = st7571_i2c_regmap_write, 282 151 }; 283 152 284 - static const struct st7571_panel_format st7571_monochrome = { 285 - .prepare_buffer = st7571_prepare_buffer_monochrome, 286 - .update_rect = st7571_fb_update_rect_monochrome, 287 - .mode = ST7571_COLOR_MODE_BLACKWHITE, 288 - .formats = { 289 - DRM_FORMAT_XRGB8888, 290 - DRM_FORMAT_R1, 291 - }, 292 - .nformats = 2, 293 - }; 294 - 295 - static const struct st7571_panel_format st7571_grayscale = { 296 - .prepare_buffer = st7571_prepare_buffer_grayscale, 297 - .update_rect = st7571_fb_update_rect_grayscale, 298 - .mode = ST7571_COLOR_MODE_GRAY, 299 - .formats = { 300 - DRM_FORMAT_XRGB8888, 301 - DRM_FORMAT_R1, 302 - DRM_FORMAT_R2, 303 - }, 304 - .nformats = 3, 305 - }; 306 - 307 - static const u64 st7571_primary_plane_fmtmods[] = { 308 - DRM_FORMAT_MOD_LINEAR, 309 - DRM_FORMAT_MOD_INVALID 310 - }; 311 - 312 - static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane, 313 - struct drm_atomic_state *state) 314 - { 315 - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 316 - struct drm_crtc *new_crtc = new_plane_state->crtc; 317 - struct drm_crtc_state *new_crtc_state = NULL; 318 - 319 - if (new_crtc) 320 - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 321 - 322 - return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 323 - DRM_PLANE_NO_SCALING, 324 - DRM_PLANE_NO_SCALING, 325 - false, false); 326 - } 327 - 328 - static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane, 329 - struct drm_atomic_state *state) 330 - { 331 - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 332 - struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 333 - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 334 - struct drm_framebuffer *fb = plane_state->fb; 335 - struct drm_atomic_helper_damage_iter iter; 336 - struct drm_device *drm = plane->dev; 337 - struct drm_rect damage; 338 - struct st7571_device *st7571 = drm_to_st7571(plane->dev); 339 - int ret, idx; 340 - 341 - if (!fb) 342 - return; /* no framebuffer; plane is disabled */ 343 - 344 - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 345 - if (ret) 346 - return; 347 - 348 - if (!drm_dev_enter(drm, &idx)) 349 - goto out_drm_gem_fb_end_cpu_access; 350 - 351 - drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 352 - drm_atomic_for_each_plane_damage(&iter, &damage) { 353 - st7571->pformat->prepare_buffer(st7571, 354 - &shadow_plane_state->data[0], 355 - fb, &damage, 356 - &shadow_plane_state->fmtcnv_state); 357 - 358 - st7571->pformat->update_rect(fb, &damage); 359 - } 360 - 361 - drm_dev_exit(idx); 362 - 363 - out_drm_gem_fb_end_cpu_access: 364 - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 365 - } 366 - 367 - static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane, 368 - struct drm_atomic_state *state) 369 - { 370 - struct drm_device *drm = plane->dev; 371 - struct st7571_device *st7571 = drm_to_st7571(plane->dev); 372 - int idx; 373 - 374 - if (!drm_dev_enter(drm, &idx)) 375 - return; 376 - 377 - st7571_fb_clear_screen(st7571); 378 - drm_dev_exit(idx); 379 - } 380 - 381 - static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = { 382 - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 383 - .atomic_check = st7571_primary_plane_helper_atomic_check, 384 - .atomic_update = st7571_primary_plane_helper_atomic_update, 385 - .atomic_disable = st7571_primary_plane_helper_atomic_disable, 386 - }; 387 - 388 - static const struct drm_plane_funcs st7571_primary_plane_funcs = { 389 - .update_plane = drm_atomic_helper_update_plane, 390 - .disable_plane = drm_atomic_helper_disable_plane, 391 - .destroy = drm_plane_cleanup, 392 - DRM_GEM_SHADOW_PLANE_FUNCS, 393 - }; 394 - 395 - /* 396 - * CRTC 397 - */ 398 - 399 - static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc, 400 - const struct drm_display_mode *mode) 401 - { 402 - struct st7571_device *st7571 = drm_to_st7571(crtc->dev); 403 - 404 - return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode); 405 - } 406 - 407 - static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = { 408 - .atomic_check = drm_crtc_helper_atomic_check, 409 - .mode_valid = st7571_crtc_mode_valid, 410 - }; 411 - 412 - static const struct drm_crtc_funcs st7571_crtc_funcs = { 413 - .reset = drm_atomic_helper_crtc_reset, 414 - .destroy = drm_crtc_cleanup, 415 - .set_config = drm_atomic_helper_set_config, 416 - .page_flip = drm_atomic_helper_page_flip, 417 - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 418 - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 419 - }; 420 - 421 - /* 422 - * Encoder 423 - */ 424 - 425 - static void st7571_encoder_atomic_enable(struct drm_encoder *encoder, 426 - struct drm_atomic_state *state) 427 - { 428 - struct drm_device *drm = encoder->dev; 429 - struct st7571_device *st7571 = drm_to_st7571(drm); 430 - u8 command = ST7571_DISPLAY_ON; 431 - int ret; 432 - 433 - ret = st7571->pdata->init(st7571); 434 - if (ret) 435 - return; 436 - 437 - st7571_send_command_list(st7571, &command, 1); 438 - } 439 - 440 - static void st7571_encoder_atomic_disable(struct drm_encoder *encoder, 441 - struct drm_atomic_state *state) 442 - { 443 - struct drm_device *drm = encoder->dev; 444 - struct st7571_device *st7571 = drm_to_st7571(drm); 445 - u8 command = ST7571_DISPLAY_OFF; 446 - 447 - st7571_send_command_list(st7571, &command, 1); 448 - } 449 - 450 - static const struct drm_encoder_funcs st7571_encoder_funcs = { 451 - .destroy = drm_encoder_cleanup, 452 - 453 - }; 454 - 455 - static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = { 456 - .atomic_enable = st7571_encoder_atomic_enable, 457 - .atomic_disable = st7571_encoder_atomic_disable, 458 - }; 459 - 460 - /* 461 - * Connector 462 - */ 463 - 464 - static const struct drm_connector_funcs st7571_connector_funcs = { 465 - .reset = drm_atomic_helper_connector_reset, 466 - .fill_modes = drm_helper_probe_single_connector_modes, 467 - .destroy = drm_connector_cleanup, 468 - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 469 - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 470 - }; 471 - 472 - static const struct drm_mode_config_funcs st7571_mode_config_funcs = { 473 - .fb_create = drm_gem_fb_create_with_dirty, 474 - .atomic_check = drm_atomic_helper_check, 475 - .atomic_commit = drm_atomic_helper_commit, 476 - }; 477 - 478 - static struct drm_display_mode st7571_mode(struct st7571_device *st7571) 479 - { 480 - struct drm_display_mode mode = { 481 - DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines, 482 - st7571->width_mm, st7571->height_mm), 483 - }; 484 - 485 - return mode; 486 - } 487 - 488 - static int st7571_mode_config_init(struct st7571_device *st7571) 489 - { 490 - struct drm_device *drm = &st7571->drm; 491 - const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 492 - int ret; 493 - 494 - ret = drmm_mode_config_init(drm); 495 - if (ret) 496 - return ret; 497 - 498 - drm->mode_config.min_width = constraints->min_ncols; 499 - drm->mode_config.min_height = constraints->min_nlines; 500 - drm->mode_config.max_width = constraints->max_ncols; 501 - drm->mode_config.max_height = constraints->max_nlines; 502 - drm->mode_config.preferred_depth = 24; 503 - drm->mode_config.funcs = &st7571_mode_config_funcs; 504 - 505 - return 0; 506 - } 507 - 508 - static int st7571_plane_init(struct st7571_device *st7571, 509 - const struct st7571_panel_format *pformat) 510 - { 511 - struct drm_plane *primary_plane = &st7571->primary_plane; 512 - struct drm_device *drm = &st7571->drm; 513 - int ret; 514 - 515 - ret = drm_universal_plane_init(drm, primary_plane, 0, 516 - &st7571_primary_plane_funcs, 517 - pformat->formats, 518 - pformat->nformats, 519 - st7571_primary_plane_fmtmods, 520 - DRM_PLANE_TYPE_PRIMARY, NULL); 521 - if (ret) 522 - return ret; 523 - 524 - drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs); 525 - drm_plane_enable_fb_damage_clips(primary_plane); 526 - 527 - return 0; 528 - } 529 - 530 - static int st7571_crtc_init(struct st7571_device *st7571) 531 - { 532 - struct drm_plane *primary_plane = &st7571->primary_plane; 533 - struct drm_crtc *crtc = &st7571->crtc; 534 - struct drm_device *drm = &st7571->drm; 535 - int ret; 536 - 537 - ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, 538 - &st7571_crtc_funcs, NULL); 539 - if (ret) 540 - return ret; 541 - 542 - drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs); 543 - 544 - return 0; 545 - } 546 - 547 - static int st7571_encoder_init(struct st7571_device *st7571) 548 - { 549 - struct drm_encoder *encoder = &st7571->encoder; 550 - struct drm_crtc *crtc = &st7571->crtc; 551 - struct drm_device *drm = &st7571->drm; 552 - int ret; 553 - 554 - ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); 555 - if (ret) 556 - return ret; 557 - 558 - drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs); 559 - 560 - encoder->possible_crtcs = drm_crtc_mask(crtc); 561 - 562 - return 0; 563 - } 564 - 565 - static int st7571_connector_init(struct st7571_device *st7571) 566 - { 567 - struct drm_connector *connector = &st7571->connector; 568 - struct drm_encoder *encoder = &st7571->encoder; 569 - struct drm_device *drm = &st7571->drm; 570 - int ret; 571 - 572 - ret = drm_connector_init(drm, connector, &st7571_connector_funcs, 573 - DRM_MODE_CONNECTOR_Unknown); 574 - if (ret) 575 - return ret; 576 - 577 - drm_connector_helper_add(connector, &st7571_connector_helper_funcs); 578 - 579 - return drm_connector_attach_encoder(connector, encoder); 580 - } 581 - 582 - DEFINE_DRM_GEM_FOPS(st7571_fops); 583 - 584 - static const struct drm_driver st7571_driver = { 585 - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 586 - 587 - .name = DRIVER_NAME, 588 - .desc = DRIVER_DESC, 589 - .major = DRIVER_MAJOR, 590 - .minor = DRIVER_MINOR, 591 - 592 - .fops = &st7571_fops, 593 - DRM_GEM_SHMEM_DRIVER_OPS, 594 - DRM_FBDEV_SHMEM_DRIVER_OPS, 595 - }; 596 - 597 - static const struct regmap_bus st7571_regmap_bus = { 598 - .read = st7571_regmap_read, 599 - .write = st7571_regmap_write, 600 - }; 601 - 602 - static const struct regmap_config st7571_regmap_config = { 153 + static const struct regmap_config st7571_i2c_regmap_config = { 603 154 .reg_bits = 8, 604 155 .val_bits = 8, 605 156 .use_single_write = true, 606 157 }; 607 - 608 - static int st7571_validate_parameters(struct st7571_device *st7571) 609 - { 610 - struct device *dev = st7571->dev; 611 - const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 612 - 613 - if (st7571->width_mm == 0) { 614 - dev_err(dev, "Invalid panel width\n"); 615 - return -EINVAL; 616 - } 617 - 618 - if (st7571->height_mm == 0) { 619 - dev_err(dev, "Invalid panel height\n"); 620 - return -EINVAL; 621 - } 622 - 623 - if (st7571->nlines < constraints->min_nlines || 624 - st7571->nlines > constraints->max_nlines) { 625 - dev_err(dev, "Invalid timing configuration.\n"); 626 - return -EINVAL; 627 - } 628 - 629 - if (st7571->startline + st7571->nlines > constraints->max_nlines) { 630 - dev_err(dev, "Invalid timing configuration.\n"); 631 - return -EINVAL; 632 - } 633 - 634 - if (st7571->ncols < constraints->min_ncols || 635 - st7571->ncols > constraints->max_ncols) { 636 - dev_err(dev, "Invalid timing configuration.\n"); 637 - return -EINVAL; 638 - } 639 - 640 - if (st7571->grayscale && !constraints->support_grayscale) { 641 - dev_err(dev, "Grayscale not supported\n"); 642 - return -EINVAL; 643 - } 644 - 645 - return 0; 646 - } 647 - 648 - static int st7567_parse_dt(struct st7571_device *st7567) 649 - { 650 - struct device *dev = st7567->dev; 651 - struct device_node *np = dev->of_node; 652 - struct display_timing dt; 653 - int ret; 654 - 655 - ret = of_get_display_timing(np, "panel-timing", &dt); 656 - if (ret) { 657 - dev_err(dev, "Failed to get display timing from DT\n"); 658 - return ret; 659 - } 660 - 661 - of_property_read_u32(np, "width-mm", &st7567->width_mm); 662 - of_property_read_u32(np, "height-mm", &st7567->height_mm); 663 - st7567->inverted = of_property_read_bool(np, "sitronix,inverted"); 664 - 665 - st7567->pformat = &st7571_monochrome; 666 - st7567->bpp = 1; 667 - 668 - st7567->startline = dt.vfront_porch.typ; 669 - st7567->nlines = dt.vactive.typ; 670 - st7567->ncols = dt.hactive.typ; 671 - 672 - return 0; 673 - } 674 - 675 - static int st7571_parse_dt(struct st7571_device *st7571) 676 - { 677 - struct device *dev = st7571->dev; 678 - struct device_node *np = dev->of_node; 679 - struct display_timing dt; 680 - int ret; 681 - 682 - ret = of_get_display_timing(np, "panel-timing", &dt); 683 - if (ret) { 684 - dev_err(dev, "Failed to get display timing from DT\n"); 685 - return ret; 686 - } 687 - 688 - of_property_read_u32(np, "width-mm", &st7571->width_mm); 689 - of_property_read_u32(np, "height-mm", &st7571->height_mm); 690 - st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale"); 691 - st7571->inverted = of_property_read_bool(np, "sitronix,inverted"); 692 - 693 - if (st7571->grayscale) { 694 - st7571->pformat = &st7571_grayscale; 695 - st7571->bpp = 2; 696 - } else { 697 - st7571->pformat = &st7571_monochrome; 698 - st7571->bpp = 1; 699 - } 700 - 701 - st7571->startline = dt.vfront_porch.typ; 702 - st7571->nlines = dt.vactive.typ; 703 - st7571->ncols = dt.hactive.typ; 704 - 705 - st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 706 - if (IS_ERR(st7571->reset)) 707 - return dev_err_probe(dev, PTR_ERR(st7571->reset), 708 - "Failed to get reset gpio\n"); 709 - 710 - 711 - return 0; 712 - } 713 - 714 - static void st7571_reset(struct st7571_device *st7571) 715 - { 716 - gpiod_set_value_cansleep(st7571->reset, 1); 717 - fsleep(20); 718 - gpiod_set_value_cansleep(st7571->reset, 0); 719 - } 720 - 721 - static int st7567_lcd_init(struct st7571_device *st7567) 722 - { 723 - /* 724 - * Most of the initialization sequence is taken directly from the 725 - * referential initial code in the ST7567 datasheet. 726 - */ 727 - u8 commands[] = { 728 - ST7571_DISPLAY_OFF, 729 - 730 - ST7567_SET_LCD_BIAS(1), 731 - 732 - ST7571_SET_SEG_SCAN_DIR(0), 733 - ST7571_SET_COM_SCAN_DIR(1), 734 - 735 - ST7571_SET_REGULATOR_REG(4), 736 - ST7571_SET_CONTRAST_MSB, 737 - ST7571_SET_CONTRAST_LSB(0x20), 738 - 739 - ST7571_SET_START_LINE_MSB, 740 - ST7571_SET_START_LINE_LSB(st7567->startline), 741 - 742 - ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 743 - ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 744 - ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 745 - 746 - ST7571_SET_REVERSE(st7567->inverted ? 1 : 0), 747 - ST7571_SET_ENTIRE_DISPLAY_ON(0), 748 - }; 749 - 750 - return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands)); 751 - } 752 - 753 - static int st7571_lcd_init(struct st7571_device *st7571) 754 - { 755 - /* 756 - * Most of the initialization sequence is taken directly from the 757 - * referential initial code in the ST7571 datasheet. 758 - */ 759 - u8 commands[] = { 760 - ST7571_DISPLAY_OFF, 761 - 762 - ST7571_SET_MODE_MSB, 763 - ST7571_SET_MODE_LSB(0x2e), 764 - 765 - ST7571_SET_SEG_SCAN_DIR(0), 766 - ST7571_SET_COM_SCAN_DIR(1), 767 - 768 - ST7571_SET_COM0_MSB, 769 - ST7571_SET_COM0_LSB(0x00), 770 - 771 - ST7571_SET_START_LINE_MSB, 772 - ST7571_SET_START_LINE_LSB(st7571->startline), 773 - 774 - ST7571_OSC_ON, 775 - ST7571_SET_REGULATOR_REG(5), 776 - ST7571_SET_CONTRAST_MSB, 777 - ST7571_SET_CONTRAST_LSB(0x33), 778 - ST7571_SET_LCD_BIAS(0x04), 779 - ST7571_SET_DISPLAY_DUTY_MSB, 780 - ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines), 781 - 782 - ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 783 - ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 784 - ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 785 - 786 - ST7571_COMMAND_SET_3, 787 - ST7571_SET_COLOR_MODE(st7571->pformat->mode), 788 - ST7571_COMMAND_SET_NORMAL, 789 - 790 - ST7571_SET_REVERSE(st7571->inverted ? 1 : 0), 791 - ST7571_SET_ENTIRE_DISPLAY_ON(0), 792 - }; 793 - 794 - /* Perform a reset before initializing the controller */ 795 - st7571_reset(st7571); 796 - 797 - return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands)); 798 - } 799 - 800 - static struct st7571_device *st7571_probe(struct device *dev, 801 - struct regmap *regmap) 802 - { 803 - struct st7571_device *st7571; 804 - struct drm_device *drm; 805 - int ret; 806 - 807 - st7571 = devm_drm_dev_alloc(dev, &st7571_driver, 808 - struct st7571_device, drm); 809 - if (IS_ERR(st7571)) 810 - return st7571; 811 - 812 - drm = &st7571->drm; 813 - st7571->dev = dev; 814 - st7571->pdata = device_get_match_data(st7571->dev); 815 - 816 - ret = st7571->pdata->parse_dt(st7571); 817 - if (ret) 818 - return ERR_PTR(ret); 819 - 820 - ret = st7571_validate_parameters(st7571); 821 - if (ret) 822 - return ERR_PTR(ret); 823 - 824 - st7571->mode = st7571_mode(st7571); 825 - st7571->regmap = regmap; 826 - 827 - 828 - st7571->hwbuf = devm_kzalloc(st7571->dev, 829 - (st7571->nlines * st7571->ncols * st7571->bpp) / 8, 830 - GFP_KERNEL); 831 - if (!st7571->hwbuf) 832 - return ERR_PTR(-ENOMEM); 833 - 834 - st7571->row = devm_kzalloc(st7571->dev, 835 - (st7571->ncols * st7571->bpp), 836 - GFP_KERNEL); 837 - if (!st7571->row) 838 - return ERR_PTR(-ENOMEM); 839 - 840 - ret = st7571_mode_config_init(st7571); 841 - if (ret) { 842 - dev_err(st7571->dev, "Failed to initialize mode config\n"); 843 - return ERR_PTR(ret); 844 - } 845 - 846 - ret = st7571_plane_init(st7571, st7571->pformat); 847 - if (ret) { 848 - dev_err(st7571->dev, "Failed to initialize primary plane\n"); 849 - return ERR_PTR(ret); 850 - } 851 - 852 - ret = st7571_crtc_init(st7571); 853 - if (ret < 0) { 854 - dev_err(st7571->dev, "Failed to initialize CRTC\n"); 855 - return ERR_PTR(ret); 856 - } 857 - 858 - ret = st7571_encoder_init(st7571); 859 - if (ret < 0) { 860 - dev_err(st7571->dev, "Failed to initialize encoder\n"); 861 - return ERR_PTR(ret); 862 - } 863 - 864 - ret = st7571_connector_init(st7571); 865 - if (ret < 0) { 866 - dev_err(st7571->dev, "Failed to initialize connector\n"); 867 - return ERR_PTR(ret); 868 - } 869 - 870 - drm_mode_config_reset(drm); 871 - 872 - ret = drm_dev_register(drm, 0); 873 - if (ret) { 874 - dev_err(st7571->dev, "Failed to register DRM device\n"); 875 - return ERR_PTR(ret); 876 - } 877 - 878 - drm_client_setup(drm, NULL); 879 - return st7571; 880 - } 881 - 882 - static void st7571_remove(struct st7571_device *st7571) 883 - { 884 - drm_dev_unplug(&st7571->drm); 885 - } 886 158 887 159 static int st7571_i2c_probe(struct i2c_client *client) 888 160 { ··· 103 979 if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING)) 104 980 t->ignore_nak = true; 105 981 106 - regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus, 107 - t, &st7571_regmap_config); 982 + regmap = devm_regmap_init(&client->dev, &st7571_i2c_regmap_bus, 983 + t, &st7571_i2c_regmap_config); 108 984 if (IS_ERR(regmap)) { 109 985 return dev_err_probe(&client->dev, PTR_ERR(regmap), 110 986 "Failed to initialize regmap\n"); ··· 126 1002 st7571_remove(st7571); 127 1003 } 128 1004 129 - static const struct st7571_panel_data st7567_config = { 130 - .init = st7567_lcd_init, 131 - .parse_dt = st7567_parse_dt, 132 - .constraints = { 133 - .min_nlines = 1, 134 - .max_nlines = 64, 135 - .min_ncols = 128, 136 - .max_ncols = 128, 137 - .support_grayscale = false, 138 - }, 139 - }; 140 - 141 - static const struct st7571_panel_data st7571_config = { 142 - .init = st7571_lcd_init, 143 - .parse_dt = st7571_parse_dt, 144 - .constraints = { 145 - .min_nlines = 1, 146 - .max_nlines = 128, 147 - .min_ncols = 128, 148 - .max_ncols = 128, 149 - .support_grayscale = true, 150 - }, 151 - }; 152 - 153 1005 static const struct of_device_id st7571_of_match[] = { 154 1006 { .compatible = "sitronix,st7567", .data = &st7567_config }, 155 1007 { .compatible = "sitronix,st7571", .data = &st7571_config }, ··· 142 1042 143 1043 static struct i2c_driver st7571_i2c_driver = { 144 1044 .driver = { 145 - .name = "st7571", 1045 + .name = "st7571-i2c", 146 1046 .of_match_table = st7571_of_match, 147 1047 }, 148 1048 .probe = st7571_i2c_probe, ··· 153 1053 module_i2c_driver(st7571_i2c_driver); 154 1054 155 1055 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 156 - MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller"); 1056 + MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (I2C)"); 157 1057 MODULE_LICENSE("GPL"); 1058 + MODULE_IMPORT_NS("DRM_ST7571");
+918
drivers/gpu/drm/sitronix/st7571.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller 4 + * 5 + * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/delay.h> 10 + #include <linux/gpio/consumer.h> 11 + #include <linux/i2c.h> 12 + #include <linux/module.h> 13 + #include <linux/regmap.h> 14 + 15 + #include <drm/clients/drm_client_setup.h> 16 + #include <drm/drm_atomic.h> 17 + #include <drm/drm_atomic_helper.h> 18 + #include <drm/drm_connector.h> 19 + #include <drm/drm_crtc_helper.h> 20 + #include <drm/drm_damage_helper.h> 21 + #include <drm/drm_drv.h> 22 + #include <drm/drm_encoder.h> 23 + #include <drm/drm_fb_helper.h> 24 + #include <drm/drm_fbdev_shmem.h> 25 + #include <drm/drm_fourcc.h> 26 + #include <drm/drm_framebuffer.h> 27 + #include <drm/drm_gem_atomic_helper.h> 28 + #include <drm/drm_gem_framebuffer_helper.h> 29 + #include <drm/drm_gem_shmem_helper.h> 30 + #include <drm/drm_modeset_helper_vtables.h> 31 + #include <drm/drm_module.h> 32 + #include <drm/drm_plane.h> 33 + #include <drm/drm_probe_helper.h> 34 + 35 + #include <video/display_timing.h> 36 + #include <video/of_display_timing.h> 37 + 38 + #include "st7571.h" 39 + 40 + #define ST7571_COMMAND_MODE (0x00) 41 + #define ST7571_DATA_MODE (0x40) 42 + 43 + /* Normal mode command set */ 44 + #define ST7571_DISPLAY_OFF (0xae) 45 + #define ST7571_DISPLAY_ON (0xaf) 46 + #define ST7571_OSC_ON (0xab) 47 + #define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c))) 48 + #define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4)) 49 + #define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x))) 50 + #define ST7571_SET_COM0_MSB (0x44) 51 + #define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d))) 52 + #define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c))) 53 + #define ST7571_SET_CONTRAST_MSB (0x81) 54 + #define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d))) 55 + #define ST7571_SET_DISPLAY_DUTY_MSB (0x48) 56 + #define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p))) 57 + #define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b))) 58 + #define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m))) 59 + #define ST7571_SET_MODE_MSB (0x38) 60 + #define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p))) 61 + #define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p))) 62 + #define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r))) 63 + #define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r))) 64 + #define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d))) 65 + #define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l))) 66 + #define ST7571_SET_START_LINE_MSB (0x40) 67 + 68 + /* Extension command set 3 */ 69 + #define ST7571_COMMAND_SET_3 (0x7b) 70 + #define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c))) 71 + #define ST7571_COMMAND_SET_NORMAL (0x00) 72 + 73 + /* ST7567 commands */ 74 + #define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m))) 75 + 76 + #define ST7571_PAGE_HEIGHT 8 77 + 78 + #define DRIVER_NAME "st7571" 79 + #define DRIVER_DESC "ST7571 DRM driver" 80 + #define DRIVER_MAJOR 1 81 + #define DRIVER_MINOR 0 82 + 83 + static inline struct st7571_device *drm_to_st7571(struct drm_device *drm) 84 + { 85 + return container_of(drm, struct st7571_device, drm); 86 + } 87 + 88 + static int st7571_send_command_list(struct st7571_device *st7571, 89 + const u8 *cmd_list, size_t len) 90 + { 91 + int ret; 92 + 93 + for (int i = 0; i < len; i++) { 94 + ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]); 95 + if (ret < 0) 96 + return ret; 97 + } 98 + 99 + return ret; 100 + } 101 + 102 + static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp) 103 + { 104 + int xrest = x % 8; 105 + u8 result = 0; 106 + u8 row_len = 16 * bpp; 107 + 108 + /* 109 + * Transforms an (x, y) pixel coordinate into a vertical 8-bit 110 + * column from the framebuffer. It calculates the corresponding byte in the 111 + * framebuffer, extracts the bit at the given x position across 8 consecutive 112 + * rows, and packs those bits into a single byte. 113 + * 114 + * Return an 8-bit value representing a vertical column of pixels. 115 + */ 116 + x = x / 8; 117 + y = (y / 8) * 8; 118 + 119 + for (int i = 0; i < 8; i++) { 120 + int row_idx = y + i; 121 + u8 byte = p[row_idx * row_len + x]; 122 + u8 bit = (byte >> xrest) & 1; 123 + 124 + result |= (bit << i); 125 + } 126 + 127 + return result; 128 + } 129 + 130 + static int st7571_set_position(struct st7571_device *st7571, int x, int y) 131 + { 132 + u8 cmd_list[] = { 133 + ST7571_SET_COLUMN_LSB(x), 134 + ST7571_SET_COLUMN_MSB(x), 135 + ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT), 136 + }; 137 + 138 + return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list)); 139 + } 140 + 141 + static int st7571_fb_clear_screen(struct st7571_device *st7571) 142 + { 143 + u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp; 144 + char pixelvalue = 0x00; 145 + 146 + st7571_set_position(st7571, 0, 0); 147 + for (int i = 0; i < npixels; i++) 148 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1); 149 + 150 + return 0; 151 + } 152 + 153 + static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571, 154 + const struct iosys_map *vmap, 155 + struct drm_framebuffer *fb, 156 + struct drm_rect *rect, 157 + struct drm_format_conv_state *fmtcnv_state) 158 + { 159 + unsigned int dst_pitch; 160 + struct iosys_map dst; 161 + u32 size; 162 + 163 + switch (fb->format->format) { 164 + case DRM_FORMAT_XRGB8888: 165 + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 166 + iosys_map_set_vaddr(&dst, st7571->hwbuf); 167 + 168 + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 169 + break; 170 + 171 + case DRM_FORMAT_R1: 172 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 173 + memcpy(st7571->hwbuf, vmap->vaddr, size); 174 + break; 175 + } 176 + } 177 + 178 + static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571, 179 + const struct iosys_map *vmap, 180 + struct drm_framebuffer *fb, 181 + struct drm_rect *rect, 182 + struct drm_format_conv_state *fmtcnv_state) 183 + { 184 + u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 185 + unsigned int dst_pitch; 186 + struct iosys_map dst; 187 + 188 + switch (fb->format->format) { 189 + case DRM_FORMAT_XRGB8888: 190 + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4); 191 + iosys_map_set_vaddr(&dst, st7571->hwbuf); 192 + 193 + drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 194 + break; 195 + 196 + case DRM_FORMAT_R1: 197 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 198 + memcpy(st7571->hwbuf, vmap->vaddr, size); 199 + break; 200 + 201 + case DRM_FORMAT_R2: 202 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4; 203 + memcpy(st7571->hwbuf, vmap->vaddr, size); 204 + break; 205 + } 206 + } 207 + 208 + static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect) 209 + { 210 + struct st7571_device *st7571 = drm_to_st7571(fb->dev); 211 + char *row = st7571->row; 212 + 213 + /* Align y to display page boundaries */ 214 + rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 215 + rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 216 + 217 + for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 218 + for (int x = rect->x1; x < rect->x2; x++) 219 + row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1); 220 + 221 + st7571_set_position(st7571, rect->x1, y); 222 + 223 + /* TODO: Investige why we can't write multiple bytes at once */ 224 + for (int x = rect->x1; x < rect->x2; x++) 225 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 226 + } 227 + 228 + return 0; 229 + } 230 + 231 + static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect) 232 + { 233 + struct st7571_device *st7571 = drm_to_st7571(fb->dev); 234 + u32 format = fb->format->format; 235 + char *row = st7571->row; 236 + int x1; 237 + int x2; 238 + 239 + /* Align y to display page boundaries */ 240 + rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 241 + rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 242 + 243 + switch (format) { 244 + case DRM_FORMAT_R1: 245 + x1 = rect->x1 * 1; 246 + x2 = rect->x2 * 1; 247 + break; 248 + case DRM_FORMAT_R2: 249 + fallthrough; 250 + case DRM_FORMAT_XRGB8888: 251 + x1 = rect->x1 * 2; 252 + x2 = rect->x2 * 2; 253 + break; 254 + } 255 + 256 + for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 257 + for (int x = x1; x < x2; x++) 258 + row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2); 259 + 260 + st7571_set_position(st7571, rect->x1, y); 261 + 262 + /* TODO: Investige why we can't write multiple bytes at once */ 263 + for (int x = x1; x < x2; x++) { 264 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 265 + 266 + /* 267 + * As the display supports grayscale, all pixels must be written as two bits 268 + * even if the format is monochrome. 269 + * 270 + * The bit values maps to the following grayscale: 271 + * 0 0 = Black 272 + * 0 1 = Dark gray 273 + * 1 0 = Light gray 274 + * 1 1 = White 275 + * 276 + * For monochrome formats, write the same value twice to get 277 + * either a black or white pixel. 278 + */ 279 + if (format == DRM_FORMAT_R1) 280 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 281 + } 282 + } 283 + 284 + return 0; 285 + } 286 + 287 + static int st7571_connector_get_modes(struct drm_connector *conn) 288 + { 289 + struct st7571_device *st7571 = drm_to_st7571(conn->dev); 290 + 291 + return drm_connector_helper_get_modes_fixed(conn, &st7571->mode); 292 + } 293 + 294 + static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = { 295 + .get_modes = st7571_connector_get_modes, 296 + }; 297 + 298 + static const struct st7571_panel_format st7571_monochrome = { 299 + .prepare_buffer = st7571_prepare_buffer_monochrome, 300 + .update_rect = st7571_fb_update_rect_monochrome, 301 + .mode = ST7571_COLOR_MODE_BLACKWHITE, 302 + .formats = { 303 + DRM_FORMAT_XRGB8888, 304 + DRM_FORMAT_R1, 305 + }, 306 + .nformats = 2, 307 + }; 308 + 309 + static const struct st7571_panel_format st7571_grayscale = { 310 + .prepare_buffer = st7571_prepare_buffer_grayscale, 311 + .update_rect = st7571_fb_update_rect_grayscale, 312 + .mode = ST7571_COLOR_MODE_GRAY, 313 + .formats = { 314 + DRM_FORMAT_XRGB8888, 315 + DRM_FORMAT_R1, 316 + DRM_FORMAT_R2, 317 + }, 318 + .nformats = 3, 319 + }; 320 + 321 + static const u64 st7571_primary_plane_fmtmods[] = { 322 + DRM_FORMAT_MOD_LINEAR, 323 + DRM_FORMAT_MOD_INVALID 324 + }; 325 + 326 + static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane, 327 + struct drm_atomic_state *state) 328 + { 329 + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 330 + struct drm_crtc *new_crtc = new_plane_state->crtc; 331 + struct drm_crtc_state *new_crtc_state = NULL; 332 + 333 + if (new_crtc) 334 + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 335 + 336 + return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 337 + DRM_PLANE_NO_SCALING, 338 + DRM_PLANE_NO_SCALING, 339 + false, false); 340 + } 341 + 342 + static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane, 343 + struct drm_atomic_state *state) 344 + { 345 + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 346 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 347 + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 348 + struct drm_framebuffer *fb = plane_state->fb; 349 + struct drm_atomic_helper_damage_iter iter; 350 + struct drm_device *drm = plane->dev; 351 + struct drm_rect damage; 352 + struct st7571_device *st7571 = drm_to_st7571(plane->dev); 353 + int ret, idx; 354 + 355 + if (!fb) 356 + return; /* no framebuffer; plane is disabled */ 357 + 358 + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 359 + if (ret) 360 + return; 361 + 362 + if (!drm_dev_enter(drm, &idx)) 363 + goto out_drm_gem_fb_end_cpu_access; 364 + 365 + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 366 + drm_atomic_for_each_plane_damage(&iter, &damage) { 367 + st7571->pformat->prepare_buffer(st7571, 368 + &shadow_plane_state->data[0], 369 + fb, &damage, 370 + &shadow_plane_state->fmtcnv_state); 371 + 372 + st7571->pformat->update_rect(fb, &damage); 373 + } 374 + 375 + drm_dev_exit(idx); 376 + 377 + out_drm_gem_fb_end_cpu_access: 378 + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 379 + } 380 + 381 + static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane, 382 + struct drm_atomic_state *state) 383 + { 384 + struct drm_device *drm = plane->dev; 385 + struct st7571_device *st7571 = drm_to_st7571(plane->dev); 386 + int idx; 387 + 388 + if (!drm_dev_enter(drm, &idx)) 389 + return; 390 + 391 + st7571_fb_clear_screen(st7571); 392 + drm_dev_exit(idx); 393 + } 394 + 395 + static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = { 396 + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 397 + .atomic_check = st7571_primary_plane_helper_atomic_check, 398 + .atomic_update = st7571_primary_plane_helper_atomic_update, 399 + .atomic_disable = st7571_primary_plane_helper_atomic_disable, 400 + }; 401 + 402 + static const struct drm_plane_funcs st7571_primary_plane_funcs = { 403 + .update_plane = drm_atomic_helper_update_plane, 404 + .disable_plane = drm_atomic_helper_disable_plane, 405 + .destroy = drm_plane_cleanup, 406 + DRM_GEM_SHADOW_PLANE_FUNCS, 407 + }; 408 + 409 + /* 410 + * CRTC 411 + */ 412 + 413 + static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc, 414 + const struct drm_display_mode *mode) 415 + { 416 + struct st7571_device *st7571 = drm_to_st7571(crtc->dev); 417 + 418 + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode); 419 + } 420 + 421 + static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = { 422 + .atomic_check = drm_crtc_helper_atomic_check, 423 + .mode_valid = st7571_crtc_mode_valid, 424 + }; 425 + 426 + static const struct drm_crtc_funcs st7571_crtc_funcs = { 427 + .reset = drm_atomic_helper_crtc_reset, 428 + .destroy = drm_crtc_cleanup, 429 + .set_config = drm_atomic_helper_set_config, 430 + .page_flip = drm_atomic_helper_page_flip, 431 + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 432 + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 433 + }; 434 + 435 + /* 436 + * Encoder 437 + */ 438 + 439 + static void st7571_encoder_atomic_enable(struct drm_encoder *encoder, 440 + struct drm_atomic_state *state) 441 + { 442 + struct drm_device *drm = encoder->dev; 443 + struct st7571_device *st7571 = drm_to_st7571(drm); 444 + u8 command = ST7571_DISPLAY_ON; 445 + int ret; 446 + 447 + ret = st7571->pdata->init(st7571); 448 + if (ret) 449 + return; 450 + 451 + st7571_send_command_list(st7571, &command, 1); 452 + } 453 + 454 + static void st7571_encoder_atomic_disable(struct drm_encoder *encoder, 455 + struct drm_atomic_state *state) 456 + { 457 + struct drm_device *drm = encoder->dev; 458 + struct st7571_device *st7571 = drm_to_st7571(drm); 459 + u8 command = ST7571_DISPLAY_OFF; 460 + 461 + st7571_send_command_list(st7571, &command, 1); 462 + } 463 + 464 + static const struct drm_encoder_funcs st7571_encoder_funcs = { 465 + .destroy = drm_encoder_cleanup, 466 + 467 + }; 468 + 469 + static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = { 470 + .atomic_enable = st7571_encoder_atomic_enable, 471 + .atomic_disable = st7571_encoder_atomic_disable, 472 + }; 473 + 474 + /* 475 + * Connector 476 + */ 477 + 478 + static const struct drm_connector_funcs st7571_connector_funcs = { 479 + .reset = drm_atomic_helper_connector_reset, 480 + .fill_modes = drm_helper_probe_single_connector_modes, 481 + .destroy = drm_connector_cleanup, 482 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 483 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 484 + }; 485 + 486 + static const struct drm_mode_config_funcs st7571_mode_config_funcs = { 487 + .fb_create = drm_gem_fb_create_with_dirty, 488 + .atomic_check = drm_atomic_helper_check, 489 + .atomic_commit = drm_atomic_helper_commit, 490 + }; 491 + 492 + static struct drm_display_mode st7571_mode(struct st7571_device *st7571) 493 + { 494 + struct drm_display_mode mode = { 495 + DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines, 496 + st7571->width_mm, st7571->height_mm), 497 + }; 498 + 499 + return mode; 500 + } 501 + 502 + static int st7571_mode_config_init(struct st7571_device *st7571) 503 + { 504 + struct drm_device *drm = &st7571->drm; 505 + const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 506 + int ret; 507 + 508 + ret = drmm_mode_config_init(drm); 509 + if (ret) 510 + return ret; 511 + 512 + drm->mode_config.min_width = constraints->min_ncols; 513 + drm->mode_config.min_height = constraints->min_nlines; 514 + drm->mode_config.max_width = constraints->max_ncols; 515 + drm->mode_config.max_height = constraints->max_nlines; 516 + drm->mode_config.preferred_depth = 24; 517 + drm->mode_config.funcs = &st7571_mode_config_funcs; 518 + 519 + return 0; 520 + } 521 + 522 + static int st7571_plane_init(struct st7571_device *st7571, 523 + const struct st7571_panel_format *pformat) 524 + { 525 + struct drm_plane *primary_plane = &st7571->primary_plane; 526 + struct drm_device *drm = &st7571->drm; 527 + int ret; 528 + 529 + ret = drm_universal_plane_init(drm, primary_plane, 0, 530 + &st7571_primary_plane_funcs, 531 + pformat->formats, 532 + pformat->nformats, 533 + st7571_primary_plane_fmtmods, 534 + DRM_PLANE_TYPE_PRIMARY, NULL); 535 + if (ret) 536 + return ret; 537 + 538 + drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs); 539 + drm_plane_enable_fb_damage_clips(primary_plane); 540 + 541 + return 0; 542 + } 543 + 544 + static int st7571_crtc_init(struct st7571_device *st7571) 545 + { 546 + struct drm_plane *primary_plane = &st7571->primary_plane; 547 + struct drm_crtc *crtc = &st7571->crtc; 548 + struct drm_device *drm = &st7571->drm; 549 + int ret; 550 + 551 + ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, 552 + &st7571_crtc_funcs, NULL); 553 + if (ret) 554 + return ret; 555 + 556 + drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs); 557 + 558 + return 0; 559 + } 560 + 561 + static int st7571_encoder_init(struct st7571_device *st7571) 562 + { 563 + struct drm_encoder *encoder = &st7571->encoder; 564 + struct drm_crtc *crtc = &st7571->crtc; 565 + struct drm_device *drm = &st7571->drm; 566 + int ret; 567 + 568 + ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); 569 + if (ret) 570 + return ret; 571 + 572 + drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs); 573 + 574 + encoder->possible_crtcs = drm_crtc_mask(crtc); 575 + 576 + return 0; 577 + } 578 + 579 + static int st7571_connector_init(struct st7571_device *st7571) 580 + { 581 + struct drm_connector *connector = &st7571->connector; 582 + struct drm_encoder *encoder = &st7571->encoder; 583 + struct drm_device *drm = &st7571->drm; 584 + int ret; 585 + 586 + ret = drm_connector_init(drm, connector, &st7571_connector_funcs, 587 + DRM_MODE_CONNECTOR_Unknown); 588 + if (ret) 589 + return ret; 590 + 591 + drm_connector_helper_add(connector, &st7571_connector_helper_funcs); 592 + 593 + return drm_connector_attach_encoder(connector, encoder); 594 + } 595 + 596 + DEFINE_DRM_GEM_FOPS(st7571_fops); 597 + 598 + static const struct drm_driver st7571_driver = { 599 + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 600 + 601 + .name = DRIVER_NAME, 602 + .desc = DRIVER_DESC, 603 + .major = DRIVER_MAJOR, 604 + .minor = DRIVER_MINOR, 605 + 606 + .fops = &st7571_fops, 607 + DRM_GEM_SHMEM_DRIVER_OPS, 608 + DRM_FBDEV_SHMEM_DRIVER_OPS, 609 + }; 610 + 611 + static int st7571_validate_parameters(struct st7571_device *st7571) 612 + { 613 + struct device *dev = st7571->dev; 614 + const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 615 + 616 + if (st7571->width_mm == 0) { 617 + dev_err(dev, "Invalid panel width\n"); 618 + return -EINVAL; 619 + } 620 + 621 + if (st7571->height_mm == 0) { 622 + dev_err(dev, "Invalid panel height\n"); 623 + return -EINVAL; 624 + } 625 + 626 + if (st7571->nlines < constraints->min_nlines || 627 + st7571->nlines > constraints->max_nlines) { 628 + dev_err(dev, "Invalid timing configuration.\n"); 629 + return -EINVAL; 630 + } 631 + 632 + if (st7571->startline + st7571->nlines > constraints->max_nlines) { 633 + dev_err(dev, "Invalid timing configuration.\n"); 634 + return -EINVAL; 635 + } 636 + 637 + if (st7571->ncols < constraints->min_ncols || 638 + st7571->ncols > constraints->max_ncols) { 639 + dev_err(dev, "Invalid timing configuration.\n"); 640 + return -EINVAL; 641 + } 642 + 643 + if (st7571->grayscale && !constraints->support_grayscale) { 644 + dev_err(dev, "Grayscale not supported\n"); 645 + return -EINVAL; 646 + } 647 + 648 + return 0; 649 + } 650 + 651 + static int st7567_parse_dt(struct st7571_device *st7567) 652 + { 653 + struct device *dev = st7567->dev; 654 + struct device_node *np = dev->of_node; 655 + struct display_timing dt; 656 + int ret; 657 + 658 + ret = of_get_display_timing(np, "panel-timing", &dt); 659 + if (ret) { 660 + dev_err(dev, "Failed to get display timing from DT\n"); 661 + return ret; 662 + } 663 + 664 + of_property_read_u32(np, "width-mm", &st7567->width_mm); 665 + of_property_read_u32(np, "height-mm", &st7567->height_mm); 666 + st7567->inverted = of_property_read_bool(np, "sitronix,inverted"); 667 + 668 + st7567->pformat = &st7571_monochrome; 669 + st7567->bpp = 1; 670 + 671 + st7567->startline = dt.vfront_porch.typ; 672 + st7567->nlines = dt.vactive.typ; 673 + st7567->ncols = dt.hactive.typ; 674 + 675 + return 0; 676 + } 677 + 678 + static int st7571_parse_dt(struct st7571_device *st7571) 679 + { 680 + struct device *dev = st7571->dev; 681 + struct device_node *np = dev->of_node; 682 + struct display_timing dt; 683 + int ret; 684 + 685 + ret = of_get_display_timing(np, "panel-timing", &dt); 686 + if (ret) { 687 + dev_err(dev, "Failed to get display timing from DT\n"); 688 + return ret; 689 + } 690 + 691 + of_property_read_u32(np, "width-mm", &st7571->width_mm); 692 + of_property_read_u32(np, "height-mm", &st7571->height_mm); 693 + st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale"); 694 + st7571->inverted = of_property_read_bool(np, "sitronix,inverted"); 695 + 696 + if (st7571->grayscale) { 697 + st7571->pformat = &st7571_grayscale; 698 + st7571->bpp = 2; 699 + } else { 700 + st7571->pformat = &st7571_monochrome; 701 + st7571->bpp = 1; 702 + } 703 + 704 + st7571->startline = dt.vfront_porch.typ; 705 + st7571->nlines = dt.vactive.typ; 706 + st7571->ncols = dt.hactive.typ; 707 + 708 + st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 709 + if (IS_ERR(st7571->reset)) 710 + return dev_err_probe(dev, PTR_ERR(st7571->reset), 711 + "Failed to get reset gpio\n"); 712 + 713 + return 0; 714 + } 715 + 716 + static void st7571_reset(struct st7571_device *st7571) 717 + { 718 + gpiod_set_value_cansleep(st7571->reset, 1); 719 + fsleep(20); 720 + gpiod_set_value_cansleep(st7571->reset, 0); 721 + } 722 + 723 + static int st7567_lcd_init(struct st7571_device *st7567) 724 + { 725 + /* 726 + * Most of the initialization sequence is taken directly from the 727 + * referential initial code in the ST7567 datasheet. 728 + */ 729 + u8 commands[] = { 730 + ST7571_DISPLAY_OFF, 731 + 732 + ST7567_SET_LCD_BIAS(1), 733 + 734 + ST7571_SET_SEG_SCAN_DIR(0), 735 + ST7571_SET_COM_SCAN_DIR(1), 736 + 737 + ST7571_SET_REGULATOR_REG(4), 738 + ST7571_SET_CONTRAST_MSB, 739 + ST7571_SET_CONTRAST_LSB(0x20), 740 + 741 + ST7571_SET_START_LINE_MSB, 742 + ST7571_SET_START_LINE_LSB(st7567->startline), 743 + 744 + ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 745 + ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 746 + ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 747 + 748 + ST7571_SET_REVERSE(st7567->inverted ? 1 : 0), 749 + ST7571_SET_ENTIRE_DISPLAY_ON(0), 750 + }; 751 + 752 + return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands)); 753 + } 754 + 755 + static int st7571_lcd_init(struct st7571_device *st7571) 756 + { 757 + /* 758 + * Most of the initialization sequence is taken directly from the 759 + * referential initial code in the ST7571 datasheet. 760 + */ 761 + u8 commands[] = { 762 + ST7571_DISPLAY_OFF, 763 + 764 + ST7571_SET_MODE_MSB, 765 + ST7571_SET_MODE_LSB(0x2e), 766 + 767 + ST7571_SET_SEG_SCAN_DIR(0), 768 + ST7571_SET_COM_SCAN_DIR(1), 769 + 770 + ST7571_SET_COM0_MSB, 771 + ST7571_SET_COM0_LSB(0x00), 772 + 773 + ST7571_SET_START_LINE_MSB, 774 + ST7571_SET_START_LINE_LSB(st7571->startline), 775 + 776 + ST7571_OSC_ON, 777 + ST7571_SET_REGULATOR_REG(5), 778 + ST7571_SET_CONTRAST_MSB, 779 + ST7571_SET_CONTRAST_LSB(0x33), 780 + ST7571_SET_LCD_BIAS(0x04), 781 + ST7571_SET_DISPLAY_DUTY_MSB, 782 + ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines), 783 + 784 + ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 785 + ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 786 + ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 787 + 788 + ST7571_COMMAND_SET_3, 789 + ST7571_SET_COLOR_MODE(st7571->pformat->mode), 790 + ST7571_COMMAND_SET_NORMAL, 791 + 792 + ST7571_SET_REVERSE(st7571->inverted ? 1 : 0), 793 + ST7571_SET_ENTIRE_DISPLAY_ON(0), 794 + }; 795 + 796 + /* Perform a reset before initializing the controller */ 797 + st7571_reset(st7571); 798 + 799 + return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands)); 800 + } 801 + 802 + struct st7571_device *st7571_probe(struct device *dev, 803 + struct regmap *regmap) 804 + { 805 + struct st7571_device *st7571; 806 + struct drm_device *drm; 807 + int ret; 808 + 809 + st7571 = devm_drm_dev_alloc(dev, &st7571_driver, 810 + struct st7571_device, drm); 811 + if (IS_ERR(st7571)) 812 + return st7571; 813 + 814 + drm = &st7571->drm; 815 + st7571->dev = dev; 816 + st7571->pdata = device_get_match_data(st7571->dev); 817 + 818 + ret = st7571->pdata->parse_dt(st7571); 819 + if (ret) 820 + return ERR_PTR(ret); 821 + 822 + ret = st7571_validate_parameters(st7571); 823 + if (ret) 824 + return ERR_PTR(ret); 825 + 826 + st7571->mode = st7571_mode(st7571); 827 + st7571->regmap = regmap; 828 + 829 + st7571->hwbuf = devm_kzalloc(st7571->dev, 830 + (st7571->nlines * st7571->ncols * st7571->bpp) / 8, 831 + GFP_KERNEL); 832 + if (!st7571->hwbuf) 833 + return ERR_PTR(-ENOMEM); 834 + 835 + st7571->row = devm_kzalloc(st7571->dev, 836 + (st7571->ncols * st7571->bpp), 837 + GFP_KERNEL); 838 + if (!st7571->row) 839 + return ERR_PTR(-ENOMEM); 840 + 841 + ret = st7571_mode_config_init(st7571); 842 + if (ret) { 843 + dev_err(st7571->dev, "Failed to initialize mode config\n"); 844 + return ERR_PTR(ret); 845 + } 846 + 847 + ret = st7571_plane_init(st7571, st7571->pformat); 848 + if (ret) { 849 + dev_err(st7571->dev, "Failed to initialize primary plane\n"); 850 + return ERR_PTR(ret); 851 + } 852 + 853 + ret = st7571_crtc_init(st7571); 854 + if (ret < 0) { 855 + dev_err(st7571->dev, "Failed to initialize CRTC\n"); 856 + return ERR_PTR(ret); 857 + } 858 + 859 + ret = st7571_encoder_init(st7571); 860 + if (ret < 0) { 861 + dev_err(st7571->dev, "Failed to initialize encoder\n"); 862 + return ERR_PTR(ret); 863 + } 864 + 865 + ret = st7571_connector_init(st7571); 866 + if (ret < 0) { 867 + dev_err(st7571->dev, "Failed to initialize connector\n"); 868 + return ERR_PTR(ret); 869 + } 870 + 871 + drm_mode_config_reset(drm); 872 + 873 + ret = drm_dev_register(drm, 0); 874 + if (ret) { 875 + dev_err(st7571->dev, "Failed to register DRM device\n"); 876 + return ERR_PTR(ret); 877 + } 878 + 879 + drm_client_setup(drm, NULL); 880 + return st7571; 881 + } 882 + EXPORT_SYMBOL_GPL(st7571_probe); 883 + 884 + void st7571_remove(struct st7571_device *st7571) 885 + { 886 + drm_dev_unplug(&st7571->drm); 887 + } 888 + EXPORT_SYMBOL_GPL(st7571_remove); 889 + 890 + const struct st7571_panel_data st7567_config = { 891 + .init = st7567_lcd_init, 892 + .parse_dt = st7567_parse_dt, 893 + .constraints = { 894 + .min_nlines = 1, 895 + .max_nlines = 64, 896 + .min_ncols = 128, 897 + .max_ncols = 128, 898 + .support_grayscale = false, 899 + }, 900 + }; 901 + EXPORT_SYMBOL_NS_GPL(st7567_config, "DRM_ST7571"); 902 + 903 + const struct st7571_panel_data st7571_config = { 904 + .init = st7571_lcd_init, 905 + .parse_dt = st7571_parse_dt, 906 + .constraints = { 907 + .min_nlines = 1, 908 + .max_nlines = 128, 909 + .min_ncols = 128, 910 + .max_ncols = 128, 911 + .support_grayscale = true, 912 + }, 913 + }; 914 + EXPORT_SYMBOL_NS_GPL(st7571_config, "DRM_ST7571"); 915 + 916 + MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 917 + MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller"); 918 + MODULE_LICENSE("GPL");
+6
drivers/gpu/drm/sitronix/st7571.h
··· 82 82 u8 *row; 83 83 }; 84 84 85 + extern const struct st7571_panel_data st7567_config; 86 + extern const struct st7571_panel_data st7571_config; 87 + 88 + struct st7571_device *st7571_probe(struct device *dev, struct regmap *regmap); 89 + void st7571_remove(struct st7571_device *st7571); 90 + 85 91 #endif /* __ST7571_H__ */