1043386a0SNoralf Trønnes // SPDX-License-Identifier: GPL-2.0-or-later 2043386a0SNoralf Trønnes /* 3043386a0SNoralf Trønnes * DRM driver for Ilitek ILI9225 panels 4043386a0SNoralf Trønnes * 5043386a0SNoralf Trønnes * Copyright 2017 David Lechner <david@lechnology.com> 6043386a0SNoralf Trønnes * 7043386a0SNoralf Trønnes * Some code copied from mipi-dbi.c 8043386a0SNoralf Trønnes * Copyright 2016 Noralf Trønnes 9043386a0SNoralf Trønnes */ 10043386a0SNoralf Trønnes 11043386a0SNoralf Trønnes #include <linux/delay.h> 12043386a0SNoralf Trønnes #include <linux/dma-buf.h> 13043386a0SNoralf Trønnes #include <linux/gpio/consumer.h> 14043386a0SNoralf Trønnes #include <linux/module.h> 15043386a0SNoralf Trønnes #include <linux/property.h> 16043386a0SNoralf Trønnes #include <linux/spi/spi.h> 17043386a0SNoralf Trønnes #include <video/mipi_display.h> 18043386a0SNoralf Trønnes 19043386a0SNoralf Trønnes #include <drm/drm_atomic_helper.h> 20043386a0SNoralf Trønnes #include <drm/drm_damage_helper.h> 21043386a0SNoralf Trønnes #include <drm/drm_drv.h> 22043386a0SNoralf Trønnes #include <drm/drm_fb_cma_helper.h> 23043386a0SNoralf Trønnes #include <drm/drm_fb_helper.h> 24043386a0SNoralf Trønnes #include <drm/drm_fourcc.h> 25043386a0SNoralf Trønnes #include <drm/drm_gem_cma_helper.h> 26043386a0SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 27f5ad671bSDaniel Vetter #include <drm/drm_managed.h> 28043386a0SNoralf Trønnes #include <drm/drm_mipi_dbi.h> 29043386a0SNoralf Trønnes #include <drm/drm_rect.h> 30043386a0SNoralf Trønnes 31043386a0SNoralf Trønnes #define ILI9225_DRIVER_READ_CODE 0x00 32043386a0SNoralf Trønnes #define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 33043386a0SNoralf Trønnes #define ILI9225_LCD_AC_DRIVING_CONTROL 0x02 34043386a0SNoralf Trønnes #define ILI9225_ENTRY_MODE 0x03 35043386a0SNoralf Trønnes #define ILI9225_DISPLAY_CONTROL_1 0x07 36043386a0SNoralf Trønnes #define ILI9225_BLANK_PERIOD_CONTROL_1 0x08 37043386a0SNoralf Trønnes #define ILI9225_FRAME_CYCLE_CONTROL 0x0b 38043386a0SNoralf Trønnes #define ILI9225_INTERFACE_CONTROL 0x0c 39043386a0SNoralf Trønnes #define ILI9225_OSCILLATION_CONTROL 0x0f 40043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_1 0x10 41043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_2 0x11 42043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_3 0x12 43043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_4 0x13 44043386a0SNoralf Trønnes #define ILI9225_POWER_CONTROL_5 0x14 45043386a0SNoralf Trønnes #define ILI9225_VCI_RECYCLING 0x15 46043386a0SNoralf Trønnes #define ILI9225_RAM_ADDRESS_SET_1 0x20 47043386a0SNoralf Trønnes #define ILI9225_RAM_ADDRESS_SET_2 0x21 48043386a0SNoralf Trønnes #define ILI9225_WRITE_DATA_TO_GRAM 0x22 49043386a0SNoralf Trønnes #define ILI9225_SOFTWARE_RESET 0x28 50043386a0SNoralf Trønnes #define ILI9225_GATE_SCAN_CONTROL 0x30 51043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_1 0x31 52043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_2 0x32 53043386a0SNoralf Trønnes #define ILI9225_VERTICAL_SCROLL_3 0x33 54043386a0SNoralf Trønnes #define ILI9225_PARTIAL_DRIVING_POS_1 0x34 55043386a0SNoralf Trønnes #define ILI9225_PARTIAL_DRIVING_POS_2 0x35 56043386a0SNoralf Trønnes #define ILI9225_HORIZ_WINDOW_ADDR_1 0x36 57043386a0SNoralf Trønnes #define ILI9225_HORIZ_WINDOW_ADDR_2 0x37 58043386a0SNoralf Trønnes #define ILI9225_VERT_WINDOW_ADDR_1 0x38 59043386a0SNoralf Trønnes #define ILI9225_VERT_WINDOW_ADDR_2 0x39 60043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_1 0x50 61043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_2 0x51 62043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_3 0x52 63043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_4 0x53 64043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_5 0x54 65043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_6 0x55 66043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_7 0x56 67043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_8 0x57 68043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_9 0x58 69043386a0SNoralf Trønnes #define ILI9225_GAMMA_CONTROL_10 0x59 70043386a0SNoralf Trønnes 71043386a0SNoralf Trønnes static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) 72043386a0SNoralf Trønnes { 73043386a0SNoralf Trønnes u8 par[2] = { data >> 8, data & 0xff }; 74043386a0SNoralf Trønnes 75043386a0SNoralf Trønnes return mipi_dbi_command_buf(dbi, cmd, par, 2); 76043386a0SNoralf Trønnes } 77043386a0SNoralf Trønnes 78043386a0SNoralf Trønnes static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) 79043386a0SNoralf Trønnes { 80043386a0SNoralf Trønnes struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 81043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); 82043386a0SNoralf Trønnes unsigned int height = rect->y2 - rect->y1; 83043386a0SNoralf Trønnes unsigned int width = rect->x2 - rect->x1; 84043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 85043386a0SNoralf Trønnes bool swap = dbi->swap_bytes; 86043386a0SNoralf Trønnes u16 x_start, y_start; 87043386a0SNoralf Trønnes u16 x1, x2, y1, y2; 88043386a0SNoralf Trønnes int idx, ret = 0; 89043386a0SNoralf Trønnes bool full; 90043386a0SNoralf Trønnes void *tr; 91043386a0SNoralf Trønnes 92043386a0SNoralf Trønnes if (!dbidev->enabled) 93043386a0SNoralf Trønnes return; 94043386a0SNoralf Trønnes 95043386a0SNoralf Trønnes if (!drm_dev_enter(fb->dev, &idx)) 96043386a0SNoralf Trønnes return; 97043386a0SNoralf Trønnes 98043386a0SNoralf Trønnes full = width == fb->width && height == fb->height; 99043386a0SNoralf Trønnes 100043386a0SNoralf Trønnes DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); 101043386a0SNoralf Trønnes 102043386a0SNoralf Trønnes if (!dbi->dc || !full || swap || 103043386a0SNoralf Trønnes fb->format->format == DRM_FORMAT_XRGB8888) { 104043386a0SNoralf Trønnes tr = dbidev->tx_buf; 105043386a0SNoralf Trønnes ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap); 106043386a0SNoralf Trønnes if (ret) 107043386a0SNoralf Trønnes goto err_msg; 108043386a0SNoralf Trønnes } else { 109043386a0SNoralf Trønnes tr = cma_obj->vaddr; 110043386a0SNoralf Trønnes } 111043386a0SNoralf Trønnes 112043386a0SNoralf Trønnes switch (dbidev->rotation) { 113043386a0SNoralf Trønnes default: 114043386a0SNoralf Trønnes x1 = rect->x1; 115043386a0SNoralf Trønnes x2 = rect->x2 - 1; 116043386a0SNoralf Trønnes y1 = rect->y1; 117043386a0SNoralf Trønnes y2 = rect->y2 - 1; 118043386a0SNoralf Trønnes x_start = x1; 119043386a0SNoralf Trønnes y_start = y1; 120043386a0SNoralf Trønnes break; 121043386a0SNoralf Trønnes case 90: 122043386a0SNoralf Trønnes x1 = rect->y1; 123043386a0SNoralf Trønnes x2 = rect->y2 - 1; 124043386a0SNoralf Trønnes y1 = fb->width - rect->x2; 125043386a0SNoralf Trønnes y2 = fb->width - rect->x1 - 1; 126043386a0SNoralf Trønnes x_start = x1; 127043386a0SNoralf Trønnes y_start = y2; 128043386a0SNoralf Trønnes break; 129043386a0SNoralf Trønnes case 180: 130043386a0SNoralf Trønnes x1 = fb->width - rect->x2; 131043386a0SNoralf Trønnes x2 = fb->width - rect->x1 - 1; 132043386a0SNoralf Trønnes y1 = fb->height - rect->y2; 133043386a0SNoralf Trønnes y2 = fb->height - rect->y1 - 1; 134043386a0SNoralf Trønnes x_start = x2; 135043386a0SNoralf Trønnes y_start = y2; 136043386a0SNoralf Trønnes break; 137043386a0SNoralf Trønnes case 270: 138043386a0SNoralf Trønnes x1 = fb->height - rect->y2; 139043386a0SNoralf Trønnes x2 = fb->height - rect->y1 - 1; 140043386a0SNoralf Trønnes y1 = rect->x1; 141043386a0SNoralf Trønnes y2 = rect->x2 - 1; 142043386a0SNoralf Trønnes x_start = x2; 143043386a0SNoralf Trønnes y_start = y1; 144043386a0SNoralf Trønnes break; 145043386a0SNoralf Trønnes } 146043386a0SNoralf Trønnes 147043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2); 148043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1); 149043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2); 150043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1); 151043386a0SNoralf Trønnes 152043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start); 153043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start); 154043386a0SNoralf Trønnes 155043386a0SNoralf Trønnes ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr, 156043386a0SNoralf Trønnes width * height * 2); 157043386a0SNoralf Trønnes err_msg: 158043386a0SNoralf Trønnes if (ret) 159043386a0SNoralf Trønnes dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); 160043386a0SNoralf Trønnes 161043386a0SNoralf Trønnes drm_dev_exit(idx); 162043386a0SNoralf Trønnes } 163043386a0SNoralf Trønnes 164043386a0SNoralf Trønnes static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, 165043386a0SNoralf Trønnes struct drm_plane_state *old_state) 166043386a0SNoralf Trønnes { 167043386a0SNoralf Trønnes struct drm_plane_state *state = pipe->plane.state; 168043386a0SNoralf Trønnes struct drm_rect rect; 169043386a0SNoralf Trønnes 170043386a0SNoralf Trønnes if (drm_atomic_helper_damage_merged(old_state, state, &rect)) 171043386a0SNoralf Trønnes ili9225_fb_dirty(state->fb, &rect); 172043386a0SNoralf Trønnes } 173043386a0SNoralf Trønnes 174043386a0SNoralf Trønnes static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, 175043386a0SNoralf Trønnes struct drm_crtc_state *crtc_state, 176043386a0SNoralf Trønnes struct drm_plane_state *plane_state) 177043386a0SNoralf Trønnes { 178043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 179043386a0SNoralf Trønnes struct drm_framebuffer *fb = plane_state->fb; 180043386a0SNoralf Trønnes struct device *dev = pipe->crtc.dev->dev; 181043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 182043386a0SNoralf Trønnes struct drm_rect rect = { 183043386a0SNoralf Trønnes .x1 = 0, 184043386a0SNoralf Trønnes .x2 = fb->width, 185043386a0SNoralf Trønnes .y1 = 0, 186043386a0SNoralf Trønnes .y2 = fb->height, 187043386a0SNoralf Trønnes }; 188043386a0SNoralf Trønnes int ret, idx; 189043386a0SNoralf Trønnes u8 am_id; 190043386a0SNoralf Trønnes 191043386a0SNoralf Trønnes if (!drm_dev_enter(pipe->crtc.dev, &idx)) 192043386a0SNoralf Trønnes return; 193043386a0SNoralf Trønnes 194043386a0SNoralf Trønnes DRM_DEBUG_KMS("\n"); 195043386a0SNoralf Trønnes 196043386a0SNoralf Trønnes mipi_dbi_hw_reset(dbi); 197043386a0SNoralf Trønnes 198043386a0SNoralf Trønnes /* 199043386a0SNoralf Trønnes * There don't seem to be two example init sequences that match, so 200043386a0SNoralf Trønnes * using the one from the popular Arduino library for this display. 201043386a0SNoralf Trønnes * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp 202043386a0SNoralf Trønnes */ 203043386a0SNoralf Trønnes 204043386a0SNoralf Trønnes ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000); 205043386a0SNoralf Trønnes if (ret) { 206043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Error sending command %d\n", ret); 207043386a0SNoralf Trønnes goto out_exit; 208043386a0SNoralf Trønnes } 209043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000); 210043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000); 211043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000); 212043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000); 213043386a0SNoralf Trønnes 214043386a0SNoralf Trønnes msleep(40); 215043386a0SNoralf Trønnes 216043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018); 217043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121); 218043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f); 219043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f); 220043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800); 221043386a0SNoralf Trønnes 222043386a0SNoralf Trønnes msleep(10); 223043386a0SNoralf Trønnes 224043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b); 225043386a0SNoralf Trønnes 226043386a0SNoralf Trønnes msleep(50); 227043386a0SNoralf Trønnes 228043386a0SNoralf Trønnes switch (dbidev->rotation) { 229043386a0SNoralf Trønnes default: 230043386a0SNoralf Trønnes am_id = 0x30; 231043386a0SNoralf Trønnes break; 232043386a0SNoralf Trønnes case 90: 233043386a0SNoralf Trønnes am_id = 0x18; 234043386a0SNoralf Trønnes break; 235043386a0SNoralf Trønnes case 180: 236043386a0SNoralf Trønnes am_id = 0x00; 237043386a0SNoralf Trønnes break; 238043386a0SNoralf Trønnes case 270: 239043386a0SNoralf Trønnes am_id = 0x28; 240043386a0SNoralf Trønnes break; 241043386a0SNoralf Trønnes } 242043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c); 243043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100); 244043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id); 245043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 246043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808); 247043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100); 248043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000); 249043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01); 250043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020); 251043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000); 252043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000); 253043386a0SNoralf Trønnes 254043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000); 255043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db); 256043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000); 257043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000); 258043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db); 259043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000); 260043386a0SNoralf Trønnes 261043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000); 262043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808); 263043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a); 264043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a); 265043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08); 266043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808); 267043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000); 268043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00); 269043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710); 270043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710); 271043386a0SNoralf Trønnes 272043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012); 273043386a0SNoralf Trønnes 274043386a0SNoralf Trønnes msleep(50); 275043386a0SNoralf Trønnes 276043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); 277043386a0SNoralf Trønnes 278043386a0SNoralf Trønnes dbidev->enabled = true; 279043386a0SNoralf Trønnes ili9225_fb_dirty(fb, &rect); 280043386a0SNoralf Trønnes out_exit: 281043386a0SNoralf Trønnes drm_dev_exit(idx); 282043386a0SNoralf Trønnes } 283043386a0SNoralf Trønnes 284043386a0SNoralf Trønnes static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe) 285043386a0SNoralf Trønnes { 286043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 287043386a0SNoralf Trønnes struct mipi_dbi *dbi = &dbidev->dbi; 288043386a0SNoralf Trønnes 289043386a0SNoralf Trønnes DRM_DEBUG_KMS("\n"); 290043386a0SNoralf Trønnes 291043386a0SNoralf Trønnes /* 292043386a0SNoralf Trønnes * This callback is not protected by drm_dev_enter/exit since we want to 293043386a0SNoralf Trønnes * turn off the display on regular driver unload. It's highly unlikely 294043386a0SNoralf Trønnes * that the underlying SPI controller is gone should this be called after 295043386a0SNoralf Trønnes * unplug. 296043386a0SNoralf Trønnes */ 297043386a0SNoralf Trønnes 298043386a0SNoralf Trønnes if (!dbidev->enabled) 299043386a0SNoralf Trønnes return; 300043386a0SNoralf Trønnes 301043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 302043386a0SNoralf Trønnes msleep(50); 303043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007); 304043386a0SNoralf Trønnes msleep(50); 305043386a0SNoralf Trønnes ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02); 306043386a0SNoralf Trønnes 307043386a0SNoralf Trønnes dbidev->enabled = false; 308043386a0SNoralf Trønnes } 309043386a0SNoralf Trønnes 310043386a0SNoralf Trønnes static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, 311043386a0SNoralf Trønnes size_t num) 312043386a0SNoralf Trønnes { 313043386a0SNoralf Trønnes struct spi_device *spi = dbi->spi; 314043386a0SNoralf Trønnes unsigned int bpw = 8; 315043386a0SNoralf Trønnes u32 speed_hz; 316043386a0SNoralf Trønnes int ret; 317043386a0SNoralf Trønnes 318043386a0SNoralf Trønnes gpiod_set_value_cansleep(dbi->dc, 0); 319043386a0SNoralf Trønnes speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); 320043386a0SNoralf Trønnes ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); 321043386a0SNoralf Trønnes if (ret || !num) 322043386a0SNoralf Trønnes return ret; 323043386a0SNoralf Trønnes 324043386a0SNoralf Trønnes if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes) 325043386a0SNoralf Trønnes bpw = 16; 326043386a0SNoralf Trønnes 327043386a0SNoralf Trønnes gpiod_set_value_cansleep(dbi->dc, 1); 328043386a0SNoralf Trønnes speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); 329043386a0SNoralf Trønnes 330043386a0SNoralf Trønnes return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); 331043386a0SNoralf Trønnes } 332043386a0SNoralf Trønnes 333043386a0SNoralf Trønnes static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { 334043386a0SNoralf Trønnes .enable = ili9225_pipe_enable, 335043386a0SNoralf Trønnes .disable = ili9225_pipe_disable, 336043386a0SNoralf Trønnes .update = ili9225_pipe_update, 337043386a0SNoralf Trønnes .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 338043386a0SNoralf Trønnes }; 339043386a0SNoralf Trønnes 340043386a0SNoralf Trønnes static const struct drm_display_mode ili9225_mode = { 341043386a0SNoralf Trønnes DRM_SIMPLE_MODE(176, 220, 35, 44), 342043386a0SNoralf Trønnes }; 343043386a0SNoralf Trønnes 344043386a0SNoralf Trønnes DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops); 345043386a0SNoralf Trønnes 346043386a0SNoralf Trønnes static struct drm_driver ili9225_driver = { 347043386a0SNoralf Trønnes .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 348043386a0SNoralf Trønnes .fops = &ili9225_fops, 349043386a0SNoralf Trønnes .release = mipi_dbi_release, 350043386a0SNoralf Trønnes DRM_GEM_CMA_VMAP_DRIVER_OPS, 351043386a0SNoralf Trønnes .name = "ili9225", 352043386a0SNoralf Trønnes .desc = "Ilitek ILI9225", 353043386a0SNoralf Trønnes .date = "20171106", 354043386a0SNoralf Trønnes .major = 1, 355043386a0SNoralf Trønnes .minor = 0, 356043386a0SNoralf Trønnes }; 357043386a0SNoralf Trønnes 358043386a0SNoralf Trønnes static const struct of_device_id ili9225_of_match[] = { 359043386a0SNoralf Trønnes { .compatible = "vot,v220hf01a-t" }, 360043386a0SNoralf Trønnes {}, 361043386a0SNoralf Trønnes }; 362043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(of, ili9225_of_match); 363043386a0SNoralf Trønnes 364043386a0SNoralf Trønnes static const struct spi_device_id ili9225_id[] = { 365043386a0SNoralf Trønnes { "v220hf01a-t", 0 }, 366043386a0SNoralf Trønnes { }, 367043386a0SNoralf Trønnes }; 368043386a0SNoralf Trønnes MODULE_DEVICE_TABLE(spi, ili9225_id); 369043386a0SNoralf Trønnes 370043386a0SNoralf Trønnes static int ili9225_probe(struct spi_device *spi) 371043386a0SNoralf Trønnes { 372043386a0SNoralf Trønnes struct device *dev = &spi->dev; 373043386a0SNoralf Trønnes struct mipi_dbi_dev *dbidev; 374043386a0SNoralf Trønnes struct drm_device *drm; 375043386a0SNoralf Trønnes struct mipi_dbi *dbi; 376043386a0SNoralf Trønnes struct gpio_desc *rs; 377043386a0SNoralf Trønnes u32 rotation = 0; 378043386a0SNoralf Trønnes int ret; 379043386a0SNoralf Trønnes 380043386a0SNoralf Trønnes dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); 381043386a0SNoralf Trønnes if (!dbidev) 382043386a0SNoralf Trønnes return -ENOMEM; 383043386a0SNoralf Trønnes 384043386a0SNoralf Trønnes dbi = &dbidev->dbi; 385043386a0SNoralf Trønnes drm = &dbidev->drm; 386043386a0SNoralf Trønnes ret = devm_drm_dev_init(dev, drm, &ili9225_driver); 387043386a0SNoralf Trønnes if (ret) { 388043386a0SNoralf Trønnes kfree(dbidev); 389043386a0SNoralf Trønnes return ret; 390043386a0SNoralf Trønnes } 391f5ad671bSDaniel Vetter drmm_add_final_kfree(drm, dbidev); 392043386a0SNoralf Trønnes 393043386a0SNoralf Trønnes drm_mode_config_init(drm); 394043386a0SNoralf Trønnes 395043386a0SNoralf Trønnes dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 396043386a0SNoralf Trønnes if (IS_ERR(dbi->reset)) { 397043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 398043386a0SNoralf Trønnes return PTR_ERR(dbi->reset); 399043386a0SNoralf Trønnes } 400043386a0SNoralf Trønnes 401043386a0SNoralf Trønnes rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW); 402043386a0SNoralf Trønnes if (IS_ERR(rs)) { 403043386a0SNoralf Trønnes DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n"); 404043386a0SNoralf Trønnes return PTR_ERR(rs); 405043386a0SNoralf Trønnes } 406043386a0SNoralf Trønnes 407043386a0SNoralf Trønnes device_property_read_u32(dev, "rotation", &rotation); 408043386a0SNoralf Trønnes 409043386a0SNoralf Trønnes ret = mipi_dbi_spi_init(spi, dbi, rs); 410043386a0SNoralf Trønnes if (ret) 411043386a0SNoralf Trønnes return ret; 412043386a0SNoralf Trønnes 413043386a0SNoralf Trønnes /* override the command function set in mipi_dbi_spi_init() */ 414043386a0SNoralf Trønnes dbi->command = ili9225_dbi_command; 415043386a0SNoralf Trønnes 416043386a0SNoralf Trønnes ret = mipi_dbi_dev_init(dbidev, &ili9225_pipe_funcs, &ili9225_mode, rotation); 417043386a0SNoralf Trønnes if (ret) 418043386a0SNoralf Trønnes return ret; 419043386a0SNoralf Trønnes 420043386a0SNoralf Trønnes drm_mode_config_reset(drm); 421043386a0SNoralf Trønnes 422043386a0SNoralf Trønnes ret = drm_dev_register(drm, 0); 423043386a0SNoralf Trønnes if (ret) 424043386a0SNoralf Trønnes return ret; 425043386a0SNoralf Trønnes 426043386a0SNoralf Trønnes spi_set_drvdata(spi, drm); 427043386a0SNoralf Trønnes 428043386a0SNoralf Trønnes drm_fbdev_generic_setup(drm, 0); 429043386a0SNoralf Trønnes 430043386a0SNoralf Trønnes return 0; 431043386a0SNoralf Trønnes } 432043386a0SNoralf Trønnes 433043386a0SNoralf Trønnes static int ili9225_remove(struct spi_device *spi) 434043386a0SNoralf Trønnes { 435043386a0SNoralf Trønnes struct drm_device *drm = spi_get_drvdata(spi); 436043386a0SNoralf Trønnes 437043386a0SNoralf Trønnes drm_dev_unplug(drm); 438043386a0SNoralf Trønnes drm_atomic_helper_shutdown(drm); 439043386a0SNoralf Trønnes 440043386a0SNoralf Trønnes return 0; 441043386a0SNoralf Trønnes } 442043386a0SNoralf Trønnes 443043386a0SNoralf Trønnes static void ili9225_shutdown(struct spi_device *spi) 444043386a0SNoralf Trønnes { 445043386a0SNoralf Trønnes drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 446043386a0SNoralf Trønnes } 447043386a0SNoralf Trønnes 448043386a0SNoralf Trønnes static struct spi_driver ili9225_spi_driver = { 449043386a0SNoralf Trønnes .driver = { 450043386a0SNoralf Trønnes .name = "ili9225", 451043386a0SNoralf Trønnes .owner = THIS_MODULE, 452043386a0SNoralf Trønnes .of_match_table = ili9225_of_match, 453043386a0SNoralf Trønnes }, 454043386a0SNoralf Trønnes .id_table = ili9225_id, 455043386a0SNoralf Trønnes .probe = ili9225_probe, 456043386a0SNoralf Trønnes .remove = ili9225_remove, 457043386a0SNoralf Trønnes .shutdown = ili9225_shutdown, 458043386a0SNoralf Trønnes }; 459043386a0SNoralf Trønnes module_spi_driver(ili9225_spi_driver); 460043386a0SNoralf Trønnes 461043386a0SNoralf Trønnes MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver"); 462043386a0SNoralf Trønnes MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 463043386a0SNoralf Trønnes MODULE_LICENSE("GPL"); 464