162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DRM driver for Ilitek ILI9225 panels 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2017 David Lechner <david@lechnology.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Some code copied from mipi-dbi.c 862306a36Sopenharmony_ci * Copyright 2016 Noralf Trønnes 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/dma-buf.h> 1362306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/property.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <video/mipi_display.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2062306a36Sopenharmony_ci#include <drm/drm_damage_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_drv.h> 2262306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_fbdev_generic.h> 2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 2662306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 2762306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 2862306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 2962306a36Sopenharmony_ci#include <drm/drm_managed.h> 3062306a36Sopenharmony_ci#include <drm/drm_mipi_dbi.h> 3162306a36Sopenharmony_ci#include <drm/drm_rect.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define ILI9225_DRIVER_READ_CODE 0x00 3462306a36Sopenharmony_ci#define ILI9225_DRIVER_OUTPUT_CONTROL 0x01 3562306a36Sopenharmony_ci#define ILI9225_LCD_AC_DRIVING_CONTROL 0x02 3662306a36Sopenharmony_ci#define ILI9225_ENTRY_MODE 0x03 3762306a36Sopenharmony_ci#define ILI9225_DISPLAY_CONTROL_1 0x07 3862306a36Sopenharmony_ci#define ILI9225_BLANK_PERIOD_CONTROL_1 0x08 3962306a36Sopenharmony_ci#define ILI9225_FRAME_CYCLE_CONTROL 0x0b 4062306a36Sopenharmony_ci#define ILI9225_INTERFACE_CONTROL 0x0c 4162306a36Sopenharmony_ci#define ILI9225_OSCILLATION_CONTROL 0x0f 4262306a36Sopenharmony_ci#define ILI9225_POWER_CONTROL_1 0x10 4362306a36Sopenharmony_ci#define ILI9225_POWER_CONTROL_2 0x11 4462306a36Sopenharmony_ci#define ILI9225_POWER_CONTROL_3 0x12 4562306a36Sopenharmony_ci#define ILI9225_POWER_CONTROL_4 0x13 4662306a36Sopenharmony_ci#define ILI9225_POWER_CONTROL_5 0x14 4762306a36Sopenharmony_ci#define ILI9225_VCI_RECYCLING 0x15 4862306a36Sopenharmony_ci#define ILI9225_RAM_ADDRESS_SET_1 0x20 4962306a36Sopenharmony_ci#define ILI9225_RAM_ADDRESS_SET_2 0x21 5062306a36Sopenharmony_ci#define ILI9225_WRITE_DATA_TO_GRAM 0x22 5162306a36Sopenharmony_ci#define ILI9225_SOFTWARE_RESET 0x28 5262306a36Sopenharmony_ci#define ILI9225_GATE_SCAN_CONTROL 0x30 5362306a36Sopenharmony_ci#define ILI9225_VERTICAL_SCROLL_1 0x31 5462306a36Sopenharmony_ci#define ILI9225_VERTICAL_SCROLL_2 0x32 5562306a36Sopenharmony_ci#define ILI9225_VERTICAL_SCROLL_3 0x33 5662306a36Sopenharmony_ci#define ILI9225_PARTIAL_DRIVING_POS_1 0x34 5762306a36Sopenharmony_ci#define ILI9225_PARTIAL_DRIVING_POS_2 0x35 5862306a36Sopenharmony_ci#define ILI9225_HORIZ_WINDOW_ADDR_1 0x36 5962306a36Sopenharmony_ci#define ILI9225_HORIZ_WINDOW_ADDR_2 0x37 6062306a36Sopenharmony_ci#define ILI9225_VERT_WINDOW_ADDR_1 0x38 6162306a36Sopenharmony_ci#define ILI9225_VERT_WINDOW_ADDR_2 0x39 6262306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_1 0x50 6362306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_2 0x51 6462306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_3 0x52 6562306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_4 0x53 6662306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_5 0x54 6762306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_6 0x55 6862306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_7 0x56 6962306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_8 0x57 7062306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_9 0x58 7162306a36Sopenharmony_ci#define ILI9225_GAMMA_CONTROL_10 0x59 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci u8 par[2] = { data >> 8, data & 0xff }; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return mipi_dbi_command_buf(dbi, cmd, par, 2); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, 8162306a36Sopenharmony_ci struct drm_rect *rect) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); 8462306a36Sopenharmony_ci unsigned int height = rect->y2 - rect->y1; 8562306a36Sopenharmony_ci unsigned int width = rect->x2 - rect->x1; 8662306a36Sopenharmony_ci struct mipi_dbi *dbi = &dbidev->dbi; 8762306a36Sopenharmony_ci bool swap = dbi->swap_bytes; 8862306a36Sopenharmony_ci u16 x_start, y_start; 8962306a36Sopenharmony_ci u16 x1, x2, y1, y2; 9062306a36Sopenharmony_ci int ret = 0; 9162306a36Sopenharmony_ci bool full; 9262306a36Sopenharmony_ci void *tr; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci full = width == fb->width && height == fb->height; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!dbi->dc || !full || swap || 9962306a36Sopenharmony_ci fb->format->format == DRM_FORMAT_XRGB8888) { 10062306a36Sopenharmony_ci tr = dbidev->tx_buf; 10162306a36Sopenharmony_ci ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap); 10262306a36Sopenharmony_ci if (ret) 10362306a36Sopenharmony_ci goto err_msg; 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci tr = src->vaddr; /* TODO: Use mapping abstraction properly */ 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci switch (dbidev->rotation) { 10962306a36Sopenharmony_ci default: 11062306a36Sopenharmony_ci x1 = rect->x1; 11162306a36Sopenharmony_ci x2 = rect->x2 - 1; 11262306a36Sopenharmony_ci y1 = rect->y1; 11362306a36Sopenharmony_ci y2 = rect->y2 - 1; 11462306a36Sopenharmony_ci x_start = x1; 11562306a36Sopenharmony_ci y_start = y1; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case 90: 11862306a36Sopenharmony_ci x1 = rect->y1; 11962306a36Sopenharmony_ci x2 = rect->y2 - 1; 12062306a36Sopenharmony_ci y1 = fb->width - rect->x2; 12162306a36Sopenharmony_ci y2 = fb->width - rect->x1 - 1; 12262306a36Sopenharmony_ci x_start = x1; 12362306a36Sopenharmony_ci y_start = y2; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case 180: 12662306a36Sopenharmony_ci x1 = fb->width - rect->x2; 12762306a36Sopenharmony_ci x2 = fb->width - rect->x1 - 1; 12862306a36Sopenharmony_ci y1 = fb->height - rect->y2; 12962306a36Sopenharmony_ci y2 = fb->height - rect->y1 - 1; 13062306a36Sopenharmony_ci x_start = x2; 13162306a36Sopenharmony_ci y_start = y2; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case 270: 13462306a36Sopenharmony_ci x1 = fb->height - rect->y2; 13562306a36Sopenharmony_ci x2 = fb->height - rect->y1 - 1; 13662306a36Sopenharmony_ci y1 = rect->x1; 13762306a36Sopenharmony_ci y2 = rect->x2 - 1; 13862306a36Sopenharmony_ci x_start = x2; 13962306a36Sopenharmony_ci y_start = y1; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2); 14462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1); 14562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2); 14662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start); 14962306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr, 15262306a36Sopenharmony_ci width * height * 2); 15362306a36Sopenharmony_cierr_msg: 15462306a36Sopenharmony_ci if (ret) 15562306a36Sopenharmony_ci dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, 15962306a36Sopenharmony_ci struct drm_plane_state *old_state) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct drm_plane_state *state = pipe->plane.state; 16262306a36Sopenharmony_ci struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); 16362306a36Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 16462306a36Sopenharmony_ci struct drm_rect rect; 16562306a36Sopenharmony_ci int idx; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!pipe->crtc.state->active) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!drm_dev_enter(fb->dev, &idx)) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (drm_atomic_helper_damage_merged(old_state, state, &rect)) 17462306a36Sopenharmony_ci ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci drm_dev_exit(idx); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, 18062306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 18162306a36Sopenharmony_ci struct drm_plane_state *plane_state) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 18462306a36Sopenharmony_ci struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 18562306a36Sopenharmony_ci struct drm_framebuffer *fb = plane_state->fb; 18662306a36Sopenharmony_ci struct device *dev = pipe->crtc.dev->dev; 18762306a36Sopenharmony_ci struct mipi_dbi *dbi = &dbidev->dbi; 18862306a36Sopenharmony_ci struct drm_rect rect = { 18962306a36Sopenharmony_ci .x1 = 0, 19062306a36Sopenharmony_ci .x2 = fb->width, 19162306a36Sopenharmony_ci .y1 = 0, 19262306a36Sopenharmony_ci .y2 = fb->height, 19362306a36Sopenharmony_ci }; 19462306a36Sopenharmony_ci int ret, idx; 19562306a36Sopenharmony_ci u8 am_id; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!drm_dev_enter(pipe->crtc.dev, &idx)) 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci mipi_dbi_hw_reset(dbi); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * There don't seem to be two example init sequences that match, so 20662306a36Sopenharmony_ci * using the one from the popular Arduino library for this display. 20762306a36Sopenharmony_ci * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000); 21162306a36Sopenharmony_ci if (ret) { 21262306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "Error sending command %d\n", ret); 21362306a36Sopenharmony_ci goto out_exit; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000); 21662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000); 21762306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000); 21862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci msleep(40); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018); 22362306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121); 22462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f); 22562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f); 22662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci msleep(10); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci msleep(50); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (dbidev->rotation) { 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci am_id = 0x30; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case 90: 23962306a36Sopenharmony_ci am_id = 0x18; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case 180: 24262306a36Sopenharmony_ci am_id = 0x00; 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case 270: 24562306a36Sopenharmony_ci am_id = 0x28; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c); 24962306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100); 25062306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id); 25162306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 25262306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808); 25362306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100); 25462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000); 25562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01); 25662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020); 25762306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000); 25862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000); 26162306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db); 26262306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000); 26362306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000); 26462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db); 26562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000); 26862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808); 26962306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a); 27062306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a); 27162306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08); 27262306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808); 27362306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000); 27462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00); 27562306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710); 27662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci msleep(50); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciout_exit: 28762306a36Sopenharmony_ci drm_dev_exit(idx); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); 29362306a36Sopenharmony_ci struct mipi_dbi *dbi = &dbidev->dbi; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * This callback is not protected by drm_dev_enter/exit since we want to 29962306a36Sopenharmony_ci * turn off the display on regular driver unload. It's highly unlikely 30062306a36Sopenharmony_ci * that the underlying SPI controller is gone should this be called after 30162306a36Sopenharmony_ci * unplug. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000); 30562306a36Sopenharmony_ci msleep(50); 30662306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007); 30762306a36Sopenharmony_ci msleep(50); 30862306a36Sopenharmony_ci ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, 31262306a36Sopenharmony_ci size_t num) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct spi_device *spi = dbi->spi; 31562306a36Sopenharmony_ci unsigned int bpw = 8; 31662306a36Sopenharmony_ci u32 speed_hz; 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci spi_bus_lock(spi->controller); 32062306a36Sopenharmony_ci gpiod_set_value_cansleep(dbi->dc, 0); 32162306a36Sopenharmony_ci speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); 32262306a36Sopenharmony_ci ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); 32362306a36Sopenharmony_ci spi_bus_unlock(spi->controller); 32462306a36Sopenharmony_ci if (ret || !num) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes) 32862306a36Sopenharmony_ci bpw = 16; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci spi_bus_lock(spi->controller); 33162306a36Sopenharmony_ci gpiod_set_value_cansleep(dbi->dc, 1); 33262306a36Sopenharmony_ci speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); 33362306a36Sopenharmony_ci ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); 33462306a36Sopenharmony_ci spi_bus_unlock(spi->controller); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { 34062306a36Sopenharmony_ci .mode_valid = mipi_dbi_pipe_mode_valid, 34162306a36Sopenharmony_ci .enable = ili9225_pipe_enable, 34262306a36Sopenharmony_ci .disable = ili9225_pipe_disable, 34362306a36Sopenharmony_ci .update = ili9225_pipe_update, 34462306a36Sopenharmony_ci .begin_fb_access = mipi_dbi_pipe_begin_fb_access, 34562306a36Sopenharmony_ci .end_fb_access = mipi_dbi_pipe_end_fb_access, 34662306a36Sopenharmony_ci .reset_plane = mipi_dbi_pipe_reset_plane, 34762306a36Sopenharmony_ci .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, 34862306a36Sopenharmony_ci .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state, 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic const struct drm_display_mode ili9225_mode = { 35262306a36Sopenharmony_ci DRM_SIMPLE_MODE(176, 220, 35, 44), 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciDEFINE_DRM_GEM_DMA_FOPS(ili9225_fops); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic const struct drm_driver ili9225_driver = { 35862306a36Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 35962306a36Sopenharmony_ci .fops = &ili9225_fops, 36062306a36Sopenharmony_ci DRM_GEM_DMA_DRIVER_OPS_VMAP, 36162306a36Sopenharmony_ci .name = "ili9225", 36262306a36Sopenharmony_ci .desc = "Ilitek ILI9225", 36362306a36Sopenharmony_ci .date = "20171106", 36462306a36Sopenharmony_ci .major = 1, 36562306a36Sopenharmony_ci .minor = 0, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct of_device_id ili9225_of_match[] = { 36962306a36Sopenharmony_ci { .compatible = "vot,v220hf01a-t" }, 37062306a36Sopenharmony_ci {}, 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ili9225_of_match); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic const struct spi_device_id ili9225_id[] = { 37562306a36Sopenharmony_ci { "v220hf01a-t", 0 }, 37662306a36Sopenharmony_ci { }, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ili9225_id); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int ili9225_probe(struct spi_device *spi) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct device *dev = &spi->dev; 38362306a36Sopenharmony_ci struct mipi_dbi_dev *dbidev; 38462306a36Sopenharmony_ci struct drm_device *drm; 38562306a36Sopenharmony_ci struct mipi_dbi *dbi; 38662306a36Sopenharmony_ci struct gpio_desc *rs; 38762306a36Sopenharmony_ci u32 rotation = 0; 38862306a36Sopenharmony_ci int ret; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci dbidev = devm_drm_dev_alloc(dev, &ili9225_driver, 39162306a36Sopenharmony_ci struct mipi_dbi_dev, drm); 39262306a36Sopenharmony_ci if (IS_ERR(dbidev)) 39362306a36Sopenharmony_ci return PTR_ERR(dbidev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dbi = &dbidev->dbi; 39662306a36Sopenharmony_ci drm = &dbidev->drm; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 39962306a36Sopenharmony_ci if (IS_ERR(dbi->reset)) 40062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n"); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW); 40362306a36Sopenharmony_ci if (IS_ERR(rs)) 40462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(rs), "Failed to get GPIO 'rs'\n"); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci device_property_read_u32(dev, "rotation", &rotation); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ret = mipi_dbi_spi_init(spi, dbi, rs); 40962306a36Sopenharmony_ci if (ret) 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* override the command function set in mipi_dbi_spi_init() */ 41362306a36Sopenharmony_ci dbi->command = ili9225_dbi_command; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = mipi_dbi_dev_init(dbidev, &ili9225_pipe_funcs, &ili9225_mode, rotation); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci drm_mode_config_reset(drm); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci ret = drm_dev_register(drm, 0); 42262306a36Sopenharmony_ci if (ret) 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci spi_set_drvdata(spi, drm); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci drm_fbdev_generic_setup(drm, 0); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void ili9225_remove(struct spi_device *spi) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct drm_device *drm = spi_get_drvdata(spi); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci drm_dev_unplug(drm); 43762306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void ili9225_shutdown(struct spi_device *spi) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic struct spi_driver ili9225_spi_driver = { 44662306a36Sopenharmony_ci .driver = { 44762306a36Sopenharmony_ci .name = "ili9225", 44862306a36Sopenharmony_ci .owner = THIS_MODULE, 44962306a36Sopenharmony_ci .of_match_table = ili9225_of_match, 45062306a36Sopenharmony_ci }, 45162306a36Sopenharmony_ci .id_table = ili9225_id, 45262306a36Sopenharmony_ci .probe = ili9225_probe, 45362306a36Sopenharmony_ci .remove = ili9225_remove, 45462306a36Sopenharmony_ci .shutdown = ili9225_shutdown, 45562306a36Sopenharmony_ci}; 45662306a36Sopenharmony_cimodule_spi_driver(ili9225_spi_driver); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciMODULE_DESCRIPTION("Ilitek ILI9225 DRM driver"); 45962306a36Sopenharmony_ciMODULE_AUTHOR("David Lechner <david@lechnology.com>"); 46062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 461