1*9b8f3200SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0-or-later 2*9b8f3200SMarcus Folkesson /* 3*9b8f3200SMarcus Folkesson * DRM driver for Sitronix ST7586 panels 4*9b8f3200SMarcus Folkesson * 5*9b8f3200SMarcus Folkesson * Copyright 2017 David Lechner <david@lechnology.com> 6*9b8f3200SMarcus Folkesson */ 7*9b8f3200SMarcus Folkesson 8*9b8f3200SMarcus Folkesson #include <linux/delay.h> 9*9b8f3200SMarcus Folkesson #include <linux/gpio/consumer.h> 10*9b8f3200SMarcus Folkesson #include <linux/module.h> 11*9b8f3200SMarcus Folkesson #include <linux/property.h> 12*9b8f3200SMarcus Folkesson #include <linux/spi/spi.h> 13*9b8f3200SMarcus Folkesson #include <video/mipi_display.h> 14*9b8f3200SMarcus Folkesson 15*9b8f3200SMarcus Folkesson #include <drm/clients/drm_client_setup.h> 16*9b8f3200SMarcus Folkesson #include <drm/drm_atomic_helper.h> 17*9b8f3200SMarcus Folkesson #include <drm/drm_damage_helper.h> 18*9b8f3200SMarcus Folkesson #include <drm/drm_drv.h> 19*9b8f3200SMarcus Folkesson #include <drm/drm_fb_dma_helper.h> 20*9b8f3200SMarcus Folkesson #include <drm/drm_fbdev_dma.h> 21*9b8f3200SMarcus Folkesson #include <drm/drm_format_helper.h> 22*9b8f3200SMarcus Folkesson #include <drm/drm_framebuffer.h> 23*9b8f3200SMarcus Folkesson #include <drm/drm_gem_atomic_helper.h> 24*9b8f3200SMarcus Folkesson #include <drm/drm_gem_dma_helper.h> 25*9b8f3200SMarcus Folkesson #include <drm/drm_gem_framebuffer_helper.h> 26*9b8f3200SMarcus Folkesson #include <drm/drm_managed.h> 27*9b8f3200SMarcus Folkesson #include <drm/drm_mipi_dbi.h> 28*9b8f3200SMarcus Folkesson #include <drm/drm_rect.h> 29*9b8f3200SMarcus Folkesson 30*9b8f3200SMarcus Folkesson /* controller-specific commands */ 31*9b8f3200SMarcus Folkesson #define ST7586_DISP_MODE_GRAY 0x38 32*9b8f3200SMarcus Folkesson #define ST7586_DISP_MODE_MONO 0x39 33*9b8f3200SMarcus Folkesson #define ST7586_ENABLE_DDRAM 0x3a 34*9b8f3200SMarcus Folkesson #define ST7586_SET_DISP_DUTY 0xb0 35*9b8f3200SMarcus Folkesson #define ST7586_SET_PART_DISP 0xb4 36*9b8f3200SMarcus Folkesson #define ST7586_SET_NLINE_INV 0xb5 37*9b8f3200SMarcus Folkesson #define ST7586_SET_VOP 0xc0 38*9b8f3200SMarcus Folkesson #define ST7586_SET_BIAS_SYSTEM 0xc3 39*9b8f3200SMarcus Folkesson #define ST7586_SET_BOOST_LEVEL 0xc4 40*9b8f3200SMarcus Folkesson #define ST7586_SET_VOP_OFFSET 0xc7 41*9b8f3200SMarcus Folkesson #define ST7586_ENABLE_ANALOG 0xd0 42*9b8f3200SMarcus Folkesson #define ST7586_AUTO_READ_CTRL 0xd7 43*9b8f3200SMarcus Folkesson #define ST7586_OTP_RW_CTRL 0xe0 44*9b8f3200SMarcus Folkesson #define ST7586_OTP_CTRL_OUT 0xe1 45*9b8f3200SMarcus Folkesson #define ST7586_OTP_READ 0xe3 46*9b8f3200SMarcus Folkesson 47*9b8f3200SMarcus Folkesson #define ST7586_DISP_CTRL_MX BIT(6) 48*9b8f3200SMarcus Folkesson #define ST7586_DISP_CTRL_MY BIT(7) 49*9b8f3200SMarcus Folkesson 50*9b8f3200SMarcus Folkesson /* 51*9b8f3200SMarcus Folkesson * The ST7586 controller has an unusual pixel format where 2bpp grayscale is 52*9b8f3200SMarcus Folkesson * packed 3 pixels per byte with the first two pixels using 3 bits and the 3rd 53*9b8f3200SMarcus Folkesson * pixel using only 2 bits. 54*9b8f3200SMarcus Folkesson * 55*9b8f3200SMarcus Folkesson * | D7 | D6 | D5 || | || 2bpp | 56*9b8f3200SMarcus Folkesson * | (D4) | (D3) | (D2) || D1 | D0 || GRAY | 57*9b8f3200SMarcus Folkesson * +------+------+------++------+------++------+ 58*9b8f3200SMarcus Folkesson * | 1 | 1 | 1 || 1 | 1 || 0 0 | black 59*9b8f3200SMarcus Folkesson * | 1 | 0 | 0 || 1 | 0 || 0 1 | dark gray 60*9b8f3200SMarcus Folkesson * | 0 | 1 | 0 || 0 | 1 || 1 0 | light gray 61*9b8f3200SMarcus Folkesson * | 0 | 0 | 0 || 0 | 0 || 1 1 | white 62*9b8f3200SMarcus Folkesson */ 63*9b8f3200SMarcus Folkesson 64*9b8f3200SMarcus Folkesson static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 }; 65*9b8f3200SMarcus Folkesson 66*9b8f3200SMarcus Folkesson static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, 67*9b8f3200SMarcus Folkesson struct drm_framebuffer *fb, 68*9b8f3200SMarcus Folkesson struct drm_rect *clip, 69*9b8f3200SMarcus Folkesson struct drm_format_conv_state *fmtcnv_state) 70*9b8f3200SMarcus Folkesson { 71*9b8f3200SMarcus Folkesson size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1); 72*9b8f3200SMarcus Folkesson unsigned int x, y; 73*9b8f3200SMarcus Folkesson u8 *src, *buf, val; 74*9b8f3200SMarcus Folkesson struct iosys_map dst_map, vmap; 75*9b8f3200SMarcus Folkesson 76*9b8f3200SMarcus Folkesson buf = kmalloc(len, GFP_KERNEL); 77*9b8f3200SMarcus Folkesson if (!buf) 78*9b8f3200SMarcus Folkesson return; 79*9b8f3200SMarcus Folkesson 80*9b8f3200SMarcus Folkesson iosys_map_set_vaddr(&dst_map, buf); 81*9b8f3200SMarcus Folkesson iosys_map_set_vaddr(&vmap, vaddr); 82*9b8f3200SMarcus Folkesson drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, clip, fmtcnv_state); 83*9b8f3200SMarcus Folkesson src = buf; 84*9b8f3200SMarcus Folkesson 85*9b8f3200SMarcus Folkesson for (y = clip->y1; y < clip->y2; y++) { 86*9b8f3200SMarcus Folkesson for (x = clip->x1; x < clip->x2; x += 3) { 87*9b8f3200SMarcus Folkesson val = st7586_lookup[*src++ >> 6] << 5; 88*9b8f3200SMarcus Folkesson val |= st7586_lookup[*src++ >> 6] << 2; 89*9b8f3200SMarcus Folkesson val |= st7586_lookup[*src++ >> 6] >> 1; 90*9b8f3200SMarcus Folkesson *dst++ = val; 91*9b8f3200SMarcus Folkesson } 92*9b8f3200SMarcus Folkesson } 93*9b8f3200SMarcus Folkesson 94*9b8f3200SMarcus Folkesson kfree(buf); 95*9b8f3200SMarcus Folkesson } 96*9b8f3200SMarcus Folkesson 97*9b8f3200SMarcus Folkesson static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, 98*9b8f3200SMarcus Folkesson struct drm_rect *clip, struct drm_format_conv_state *fmtcnv_state) 99*9b8f3200SMarcus Folkesson { 100*9b8f3200SMarcus Folkesson int ret; 101*9b8f3200SMarcus Folkesson 102*9b8f3200SMarcus Folkesson ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 103*9b8f3200SMarcus Folkesson if (ret) 104*9b8f3200SMarcus Folkesson return ret; 105*9b8f3200SMarcus Folkesson 106*9b8f3200SMarcus Folkesson st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip, fmtcnv_state); 107*9b8f3200SMarcus Folkesson 108*9b8f3200SMarcus Folkesson drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 109*9b8f3200SMarcus Folkesson 110*9b8f3200SMarcus Folkesson return 0; 111*9b8f3200SMarcus Folkesson } 112*9b8f3200SMarcus Folkesson 113*9b8f3200SMarcus Folkesson static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, 114*9b8f3200SMarcus Folkesson struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) 115*9b8f3200SMarcus Folkesson { 116*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); 117*9b8f3200SMarcus Folkesson struct mipi_dbi *dbi = &dbidev->dbi; 118*9b8f3200SMarcus Folkesson int start, end, ret = 0; 119*9b8f3200SMarcus Folkesson 120*9b8f3200SMarcus Folkesson /* 3 pixels per byte, so grow clip to nearest multiple of 3 */ 121*9b8f3200SMarcus Folkesson rect->x1 = rounddown(rect->x1, 3); 122*9b8f3200SMarcus Folkesson rect->x2 = roundup(rect->x2, 3); 123*9b8f3200SMarcus Folkesson 124*9b8f3200SMarcus Folkesson DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); 125*9b8f3200SMarcus Folkesson 126*9b8f3200SMarcus Folkesson ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect, fmtcnv_state); 127*9b8f3200SMarcus Folkesson if (ret) 128*9b8f3200SMarcus Folkesson goto err_msg; 129*9b8f3200SMarcus Folkesson 130*9b8f3200SMarcus Folkesson /* Pixels are packed 3 per byte */ 131*9b8f3200SMarcus Folkesson start = rect->x1 / 3; 132*9b8f3200SMarcus Folkesson end = rect->x2 / 3; 133*9b8f3200SMarcus Folkesson 134*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, 135*9b8f3200SMarcus Folkesson (start >> 8) & 0xFF, start & 0xFF, 136*9b8f3200SMarcus Folkesson (end >> 8) & 0xFF, (end - 1) & 0xFF); 137*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, 138*9b8f3200SMarcus Folkesson (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF, 139*9b8f3200SMarcus Folkesson (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF); 140*9b8f3200SMarcus Folkesson 141*9b8f3200SMarcus Folkesson ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, 142*9b8f3200SMarcus Folkesson (u8 *)dbidev->tx_buf, 143*9b8f3200SMarcus Folkesson (end - start) * (rect->y2 - rect->y1)); 144*9b8f3200SMarcus Folkesson err_msg: 145*9b8f3200SMarcus Folkesson if (ret) 146*9b8f3200SMarcus Folkesson dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); 147*9b8f3200SMarcus Folkesson } 148*9b8f3200SMarcus Folkesson 149*9b8f3200SMarcus Folkesson static void st7586_pipe_update(struct drm_simple_display_pipe *pipe, 150*9b8f3200SMarcus Folkesson struct drm_plane_state *old_state) 151*9b8f3200SMarcus Folkesson { 152*9b8f3200SMarcus Folkesson struct drm_plane_state *state = pipe->plane.state; 153*9b8f3200SMarcus Folkesson struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); 154*9b8f3200SMarcus Folkesson struct drm_framebuffer *fb = state->fb; 155*9b8f3200SMarcus Folkesson struct drm_rect rect; 156*9b8f3200SMarcus Folkesson int idx; 157*9b8f3200SMarcus Folkesson 158*9b8f3200SMarcus Folkesson if (!pipe->crtc.state->active) 159*9b8f3200SMarcus Folkesson return; 160*9b8f3200SMarcus Folkesson 161*9b8f3200SMarcus Folkesson if (!drm_dev_enter(fb->dev, &idx)) 162*9b8f3200SMarcus Folkesson return; 163*9b8f3200SMarcus Folkesson 164*9b8f3200SMarcus Folkesson if (drm_atomic_helper_damage_merged(old_state, state, &rect)) 165*9b8f3200SMarcus Folkesson st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect, 166*9b8f3200SMarcus Folkesson &shadow_plane_state->fmtcnv_state); 167*9b8f3200SMarcus Folkesson 168*9b8f3200SMarcus Folkesson drm_dev_exit(idx); 169*9b8f3200SMarcus Folkesson } 170*9b8f3200SMarcus Folkesson 171*9b8f3200SMarcus Folkesson static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, 172*9b8f3200SMarcus Folkesson struct drm_crtc_state *crtc_state, 173*9b8f3200SMarcus Folkesson struct drm_plane_state *plane_state) 174*9b8f3200SMarcus Folkesson { 175*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 176*9b8f3200SMarcus Folkesson struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 177*9b8f3200SMarcus Folkesson struct drm_framebuffer *fb = plane_state->fb; 178*9b8f3200SMarcus Folkesson struct mipi_dbi *dbi = &dbidev->dbi; 179*9b8f3200SMarcus Folkesson struct drm_rect rect = { 180*9b8f3200SMarcus Folkesson .x1 = 0, 181*9b8f3200SMarcus Folkesson .x2 = fb->width, 182*9b8f3200SMarcus Folkesson .y1 = 0, 183*9b8f3200SMarcus Folkesson .y2 = fb->height, 184*9b8f3200SMarcus Folkesson }; 185*9b8f3200SMarcus Folkesson int idx, ret; 186*9b8f3200SMarcus Folkesson u8 addr_mode; 187*9b8f3200SMarcus Folkesson 188*9b8f3200SMarcus Folkesson if (!drm_dev_enter(pipe->crtc.dev, &idx)) 189*9b8f3200SMarcus Folkesson return; 190*9b8f3200SMarcus Folkesson 191*9b8f3200SMarcus Folkesson DRM_DEBUG_KMS("\n"); 192*9b8f3200SMarcus Folkesson 193*9b8f3200SMarcus Folkesson ret = mipi_dbi_poweron_reset(dbidev); 194*9b8f3200SMarcus Folkesson if (ret) 195*9b8f3200SMarcus Folkesson goto out_exit; 196*9b8f3200SMarcus Folkesson 197*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_AUTO_READ_CTRL, 0x9f); 198*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_OTP_RW_CTRL, 0x00); 199*9b8f3200SMarcus Folkesson 200*9b8f3200SMarcus Folkesson msleep(10); 201*9b8f3200SMarcus Folkesson 202*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_OTP_READ); 203*9b8f3200SMarcus Folkesson 204*9b8f3200SMarcus Folkesson msleep(20); 205*9b8f3200SMarcus Folkesson 206*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_OTP_CTRL_OUT); 207*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); 208*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); 209*9b8f3200SMarcus Folkesson 210*9b8f3200SMarcus Folkesson msleep(50); 211*9b8f3200SMarcus Folkesson 212*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_VOP_OFFSET, 0x00); 213*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_VOP, 0xe3, 0x00); 214*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_BIAS_SYSTEM, 0x02); 215*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_BOOST_LEVEL, 0x04); 216*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_ENABLE_ANALOG, 0x1d); 217*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_NLINE_INV, 0x00); 218*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_DISP_MODE_GRAY); 219*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_ENABLE_DDRAM, 0x02); 220*9b8f3200SMarcus Folkesson 221*9b8f3200SMarcus Folkesson switch (dbidev->rotation) { 222*9b8f3200SMarcus Folkesson default: 223*9b8f3200SMarcus Folkesson addr_mode = 0x00; 224*9b8f3200SMarcus Folkesson break; 225*9b8f3200SMarcus Folkesson case 90: 226*9b8f3200SMarcus Folkesson addr_mode = ST7586_DISP_CTRL_MY; 227*9b8f3200SMarcus Folkesson break; 228*9b8f3200SMarcus Folkesson case 180: 229*9b8f3200SMarcus Folkesson addr_mode = ST7586_DISP_CTRL_MX | ST7586_DISP_CTRL_MY; 230*9b8f3200SMarcus Folkesson break; 231*9b8f3200SMarcus Folkesson case 270: 232*9b8f3200SMarcus Folkesson addr_mode = ST7586_DISP_CTRL_MX; 233*9b8f3200SMarcus Folkesson break; 234*9b8f3200SMarcus Folkesson } 235*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); 236*9b8f3200SMarcus Folkesson 237*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_DISP_DUTY, 0x7f); 238*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, ST7586_SET_PART_DISP, 0xa0); 239*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x00, 0x77); 240*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE); 241*9b8f3200SMarcus Folkesson 242*9b8f3200SMarcus Folkesson msleep(100); 243*9b8f3200SMarcus Folkesson 244*9b8f3200SMarcus Folkesson st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect, 245*9b8f3200SMarcus Folkesson &shadow_plane_state->fmtcnv_state); 246*9b8f3200SMarcus Folkesson 247*9b8f3200SMarcus Folkesson mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 248*9b8f3200SMarcus Folkesson out_exit: 249*9b8f3200SMarcus Folkesson drm_dev_exit(idx); 250*9b8f3200SMarcus Folkesson } 251*9b8f3200SMarcus Folkesson 252*9b8f3200SMarcus Folkesson static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe) 253*9b8f3200SMarcus Folkesson { 254*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 255*9b8f3200SMarcus Folkesson 256*9b8f3200SMarcus Folkesson /* 257*9b8f3200SMarcus Folkesson * This callback is not protected by drm_dev_enter/exit since we want to 258*9b8f3200SMarcus Folkesson * turn off the display on regular driver unload. It's highly unlikely 259*9b8f3200SMarcus Folkesson * that the underlying SPI controller is gone should this be called after 260*9b8f3200SMarcus Folkesson * unplug. 261*9b8f3200SMarcus Folkesson */ 262*9b8f3200SMarcus Folkesson 263*9b8f3200SMarcus Folkesson DRM_DEBUG_KMS("\n"); 264*9b8f3200SMarcus Folkesson 265*9b8f3200SMarcus Folkesson mipi_dbi_command(&dbidev->dbi, MIPI_DCS_SET_DISPLAY_OFF); 266*9b8f3200SMarcus Folkesson } 267*9b8f3200SMarcus Folkesson 268*9b8f3200SMarcus Folkesson static const u32 st7586_formats[] = { 269*9b8f3200SMarcus Folkesson DRM_FORMAT_XRGB8888, 270*9b8f3200SMarcus Folkesson }; 271*9b8f3200SMarcus Folkesson 272*9b8f3200SMarcus Folkesson static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = { 273*9b8f3200SMarcus Folkesson .mode_valid = mipi_dbi_pipe_mode_valid, 274*9b8f3200SMarcus Folkesson .enable = st7586_pipe_enable, 275*9b8f3200SMarcus Folkesson .disable = st7586_pipe_disable, 276*9b8f3200SMarcus Folkesson .update = st7586_pipe_update, 277*9b8f3200SMarcus Folkesson .begin_fb_access = mipi_dbi_pipe_begin_fb_access, 278*9b8f3200SMarcus Folkesson .end_fb_access = mipi_dbi_pipe_end_fb_access, 279*9b8f3200SMarcus Folkesson .reset_plane = mipi_dbi_pipe_reset_plane, 280*9b8f3200SMarcus Folkesson .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, 281*9b8f3200SMarcus Folkesson .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state, 282*9b8f3200SMarcus Folkesson }; 283*9b8f3200SMarcus Folkesson 284*9b8f3200SMarcus Folkesson static const struct drm_display_mode st7586_mode = { 285*9b8f3200SMarcus Folkesson DRM_SIMPLE_MODE(178, 128, 37, 27), 286*9b8f3200SMarcus Folkesson }; 287*9b8f3200SMarcus Folkesson 288*9b8f3200SMarcus Folkesson DEFINE_DRM_GEM_DMA_FOPS(st7586_fops); 289*9b8f3200SMarcus Folkesson 290*9b8f3200SMarcus Folkesson static const struct drm_driver st7586_driver = { 291*9b8f3200SMarcus Folkesson .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 292*9b8f3200SMarcus Folkesson .fops = &st7586_fops, 293*9b8f3200SMarcus Folkesson DRM_GEM_DMA_DRIVER_OPS_VMAP, 294*9b8f3200SMarcus Folkesson DRM_FBDEV_DMA_DRIVER_OPS, 295*9b8f3200SMarcus Folkesson .debugfs_init = mipi_dbi_debugfs_init, 296*9b8f3200SMarcus Folkesson .name = "st7586", 297*9b8f3200SMarcus Folkesson .desc = "Sitronix ST7586", 298*9b8f3200SMarcus Folkesson .major = 1, 299*9b8f3200SMarcus Folkesson .minor = 0, 300*9b8f3200SMarcus Folkesson }; 301*9b8f3200SMarcus Folkesson 302*9b8f3200SMarcus Folkesson static const struct of_device_id st7586_of_match[] = { 303*9b8f3200SMarcus Folkesson { .compatible = "lego,ev3-lcd" }, 304*9b8f3200SMarcus Folkesson {}, 305*9b8f3200SMarcus Folkesson }; 306*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(of, st7586_of_match); 307*9b8f3200SMarcus Folkesson 308*9b8f3200SMarcus Folkesson static const struct spi_device_id st7586_id[] = { 309*9b8f3200SMarcus Folkesson { "ev3-lcd", 0 }, 310*9b8f3200SMarcus Folkesson { }, 311*9b8f3200SMarcus Folkesson }; 312*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(spi, st7586_id); 313*9b8f3200SMarcus Folkesson 314*9b8f3200SMarcus Folkesson static int st7586_probe(struct spi_device *spi) 315*9b8f3200SMarcus Folkesson { 316*9b8f3200SMarcus Folkesson struct device *dev = &spi->dev; 317*9b8f3200SMarcus Folkesson struct mipi_dbi_dev *dbidev; 318*9b8f3200SMarcus Folkesson struct drm_device *drm; 319*9b8f3200SMarcus Folkesson struct mipi_dbi *dbi; 320*9b8f3200SMarcus Folkesson struct gpio_desc *a0; 321*9b8f3200SMarcus Folkesson u32 rotation = 0; 322*9b8f3200SMarcus Folkesson size_t bufsize; 323*9b8f3200SMarcus Folkesson int ret; 324*9b8f3200SMarcus Folkesson 325*9b8f3200SMarcus Folkesson dbidev = devm_drm_dev_alloc(dev, &st7586_driver, 326*9b8f3200SMarcus Folkesson struct mipi_dbi_dev, drm); 327*9b8f3200SMarcus Folkesson if (IS_ERR(dbidev)) 328*9b8f3200SMarcus Folkesson return PTR_ERR(dbidev); 329*9b8f3200SMarcus Folkesson 330*9b8f3200SMarcus Folkesson dbi = &dbidev->dbi; 331*9b8f3200SMarcus Folkesson drm = &dbidev->drm; 332*9b8f3200SMarcus Folkesson 333*9b8f3200SMarcus Folkesson bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay; 334*9b8f3200SMarcus Folkesson 335*9b8f3200SMarcus Folkesson dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 336*9b8f3200SMarcus Folkesson if (IS_ERR(dbi->reset)) 337*9b8f3200SMarcus Folkesson return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n"); 338*9b8f3200SMarcus Folkesson 339*9b8f3200SMarcus Folkesson a0 = devm_gpiod_get(dev, "a0", GPIOD_OUT_LOW); 340*9b8f3200SMarcus Folkesson if (IS_ERR(a0)) 341*9b8f3200SMarcus Folkesson return dev_err_probe(dev, PTR_ERR(a0), "Failed to get GPIO 'a0'\n"); 342*9b8f3200SMarcus Folkesson 343*9b8f3200SMarcus Folkesson device_property_read_u32(dev, "rotation", &rotation); 344*9b8f3200SMarcus Folkesson 345*9b8f3200SMarcus Folkesson ret = mipi_dbi_spi_init(spi, dbi, a0); 346*9b8f3200SMarcus Folkesson if (ret) 347*9b8f3200SMarcus Folkesson return ret; 348*9b8f3200SMarcus Folkesson 349*9b8f3200SMarcus Folkesson /* Cannot read from this controller via SPI */ 350*9b8f3200SMarcus Folkesson dbi->read_commands = NULL; 351*9b8f3200SMarcus Folkesson 352*9b8f3200SMarcus Folkesson ret = mipi_dbi_dev_init_with_formats(dbidev, &st7586_pipe_funcs, 353*9b8f3200SMarcus Folkesson st7586_formats, ARRAY_SIZE(st7586_formats), 354*9b8f3200SMarcus Folkesson &st7586_mode, rotation, bufsize); 355*9b8f3200SMarcus Folkesson if (ret) 356*9b8f3200SMarcus Folkesson return ret; 357*9b8f3200SMarcus Folkesson 358*9b8f3200SMarcus Folkesson /* 359*9b8f3200SMarcus Folkesson * we are using 8-bit data, so we are not actually swapping anything, 360*9b8f3200SMarcus Folkesson * but setting mipi->swap_bytes makes mipi_dbi_typec3_command() do the 361*9b8f3200SMarcus Folkesson * right thing and not use 16-bit transfers (which results in swapped 362*9b8f3200SMarcus Folkesson * bytes on little-endian systems and causes out of order data to be 363*9b8f3200SMarcus Folkesson * sent to the display). 364*9b8f3200SMarcus Folkesson */ 365*9b8f3200SMarcus Folkesson dbi->swap_bytes = true; 366*9b8f3200SMarcus Folkesson 367*9b8f3200SMarcus Folkesson drm_mode_config_reset(drm); 368*9b8f3200SMarcus Folkesson 369*9b8f3200SMarcus Folkesson ret = drm_dev_register(drm, 0); 370*9b8f3200SMarcus Folkesson if (ret) 371*9b8f3200SMarcus Folkesson return ret; 372*9b8f3200SMarcus Folkesson 373*9b8f3200SMarcus Folkesson spi_set_drvdata(spi, drm); 374*9b8f3200SMarcus Folkesson 375*9b8f3200SMarcus Folkesson drm_client_setup(drm, NULL); 376*9b8f3200SMarcus Folkesson 377*9b8f3200SMarcus Folkesson return 0; 378*9b8f3200SMarcus Folkesson } 379*9b8f3200SMarcus Folkesson 380*9b8f3200SMarcus Folkesson static void st7586_remove(struct spi_device *spi) 381*9b8f3200SMarcus Folkesson { 382*9b8f3200SMarcus Folkesson struct drm_device *drm = spi_get_drvdata(spi); 383*9b8f3200SMarcus Folkesson 384*9b8f3200SMarcus Folkesson drm_dev_unplug(drm); 385*9b8f3200SMarcus Folkesson drm_atomic_helper_shutdown(drm); 386*9b8f3200SMarcus Folkesson } 387*9b8f3200SMarcus Folkesson 388*9b8f3200SMarcus Folkesson static void st7586_shutdown(struct spi_device *spi) 389*9b8f3200SMarcus Folkesson { 390*9b8f3200SMarcus Folkesson drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 391*9b8f3200SMarcus Folkesson } 392*9b8f3200SMarcus Folkesson 393*9b8f3200SMarcus Folkesson static struct spi_driver st7586_spi_driver = { 394*9b8f3200SMarcus Folkesson .driver = { 395*9b8f3200SMarcus Folkesson .name = "st7586", 396*9b8f3200SMarcus Folkesson .of_match_table = st7586_of_match, 397*9b8f3200SMarcus Folkesson }, 398*9b8f3200SMarcus Folkesson .id_table = st7586_id, 399*9b8f3200SMarcus Folkesson .probe = st7586_probe, 400*9b8f3200SMarcus Folkesson .remove = st7586_remove, 401*9b8f3200SMarcus Folkesson .shutdown = st7586_shutdown, 402*9b8f3200SMarcus Folkesson }; 403*9b8f3200SMarcus Folkesson module_spi_driver(st7586_spi_driver); 404*9b8f3200SMarcus Folkesson 405*9b8f3200SMarcus Folkesson MODULE_DESCRIPTION("Sitronix ST7586 DRM driver"); 406*9b8f3200SMarcus Folkesson MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 407*9b8f3200SMarcus Folkesson MODULE_LICENSE("GPL"); 408