284 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2022 Joel Selvaraj <jo@jsfamily.in>4 * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree:5 * Copyright (c) 2013, The Linux Foundation. All rights reserved.6 */7 8#include <linux/delay.h>9#include <linux/gpio/consumer.h>10#include <linux/regulator/consumer.h>11#include <linux/module.h>12#include <linux/of.h>13 14#include <video/mipi_display.h>15 16#include <drm/drm_mipi_dsi.h>17#include <drm/drm_modes.h>18#include <drm/drm_panel.h>19 20static const char * const regulator_names[] = {21 "vddio",22 "vddpos",23 "vddneg",24};25 26static const unsigned long regulator_enable_loads[] = {27 62000,28 100000,29 10000030};31 32struct ebbg_ft8719 {33 struct drm_panel panel;34 struct mipi_dsi_device *dsi;35 36 struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];37 38 struct gpio_desc *reset_gpio;39};40 41static inline42struct ebbg_ft8719 *to_ebbg_ft8719(struct drm_panel *panel)43{44 return container_of(panel, struct ebbg_ft8719, panel);45}46 47static void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx)48{49 gpiod_set_value_cansleep(ctx->reset_gpio, 0);50 usleep_range(4000, 5000);51 gpiod_set_value_cansleep(ctx->reset_gpio, 1);52 usleep_range(1000, 2000);53 gpiod_set_value_cansleep(ctx->reset_gpio, 0);54 usleep_range(15000, 16000);55}56 57static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx)58{59 struct mipi_dsi_device *dsi = ctx->dsi;60 struct device *dev = &dsi->dev;61 int ret;62 63 dsi->mode_flags |= MIPI_DSI_MODE_LPM;64 65 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);66 if (ret < 0) {67 dev_err(dev, "Failed to set display brightness: %d\n", ret);68 return ret;69 }70 71 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);72 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);73 74 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);75 if (ret < 0) {76 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);77 return ret;78 }79 msleep(90);80 81 ret = mipi_dsi_dcs_set_display_on(dsi);82 if (ret < 0) {83 dev_err(dev, "Failed to set display on: %d\n", ret);84 return ret;85 }86 87 return 0;88}89 90static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx)91{92 struct mipi_dsi_device *dsi = ctx->dsi;93 struct device *dev = &dsi->dev;94 int ret;95 96 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;97 98 ret = mipi_dsi_dcs_set_display_off(dsi);99 if (ret < 0) {100 dev_err(dev, "Failed to set display off: %d\n", ret);101 return ret;102 }103 usleep_range(10000, 11000);104 105 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);106 if (ret < 0) {107 dev_err(dev, "Failed to enter sleep mode: %d\n", ret);108 return ret;109 }110 msleep(90);111 112 return 0;113}114 115static int ebbg_ft8719_prepare(struct drm_panel *panel)116{117 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);118 struct device *dev = &ctx->dsi->dev;119 int ret;120 121 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);122 if (ret < 0)123 return ret;124 125 ebbg_ft8719_reset(ctx);126 127 ret = ebbg_ft8719_on(ctx);128 if (ret < 0) {129 dev_err(dev, "Failed to initialize panel: %d\n", ret);130 gpiod_set_value_cansleep(ctx->reset_gpio, 1);131 return ret;132 }133 134 return 0;135}136 137static int ebbg_ft8719_unprepare(struct drm_panel *panel)138{139 struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel);140 struct device *dev = &ctx->dsi->dev;141 int ret;142 143 ret = ebbg_ft8719_off(ctx);144 if (ret < 0)145 dev_err(dev, "Failed to un-initialize panel: %d\n", ret);146 147 gpiod_set_value_cansleep(ctx->reset_gpio, 1);148 149 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);150 if (ret)151 dev_err(panel->dev, "Failed to disable regulators: %d\n", ret);152 153 return 0;154}155 156static const struct drm_display_mode ebbg_ft8719_mode = {157 .clock = (1080 + 28 + 4 + 16) * (2246 + 120 + 4 + 12) * 60 / 1000,158 .hdisplay = 1080,159 .hsync_start = 1080 + 28,160 .hsync_end = 1080 + 28 + 4,161 .htotal = 1080 + 28 + 4 + 16,162 .vdisplay = 2246,163 .vsync_start = 2246 + 120,164 .vsync_end = 2246 + 120 + 4,165 .vtotal = 2246 + 120 + 4 + 12,166 .width_mm = 68,167 .height_mm = 141,168};169 170static int ebbg_ft8719_get_modes(struct drm_panel *panel,171 struct drm_connector *connector)172{173 struct drm_display_mode *mode;174 175 mode = drm_mode_duplicate(connector->dev, &ebbg_ft8719_mode);176 if (!mode)177 return -ENOMEM;178 179 drm_mode_set_name(mode);180 181 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;182 connector->display_info.width_mm = mode->width_mm;183 connector->display_info.height_mm = mode->height_mm;184 drm_mode_probed_add(connector, mode);185 186 return 1;187}188 189static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = {190 .prepare = ebbg_ft8719_prepare,191 .unprepare = ebbg_ft8719_unprepare,192 .get_modes = ebbg_ft8719_get_modes,193};194 195static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi)196{197 struct device *dev = &dsi->dev;198 struct ebbg_ft8719 *ctx;199 int i, ret;200 201 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);202 if (!ctx)203 return -ENOMEM;204 205 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)206 ctx->supplies[i].supply = regulator_names[i];207 208 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),209 ctx->supplies);210 if (ret < 0)211 return dev_err_probe(dev, ret, "Failed to get regulators\n");212 213 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {214 ret = regulator_set_load(ctx->supplies[i].consumer,215 regulator_enable_loads[i]);216 if (ret)217 return dev_err_probe(dev, ret,218 "Failed to set regulator load\n");219 }220 221 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);222 if (IS_ERR(ctx->reset_gpio))223 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),224 "Failed to get reset-gpios\n");225 226 ctx->dsi = dsi;227 mipi_dsi_set_drvdata(dsi, ctx);228 229 dsi->lanes = 4;230 dsi->format = MIPI_DSI_FMT_RGB888;231 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |232 MIPI_DSI_CLOCK_NON_CONTINUOUS;233 234 drm_panel_init(&ctx->panel, dev, &ebbg_ft8719_panel_funcs,235 DRM_MODE_CONNECTOR_DSI);236 237 ret = drm_panel_of_backlight(&ctx->panel);238 if (ret)239 return dev_err_probe(dev, ret, "Failed to get backlight\n");240 241 drm_panel_add(&ctx->panel);242 243 ret = mipi_dsi_attach(dsi);244 if (ret < 0) {245 dev_err(dev, "Failed to attach to DSI host: %d\n", ret);246 drm_panel_remove(&ctx->panel);247 return ret;248 }249 250 return 0;251}252 253static void ebbg_ft8719_remove(struct mipi_dsi_device *dsi)254{255 struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi);256 int ret;257 258 ret = mipi_dsi_detach(dsi);259 if (ret < 0)260 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);261 262 drm_panel_remove(&ctx->panel);263}264 265static const struct of_device_id ebbg_ft8719_of_match[] = {266 { .compatible = "ebbg,ft8719" },267 { /* sentinel */ }268};269MODULE_DEVICE_TABLE(of, ebbg_ft8719_of_match);270 271static struct mipi_dsi_driver ebbg_ft8719_driver = {272 .probe = ebbg_ft8719_probe,273 .remove = ebbg_ft8719_remove,274 .driver = {275 .name = "panel-ebbg-ft8719",276 .of_match_table = ebbg_ft8719_of_match,277 },278};279module_mipi_dsi_driver(ebbg_ft8719_driver);280 281MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>");282MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel");283MODULE_LICENSE("GPL v2");284