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