0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/backlight.h>
0013 #include <linux/bitfield.h>
0014 #include <linux/bits.h>
0015 #include <linux/delay.h>
0016 #include <linux/gpio/consumer.h>
0017 #include <linux/property.h>
0018 #include <linux/pwm.h>
0019 #include <linux/regulator/consumer.h>
0020
0021 #include <drm/drm_atomic_helper.h>
0022 #include <drm/drm_damage_helper.h>
0023 #include <drm/drm_edid.h>
0024 #include <drm/drm_fb_cma_helper.h>
0025 #include <drm/drm_fb_helper.h>
0026 #include <drm/drm_format_helper.h>
0027 #include <drm/drm_framebuffer.h>
0028 #include <drm/drm_gem_atomic_helper.h>
0029 #include <drm/drm_gem_framebuffer_helper.h>
0030 #include <drm/drm_gem_shmem_helper.h>
0031 #include <drm/drm_managed.h>
0032 #include <drm/drm_modes.h>
0033 #include <drm/drm_rect.h>
0034 #include <drm/drm_probe_helper.h>
0035
0036 #include "ssd130x.h"
0037
0038 #define DRIVER_NAME "ssd130x"
0039 #define DRIVER_DESC "DRM driver for Solomon SSD130x OLED displays"
0040 #define DRIVER_DATE "20220131"
0041 #define DRIVER_MAJOR 1
0042 #define DRIVER_MINOR 0
0043
0044 #define SSD130X_PAGE_COL_START_LOW 0x00
0045 #define SSD130X_PAGE_COL_START_HIGH 0x10
0046 #define SSD130X_SET_ADDRESS_MODE 0x20
0047 #define SSD130X_SET_COL_RANGE 0x21
0048 #define SSD130X_SET_PAGE_RANGE 0x22
0049 #define SSD130X_CONTRAST 0x81
0050 #define SSD130X_SET_LOOKUP_TABLE 0x91
0051 #define SSD130X_CHARGE_PUMP 0x8d
0052 #define SSD130X_SET_SEG_REMAP 0xa0
0053 #define SSD130X_DISPLAY_OFF 0xae
0054 #define SSD130X_SET_MULTIPLEX_RATIO 0xa8
0055 #define SSD130X_DISPLAY_ON 0xaf
0056 #define SSD130X_START_PAGE_ADDRESS 0xb0
0057 #define SSD130X_SET_COM_SCAN_DIR 0xc0
0058 #define SSD130X_SET_DISPLAY_OFFSET 0xd3
0059 #define SSD130X_SET_CLOCK_FREQ 0xd5
0060 #define SSD130X_SET_AREA_COLOR_MODE 0xd8
0061 #define SSD130X_SET_PRECHARGE_PERIOD 0xd9
0062 #define SSD130X_SET_COM_PINS_CONFIG 0xda
0063 #define SSD130X_SET_VCOMH 0xdb
0064
0065 #define SSD130X_PAGE_COL_START_MASK GENMASK(3, 0)
0066 #define SSD130X_PAGE_COL_START_HIGH_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val) >> 4)
0067 #define SSD130X_PAGE_COL_START_LOW_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val))
0068 #define SSD130X_START_PAGE_ADDRESS_MASK GENMASK(2, 0)
0069 #define SSD130X_START_PAGE_ADDRESS_SET(val) FIELD_PREP(SSD130X_START_PAGE_ADDRESS_MASK, (val))
0070 #define SSD130X_SET_SEG_REMAP_MASK GENMASK(0, 0)
0071 #define SSD130X_SET_SEG_REMAP_SET(val) FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val))
0072 #define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3)
0073 #define SSD130X_SET_COM_SCAN_DIR_SET(val) FIELD_PREP(SSD130X_SET_COM_SCAN_DIR_MASK, (val))
0074 #define SSD130X_SET_CLOCK_DIV_MASK GENMASK(3, 0)
0075 #define SSD130X_SET_CLOCK_DIV_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_DIV_MASK, (val))
0076 #define SSD130X_SET_CLOCK_FREQ_MASK GENMASK(7, 4)
0077 #define SSD130X_SET_CLOCK_FREQ_SET(val) FIELD_PREP(SSD130X_SET_CLOCK_FREQ_MASK, (val))
0078 #define SSD130X_SET_PRECHARGE_PERIOD1_MASK GENMASK(3, 0)
0079 #define SSD130X_SET_PRECHARGE_PERIOD1_SET(val) FIELD_PREP(SSD130X_SET_PRECHARGE_PERIOD1_MASK, (val))
0080 #define SSD130X_SET_PRECHARGE_PERIOD2_MASK GENMASK(7, 4)
0081 #define SSD130X_SET_PRECHARGE_PERIOD2_SET(val) FIELD_PREP(SSD130X_SET_PRECHARGE_PERIOD2_MASK, (val))
0082 #define SSD130X_SET_COM_PINS_CONFIG1_MASK GENMASK(4, 4)
0083 #define SSD130X_SET_COM_PINS_CONFIG1_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG1_MASK, !(val))
0084 #define SSD130X_SET_COM_PINS_CONFIG2_MASK GENMASK(5, 5)
0085 #define SSD130X_SET_COM_PINS_CONFIG2_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG2_MASK, (val))
0086
0087 #define SSD130X_SET_ADDRESS_MODE_HORIZONTAL 0x00
0088 #define SSD130X_SET_ADDRESS_MODE_VERTICAL 0x01
0089 #define SSD130X_SET_ADDRESS_MODE_PAGE 0x02
0090
0091 #define SSD130X_SET_AREA_COLOR_MODE_ENABLE 0x1e
0092 #define SSD130X_SET_AREA_COLOR_MODE_LOW_POWER 0x05
0093
0094 #define MAX_CONTRAST 255
0095
0096 const struct ssd130x_deviceinfo ssd130x_variants[] = {
0097 [SH1106_ID] = {
0098 .default_vcomh = 0x40,
0099 .default_dclk_div = 1,
0100 .default_dclk_frq = 5,
0101 .page_mode_only = 1,
0102 },
0103 [SSD1305_ID] = {
0104 .default_vcomh = 0x34,
0105 .default_dclk_div = 1,
0106 .default_dclk_frq = 7,
0107 },
0108 [SSD1306_ID] = {
0109 .default_vcomh = 0x20,
0110 .default_dclk_div = 1,
0111 .default_dclk_frq = 8,
0112 .need_chargepump = 1,
0113 },
0114 [SSD1307_ID] = {
0115 .default_vcomh = 0x20,
0116 .default_dclk_div = 2,
0117 .default_dclk_frq = 12,
0118 .need_pwm = 1,
0119 },
0120 [SSD1309_ID] = {
0121 .default_vcomh = 0x34,
0122 .default_dclk_div = 1,
0123 .default_dclk_frq = 10,
0124 }
0125 };
0126 EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
0127
0128 static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)
0129 {
0130 return container_of(drm, struct ssd130x_device, drm);
0131 }
0132
0133
0134
0135
0136 static int ssd130x_write_data(struct ssd130x_device *ssd130x, u8 *values, int count)
0137 {
0138 return regmap_bulk_write(ssd130x->regmap, SSD130X_DATA, values, count);
0139 }
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 static int ssd130x_write_cmd(struct ssd130x_device *ssd130x, int count,
0150 ...)
0151 {
0152 va_list ap;
0153 u8 value;
0154 int ret;
0155
0156 va_start(ap, count);
0157
0158 do {
0159 value = va_arg(ap, int);
0160 ret = regmap_write(ssd130x->regmap, SSD130X_COMMAND, value);
0161 if (ret)
0162 goto out_end;
0163 } while (--count);
0164
0165 out_end:
0166 va_end(ap);
0167
0168 return ret;
0169 }
0170
0171
0172 static int ssd130x_set_col_range(struct ssd130x_device *ssd130x,
0173 u8 col_start, u8 cols)
0174 {
0175 u8 col_end = col_start + cols - 1;
0176 int ret;
0177
0178 if (col_start == ssd130x->col_start && col_end == ssd130x->col_end)
0179 return 0;
0180
0181 ret = ssd130x_write_cmd(ssd130x, 3, SSD130X_SET_COL_RANGE, col_start, col_end);
0182 if (ret < 0)
0183 return ret;
0184
0185 ssd130x->col_start = col_start;
0186 ssd130x->col_end = col_end;
0187 return 0;
0188 }
0189
0190 static int ssd130x_set_page_range(struct ssd130x_device *ssd130x,
0191 u8 page_start, u8 pages)
0192 {
0193 u8 page_end = page_start + pages - 1;
0194 int ret;
0195
0196 if (page_start == ssd130x->page_start && page_end == ssd130x->page_end)
0197 return 0;
0198
0199 ret = ssd130x_write_cmd(ssd130x, 3, SSD130X_SET_PAGE_RANGE, page_start, page_end);
0200 if (ret < 0)
0201 return ret;
0202
0203 ssd130x->page_start = page_start;
0204 ssd130x->page_end = page_end;
0205 return 0;
0206 }
0207
0208
0209 static int ssd130x_set_page_pos(struct ssd130x_device *ssd130x,
0210 u8 page_start, u8 col_start)
0211 {
0212 int ret;
0213 u32 page, col_low, col_high;
0214
0215 page = SSD130X_START_PAGE_ADDRESS |
0216 SSD130X_START_PAGE_ADDRESS_SET(page_start);
0217 col_low = SSD130X_PAGE_COL_START_LOW |
0218 SSD130X_PAGE_COL_START_LOW_SET(col_start);
0219 col_high = SSD130X_PAGE_COL_START_HIGH |
0220 SSD130X_PAGE_COL_START_HIGH_SET(col_start);
0221 ret = ssd130x_write_cmd(ssd130x, 3, page, col_low, col_high);
0222 if (ret < 0)
0223 return ret;
0224
0225 return 0;
0226 }
0227
0228 static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x)
0229 {
0230 struct device *dev = ssd130x->dev;
0231 struct pwm_state pwmstate;
0232
0233 ssd130x->pwm = pwm_get(dev, NULL);
0234 if (IS_ERR(ssd130x->pwm)) {
0235 dev_err(dev, "Could not get PWM from firmware description!\n");
0236 return PTR_ERR(ssd130x->pwm);
0237 }
0238
0239 pwm_init_state(ssd130x->pwm, &pwmstate);
0240 pwm_set_relative_duty_cycle(&pwmstate, 50, 100);
0241 pwm_apply_state(ssd130x->pwm, &pwmstate);
0242
0243
0244 pwm_enable(ssd130x->pwm);
0245
0246 dev_dbg(dev, "Using PWM%d with a %lluns period.\n",
0247 ssd130x->pwm->pwm, pwm_get_period(ssd130x->pwm));
0248
0249 return 0;
0250 }
0251
0252 static void ssd130x_reset(struct ssd130x_device *ssd130x)
0253 {
0254 if (!ssd130x->reset)
0255 return;
0256
0257
0258 gpiod_set_value_cansleep(ssd130x->reset, 1);
0259 udelay(4);
0260 gpiod_set_value_cansleep(ssd130x->reset, 0);
0261 udelay(4);
0262 }
0263
0264 static int ssd130x_power_on(struct ssd130x_device *ssd130x)
0265 {
0266 struct device *dev = ssd130x->dev;
0267 int ret;
0268
0269 ssd130x_reset(ssd130x);
0270
0271 ret = regulator_enable(ssd130x->vcc_reg);
0272 if (ret) {
0273 dev_err(dev, "Failed to enable VCC: %d\n", ret);
0274 return ret;
0275 }
0276
0277 if (ssd130x->device_info->need_pwm) {
0278 ret = ssd130x_pwm_enable(ssd130x);
0279 if (ret) {
0280 dev_err(dev, "Failed to enable PWM: %d\n", ret);
0281 regulator_disable(ssd130x->vcc_reg);
0282 return ret;
0283 }
0284 }
0285
0286 return 0;
0287 }
0288
0289 static void ssd130x_power_off(struct ssd130x_device *ssd130x)
0290 {
0291 pwm_disable(ssd130x->pwm);
0292 pwm_put(ssd130x->pwm);
0293
0294 regulator_disable(ssd130x->vcc_reg);
0295 }
0296
0297 static int ssd130x_init(struct ssd130x_device *ssd130x)
0298 {
0299 u32 precharge, dclk, com_invdir, compins, chargepump, seg_remap;
0300 int ret;
0301
0302
0303 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CONTRAST, ssd130x->contrast);
0304 if (ret < 0)
0305 return ret;
0306
0307
0308 seg_remap = (SSD130X_SET_SEG_REMAP |
0309 SSD130X_SET_SEG_REMAP_SET(ssd130x->seg_remap));
0310 ret = ssd130x_write_cmd(ssd130x, 1, seg_remap);
0311 if (ret < 0)
0312 return ret;
0313
0314
0315 com_invdir = (SSD130X_SET_COM_SCAN_DIR |
0316 SSD130X_SET_COM_SCAN_DIR_SET(ssd130x->com_invdir));
0317 ret = ssd130x_write_cmd(ssd130x, 1, com_invdir);
0318 if (ret < 0)
0319 return ret;
0320
0321
0322 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
0323 if (ret < 0)
0324 return ret;
0325
0326
0327 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset);
0328 if (ret < 0)
0329 return ret;
0330
0331
0332 dclk = (SSD130X_SET_CLOCK_DIV_SET(ssd130x->dclk_div - 1) |
0333 SSD130X_SET_CLOCK_FREQ_SET(ssd130x->dclk_frq));
0334 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_CLOCK_FREQ, dclk);
0335 if (ret < 0)
0336 return ret;
0337
0338
0339 if (ssd130x->area_color_enable || ssd130x->low_power) {
0340 u32 mode = 0;
0341
0342 if (ssd130x->area_color_enable)
0343 mode |= SSD130X_SET_AREA_COLOR_MODE_ENABLE;
0344
0345 if (ssd130x->low_power)
0346 mode |= SSD130X_SET_AREA_COLOR_MODE_LOW_POWER;
0347
0348 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_AREA_COLOR_MODE, mode);
0349 if (ret < 0)
0350 return ret;
0351 }
0352
0353
0354 precharge = (SSD130X_SET_PRECHARGE_PERIOD1_SET(ssd130x->prechargep1) |
0355 SSD130X_SET_PRECHARGE_PERIOD2_SET(ssd130x->prechargep2));
0356 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_PRECHARGE_PERIOD, precharge);
0357 if (ret < 0)
0358 return ret;
0359
0360
0361 compins = BIT(1);
0362 compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(ssd130x->com_seq) |
0363 SSD130X_SET_COM_PINS_CONFIG2_SET(ssd130x->com_lrremap));
0364 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_COM_PINS_CONFIG, compins);
0365 if (ret < 0)
0366 return ret;
0367
0368
0369 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH, ssd130x->vcomh);
0370 if (ret < 0)
0371 return ret;
0372
0373
0374 chargepump = BIT(4);
0375
0376 if (ssd130x->device_info->need_chargepump)
0377 chargepump |= BIT(2);
0378
0379 ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CHARGE_PUMP, chargepump);
0380 if (ret < 0)
0381 return ret;
0382
0383
0384 if (ssd130x->lookup_table_set) {
0385 int i;
0386
0387 ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_SET_LOOKUP_TABLE);
0388 if (ret < 0)
0389 return ret;
0390
0391 for (i = 0; i < ARRAY_SIZE(ssd130x->lookup_table); i++) {
0392 u8 val = ssd130x->lookup_table[i];
0393
0394 if (val < 31 || val > 63)
0395 dev_warn(ssd130x->dev,
0396 "lookup table index %d value out of range 31 <= %d <= 63\n",
0397 i, val);
0398 ret = ssd130x_write_cmd(ssd130x, 1, val);
0399 if (ret < 0)
0400 return ret;
0401 }
0402 }
0403
0404
0405 if (ssd130x->page_address_mode)
0406 return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE,
0407 SSD130X_SET_ADDRESS_MODE_PAGE);
0408
0409
0410 return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE,
0411 SSD130X_SET_ADDRESS_MODE_HORIZONTAL);
0412 }
0413
0414 static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf,
0415 struct drm_rect *rect)
0416 {
0417 unsigned int x = rect->x1;
0418 unsigned int y = rect->y1;
0419 unsigned int width = drm_rect_width(rect);
0420 unsigned int height = drm_rect_height(rect);
0421 unsigned int line_length = DIV_ROUND_UP(width, 8);
0422 unsigned int pages = DIV_ROUND_UP(height, 8);
0423 struct drm_device *drm = &ssd130x->drm;
0424 u32 array_idx = 0;
0425 int ret, i, j, k;
0426 u8 *data_array = NULL;
0427
0428 drm_WARN_ONCE(drm, y % 8 != 0, "y must be aligned to screen page\n");
0429
0430 data_array = kcalloc(width, pages, GFP_KERNEL);
0431 if (!data_array)
0432 return -ENOMEM;
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463 if (!ssd130x->page_address_mode) {
0464
0465 ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width);
0466 if (ret < 0)
0467 goto out_free;
0468
0469 ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages);
0470 if (ret < 0)
0471 goto out_free;
0472 }
0473
0474 for (i = 0; i < pages; i++) {
0475 int m = 8;
0476
0477
0478 if (8 * (y / 8 + i + 1) > ssd130x->height)
0479 m = ssd130x->height % 8;
0480 for (j = 0; j < width; j++) {
0481 u8 data = 0;
0482
0483 for (k = 0; k < m; k++) {
0484 u8 byte = buf[(8 * i + k) * line_length + j / 8];
0485 u8 bit = (byte >> (j % 8)) & 1;
0486
0487 data |= bit << k;
0488 }
0489 data_array[array_idx++] = data;
0490 }
0491
0492
0493
0494
0495
0496 if (ssd130x->page_address_mode) {
0497 ret = ssd130x_set_page_pos(ssd130x,
0498 ssd130x->page_offset + i,
0499 ssd130x->col_offset + x);
0500 if (ret < 0)
0501 goto out_free;
0502
0503 ret = ssd130x_write_data(ssd130x, data_array, width);
0504 if (ret < 0)
0505 goto out_free;
0506
0507 array_idx = 0;
0508 }
0509 }
0510
0511
0512 if (!ssd130x->page_address_mode)
0513 ret = ssd130x_write_data(ssd130x, data_array, width * pages);
0514
0515 out_free:
0516 kfree(data_array);
0517 return ret;
0518 }
0519
0520 static void ssd130x_clear_screen(struct ssd130x_device *ssd130x)
0521 {
0522 u8 *buf = NULL;
0523 struct drm_rect fullscreen = {
0524 .x1 = 0,
0525 .x2 = ssd130x->width,
0526 .y1 = 0,
0527 .y2 = ssd130x->height,
0528 };
0529
0530 buf = kcalloc(DIV_ROUND_UP(ssd130x->width, 8), ssd130x->height,
0531 GFP_KERNEL);
0532 if (!buf)
0533 return;
0534
0535 ssd130x_update_rect(ssd130x, buf, &fullscreen);
0536
0537 kfree(buf);
0538 }
0539
0540 static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *map,
0541 struct drm_rect *rect)
0542 {
0543 struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
0544 void *vmap = map->vaddr;
0545 unsigned int dst_pitch;
0546 int ret = 0;
0547 u8 *buf = NULL;
0548
0549
0550 rect->y1 = round_down(rect->y1, 8);
0551 rect->y2 = min_t(unsigned int, round_up(rect->y2, 8), ssd130x->height);
0552
0553 dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
0554 buf = kcalloc(dst_pitch, drm_rect_height(rect), GFP_KERNEL);
0555 if (!buf)
0556 return -ENOMEM;
0557
0558 drm_fb_xrgb8888_to_mono(buf, dst_pitch, vmap, fb, rect);
0559
0560 ssd130x_update_rect(ssd130x, buf, rect);
0561
0562 kfree(buf);
0563
0564 return ret;
0565 }
0566
0567 static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
0568 const struct drm_display_mode *mode)
0569 {
0570 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
0571
0572 if (mode->hdisplay != ssd130x->mode.hdisplay &&
0573 mode->vdisplay != ssd130x->mode.vdisplay)
0574 return MODE_ONE_SIZE;
0575
0576 if (mode->hdisplay != ssd130x->mode.hdisplay)
0577 return MODE_ONE_WIDTH;
0578
0579 if (mode->vdisplay != ssd130x->mode.vdisplay)
0580 return MODE_ONE_HEIGHT;
0581
0582 return MODE_OK;
0583 }
0584
0585 static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe,
0586 struct drm_crtc_state *crtc_state,
0587 struct drm_plane_state *plane_state)
0588 {
0589 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
0590 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
0591 struct drm_device *drm = &ssd130x->drm;
0592 int idx, ret;
0593
0594 ret = ssd130x_power_on(ssd130x);
0595 if (ret)
0596 return;
0597
0598 ret = ssd130x_init(ssd130x);
0599 if (ret)
0600 goto out_power_off;
0601
0602 if (!drm_dev_enter(drm, &idx))
0603 goto out_power_off;
0604
0605 ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst);
0606
0607 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
0608
0609 backlight_enable(ssd130x->bl_dev);
0610
0611 drm_dev_exit(idx);
0612
0613 return;
0614 out_power_off:
0615 ssd130x_power_off(ssd130x);
0616 }
0617
0618 static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe)
0619 {
0620 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
0621 struct drm_device *drm = &ssd130x->drm;
0622 int idx;
0623
0624 if (!drm_dev_enter(drm, &idx))
0625 return;
0626
0627 ssd130x_clear_screen(ssd130x);
0628
0629 backlight_disable(ssd130x->bl_dev);
0630
0631 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF);
0632
0633 ssd130x_power_off(ssd130x);
0634
0635 drm_dev_exit(idx);
0636 }
0637
0638 static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe,
0639 struct drm_plane_state *old_plane_state)
0640 {
0641 struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
0642 struct drm_plane_state *plane_state = pipe->plane.state;
0643 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
0644 struct drm_framebuffer *fb = plane_state->fb;
0645 struct drm_device *drm = &ssd130x->drm;
0646 struct drm_rect src_clip, dst_clip;
0647 int idx;
0648
0649 if (!fb)
0650 return;
0651
0652 if (!pipe->crtc.state->active)
0653 return;
0654
0655 if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip))
0656 return;
0657
0658 dst_clip = plane_state->dst;
0659 if (!drm_rect_intersect(&dst_clip, &src_clip))
0660 return;
0661
0662 if (!drm_dev_enter(drm, &idx))
0663 return;
0664
0665 ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip);
0666
0667 drm_dev_exit(idx);
0668 }
0669
0670 static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = {
0671 .mode_valid = ssd130x_display_pipe_mode_valid,
0672 .enable = ssd130x_display_pipe_enable,
0673 .disable = ssd130x_display_pipe_disable,
0674 .update = ssd130x_display_pipe_update,
0675 DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
0676 };
0677
0678 static int ssd130x_connector_get_modes(struct drm_connector *connector)
0679 {
0680 struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev);
0681 struct drm_display_mode *mode;
0682 struct device *dev = ssd130x->dev;
0683
0684 mode = drm_mode_duplicate(connector->dev, &ssd130x->mode);
0685 if (!mode) {
0686 dev_err(dev, "Failed to duplicated mode\n");
0687 return 0;
0688 }
0689
0690 drm_mode_probed_add(connector, mode);
0691 drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay);
0692
0693
0694 return 1;
0695 }
0696
0697 static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = {
0698 .get_modes = ssd130x_connector_get_modes,
0699 };
0700
0701 static const struct drm_connector_funcs ssd130x_connector_funcs = {
0702 .reset = drm_atomic_helper_connector_reset,
0703 .fill_modes = drm_helper_probe_single_connector_modes,
0704 .destroy = drm_connector_cleanup,
0705 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
0706 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
0707 };
0708
0709 static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = {
0710 .fb_create = drm_gem_fb_create_with_dirty,
0711 .atomic_check = drm_atomic_helper_check,
0712 .atomic_commit = drm_atomic_helper_commit,
0713 };
0714
0715 static const uint32_t ssd130x_formats[] = {
0716 DRM_FORMAT_XRGB8888,
0717 };
0718
0719 DEFINE_DRM_GEM_FOPS(ssd130x_fops);
0720
0721 static const struct drm_driver ssd130x_drm_driver = {
0722 DRM_GEM_SHMEM_DRIVER_OPS,
0723 .name = DRIVER_NAME,
0724 .desc = DRIVER_DESC,
0725 .date = DRIVER_DATE,
0726 .major = DRIVER_MAJOR,
0727 .minor = DRIVER_MINOR,
0728 .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
0729 .fops = &ssd130x_fops,
0730 };
0731
0732 static int ssd130x_update_bl(struct backlight_device *bdev)
0733 {
0734 struct ssd130x_device *ssd130x = bl_get_data(bdev);
0735 int brightness = backlight_get_brightness(bdev);
0736 int ret;
0737
0738 ssd130x->contrast = brightness;
0739
0740 ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_CONTRAST);
0741 if (ret < 0)
0742 return ret;
0743
0744 ret = ssd130x_write_cmd(ssd130x, 1, ssd130x->contrast);
0745 if (ret < 0)
0746 return ret;
0747
0748 return 0;
0749 }
0750
0751 static const struct backlight_ops ssd130xfb_bl_ops = {
0752 .update_status = ssd130x_update_bl,
0753 };
0754
0755 static void ssd130x_parse_properties(struct ssd130x_device *ssd130x)
0756 {
0757 struct device *dev = ssd130x->dev;
0758
0759 if (device_property_read_u32(dev, "solomon,width", &ssd130x->width))
0760 ssd130x->width = 96;
0761
0762 if (device_property_read_u32(dev, "solomon,height", &ssd130x->height))
0763 ssd130x->height = 16;
0764
0765 if (device_property_read_u32(dev, "solomon,page-offset", &ssd130x->page_offset))
0766 ssd130x->page_offset = 1;
0767
0768 if (device_property_read_u32(dev, "solomon,col-offset", &ssd130x->col_offset))
0769 ssd130x->col_offset = 0;
0770
0771 if (device_property_read_u32(dev, "solomon,com-offset", &ssd130x->com_offset))
0772 ssd130x->com_offset = 0;
0773
0774 if (device_property_read_u32(dev, "solomon,prechargep1", &ssd130x->prechargep1))
0775 ssd130x->prechargep1 = 2;
0776
0777 if (device_property_read_u32(dev, "solomon,prechargep2", &ssd130x->prechargep2))
0778 ssd130x->prechargep2 = 2;
0779
0780 if (!device_property_read_u8_array(dev, "solomon,lookup-table",
0781 ssd130x->lookup_table,
0782 ARRAY_SIZE(ssd130x->lookup_table)))
0783 ssd130x->lookup_table_set = 1;
0784
0785 ssd130x->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap");
0786 ssd130x->com_seq = device_property_read_bool(dev, "solomon,com-seq");
0787 ssd130x->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap");
0788 ssd130x->com_invdir = device_property_read_bool(dev, "solomon,com-invdir");
0789 ssd130x->area_color_enable =
0790 device_property_read_bool(dev, "solomon,area-color-enable");
0791 ssd130x->low_power = device_property_read_bool(dev, "solomon,low-power");
0792
0793 ssd130x->contrast = 127;
0794 ssd130x->vcomh = ssd130x->device_info->default_vcomh;
0795
0796
0797 if (device_property_read_u32(dev, "solomon,dclk-div", &ssd130x->dclk_div))
0798 ssd130x->dclk_div = ssd130x->device_info->default_dclk_div;
0799 if (device_property_read_u32(dev, "solomon,dclk-frq", &ssd130x->dclk_frq))
0800 ssd130x->dclk_frq = ssd130x->device_info->default_dclk_frq;
0801 }
0802
0803 static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
0804 {
0805 struct drm_display_mode *mode = &ssd130x->mode;
0806 struct device *dev = ssd130x->dev;
0807 struct drm_device *drm = &ssd130x->drm;
0808 unsigned long max_width, max_height;
0809 int ret;
0810
0811 ret = drmm_mode_config_init(drm);
0812 if (ret) {
0813 dev_err(dev, "DRM mode config init failed: %d\n", ret);
0814 return ret;
0815 }
0816
0817 mode->type = DRM_MODE_TYPE_DRIVER;
0818 mode->clock = 1;
0819 mode->hdisplay = mode->htotal = ssd130x->width;
0820 mode->hsync_start = mode->hsync_end = ssd130x->width;
0821 mode->vdisplay = mode->vtotal = ssd130x->height;
0822 mode->vsync_start = mode->vsync_end = ssd130x->height;
0823 mode->width_mm = 27;
0824 mode->height_mm = 27;
0825
0826 max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH);
0827 max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT);
0828
0829 drm->mode_config.min_width = mode->hdisplay;
0830 drm->mode_config.max_width = max_width;
0831 drm->mode_config.min_height = mode->vdisplay;
0832 drm->mode_config.max_height = max_height;
0833 drm->mode_config.preferred_depth = 32;
0834 drm->mode_config.funcs = &ssd130x_mode_config_funcs;
0835
0836 ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs,
0837 DRM_MODE_CONNECTOR_Unknown);
0838 if (ret) {
0839 dev_err(dev, "DRM connector init failed: %d\n", ret);
0840 return ret;
0841 }
0842
0843 drm_connector_helper_add(&ssd130x->connector, &ssd130x_connector_helper_funcs);
0844
0845 ret = drm_simple_display_pipe_init(drm, &ssd130x->pipe, &ssd130x_pipe_funcs,
0846 ssd130x_formats, ARRAY_SIZE(ssd130x_formats),
0847 NULL, &ssd130x->connector);
0848 if (ret) {
0849 dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret);
0850 return ret;
0851 }
0852
0853 drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane);
0854
0855 drm_mode_config_reset(drm);
0856
0857 return 0;
0858 }
0859
0860 static int ssd130x_get_resources(struct ssd130x_device *ssd130x)
0861 {
0862 struct device *dev = ssd130x->dev;
0863
0864 ssd130x->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
0865 if (IS_ERR(ssd130x->reset))
0866 return dev_err_probe(dev, PTR_ERR(ssd130x->reset),
0867 "Failed to get reset gpio\n");
0868
0869 ssd130x->vcc_reg = devm_regulator_get(dev, "vcc");
0870 if (IS_ERR(ssd130x->vcc_reg))
0871 return dev_err_probe(dev, PTR_ERR(ssd130x->vcc_reg),
0872 "Failed to get VCC regulator\n");
0873
0874 return 0;
0875 }
0876
0877 struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)
0878 {
0879 struct ssd130x_device *ssd130x;
0880 struct backlight_device *bl;
0881 struct drm_device *drm;
0882 int ret;
0883
0884 ssd130x = devm_drm_dev_alloc(dev, &ssd130x_drm_driver,
0885 struct ssd130x_device, drm);
0886 if (IS_ERR(ssd130x))
0887 return ERR_PTR(dev_err_probe(dev, PTR_ERR(ssd130x),
0888 "Failed to allocate DRM device\n"));
0889
0890 drm = &ssd130x->drm;
0891
0892 ssd130x->dev = dev;
0893 ssd130x->regmap = regmap;
0894 ssd130x->device_info = device_get_match_data(dev);
0895
0896 if (ssd130x->device_info->page_mode_only)
0897 ssd130x->page_address_mode = 1;
0898
0899 ssd130x_parse_properties(ssd130x);
0900
0901 ret = ssd130x_get_resources(ssd130x);
0902 if (ret)
0903 return ERR_PTR(ret);
0904
0905 bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x,
0906 &ssd130xfb_bl_ops, NULL);
0907 if (IS_ERR(bl))
0908 return ERR_PTR(dev_err_probe(dev, PTR_ERR(bl),
0909 "Unable to register backlight device\n"));
0910
0911 bl->props.brightness = ssd130x->contrast;
0912 bl->props.max_brightness = MAX_CONTRAST;
0913 ssd130x->bl_dev = bl;
0914
0915 ret = ssd130x_init_modeset(ssd130x);
0916 if (ret)
0917 return ERR_PTR(ret);
0918
0919 ret = drm_dev_register(drm, 0);
0920 if (ret)
0921 return ERR_PTR(dev_err_probe(dev, ret, "DRM device register failed\n"));
0922
0923 drm_fbdev_generic_setup(drm, 0);
0924
0925 return ssd130x;
0926 }
0927 EXPORT_SYMBOL_GPL(ssd130x_probe);
0928
0929 void ssd130x_remove(struct ssd130x_device *ssd130x)
0930 {
0931 drm_dev_unplug(&ssd130x->drm);
0932 }
0933 EXPORT_SYMBOL_GPL(ssd130x_remove);
0934
0935 void ssd130x_shutdown(struct ssd130x_device *ssd130x)
0936 {
0937 drm_atomic_helper_shutdown(&ssd130x->drm);
0938 }
0939 EXPORT_SYMBOL_GPL(ssd130x_shutdown);
0940
0941 MODULE_DESCRIPTION(DRIVER_DESC);
0942 MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");
0943 MODULE_LICENSE("GPL v2");