406 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2020 BSH Hausgerate GmbH4 */5 6#include <linux/delay.h>7#include <linux/device.h>8#include <linux/err.h>9#include <linux/errno.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/of.h>13 14#include <linux/gpio/consumer.h>15#include <linux/regulator/consumer.h>16 17#include <drm/drm_mipi_dsi.h>18#include <drm/drm_modes.h>19#include <drm/drm_panel.h>20 21#include <video/mipi_display.h>22 23#define ILI9805_EXTCMD_CMD_SET_ENABLE_REG (0xff)24#define ILI9805_SETEXTC_PARAMETER1 (0xff)25#define ILI9805_SETEXTC_PARAMETER2 (0x98)26#define ILI9805_SETEXTC_PARAMETER3 (0x05)27 28#define ILI9805_INSTR(_delay, ...) { \29 .delay = (_delay), \30 .len = sizeof((u8[]) {__VA_ARGS__}), \31 .data = (u8[]){__VA_ARGS__} \32 }33 34struct ili9805_instr {35 size_t len;36 const u8 *data;37 u32 delay;38};39 40struct ili9805_desc {41 const char *name;42 const struct ili9805_instr *init;43 const size_t init_length;44 const struct drm_display_mode *mode;45 u32 width_mm;46 u32 height_mm;47};48 49struct ili9805 {50 struct drm_panel panel;51 struct mipi_dsi_device *dsi;52 const struct ili9805_desc *desc;53 54 struct regulator *dvdd;55 struct regulator *avdd;56 struct gpio_desc *reset_gpio;57};58 59static const struct ili9805_instr gpm1780a0_init[] = {60 ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,61 ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),62 ILI9805_INSTR(100, 0xFD, 0x0F, 0x10, 0x44, 0x00),63 ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,64 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00),65 ILI9805_INSTR(0, 0xB8, 0x62),66 ILI9805_INSTR(0, 0xF1, 0x00),67 ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),68 ILI9805_INSTR(0, 0xF3, 0x60, 0x83, 0x04),69 ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),70 ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),71 ILI9805_INSTR(0, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,72 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),73 ILI9805_INSTR(0, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,74 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),75 ILI9805_INSTR(10, 0xc1, 0x13, 0x39, 0x19, 0x06),76 ILI9805_INSTR(10, 0xc7, 0xe5),77 ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),78 ILI9805_INSTR(10, 0xB4, 0x02),79 ILI9805_INSTR(0, 0xBB, 0x14, 0x55),80 ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x08),81 ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),82 ILI9805_INSTR(0, 0x20),83 ILI9805_INSTR(0, 0xB0, 0x01),84 ILI9805_INSTR(0, 0xB6, 0x31, 0x00, 0xef),85 ILI9805_INSTR(0, 0xDF, 0x23),86 ILI9805_INSTR(0, 0xB9, 0x02, 0x00),87};88 89static const struct ili9805_instr tm041xdhg01_init[] = {90 ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,91 ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),92 ILI9805_INSTR(100, 0xFD, 0x0F, 0x13, 0x44, 0x00),93 ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,94 0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01),95 ILI9805_INSTR(0, 0xB8, 0x74),96 ILI9805_INSTR(0, 0xF1, 0x00),97 ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),98 ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),99 ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),100 ILI9805_INSTR(0, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,101 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),102 ILI9805_INSTR(0, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,103 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),104 ILI9805_INSTR(10, 0xc1, 0x15, 0x03, 0x03, 0x31),105 ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),106 ILI9805_INSTR(10, 0xB4, 0x02),107 ILI9805_INSTR(0, 0xBB, 0x14, 0x55),108 ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x0a),109 ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),110 ILI9805_INSTR(0, 0x20),111 ILI9805_INSTR(0, 0xB0, 0x00),112 ILI9805_INSTR(0, 0xB6, 0x01),113 ILI9805_INSTR(0, 0xc2, 0x11),114 ILI9805_INSTR(0, 0x51, 0xFF),115 ILI9805_INSTR(0, 0x53, 0x24),116 ILI9805_INSTR(0, 0x55, 0x00),117};118 119static inline struct ili9805 *panel_to_ili9805(struct drm_panel *panel)120{121 return container_of(panel, struct ili9805, panel);122}123 124static int ili9805_power_on(struct ili9805 *ctx)125{126 struct mipi_dsi_device *dsi = ctx->dsi;127 struct device *dev = &dsi->dev;128 int ret;129 130 ret = regulator_enable(ctx->avdd);131 if (ret) {132 dev_err(dev, "Failed to enable avdd regulator (%d)\n", ret);133 return ret;134 }135 136 ret = regulator_enable(ctx->dvdd);137 if (ret) {138 dev_err(dev, "Failed to enable dvdd regulator (%d)\n", ret);139 regulator_disable(ctx->avdd);140 return ret;141 }142 143 gpiod_set_value(ctx->reset_gpio, 0);144 usleep_range(5000, 10000);145 gpiod_set_value(ctx->reset_gpio, 1);146 msleep(120);147 148 return 0;149}150 151static int ili9805_power_off(struct ili9805 *ctx)152{153 gpiod_set_value(ctx->reset_gpio, 0);154 regulator_disable(ctx->dvdd);155 regulator_disable(ctx->avdd);156 157 return 0;158}159 160static int ili9805_activate(struct ili9805 *ctx)161{162 struct mipi_dsi_device *dsi = ctx->dsi;163 struct device *dev = &dsi->dev;164 int i, ret;165 166 for (i = 0; i < ctx->desc->init_length; i++) {167 const struct ili9805_instr *instr = &ctx->desc->init[i];168 169 ret = mipi_dsi_dcs_write_buffer(ctx->dsi, instr->data, instr->len);170 if (ret < 0)171 return ret;172 173 if (instr->delay > 0)174 msleep(instr->delay);175 }176 177 ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);178 if (ret) {179 dev_err(dev, "Failed to exit sleep mode (%d)\n", ret);180 return ret;181 }182 183 usleep_range(5000, 6000);184 185 ret = mipi_dsi_dcs_set_display_on(ctx->dsi);186 if (ret) {187 dev_err(dev, "Failed to set display ON (%d)\n", ret);188 return ret;189 }190 191 return 0;192}193 194static int ili9805_prepare(struct drm_panel *panel)195{196 struct ili9805 *ctx = panel_to_ili9805(panel);197 int ret;198 199 ret = ili9805_power_on(ctx);200 if (ret)201 return ret;202 203 ret = ili9805_activate(ctx);204 if (ret) {205 ili9805_power_off(ctx);206 return ret;207 }208 209 return 0;210}211 212static int ili9805_deactivate(struct ili9805 *ctx)213{214 struct mipi_dsi_device *dsi = ctx->dsi;215 struct device *dev = &dsi->dev;216 int ret;217 218 ret = mipi_dsi_dcs_set_display_off(ctx->dsi);219 if (ret < 0) {220 dev_err(dev, "Failed to set display OFF (%d)\n", ret);221 return ret;222 }223 224 usleep_range(5000, 10000);225 226 ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);227 if (ret < 0) {228 dev_err(dev, "Failed to enter sleep mode (%d)\n", ret);229 return ret;230 }231 232 return 0;233}234 235static int ili9805_unprepare(struct drm_panel *panel)236{237 struct ili9805 *ctx = panel_to_ili9805(panel);238 239 ili9805_deactivate(ctx);240 ili9805_power_off(ctx);241 242 return 0;243}244 245static const struct drm_display_mode gpm1780a0_timing = {246 .clock = 26227,247 248 .hdisplay = 480,249 .hsync_start = 480 + 10,250 .hsync_end = 480 + 10 + 2,251 .htotal = 480 + 10 + 2 + 36,252 253 .vdisplay = 480,254 .vsync_start = 480 + 2,255 .vsync_end = 480 + 10 + 4,256 .vtotal = 480 + 2 + 4 + 10,257};258 259static const struct drm_display_mode tm041xdhg01_timing = {260 .clock = 26227,261 262 .hdisplay = 480,263 .hsync_start = 480 + 10,264 .hsync_end = 480 + 10 + 2,265 .htotal = 480 + 10 + 2 + 36,266 267 .vdisplay = 768,268 .vsync_start = 768 + 2,269 .vsync_end = 768 + 10 + 4,270 .vtotal = 768 + 2 + 4 + 10,271};272 273static int ili9805_get_modes(struct drm_panel *panel,274 struct drm_connector *connector)275{276 struct ili9805 *ctx = panel_to_ili9805(panel);277 struct drm_display_mode *mode;278 279 mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);280 if (!mode) {281 dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",282 ctx->desc->mode->hdisplay,283 ctx->desc->mode->vdisplay,284 drm_mode_vrefresh(ctx->desc->mode));285 return -ENOMEM;286 }287 288 drm_mode_set_name(mode);289 290 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;291 drm_mode_probed_add(connector, mode);292 293 connector->display_info.width_mm = mode->width_mm;294 connector->display_info.height_mm = mode->height_mm;295 296 return 1;297}298 299static const struct drm_panel_funcs ili9805_funcs = {300 .prepare = ili9805_prepare,301 .unprepare = ili9805_unprepare,302 .get_modes = ili9805_get_modes,303};304 305static int ili9805_dsi_probe(struct mipi_dsi_device *dsi)306{307 struct ili9805 *ctx;308 int ret;309 310 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);311 if (!ctx)312 return -ENOMEM;313 mipi_dsi_set_drvdata(dsi, ctx);314 ctx->dsi = dsi;315 ctx->desc = of_device_get_match_data(&dsi->dev);316 317 dsi->format = MIPI_DSI_FMT_RGB888;318 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |319 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM |320 MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_NO_EOT_PACKET;321 dsi->lanes = 2;322 323 drm_panel_init(&ctx->panel, &dsi->dev, &ili9805_funcs,324 DRM_MODE_CONNECTOR_DSI);325 326 ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");327 if (IS_ERR(ctx->dvdd))328 return PTR_ERR(ctx->dvdd);329 ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");330 if (IS_ERR(ctx->avdd))331 return PTR_ERR(ctx->avdd);332 333 ctx->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);334 if (IS_ERR(ctx->reset_gpio)) {335 dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");336 return PTR_ERR(ctx->reset_gpio);337 }338 339 ctx->panel.prepare_prev_first = true;340 ret = drm_panel_of_backlight(&ctx->panel);341 if (ret)342 return ret;343 344 drm_panel_add(&ctx->panel);345 346 ret = mipi_dsi_attach(dsi);347 if (ret < 0) {348 dev_err(&dsi->dev, "mipi_dsi_attach failed: %d\n", ret);349 drm_panel_remove(&ctx->panel);350 return ret;351 }352 353 return 0;354}355 356static void ili9805_dsi_remove(struct mipi_dsi_device *dsi)357{358 struct ili9805 *ctx = mipi_dsi_get_drvdata(dsi);359 int ret;360 361 ret = mipi_dsi_detach(dsi);362 if (ret < 0)363 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",364 ret);365 366 drm_panel_remove(&ctx->panel);367}368 369static const struct ili9805_desc gpm1780a0_desc = {370 .init = gpm1780a0_init,371 .init_length = ARRAY_SIZE(gpm1780a0_init),372 .mode = &gpm1780a0_timing,373 .width_mm = 65,374 .height_mm = 65,375};376 377static const struct ili9805_desc tm041xdhg01_desc = {378 .init = tm041xdhg01_init,379 .init_length = ARRAY_SIZE(tm041xdhg01_init),380 .mode = &tm041xdhg01_timing,381 .width_mm = 42,382 .height_mm = 96,383};384 385static const struct of_device_id ili9805_of_match[] = {386 { .compatible = "giantplus,gpm1790a0", .data = &gpm1780a0_desc },387 { .compatible = "tianma,tm041xdhg01", .data = &tm041xdhg01_desc },388 { }389};390MODULE_DEVICE_TABLE(of, ili9805_of_match);391 392static struct mipi_dsi_driver ili9805_dsi_driver = {393 .probe = ili9805_dsi_probe,394 .remove = ili9805_dsi_remove,395 .driver = {396 .name = "ili9805-dsi",397 .of_match_table = ili9805_of_match,398 },399};400module_mipi_dsi_driver(ili9805_dsi_driver);401 402MODULE_AUTHOR("Matthias Proske <Matthias.Proske@bshg.com>");403MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");404MODULE_DESCRIPTION("Ilitek ILI9805 Controller Driver");405MODULE_LICENSE("GPL");406