263 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2018 Amarula Solutions4 * Author: Jagan Teki <jagan@amarulasolutions.com>5 */6 7#include <drm/drm_mipi_dsi.h>8#include <drm/drm_modes.h>9#include <drm/drm_panel.h>10 11#include <linux/gpio/consumer.h>12#include <linux/delay.h>13#include <linux/module.h>14#include <linux/mod_devicetable.h>15#include <linux/regulator/consumer.h>16 17#define FEIYANG_INIT_CMD_LEN 218 19struct feiyang {20 struct drm_panel panel;21 struct mipi_dsi_device *dsi;22 23 struct regulator *dvdd;24 struct regulator *avdd;25 struct gpio_desc *reset;26};27 28static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel)29{30 return container_of(panel, struct feiyang, panel);31}32 33struct feiyang_init_cmd {34 u8 data[FEIYANG_INIT_CMD_LEN];35};36 37static const struct feiyang_init_cmd feiyang_init_cmds[] = {38 { .data = { 0x80, 0x58 } },39 { .data = { 0x81, 0x47 } },40 { .data = { 0x82, 0xD4 } },41 { .data = { 0x83, 0x88 } },42 { .data = { 0x84, 0xA9 } },43 { .data = { 0x85, 0xC3 } },44 { .data = { 0x86, 0x82 } },45};46 47static int feiyang_prepare(struct drm_panel *panel)48{49 struct feiyang *ctx = panel_to_feiyang(panel);50 struct mipi_dsi_device *dsi = ctx->dsi;51 unsigned int i;52 int ret;53 54 ret = regulator_enable(ctx->dvdd);55 if (ret)56 return ret;57 58 /* T1 (dvdd start + dvdd rise) 0 < T1 <= 10ms */59 msleep(10);60 61 ret = regulator_enable(ctx->avdd);62 if (ret)63 return ret;64 65 /* T3 (dvdd rise + avdd start + avdd rise) T3 >= 20ms */66 msleep(20);67 68 gpiod_set_value(ctx->reset, 0);69 70 /*71 * T5 + T6 (avdd rise + video & logic signal rise)72 * T5 >= 10ms, 0 < T6 <= 10ms73 */74 msleep(20);75 76 gpiod_set_value(ctx->reset, 1);77 78 /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */79 msleep(200);80 81 for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) {82 const struct feiyang_init_cmd *cmd =83 &feiyang_init_cmds[i];84 85 ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data,86 FEIYANG_INIT_CMD_LEN);87 if (ret < 0)88 return ret;89 }90 91 return 0;92}93 94static int feiyang_enable(struct drm_panel *panel)95{96 struct feiyang *ctx = panel_to_feiyang(panel);97 98 /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */99 msleep(200);100 101 mipi_dsi_dcs_set_display_on(ctx->dsi);102 103 return 0;104}105 106static int feiyang_disable(struct drm_panel *panel)107{108 struct feiyang *ctx = panel_to_feiyang(panel);109 110 return mipi_dsi_dcs_set_display_off(ctx->dsi);111}112 113static int feiyang_unprepare(struct drm_panel *panel)114{115 struct feiyang *ctx = panel_to_feiyang(panel);116 int ret;117 118 ret = mipi_dsi_dcs_set_display_off(ctx->dsi);119 if (ret < 0)120 dev_err(panel->dev, "failed to set display off: %d\n", ret);121 122 ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);123 if (ret < 0)124 dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);125 126 /* T13 (backlight fall + video & logic signal fall) T13 >= 200ms */127 msleep(200);128 129 gpiod_set_value(ctx->reset, 0);130 131 regulator_disable(ctx->avdd);132 133 /* T11 (dvdd rise to fall) 0 < T11 <= 10ms */134 msleep(10);135 136 regulator_disable(ctx->dvdd);137 138 return 0;139}140 141static const struct drm_display_mode feiyang_default_mode = {142 .clock = 55000,143 144 .hdisplay = 1024,145 .hsync_start = 1024 + 310,146 .hsync_end = 1024 + 310 + 20,147 .htotal = 1024 + 310 + 20 + 90,148 149 .vdisplay = 600,150 .vsync_start = 600 + 12,151 .vsync_end = 600 + 12 + 2,152 .vtotal = 600 + 12 + 2 + 21,153 154 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,155};156 157static int feiyang_get_modes(struct drm_panel *panel,158 struct drm_connector *connector)159{160 struct feiyang *ctx = panel_to_feiyang(panel);161 struct drm_display_mode *mode;162 163 mode = drm_mode_duplicate(connector->dev, &feiyang_default_mode);164 if (!mode) {165 dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",166 feiyang_default_mode.hdisplay,167 feiyang_default_mode.vdisplay,168 drm_mode_vrefresh(&feiyang_default_mode));169 return -ENOMEM;170 }171 172 drm_mode_set_name(mode);173 174 drm_mode_probed_add(connector, mode);175 176 return 1;177}178 179static const struct drm_panel_funcs feiyang_funcs = {180 .disable = feiyang_disable,181 .unprepare = feiyang_unprepare,182 .prepare = feiyang_prepare,183 .enable = feiyang_enable,184 .get_modes = feiyang_get_modes,185};186 187static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)188{189 struct feiyang *ctx;190 int ret;191 192 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);193 if (!ctx)194 return -ENOMEM;195 196 mipi_dsi_set_drvdata(dsi, ctx);197 ctx->dsi = dsi;198 199 drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs,200 DRM_MODE_CONNECTOR_DSI);201 202 ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");203 if (IS_ERR(ctx->dvdd))204 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->dvdd),205 "Couldn't get dvdd regulator\n");206 207 ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");208 if (IS_ERR(ctx->avdd))209 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd),210 "Couldn't get avdd regulator\n");211 212 ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);213 if (IS_ERR(ctx->reset))214 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),215 "Couldn't get our reset GPIO\n");216 217 ret = drm_panel_of_backlight(&ctx->panel);218 if (ret)219 return ret;220 221 drm_panel_add(&ctx->panel);222 223 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;224 dsi->format = MIPI_DSI_FMT_RGB888;225 dsi->lanes = 4;226 227 ret = mipi_dsi_attach(dsi);228 if (ret < 0) {229 drm_panel_remove(&ctx->panel);230 return ret;231 }232 233 return 0;234}235 236static void feiyang_dsi_remove(struct mipi_dsi_device *dsi)237{238 struct feiyang *ctx = mipi_dsi_get_drvdata(dsi);239 240 mipi_dsi_detach(dsi);241 drm_panel_remove(&ctx->panel);242}243 244static const struct of_device_id feiyang_of_match[] = {245 { .compatible = "feiyang,fy07024di26a30d", },246 { /* sentinel */ }247};248MODULE_DEVICE_TABLE(of, feiyang_of_match);249 250static struct mipi_dsi_driver feiyang_driver = {251 .probe = feiyang_dsi_probe,252 .remove = feiyang_dsi_remove,253 .driver = {254 .name = "feiyang-fy07024di26a30d",255 .of_match_table = feiyang_of_match,256 },257};258module_mipi_dsi_driver(feiyang_driver);259 260MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");261MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel");262MODULE_LICENSE("GPL");263