ssd130x.c (26525B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * DRM driver for Solomon SSD130x OLED displays 4 * 5 * Copyright 2022 Red Hat Inc. 6 * Author: Javier Martinez Canillas <javierm@redhat.com> 7 * 8 * Based on drivers/video/fbdev/ssd1307fb.c 9 * Copyright 2012 Free Electrons 10 */ 11 12#include <linux/backlight.h> 13#include <linux/bitfield.h> 14#include <linux/bits.h> 15#include <linux/delay.h> 16#include <linux/gpio/consumer.h> 17#include <linux/property.h> 18#include <linux/pwm.h> 19#include <linux/regulator/consumer.h> 20 21#include <drm/drm_atomic_helper.h> 22#include <drm/drm_damage_helper.h> 23#include <drm/drm_fb_cma_helper.h> 24#include <drm/drm_fb_helper.h> 25#include <drm/drm_format_helper.h> 26#include <drm/drm_gem_atomic_helper.h> 27#include <drm/drm_gem_framebuffer_helper.h> 28#include <drm/drm_gem_shmem_helper.h> 29#include <drm/drm_managed.h> 30#include <drm/drm_modes.h> 31#include <drm/drm_rect.h> 32#include <drm/drm_probe_helper.h> 33 34#include "ssd130x.h" 35 36#define DRIVER_NAME "ssd130x" 37#define DRIVER_DESC "DRM driver for Solomon SSD130x OLED displays" 38#define DRIVER_DATE "20220131" 39#define DRIVER_MAJOR 1 40#define DRIVER_MINOR 0 41 42#define SSD130X_PAGE_COL_START_LOW 0x00 43#define SSD130X_PAGE_COL_START_HIGH 0x10 44#define SSD130X_SET_ADDRESS_MODE 0x20 45#define SSD130X_SET_COL_RANGE 0x21 46#define SSD130X_SET_PAGE_RANGE 0x22 47#define SSD130X_CONTRAST 0x81 48#define SSD130X_SET_LOOKUP_TABLE 0x91 49#define SSD130X_CHARGE_PUMP 0x8d 50#define SSD130X_SET_SEG_REMAP 0xa0 51#define SSD130X_DISPLAY_OFF 0xae 52#define SSD130X_SET_MULTIPLEX_RATIO 0xa8 53#define SSD130X_DISPLAY_ON 0xaf 54#define SSD130X_START_PAGE_ADDRESS 0xb0 55#define SSD130X_SET_COM_SCAN_DIR 0xc0 56#define SSD130X_SET_DISPLAY_OFFSET 0xd3 57#define SSD130X_SET_CLOCK_FREQ 0xd5 58#define SSD130X_SET_AREA_COLOR_MODE 0xd8 59#define SSD130X_SET_PRECHARGE_PERIOD 0xd9 60#define SSD130X_SET_COM_PINS_CONFIG 0xda 61#define SSD130X_SET_VCOMH 0xdb 62 63#define SSD130X_PAGE_COL_START_MASK GENMASK(3, 0) 64#define SSD130X_PAGE_COL_START_HIGH_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val) >> 4) 65#define SSD130X_PAGE_COL_START_LOW_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val)) 66#define SSD130X_START_PAGE_ADDRESS_MASK GENMASK(2, 0) 67#define SSD130X_START_PAGE_ADDRESS_SET(val) FIELD_PREP(SSD130X_START_PAGE_ADDRESS_MASK, (val)) 68#define SSD130X_SET_SEG_REMAP_MASK GENMASK(0, 0) 69#define SSD130X_SET_SEG_REMAP_SET(val) FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val)) 70#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3) 71#define SSD130X_SET_COM_SCAN_DIR_SET(val) FIELD_PREP(SSD130X_SET_COM_SCAN_DIR_MASK, (val)) 72#define SSD130X_SET_CLOCK_DIV_MASK GENMASK(3, 0) 73#define SSD130X_SET_CLOCK_DIV_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_DIV_MASK, (val)) 74#define SSD130X_SET_CLOCK_FREQ_MASK GENMASK(7, 4) 75#define SSD130X_SET_CLOCK_FREQ_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_FREQ_MASK, (val)) 76#define SSD130X_SET_PRECHARGE_PERIOD1_MASK GENMASK(3, 0) 77#define SSD130X_SET_PRECHARGE_PERIOD1_SET(val) FIELD_PREP(SSD130X_SET_PRECHARGE_PERIOD1_MASK, (val)) 78#define SSD130X_SET_PRECHARGE_PERIOD2_MASK GENMASK(7, 4) 79#define SSD130X_SET_PRECHARGE_PERIOD2_SET(val) FIELD_PREP(SSD130X_SET_PRECHARGE_PERIOD2_MASK, (val)) 80#define SSD130X_SET_COM_PINS_CONFIG1_MASK GENMASK(4, 4) 81#define SSD130X_SET_COM_PINS_CONFIG1_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG1_MASK, !(val)) 82#define SSD130X_SET_COM_PINS_CONFIG2_MASK GENMASK(5, 5) 83#define SSD130X_SET_COM_PINS_CONFIG2_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG2_MASK, (val)) 84 85#define SSD130X_SET_ADDRESS_MODE_HORIZONTAL 0x00 86#define SSD130X_SET_ADDRESS_MODE_VERTICAL 0x01 87#define SSD130X_SET_ADDRESS_MODE_PAGE 0x02 88 89#define SSD130X_SET_AREA_COLOR_MODE_ENABLE 0x1e 90#define SSD130X_SET_AREA_COLOR_MODE_LOW_POWER 0x05 91 92#define MAX_CONTRAST 255 93 94const struct ssd130x_deviceinfo ssd130x_variants[] = { 95 [SH1106_ID] = { 96 .default_vcomh = 0x40, 97 .default_dclk_div = 1, 98 .default_dclk_frq = 5, 99 .page_mode_only = 1, 100 }, 101 [SSD1305_ID] = { 102 .default_vcomh = 0x34, 103 .default_dclk_div = 1, 104 .default_dclk_frq = 7, 105 }, 106 [SSD1306_ID] = { 107 .default_vcomh = 0x20, 108 .default_dclk_div = 1, 109 .default_dclk_frq = 8, 110 .need_chargepump = 1, 111 }, 112 [SSD1307_ID] = { 113 .default_vcomh = 0x20, 114 .default_dclk_div = 2, 115 .default_dclk_frq = 12, 116 .need_pwm = 1, 117 }, 118 [SSD1309_ID] = { 119 .default_vcomh = 0x34, 120 .default_dclk_div = 1, 121 .default_dclk_frq = 10, 122 } 123}; 124EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); 125 126static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) 127{ 128 return container_of(drm, struct ssd130x_device, drm); 129} 130 131/* 132 * Helper to write data (SSD130X_DATA) to the device. 133 */ 134static int ssd130x_write_data(struct ssd130x_device *ssd130x, u8 *values, int count) 135{ 136 return regmap_bulk_write(ssd130x->regmap, SSD130X_DATA, values, count); 137} 138 139/* 140 * Helper to write command (SSD130X_COMMAND). The fist variadic argument 141 * is the command to write and the following are the command options. 142 * 143 * Note that the ssd130x protocol requires each command and option to be 144 * written as a SSD130X_COMMAND device register value. That is why a call 145 * to regmap_write(..., SSD130X_COMMAND, ...) is done for each argument. 146 */ 147static int ssd130x_write_cmd(struct ssd130x_device *ssd130x, int count, 148 /* u8 cmd, u8 option, ... */...) 149{ 150 va_list ap; 151 u8 value; 152 int ret; 153 154 va_start(ap, count); 155 156 do { 157 value = va_arg(ap, int); 158 ret = regmap_write(ssd130x->regmap, SSD130X_COMMAND, value); 159 if (ret) 160 goto out_end; 161 } while (--count); 162 163out_end: 164 va_end(ap); 165 166 return ret; 167} 168 169/* Set address range for horizontal/vertical addressing modes */ 170static int ssd130x_set_col_range(struct ssd130x_device *ssd130x, 171 u8 col_start, u8 cols) 172{ 173 u8 col_end = col_start + cols - 1; 174 int ret; 175 176 if (col_start == ssd130x->col_start && col_end == ssd130x->col_end) 177 return 0; 178 179 ret = ssd130x_write_cmd(ssd130x, 3, SSD130X_SET_COL_RANGE, col_start, col_end); 180 if (ret < 0) 181 return ret; 182 183 ssd130x->col_start = col_start; 184 ssd130x->col_end = col_end; 185 return 0; 186} 187 188static int ssd130x_set_page_range(struct ssd130x_device *ssd130x, 189 u8 page_start, u8 pages) 190{ 191 u8 page_end = page_start + pages - 1; 192 int ret; 193 194 if (page_start == ssd130x->page_start && page_end == ssd130x->page_end) 195 return 0; 196 197 ret = ssd130x_write_cmd(ssd130x, 3, SSD130X_SET_PAGE_RANGE, page_start, page_end); 198 if (ret < 0) 199 return ret; 200 201 ssd130x->page_start = page_start; 202 ssd130x->page_end = page_end; 203 return 0; 204} 205 206/* Set page and column start address for page addressing mode */ 207static int ssd130x_set_page_pos(struct ssd130x_device *ssd130x, 208 u8 page_start, u8 col_start) 209{ 210 int ret; 211 u32 page, col_low, col_high; 212 213 page = SSD130X_START_PAGE_ADDRESS | 214 SSD130X_START_PAGE_ADDRESS_SET(page_start); 215 col_low = SSD130X_PAGE_COL_START_LOW | 216 SSD130X_PAGE_COL_START_LOW_SET(col_start); 217 col_high = SSD130X_PAGE_COL_START_HIGH | 218 SSD130X_PAGE_COL_START_HIGH_SET(col_start); 219 ret = ssd130x_write_cmd(ssd130x, 3, page, col_low, col_high); 220 if (ret < 0) 221 return ret; 222 223 return 0; 224} 225 226static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x) 227{ 228 struct device *dev = ssd130x->dev; 229 struct pwm_state pwmstate; 230 231 ssd130x->pwm = pwm_get(dev, NULL); 232 if (IS_ERR(ssd130x->pwm)) { 233 dev_err(dev, "Could not get PWM from firmware description!\n"); 234 return PTR_ERR(ssd130x->pwm); 235 } 236 237 pwm_init_state(ssd130x->pwm, &pwmstate); 238 pwm_set_relative_duty_cycle(&pwmstate, 50, 100); 239 pwm_apply_state(ssd130x->pwm, &pwmstate); 240 241 /* Enable the PWM */ 242 pwm_enable(ssd130x->pwm); 243 244 dev_dbg(dev, "Using PWM%d with a %lluns period.\n", 245 ssd130x->pwm->pwm, pwm_get_period(ssd130x->pwm)); 246 247 return 0; 248} 249 250static void ssd130x_reset(struct ssd130x_device *ssd130x) 251{ 252 if (!ssd130x->reset) 253 return; 254 255 /* Reset the screen */ 256 gpiod_set_value_cansleep(ssd130x->reset, 1); 257 udelay(4); 258 gpiod_set_value_cansleep(ssd130x->reset, 0); 259 udelay(4); 260} 261 262static int ssd130x_power_on(struct ssd130x_device *ssd130x) 263{ 264 struct device *dev = ssd130x->dev; 265 int ret; 266 267 ssd130x_reset(ssd130x); 268 269 ret = regulator_enable(ssd130x->vcc_reg); 270 if (ret) { 271 dev_err(dev, "Failed to enable VCC: %d\n", ret); 272 return ret; 273 } 274 275 if (ssd130x->device_info->need_pwm) { 276 ret = ssd130x_pwm_enable(ssd130x); 277 if (ret) { 278 dev_err(dev, "Failed to enable PWM: %d\n", ret); 279 regulator_disable(ssd130x->vcc_reg); 280 return ret; 281 } 282 } 283 284 return 0; 285} 286 287static void ssd130x_power_off(struct ssd130x_device *ssd130x) 288{ 289 pwm_disable(ssd130x->pwm); 290 pwm_put(ssd130x->pwm); 291 292 regulator_disable(ssd130x->vcc_reg); 293} 294 295static int ssd130x_init(struct ssd130x_device *ssd130x) 296{ 297 u32 precharge, dclk, com_invdir, compins, chargepump, seg_remap; 298 int ret; 299 300 /* Set initial contrast */ 301 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CONTRAST, ssd130x->contrast); 302 if (ret < 0) 303 return ret; 304 305 /* Set segment re-map */ 306 seg_remap = (SSD130X_SET_SEG_REMAP | 307 SSD130X_SET_SEG_REMAP_SET(ssd130x->seg_remap)); 308 ret = ssd130x_write_cmd(ssd130x, 1, seg_remap); 309 if (ret < 0) 310 return ret; 311 312 /* Set COM direction */ 313 com_invdir = (SSD130X_SET_COM_SCAN_DIR | 314 SSD130X_SET_COM_SCAN_DIR_SET(ssd130x->com_invdir)); 315 ret = ssd130x_write_cmd(ssd130x, 1, com_invdir); 316 if (ret < 0) 317 return ret; 318 319 /* Set multiplex ratio value */ 320 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_MULTIPLEX_RATIO, ssd130x->height - 1); 321 if (ret < 0) 322 return ret; 323 324 /* set display offset value */ 325 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset); 326 if (ret < 0) 327 return ret; 328 329 /* Set clock frequency */ 330 dclk = (SSD130X_SET_CLOCK_DIV_SET(ssd130x->dclk_div - 1) | 331 SSD130X_SET_CLOCK_FREQ_SET(ssd130x->dclk_frq)); 332 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_CLOCK_FREQ, dclk); 333 if (ret < 0) 334 return ret; 335 336 /* Set Area Color Mode ON/OFF & Low Power Display Mode */ 337 if (ssd130x->area_color_enable || ssd130x->low_power) { 338 u32 mode = 0; 339 340 if (ssd130x->area_color_enable) 341 mode |= SSD130X_SET_AREA_COLOR_MODE_ENABLE; 342 343 if (ssd130x->low_power) 344 mode |= SSD130X_SET_AREA_COLOR_MODE_LOW_POWER; 345 346 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_AREA_COLOR_MODE, mode); 347 if (ret < 0) 348 return ret; 349 } 350 351 /* Set precharge period in number of ticks from the internal clock */ 352 precharge = (SSD130X_SET_PRECHARGE_PERIOD1_SET(ssd130x->prechargep1) | 353 SSD130X_SET_PRECHARGE_PERIOD1_SET(ssd130x->prechargep2)); 354 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_PRECHARGE_PERIOD, precharge); 355 if (ret < 0) 356 return ret; 357 358 /* Set COM pins configuration */ 359 compins = BIT(1); 360 compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(ssd130x->com_seq) | 361 SSD130X_SET_COM_PINS_CONFIG2_SET(ssd130x->com_lrremap)); 362 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_COM_PINS_CONFIG, compins); 363 if (ret < 0) 364 return ret; 365 366 /* Set VCOMH */ 367 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH, ssd130x->vcomh); 368 if (ret < 0) 369 return ret; 370 371 /* Turn on the DC-DC Charge Pump */ 372 chargepump = BIT(4); 373 374 if (ssd130x->device_info->need_chargepump) 375 chargepump |= BIT(2); 376 377 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CHARGE_PUMP, chargepump); 378 if (ret < 0) 379 return ret; 380 381 /* Set lookup table */ 382 if (ssd130x->lookup_table_set) { 383 int i; 384 385 ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_SET_LOOKUP_TABLE); 386 if (ret < 0) 387 return ret; 388 389 for (i = 0; i < ARRAY_SIZE(ssd130x->lookup_table); i++) { 390 u8 val = ssd130x->lookup_table[i]; 391 392 if (val < 31 || val > 63) 393 dev_warn(ssd130x->dev, 394 "lookup table index %d value out of range 31 <= %d <= 63\n", 395 i, val); 396 ret = ssd130x_write_cmd(ssd130x, 1, val); 397 if (ret < 0) 398 return ret; 399 } 400 } 401 402 /* Switch to page addressing mode */ 403 if (ssd130x->page_address_mode) 404 return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE, 405 SSD130X_SET_ADDRESS_MODE_PAGE); 406 407 /* Switch to horizontal addressing mode */ 408 return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE, 409 SSD130X_SET_ADDRESS_MODE_HORIZONTAL); 410} 411 412static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf, 413 struct drm_rect *rect) 414{ 415 unsigned int x = rect->x1; 416 unsigned int y = rect->y1; 417 unsigned int width = drm_rect_width(rect); 418 unsigned int height = drm_rect_height(rect); 419 unsigned int line_length = DIV_ROUND_UP(width, 8); 420 unsigned int pages = DIV_ROUND_UP(height, 8); 421 struct drm_device *drm = &ssd130x->drm; 422 u32 array_idx = 0; 423 int ret, i, j, k; 424 u8 *data_array = NULL; 425 426 drm_WARN_ONCE(drm, y % 8 != 0, "y must be aligned to screen page\n"); 427 428 data_array = kcalloc(width, pages, GFP_KERNEL); 429 if (!data_array) 430 return -ENOMEM; 431 432 /* 433 * The screen is divided in pages, each having a height of 8 434 * pixels, and the width of the screen. When sending a byte of 435 * data to the controller, it gives the 8 bits for the current 436 * column. I.e, the first byte are the 8 bits of the first 437 * column, then the 8 bits for the second column, etc. 438 * 439 * 440 * Representation of the screen, assuming it is 5 bits 441 * wide. Each letter-number combination is a bit that controls 442 * one pixel. 443 * 444 * A0 A1 A2 A3 A4 445 * B0 B1 B2 B3 B4 446 * C0 C1 C2 C3 C4 447 * D0 D1 D2 D3 D4 448 * E0 E1 E2 E3 E4 449 * F0 F1 F2 F3 F4 450 * G0 G1 G2 G3 G4 451 * H0 H1 H2 H3 H4 452 * 453 * If you want to update this screen, you need to send 5 bytes: 454 * (1) A0 B0 C0 D0 E0 F0 G0 H0 455 * (2) A1 B1 C1 D1 E1 F1 G1 H1 456 * (3) A2 B2 C2 D2 E2 F2 G2 H2 457 * (4) A3 B3 C3 D3 E3 F3 G3 H3 458 * (5) A4 B4 C4 D4 E4 F4 G4 H4 459 */ 460 461 if (!ssd130x->page_address_mode) { 462 /* Set address range for horizontal addressing mode */ 463 ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width); 464 if (ret < 0) 465 goto out_free; 466 467 ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages); 468 if (ret < 0) 469 goto out_free; 470 } 471 472 for (i = 0; i < pages; i++) { 473 int m = 8; 474 475 /* Last page may be partial */ 476 if (8 * (y / 8 + i + 1) > ssd130x->height) 477 m = ssd130x->height % 8; 478 for (j = 0; j < width; j++) { 479 u8 data = 0; 480 481 for (k = 0; k < m; k++) { 482 u8 byte = buf[(8 * i + k) * line_length + j / 8]; 483 u8 bit = (byte >> (j % 8)) & 1; 484 485 data |= bit << k; 486 } 487 data_array[array_idx++] = data; 488 } 489 490 /* 491 * In page addressing mode, the start address needs to be reset, 492 * and each page then needs to be written out separately. 493 */ 494 if (ssd130x->page_address_mode) { 495 ret = ssd130x_set_page_pos(ssd130x, 496 ssd130x->page_offset + i, 497 ssd130x->col_offset + x); 498 if (ret < 0) 499 goto out_free; 500 501 ret = ssd130x_write_data(ssd130x, data_array, width); 502 if (ret < 0) 503 goto out_free; 504 505 array_idx = 0; 506 } 507 } 508 509 /* Write out update in one go if we aren't using page addressing mode */ 510 if (!ssd130x->page_address_mode) 511 ret = ssd130x_write_data(ssd130x, data_array, width * pages); 512 513out_free: 514 kfree(data_array); 515 return ret; 516} 517 518static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) 519{ 520 u8 *buf = NULL; 521 struct drm_rect fullscreen = { 522 .x1 = 0, 523 .x2 = ssd130x->width, 524 .y1 = 0, 525 .y2 = ssd130x->height, 526 }; 527 528 buf = kcalloc(DIV_ROUND_UP(ssd130x->width, 8), ssd130x->height, 529 GFP_KERNEL); 530 if (!buf) 531 return; 532 533 ssd130x_update_rect(ssd130x, buf, &fullscreen); 534 535 kfree(buf); 536} 537 538static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *map, 539 struct drm_rect *rect) 540{ 541 struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); 542 void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ 543 unsigned int dst_pitch; 544 int ret = 0; 545 u8 *buf = NULL; 546 547 /* Align y to display page boundaries */ 548 rect->y1 = round_down(rect->y1, 8); 549 rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height); 550 551 dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 552 buf = kcalloc(dst_pitch, drm_rect_height(rect), GFP_KERNEL); 553 if (!buf) 554 return -ENOMEM; 555 556 drm_fb_xrgb8888_to_mono(buf, dst_pitch, vmap, fb, rect); 557 558 ssd130x_update_rect(ssd130x, buf, rect); 559 560 kfree(buf); 561 562 return ret; 563} 564 565static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, 566 const struct drm_display_mode *mode) 567{ 568 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); 569 570 if (mode->hdisplay != ssd130x->mode.hdisplay && 571 mode->vdisplay != ssd130x->mode.vdisplay) 572 return MODE_ONE_SIZE; 573 574 if (mode->hdisplay != ssd130x->mode.hdisplay) 575 return MODE_ONE_WIDTH; 576 577 if (mode->vdisplay != ssd130x->mode.vdisplay) 578 return MODE_ONE_HEIGHT; 579 580 return MODE_OK; 581} 582 583static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe, 584 struct drm_crtc_state *crtc_state, 585 struct drm_plane_state *plane_state) 586{ 587 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); 588 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 589 struct drm_device *drm = &ssd130x->drm; 590 int idx, ret; 591 592 ret = ssd130x_power_on(ssd130x); 593 if (ret) 594 return; 595 596 ret = ssd130x_init(ssd130x); 597 if (ret) 598 goto out_power_off; 599 600 if (!drm_dev_enter(drm, &idx)) 601 goto out_power_off; 602 603 ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst); 604 605 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); 606 607 backlight_enable(ssd130x->bl_dev); 608 609 drm_dev_exit(idx); 610 611 return; 612out_power_off: 613 ssd130x_power_off(ssd130x); 614} 615 616static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe) 617{ 618 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); 619 struct drm_device *drm = &ssd130x->drm; 620 int idx; 621 622 if (!drm_dev_enter(drm, &idx)) 623 return; 624 625 ssd130x_clear_screen(ssd130x); 626 627 backlight_disable(ssd130x->bl_dev); 628 629 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); 630 631 ssd130x_power_off(ssd130x); 632 633 drm_dev_exit(idx); 634} 635 636static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe, 637 struct drm_plane_state *old_plane_state) 638{ 639 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); 640 struct drm_plane_state *plane_state = pipe->plane.state; 641 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 642 struct drm_framebuffer *fb = plane_state->fb; 643 struct drm_device *drm = &ssd130x->drm; 644 struct drm_rect src_clip, dst_clip; 645 int idx; 646 647 if (!fb) 648 return; 649 650 if (!pipe->crtc.state->active) 651 return; 652 653 if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip)) 654 return; 655 656 dst_clip = plane_state->dst; 657 if (!drm_rect_intersect(&dst_clip, &src_clip)) 658 return; 659 660 if (!drm_dev_enter(drm, &idx)) 661 return; 662 663 ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip); 664 665 drm_dev_exit(idx); 666} 667 668static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = { 669 .mode_valid = ssd130x_display_pipe_mode_valid, 670 .enable = ssd130x_display_pipe_enable, 671 .disable = ssd130x_display_pipe_disable, 672 .update = ssd130x_display_pipe_update, 673 DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, 674}; 675 676static int ssd130x_connector_get_modes(struct drm_connector *connector) 677{ 678 struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev); 679 struct drm_display_mode *mode; 680 struct device *dev = ssd130x->dev; 681 682 mode = drm_mode_duplicate(connector->dev, &ssd130x->mode); 683 if (!mode) { 684 dev_err(dev, "Failed to duplicated mode\n"); 685 return 0; 686 } 687 688 drm_mode_probed_add(connector, mode); 689 drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay); 690 691 /* There is only a single mode */ 692 return 1; 693} 694 695static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = { 696 .get_modes = ssd130x_connector_get_modes, 697}; 698 699static const struct drm_connector_funcs ssd130x_connector_funcs = { 700 .reset = drm_atomic_helper_connector_reset, 701 .fill_modes = drm_helper_probe_single_connector_modes, 702 .destroy = drm_connector_cleanup, 703 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 704 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 705}; 706 707static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = { 708 .fb_create = drm_gem_fb_create_with_dirty, 709 .atomic_check = drm_atomic_helper_check, 710 .atomic_commit = drm_atomic_helper_commit, 711}; 712 713static const uint32_t ssd130x_formats[] = { 714 DRM_FORMAT_XRGB8888, 715}; 716 717DEFINE_DRM_GEM_FOPS(ssd130x_fops); 718 719static const struct drm_driver ssd130x_drm_driver = { 720 DRM_GEM_SHMEM_DRIVER_OPS, 721 .name = DRIVER_NAME, 722 .desc = DRIVER_DESC, 723 .date = DRIVER_DATE, 724 .major = DRIVER_MAJOR, 725 .minor = DRIVER_MINOR, 726 .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 727 .fops = &ssd130x_fops, 728}; 729 730static int ssd130x_update_bl(struct backlight_device *bdev) 731{ 732 struct ssd130x_device *ssd130x = bl_get_data(bdev); 733 int brightness = backlight_get_brightness(bdev); 734 int ret; 735 736 ssd130x->contrast = brightness; 737 738 ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_CONTRAST); 739 if (ret < 0) 740 return ret; 741 742 ret = ssd130x_write_cmd(ssd130x, 1, ssd130x->contrast); 743 if (ret < 0) 744 return ret; 745 746 return 0; 747} 748 749static const struct backlight_ops ssd130xfb_bl_ops = { 750 .update_status = ssd130x_update_bl, 751}; 752 753static void ssd130x_parse_properties(struct ssd130x_device *ssd130x) 754{ 755 struct device *dev = ssd130x->dev; 756 757 if (device_property_read_u32(dev, "solomon,width", &ssd130x->width)) 758 ssd130x->width = 96; 759 760 if (device_property_read_u32(dev, "solomon,height", &ssd130x->height)) 761 ssd130x->height = 16; 762 763 if (device_property_read_u32(dev, "solomon,page-offset", &ssd130x->page_offset)) 764 ssd130x->page_offset = 1; 765 766 if (device_property_read_u32(dev, "solomon,col-offset", &ssd130x->col_offset)) 767 ssd130x->col_offset = 0; 768 769 if (device_property_read_u32(dev, "solomon,com-offset", &ssd130x->com_offset)) 770 ssd130x->com_offset = 0; 771 772 if (device_property_read_u32(dev, "solomon,prechargep1", &ssd130x->prechargep1)) 773 ssd130x->prechargep1 = 2; 774 775 if (device_property_read_u32(dev, "solomon,prechargep2", &ssd130x->prechargep2)) 776 ssd130x->prechargep2 = 2; 777 778 if (!device_property_read_u8_array(dev, "solomon,lookup-table", 779 ssd130x->lookup_table, 780 ARRAY_SIZE(ssd130x->lookup_table))) 781 ssd130x->lookup_table_set = 1; 782 783 ssd130x->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap"); 784 ssd130x->com_seq = device_property_read_bool(dev, "solomon,com-seq"); 785 ssd130x->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap"); 786 ssd130x->com_invdir = device_property_read_bool(dev, "solomon,com-invdir"); 787 ssd130x->area_color_enable = 788 device_property_read_bool(dev, "solomon,area-color-enable"); 789 ssd130x->low_power = device_property_read_bool(dev, "solomon,low-power"); 790 791 ssd130x->contrast = 127; 792 ssd130x->vcomh = ssd130x->device_info->default_vcomh; 793 794 /* Setup display timing */ 795 if (device_property_read_u32(dev, "solomon,dclk-div", &ssd130x->dclk_div)) 796 ssd130x->dclk_div = ssd130x->device_info->default_dclk_div; 797 if (device_property_read_u32(dev, "solomon,dclk-frq", &ssd130x->dclk_frq)) 798 ssd130x->dclk_frq = ssd130x->device_info->default_dclk_frq; 799} 800 801static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) 802{ 803 struct drm_display_mode *mode = &ssd130x->mode; 804 struct device *dev = ssd130x->dev; 805 struct drm_device *drm = &ssd130x->drm; 806 unsigned long max_width, max_height; 807 int ret; 808 809 ret = drmm_mode_config_init(drm); 810 if (ret) { 811 dev_err(dev, "DRM mode config init failed: %d\n", ret); 812 return ret; 813 } 814 815 mode->type = DRM_MODE_TYPE_DRIVER; 816 mode->clock = 1; 817 mode->hdisplay = mode->htotal = ssd130x->width; 818 mode->hsync_start = mode->hsync_end = ssd130x->width; 819 mode->vdisplay = mode->vtotal = ssd130x->height; 820 mode->vsync_start = mode->vsync_end = ssd130x->height; 821 mode->width_mm = 27; 822 mode->height_mm = 27; 823 824 max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH); 825 max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT); 826 827 drm->mode_config.min_width = mode->hdisplay; 828 drm->mode_config.max_width = max_width; 829 drm->mode_config.min_height = mode->vdisplay; 830 drm->mode_config.max_height = max_height; 831 drm->mode_config.preferred_depth = 32; 832 drm->mode_config.funcs = &ssd130x_mode_config_funcs; 833 834 ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs, 835 DRM_MODE_CONNECTOR_Unknown); 836 if (ret) { 837 dev_err(dev, "DRM connector init failed: %d\n", ret); 838 return ret; 839 } 840 841 drm_connector_helper_add(&ssd130x->connector, &ssd130x_connector_helper_funcs); 842 843 ret = drm_simple_display_pipe_init(drm, &ssd130x->pipe, &ssd130x_pipe_funcs, 844 ssd130x_formats, ARRAY_SIZE(ssd130x_formats), 845 NULL, &ssd130x->connector); 846 if (ret) { 847 dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret); 848 return ret; 849 } 850 851 drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane); 852 853 drm_mode_config_reset(drm); 854 855 return 0; 856} 857 858static int ssd130x_get_resources(struct ssd130x_device *ssd130x) 859{ 860 struct device *dev = ssd130x->dev; 861 862 ssd130x->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 863 if (IS_ERR(ssd130x->reset)) 864 return dev_err_probe(dev, PTR_ERR(ssd130x->reset), 865 "Failed to get reset gpio\n"); 866 867 ssd130x->vcc_reg = devm_regulator_get(dev, "vcc"); 868 if (IS_ERR(ssd130x->vcc_reg)) 869 return dev_err_probe(dev, PTR_ERR(ssd130x->vcc_reg), 870 "Failed to get VCC regulator\n"); 871 872 return 0; 873} 874 875struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap) 876{ 877 struct ssd130x_device *ssd130x; 878 struct backlight_device *bl; 879 struct drm_device *drm; 880 int ret; 881 882 ssd130x = devm_drm_dev_alloc(dev, &ssd130x_drm_driver, 883 struct ssd130x_device, drm); 884 if (IS_ERR(ssd130x)) 885 return ERR_PTR(dev_err_probe(dev, PTR_ERR(ssd130x), 886 "Failed to allocate DRM device\n")); 887 888 drm = &ssd130x->drm; 889 890 ssd130x->dev = dev; 891 ssd130x->regmap = regmap; 892 ssd130x->device_info = device_get_match_data(dev); 893 894 if (ssd130x->device_info->page_mode_only) 895 ssd130x->page_address_mode = 1; 896 897 ssd130x_parse_properties(ssd130x); 898 899 ret = ssd130x_get_resources(ssd130x); 900 if (ret) 901 return ERR_PTR(ret); 902 903 bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x, 904 &ssd130xfb_bl_ops, NULL); 905 if (IS_ERR(bl)) 906 return ERR_PTR(dev_err_probe(dev, PTR_ERR(bl), 907 "Unable to register backlight device\n")); 908 909 bl->props.brightness = ssd130x->contrast; 910 bl->props.max_brightness = MAX_CONTRAST; 911 ssd130x->bl_dev = bl; 912 913 ret = ssd130x_init_modeset(ssd130x); 914 if (ret) 915 return ERR_PTR(ret); 916 917 ret = drm_dev_register(drm, 0); 918 if (ret) 919 return ERR_PTR(dev_err_probe(dev, ret, "DRM device register failed\n")); 920 921 drm_fbdev_generic_setup(drm, 0); 922 923 return ssd130x; 924} 925EXPORT_SYMBOL_GPL(ssd130x_probe); 926 927void ssd130x_remove(struct ssd130x_device *ssd130x) 928{ 929 drm_dev_unplug(&ssd130x->drm); 930} 931EXPORT_SYMBOL_GPL(ssd130x_remove); 932 933void ssd130x_shutdown(struct ssd130x_device *ssd130x) 934{ 935 drm_atomic_helper_shutdown(&ssd130x->drm); 936} 937EXPORT_SYMBOL_GPL(ssd130x_shutdown); 938 939MODULE_DESCRIPTION(DRIVER_DESC); 940MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>"); 941MODULE_LICENSE("GPL v2");