162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2017 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Philippe Cornu <philippe.cornu@st.com> 662306a36Sopenharmony_ci * Yannick Fertre <yannick.fertre@st.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/backlight.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <video/mipi_display.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 1862306a36Sopenharmony_ci#include <drm/drm_modes.h> 1962306a36Sopenharmony_ci#include <drm/drm_panel.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define OTM8009A_BACKLIGHT_DEFAULT 240 2262306a36Sopenharmony_ci#define OTM8009A_BACKLIGHT_MAX 255 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Manufacturer Command Set */ 2562306a36Sopenharmony_ci#define MCS_ADRSFT 0x0000 /* Address Shift Function */ 2662306a36Sopenharmony_ci#define MCS_PANSET 0xB3A6 /* Panel Type Setting */ 2762306a36Sopenharmony_ci#define MCS_SD_CTRL 0xC0A2 /* Source Driver Timing Setting */ 2862306a36Sopenharmony_ci#define MCS_P_DRV_M 0xC0B4 /* Panel Driving Mode */ 2962306a36Sopenharmony_ci#define MCS_OSC_ADJ 0xC181 /* Oscillator Adjustment for Idle/Normal mode */ 3062306a36Sopenharmony_ci#define MCS_RGB_VID_SET 0xC1A1 /* RGB Video Mode Setting */ 3162306a36Sopenharmony_ci#define MCS_SD_PCH_CTRL 0xC480 /* Source Driver Precharge Control */ 3262306a36Sopenharmony_ci#define MCS_NO_DOC1 0xC48A /* Command not documented */ 3362306a36Sopenharmony_ci#define MCS_PWR_CTRL1 0xC580 /* Power Control Setting 1 */ 3462306a36Sopenharmony_ci#define MCS_PWR_CTRL2 0xC590 /* Power Control Setting 2 for Normal Mode */ 3562306a36Sopenharmony_ci#define MCS_PWR_CTRL4 0xC5B0 /* Power Control Setting 4 for DC Voltage */ 3662306a36Sopenharmony_ci#define MCS_PANCTRLSET1 0xCB80 /* Panel Control Setting 1 */ 3762306a36Sopenharmony_ci#define MCS_PANCTRLSET2 0xCB90 /* Panel Control Setting 2 */ 3862306a36Sopenharmony_ci#define MCS_PANCTRLSET3 0xCBA0 /* Panel Control Setting 3 */ 3962306a36Sopenharmony_ci#define MCS_PANCTRLSET4 0xCBB0 /* Panel Control Setting 4 */ 4062306a36Sopenharmony_ci#define MCS_PANCTRLSET5 0xCBC0 /* Panel Control Setting 5 */ 4162306a36Sopenharmony_ci#define MCS_PANCTRLSET6 0xCBD0 /* Panel Control Setting 6 */ 4262306a36Sopenharmony_ci#define MCS_PANCTRLSET7 0xCBE0 /* Panel Control Setting 7 */ 4362306a36Sopenharmony_ci#define MCS_PANCTRLSET8 0xCBF0 /* Panel Control Setting 8 */ 4462306a36Sopenharmony_ci#define MCS_PANU2D1 0xCC80 /* Panel U2D Setting 1 */ 4562306a36Sopenharmony_ci#define MCS_PANU2D2 0xCC90 /* Panel U2D Setting 2 */ 4662306a36Sopenharmony_ci#define MCS_PANU2D3 0xCCA0 /* Panel U2D Setting 3 */ 4762306a36Sopenharmony_ci#define MCS_PAND2U1 0xCCB0 /* Panel D2U Setting 1 */ 4862306a36Sopenharmony_ci#define MCS_PAND2U2 0xCCC0 /* Panel D2U Setting 2 */ 4962306a36Sopenharmony_ci#define MCS_PAND2U3 0xCCD0 /* Panel D2U Setting 3 */ 5062306a36Sopenharmony_ci#define MCS_GOAVST 0xCE80 /* GOA VST Setting */ 5162306a36Sopenharmony_ci#define MCS_GOACLKA1 0xCEA0 /* GOA CLKA1 Setting */ 5262306a36Sopenharmony_ci#define MCS_GOACLKA3 0xCEB0 /* GOA CLKA3 Setting */ 5362306a36Sopenharmony_ci#define MCS_GOAECLK 0xCFC0 /* GOA ECLK Setting */ 5462306a36Sopenharmony_ci#define MCS_NO_DOC2 0xCFD0 /* Command not documented */ 5562306a36Sopenharmony_ci#define MCS_GVDDSET 0xD800 /* GVDD/NGVDD */ 5662306a36Sopenharmony_ci#define MCS_VCOMDC 0xD900 /* VCOM Voltage Setting */ 5762306a36Sopenharmony_ci#define MCS_GMCT2_2P 0xE100 /* Gamma Correction 2.2+ Setting */ 5862306a36Sopenharmony_ci#define MCS_GMCT2_2N 0xE200 /* Gamma Correction 2.2- Setting */ 5962306a36Sopenharmony_ci#define MCS_NO_DOC3 0xF5B6 /* Command not documented */ 6062306a36Sopenharmony_ci#define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ 6162306a36Sopenharmony_ci#define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define OTM8009A_HDISPLAY 480 6462306a36Sopenharmony_ci#define OTM8009A_VDISPLAY 800 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct otm8009a { 6762306a36Sopenharmony_ci struct device *dev; 6862306a36Sopenharmony_ci struct drm_panel panel; 6962306a36Sopenharmony_ci struct backlight_device *bl_dev; 7062306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 7162306a36Sopenharmony_ci struct regulator *supply; 7262306a36Sopenharmony_ci bool prepared; 7362306a36Sopenharmony_ci bool enabled; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const struct drm_display_mode modes[] = { 7762306a36Sopenharmony_ci { /* 50 Hz, preferred */ 7862306a36Sopenharmony_ci .clock = 29700, 7962306a36Sopenharmony_ci .hdisplay = 480, 8062306a36Sopenharmony_ci .hsync_start = 480 + 98, 8162306a36Sopenharmony_ci .hsync_end = 480 + 98 + 32, 8262306a36Sopenharmony_ci .htotal = 480 + 98 + 32 + 98, 8362306a36Sopenharmony_ci .vdisplay = 800, 8462306a36Sopenharmony_ci .vsync_start = 800 + 15, 8562306a36Sopenharmony_ci .vsync_end = 800 + 15 + 10, 8662306a36Sopenharmony_ci .vtotal = 800 + 15 + 10 + 14, 8762306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 8862306a36Sopenharmony_ci .width_mm = 52, 8962306a36Sopenharmony_ci .height_mm = 86, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci { /* 60 Hz */ 9262306a36Sopenharmony_ci .clock = 33000, 9362306a36Sopenharmony_ci .hdisplay = 480, 9462306a36Sopenharmony_ci .hsync_start = 480 + 70, 9562306a36Sopenharmony_ci .hsync_end = 480 + 70 + 32, 9662306a36Sopenharmony_ci .htotal = 480 + 70 + 32 + 72, 9762306a36Sopenharmony_ci .vdisplay = 800, 9862306a36Sopenharmony_ci .vsync_start = 800 + 15, 9962306a36Sopenharmony_ci .vsync_end = 800 + 15 + 10, 10062306a36Sopenharmony_ci .vtotal = 800 + 15 + 10 + 16, 10162306a36Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 10262306a36Sopenharmony_ci .width_mm = 52, 10362306a36Sopenharmony_ci .height_mm = 86, 10462306a36Sopenharmony_ci }, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return container_of(panel, struct otm8009a, panel); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data, 11362306a36Sopenharmony_ci size_t len) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0) 11862306a36Sopenharmony_ci dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n"); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define dcs_write_seq(ctx, seq...) \ 12262306a36Sopenharmony_ci({ \ 12362306a36Sopenharmony_ci static const u8 d[] = { seq }; \ 12462306a36Sopenharmony_ci otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \ 12562306a36Sopenharmony_ci}) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define dcs_write_cmd_at(ctx, cmd, seq...) \ 12862306a36Sopenharmony_ci({ \ 12962306a36Sopenharmony_ci dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \ 13062306a36Sopenharmony_ci dcs_write_seq(ctx, (cmd) >> 8, seq); \ 13162306a36Sopenharmony_ci}) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int otm8009a_init_sequence(struct otm8009a *ctx) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Enter CMD2 */ 13962306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Enter Orise Command2 */ 14262306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30); 14562306a36Sopenharmony_ci mdelay(10); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40); 14862306a36Sopenharmony_ci mdelay(10); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9); 15162306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34); 15262306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50); 15362306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E); 15462306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */ 15562306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01); 15662306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34); 15762306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33); 15862306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79); 15962306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B); 16062306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83); 16162306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83); 16262306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E); 16362306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); 16662306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 16762306a36Sopenharmony_ci 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); 16862306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 16962306a36Sopenharmony_ci 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); 17062306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 17162306a36Sopenharmony_ci 0x01, 0x02, 0x00, 0x00); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 17662306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17762306a36Sopenharmony_ci 0, 0, 0, 0, 0); 17862306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17962306a36Sopenharmony_ci 0, 0, 0, 0, 0); 18062306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 18162306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 18262306a36Sopenharmony_ci 0, 0, 0, 0, 0); 18362306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 18462306a36Sopenharmony_ci 4, 0, 0, 0, 0); 18562306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 18662306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 18762306a36Sopenharmony_ci 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 19062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00); 19162306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19262306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02); 19362306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 19462306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 19562306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 19662306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00); 19762306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19862306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01); 19962306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 20062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 20762306a36Sopenharmony_ci 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 20862306a36Sopenharmony_ci 0x01); 20962306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 21062306a36Sopenharmony_ci 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 21162306a36Sopenharmony_ci 0x01); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Exit CMD2 */ 21462306a36Sopenharmony_ci dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = mipi_dsi_dcs_nop(dsi); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 22162306a36Sopenharmony_ci if (ret) 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Wait for sleep out exit */ 22562306a36Sopenharmony_ci mdelay(120); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Default portrait 480x800 rgb24 */ 22862306a36Sopenharmony_ci dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1); 23162306a36Sopenharmony_ci if (ret) 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_page_address(dsi, 0, OTM8009A_VDISPLAY - 1); 23562306a36Sopenharmony_ci if (ret) 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* See otm8009a driver documentation for pixel format descriptions */ 23962306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT | 24062306a36Sopenharmony_ci MIPI_DCS_PIXEL_FMT_24BIT << 4); 24162306a36Sopenharmony_ci if (ret) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Disable CABC feature */ 24562306a36Sopenharmony_ci dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 24862306a36Sopenharmony_ci if (ret) 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ret = mipi_dsi_dcs_nop(dsi); 25262306a36Sopenharmony_ci if (ret) 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Send Command GRAM memory write (no parameters) */ 25662306a36Sopenharmony_ci dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Wait a short while to let the panel be ready before the 1st frame */ 25962306a36Sopenharmony_ci mdelay(10); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int otm8009a_disable(struct drm_panel *panel) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct otm8009a *ctx = panel_to_otm8009a(panel); 26762306a36Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 26862306a36Sopenharmony_ci int ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!ctx->enabled) 27162306a36Sopenharmony_ci return 0; /* This is not an issue so we return 0 here */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci backlight_disable(ctx->bl_dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 28062306a36Sopenharmony_ci if (ret) 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci msleep(120); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ctx->enabled = false; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int otm8009a_unprepare(struct drm_panel *panel) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct otm8009a *ctx = panel_to_otm8009a(panel); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!ctx->prepared) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (ctx->reset_gpio) { 29862306a36Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 1); 29962306a36Sopenharmony_ci msleep(20); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci regulator_disable(ctx->supply); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ctx->prepared = false; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int otm8009a_prepare(struct drm_panel *panel) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct otm8009a *ctx = panel_to_otm8009a(panel); 31262306a36Sopenharmony_ci int ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (ctx->prepared) 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = regulator_enable(ctx->supply); 31862306a36Sopenharmony_ci if (ret < 0) { 31962306a36Sopenharmony_ci dev_err(panel->dev, "failed to enable supply: %d\n", ret); 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (ctx->reset_gpio) { 32462306a36Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 0); 32562306a36Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 1); 32662306a36Sopenharmony_ci msleep(20); 32762306a36Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 0); 32862306a36Sopenharmony_ci msleep(100); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ret = otm8009a_init_sequence(ctx); 33262306a36Sopenharmony_ci if (ret) 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ctx->prepared = true; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int otm8009a_enable(struct drm_panel *panel) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct otm8009a *ctx = panel_to_otm8009a(panel); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (ctx->enabled) 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci backlight_enable(ctx->bl_dev); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ctx->enabled = true; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int otm8009a_get_modes(struct drm_panel *panel, 35562306a36Sopenharmony_ci struct drm_connector *connector) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct drm_display_mode *mode; 35862306a36Sopenharmony_ci unsigned int num_modes = ARRAY_SIZE(modes); 35962306a36Sopenharmony_ci unsigned int i; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci for (i = 0; i < num_modes; i++) { 36262306a36Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &modes[i]); 36362306a36Sopenharmony_ci if (!mode) { 36462306a36Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 36562306a36Sopenharmony_ci modes[i].hdisplay, 36662306a36Sopenharmony_ci modes[i].vdisplay, 36762306a36Sopenharmony_ci drm_mode_vrefresh(&modes[i])); 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Setting first mode as preferred */ 37462306a36Sopenharmony_ci if (!i) 37562306a36Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci drm_mode_set_name(mode); 37862306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 38262306a36Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return num_modes; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct drm_panel_funcs otm8009a_drm_funcs = { 38862306a36Sopenharmony_ci .disable = otm8009a_disable, 38962306a36Sopenharmony_ci .unprepare = otm8009a_unprepare, 39062306a36Sopenharmony_ci .prepare = otm8009a_prepare, 39162306a36Sopenharmony_ci .enable = otm8009a_enable, 39262306a36Sopenharmony_ci .get_modes = otm8009a_get_modes, 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * DSI-BASED BACKLIGHT 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int otm8009a_backlight_update_status(struct backlight_device *bd) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct otm8009a *ctx = bl_get_data(bd); 40262306a36Sopenharmony_ci u8 data[2]; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!ctx->prepared) { 40562306a36Sopenharmony_ci dev_dbg(&bd->dev, "lcd not ready yet for setting its backlight!\n"); 40662306a36Sopenharmony_ci return -ENXIO; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (bd->props.power <= FB_BLANK_NORMAL) { 41062306a36Sopenharmony_ci /* Power on the backlight with the requested brightness 41162306a36Sopenharmony_ci * Note We can not use mipi_dsi_dcs_set_display_brightness() 41262306a36Sopenharmony_ci * as otm8009a driver support only 8-bit brightness (1 param). 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS; 41562306a36Sopenharmony_ci data[1] = bd->props.brightness; 41662306a36Sopenharmony_ci otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* set Brightness Control & Backlight on */ 41962306a36Sopenharmony_ci data[1] = 0x24; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci } else { 42262306a36Sopenharmony_ci /* Power off the backlight: set Brightness Control & Bl off */ 42362306a36Sopenharmony_ci data[1] = 0; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Update Brightness Control & Backlight */ 42762306a36Sopenharmony_ci data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY; 42862306a36Sopenharmony_ci otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic const struct backlight_ops otm8009a_backlight_ops = { 43462306a36Sopenharmony_ci .update_status = otm8009a_backlight_update_status, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int otm8009a_probe(struct mipi_dsi_device *dsi) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct device *dev = &dsi->dev; 44062306a36Sopenharmony_ci struct otm8009a *ctx; 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 44462306a36Sopenharmony_ci if (!ctx) 44562306a36Sopenharmony_ci return -ENOMEM; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 44862306a36Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) { 44962306a36Sopenharmony_ci dev_err(dev, "cannot get reset-gpio\n"); 45062306a36Sopenharmony_ci return PTR_ERR(ctx->reset_gpio); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ctx->supply = devm_regulator_get(dev, "power"); 45462306a36Sopenharmony_ci if (IS_ERR(ctx->supply)) { 45562306a36Sopenharmony_ci ret = PTR_ERR(ctx->supply); 45662306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 45762306a36Sopenharmony_ci dev_err(dev, "failed to request regulator: %d\n", ret); 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ctx->dev = dev; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci dsi->lanes = 2; 46662306a36Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 46762306a36Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 46862306a36Sopenharmony_ci MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs, 47162306a36Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev), 47462306a36Sopenharmony_ci dev, ctx, 47562306a36Sopenharmony_ci &otm8009a_backlight_ops, 47662306a36Sopenharmony_ci NULL); 47762306a36Sopenharmony_ci if (IS_ERR(ctx->bl_dev)) { 47862306a36Sopenharmony_ci ret = PTR_ERR(ctx->bl_dev); 47962306a36Sopenharmony_ci dev_err(dev, "failed to register backlight: %d\n", ret); 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX; 48462306a36Sopenharmony_ci ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT; 48562306a36Sopenharmony_ci ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; 48662306a36Sopenharmony_ci ctx->bl_dev->props.type = BACKLIGHT_RAW; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci drm_panel_add(&ctx->panel); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = mipi_dsi_attach(dsi); 49162306a36Sopenharmony_ci if (ret < 0) { 49262306a36Sopenharmony_ci dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n"); 49362306a36Sopenharmony_ci drm_panel_remove(&ctx->panel); 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void otm8009a_remove(struct mipi_dsi_device *dsi) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci mipi_dsi_detach(dsi); 50562306a36Sopenharmony_ci drm_panel_remove(&ctx->panel); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic const struct of_device_id orisetech_otm8009a_of_match[] = { 50962306a36Sopenharmony_ci { .compatible = "orisetech,otm8009a" }, 51062306a36Sopenharmony_ci { } 51162306a36Sopenharmony_ci}; 51262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic struct mipi_dsi_driver orisetech_otm8009a_driver = { 51562306a36Sopenharmony_ci .probe = otm8009a_probe, 51662306a36Sopenharmony_ci .remove = otm8009a_remove, 51762306a36Sopenharmony_ci .driver = { 51862306a36Sopenharmony_ci .name = "panel-orisetech-otm8009a", 51962306a36Sopenharmony_ci .of_match_table = orisetech_otm8009a_of_match, 52062306a36Sopenharmony_ci }, 52162306a36Sopenharmony_ci}; 52262306a36Sopenharmony_cimodule_mipi_dsi_driver(orisetech_otm8009a_driver); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ciMODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); 52562306a36Sopenharmony_ciMODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 52662306a36Sopenharmony_ciMODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel"); 52762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 528