Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * DRM driver for Solomon SSD130x OLED displays
0004  *
0005  * Copyright 2022 Red Hat Inc.
0006  * Author: Javier Martinez Canillas <javierm@redhat.com>
0007  *
0008  * Based on drivers/video/fbdev/ssd1307fb.c
0009  * Copyright 2012 Free Electrons
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  * Helper to write data (SSD130X_DATA) to the device.
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  * Helper to write command (SSD130X_COMMAND). The fist variadic argument
0143  * is the command to write and the following are the command options.
0144  *
0145  * Note that the ssd130x protocol requires each command and option to be
0146  * written as a SSD130X_COMMAND device register value. That is why a call
0147  * to regmap_write(..., SSD130X_COMMAND, ...) is done for each argument.
0148  */
0149 static int ssd130x_write_cmd(struct ssd130x_device *ssd130x, int count,
0150                  /* u8 cmd, u8 option, ... */...)
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 /* Set address range for horizontal/vertical addressing modes */
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 /* Set page and column start address for page addressing mode */
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     /* Enable the PWM */
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     /* Reset the screen */
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     /* Set initial contrast */
0303     ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CONTRAST, ssd130x->contrast);
0304     if (ret < 0)
0305         return ret;
0306 
0307     /* Set segment re-map */
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     /* Set COM direction */
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     /* Set multiplex ratio value */
0322     ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
0323     if (ret < 0)
0324         return ret;
0325 
0326     /* set display offset value */
0327     ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset);
0328     if (ret < 0)
0329         return ret;
0330 
0331     /* Set clock frequency */
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     /* Set Area Color Mode ON/OFF & Low Power Display Mode */
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     /* Set precharge period in number of ticks from the internal clock */
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     /* Set COM pins configuration */
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     /* Set VCOMH */
0369     ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH, ssd130x->vcomh);
0370     if (ret < 0)
0371         return ret;
0372 
0373     /* Turn on the DC-DC Charge Pump */
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     /* Set lookup table */
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     /* Switch to page addressing mode */
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     /* Switch to horizontal addressing mode */
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      * The screen is divided in pages, each having a height of 8
0436      * pixels, and the width of the screen. When sending a byte of
0437      * data to the controller, it gives the 8 bits for the current
0438      * column. I.e, the first byte are the 8 bits of the first
0439      * column, then the 8 bits for the second column, etc.
0440      *
0441      *
0442      * Representation of the screen, assuming it is 5 bits
0443      * wide. Each letter-number combination is a bit that controls
0444      * one pixel.
0445      *
0446      * A0 A1 A2 A3 A4
0447      * B0 B1 B2 B3 B4
0448      * C0 C1 C2 C3 C4
0449      * D0 D1 D2 D3 D4
0450      * E0 E1 E2 E3 E4
0451      * F0 F1 F2 F3 F4
0452      * G0 G1 G2 G3 G4
0453      * H0 H1 H2 H3 H4
0454      *
0455      * If you want to update this screen, you need to send 5 bytes:
0456      *  (1) A0 B0 C0 D0 E0 F0 G0 H0
0457      *  (2) A1 B1 C1 D1 E1 F1 G1 H1
0458      *  (3) A2 B2 C2 D2 E2 F2 G2 H2
0459      *  (4) A3 B3 C3 D3 E3 F3 G3 H3
0460      *  (5) A4 B4 C4 D4 E4 F4 G4 H4
0461      */
0462 
0463     if (!ssd130x->page_address_mode) {
0464         /* Set address range for horizontal addressing mode */
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         /* Last page may be partial */
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          * In page addressing mode, the start address needs to be reset,
0494          * and each page then needs to be written out separately.
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     /* Write out update in one go if we aren't using page addressing mode */
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; /* TODO: Use mapping abstraction properly */
0545     unsigned int dst_pitch;
0546     int ret = 0;
0547     u8 *buf = NULL;
0548 
0549     /* Align y to display page boundaries */
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     /* There is only a single mode */
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     /* Setup display timing */
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");