477 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 InforceComputing4 * Author: Vinay Simha BN <simhavcs@gmail.com>5 *6 * Copyright (C) 2016 Linaro Ltd7 * Author: Sumit Semwal <sumit.semwal@linaro.org>8 */9 10#include <linux/backlight.h>11#include <linux/delay.h>12#include <linux/gpio/consumer.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/regulator/consumer.h>16 17#include <video/mipi_display.h>18 19#include <drm/drm_crtc.h>20#include <drm/drm_mipi_dsi.h>21#include <drm/drm_modes.h>22#include <drm/drm_panel.h>23 24static const char * const regulator_names[] = {25 "vddp",26 "iovcc"27};28 29struct jdi_panel {30 struct drm_panel base;31 struct mipi_dsi_device *dsi;32 33 struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];34 35 struct gpio_desc *enable_gpio;36 struct gpio_desc *reset_gpio;37 struct gpio_desc *dcdc_en_gpio;38 struct backlight_device *backlight;39 40 const struct drm_display_mode *mode;41};42 43static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)44{45 return container_of(panel, struct jdi_panel, base);46}47 48static int jdi_panel_init(struct jdi_panel *jdi)49{50 struct mipi_dsi_device *dsi = jdi->dsi;51 struct device *dev = &jdi->dsi->dev;52 int ret;53 54 dsi->mode_flags |= MIPI_DSI_MODE_LPM;55 56 ret = mipi_dsi_dcs_soft_reset(dsi);57 if (ret < 0)58 return ret;59 60 usleep_range(10000, 20000);61 62 ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);63 if (ret < 0) {64 dev_err(dev, "failed to set pixel format: %d\n", ret);65 return ret;66 }67 68 ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1);69 if (ret < 0) {70 dev_err(dev, "failed to set column address: %d\n", ret);71 return ret;72 }73 74 ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1);75 if (ret < 0) {76 dev_err(dev, "failed to set page address: %d\n", ret);77 return ret;78 }79 80 /*81 * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers82 * are active83 * BIT(3) BL = 1 Backlight Control On84 * BIT(2) DD = 0 Display Dimming is Off85 */86 ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,87 (u8[]){ 0x24 }, 1);88 if (ret < 0) {89 dev_err(dev, "failed to write control display: %d\n", ret);90 return ret;91 }92 93 /* CABC off */94 ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,95 (u8[]){ 0x00 }, 1);96 if (ret < 0) {97 dev_err(dev, "failed to set cabc off: %d\n", ret);98 return ret;99 }100 101 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);102 if (ret < 0) {103 dev_err(dev, "failed to set exit sleep mode: %d\n", ret);104 return ret;105 }106 107 msleep(120);108 109 ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);110 if (ret < 0) {111 dev_err(dev, "failed to set mcap: %d\n", ret);112 return ret;113 }114 115 mdelay(10);116 117 /* Interface setting, video mode */118 ret = mipi_dsi_generic_write(dsi, (u8[])119 {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);120 if (ret < 0) {121 dev_err(dev, "failed to set display interface setting: %d\n"122 , ret);123 return ret;124 }125 126 mdelay(20);127 128 ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);129 if (ret < 0) {130 dev_err(dev, "failed to set default values for mcap: %d\n"131 , ret);132 return ret;133 }134 135 return 0;136}137 138static int jdi_panel_on(struct jdi_panel *jdi)139{140 struct mipi_dsi_device *dsi = jdi->dsi;141 struct device *dev = &jdi->dsi->dev;142 int ret;143 144 dsi->mode_flags |= MIPI_DSI_MODE_LPM;145 146 ret = mipi_dsi_dcs_set_display_on(dsi);147 if (ret < 0)148 dev_err(dev, "failed to set display on: %d\n", ret);149 150 return ret;151}152 153static void jdi_panel_off(struct jdi_panel *jdi)154{155 struct mipi_dsi_device *dsi = jdi->dsi;156 struct device *dev = &jdi->dsi->dev;157 int ret;158 159 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;160 161 ret = mipi_dsi_dcs_set_display_off(dsi);162 if (ret < 0)163 dev_err(dev, "failed to set display off: %d\n", ret);164 165 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);166 if (ret < 0)167 dev_err(dev, "failed to enter sleep mode: %d\n", ret);168 169 msleep(100);170}171 172static int jdi_panel_disable(struct drm_panel *panel)173{174 struct jdi_panel *jdi = to_jdi_panel(panel);175 176 backlight_disable(jdi->backlight);177 178 return 0;179}180 181static int jdi_panel_unprepare(struct drm_panel *panel)182{183 struct jdi_panel *jdi = to_jdi_panel(panel);184 struct device *dev = &jdi->dsi->dev;185 int ret;186 187 jdi_panel_off(jdi);188 189 ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);190 if (ret < 0)191 dev_err(dev, "regulator disable failed, %d\n", ret);192 193 gpiod_set_value(jdi->enable_gpio, 0);194 195 gpiod_set_value(jdi->reset_gpio, 1);196 197 gpiod_set_value(jdi->dcdc_en_gpio, 0);198 199 return 0;200}201 202static int jdi_panel_prepare(struct drm_panel *panel)203{204 struct jdi_panel *jdi = to_jdi_panel(panel);205 struct device *dev = &jdi->dsi->dev;206 int ret;207 208 ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies);209 if (ret < 0) {210 dev_err(dev, "regulator enable failed, %d\n", ret);211 return ret;212 }213 214 msleep(20);215 216 gpiod_set_value(jdi->dcdc_en_gpio, 1);217 usleep_range(10, 20);218 219 gpiod_set_value(jdi->reset_gpio, 0);220 usleep_range(10, 20);221 222 gpiod_set_value(jdi->enable_gpio, 1);223 usleep_range(10, 20);224 225 ret = jdi_panel_init(jdi);226 if (ret < 0) {227 dev_err(dev, "failed to init panel: %d\n", ret);228 goto poweroff;229 }230 231 ret = jdi_panel_on(jdi);232 if (ret < 0) {233 dev_err(dev, "failed to set panel on: %d\n", ret);234 goto poweroff;235 }236 237 return 0;238 239poweroff:240 ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);241 if (ret < 0)242 dev_err(dev, "regulator disable failed, %d\n", ret);243 244 gpiod_set_value(jdi->enable_gpio, 0);245 246 gpiod_set_value(jdi->reset_gpio, 1);247 248 gpiod_set_value(jdi->dcdc_en_gpio, 0);249 250 return ret;251}252 253static int jdi_panel_enable(struct drm_panel *panel)254{255 struct jdi_panel *jdi = to_jdi_panel(panel);256 257 backlight_enable(jdi->backlight);258 259 return 0;260}261 262static const struct drm_display_mode default_mode = {263 .clock = 155493,264 .hdisplay = 1200,265 .hsync_start = 1200 + 48,266 .hsync_end = 1200 + 48 + 32,267 .htotal = 1200 + 48 + 32 + 60,268 .vdisplay = 1920,269 .vsync_start = 1920 + 3,270 .vsync_end = 1920 + 3 + 5,271 .vtotal = 1920 + 3 + 5 + 6,272 .flags = 0,273};274 275static int jdi_panel_get_modes(struct drm_panel *panel,276 struct drm_connector *connector)277{278 struct drm_display_mode *mode;279 struct jdi_panel *jdi = to_jdi_panel(panel);280 struct device *dev = &jdi->dsi->dev;281 282 mode = drm_mode_duplicate(connector->dev, &default_mode);283 if (!mode) {284 dev_err(dev, "failed to add mode %ux%ux@%u\n",285 default_mode.hdisplay, default_mode.vdisplay,286 drm_mode_vrefresh(&default_mode));287 return -ENOMEM;288 }289 290 drm_mode_set_name(mode);291 292 drm_mode_probed_add(connector, mode);293 294 connector->display_info.width_mm = 95;295 connector->display_info.height_mm = 151;296 297 return 1;298}299 300static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)301{302 struct mipi_dsi_device *dsi = bl_get_data(bl);303 int ret;304 u16 brightness = bl->props.brightness;305 306 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;307 308 ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);309 if (ret < 0)310 return ret;311 312 dsi->mode_flags |= MIPI_DSI_MODE_LPM;313 314 return brightness & 0xff;315}316 317static int dsi_dcs_bl_update_status(struct backlight_device *bl)318{319 struct mipi_dsi_device *dsi = bl_get_data(bl);320 int ret;321 322 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;323 324 ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);325 if (ret < 0)326 return ret;327 328 dsi->mode_flags |= MIPI_DSI_MODE_LPM;329 330 return 0;331}332 333static const struct backlight_ops dsi_bl_ops = {334 .update_status = dsi_dcs_bl_update_status,335 .get_brightness = dsi_dcs_bl_get_brightness,336};337 338static struct backlight_device *339drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)340{341 struct device *dev = &dsi->dev;342 struct backlight_properties props;343 344 memset(&props, 0, sizeof(props));345 props.type = BACKLIGHT_RAW;346 props.brightness = 255;347 props.max_brightness = 255;348 349 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,350 &dsi_bl_ops, &props);351}352 353static const struct drm_panel_funcs jdi_panel_funcs = {354 .disable = jdi_panel_disable,355 .unprepare = jdi_panel_unprepare,356 .prepare = jdi_panel_prepare,357 .enable = jdi_panel_enable,358 .get_modes = jdi_panel_get_modes,359};360 361static const struct of_device_id jdi_of_match[] = {362 { .compatible = "jdi,lt070me05000", },363 { }364};365MODULE_DEVICE_TABLE(of, jdi_of_match);366 367static int jdi_panel_add(struct jdi_panel *jdi)368{369 struct device *dev = &jdi->dsi->dev;370 int ret;371 unsigned int i;372 373 jdi->mode = &default_mode;374 375 for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++)376 jdi->supplies[i].supply = regulator_names[i];377 378 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies),379 jdi->supplies);380 if (ret < 0)381 return dev_err_probe(dev, ret,382 "failed to init regulator, ret=%d\n", ret);383 384 jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);385 if (IS_ERR(jdi->enable_gpio)) {386 return dev_err_probe(dev, PTR_ERR(jdi->enable_gpio),387 "cannot get enable-gpio %d\n", ret);388 }389 390 jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);391 if (IS_ERR(jdi->reset_gpio))392 return dev_err_probe(dev, PTR_ERR(jdi->reset_gpio),393 "cannot get reset-gpios %d\n", ret);394 395 jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW);396 if (IS_ERR(jdi->dcdc_en_gpio))397 return dev_err_probe(dev, PTR_ERR(jdi->dcdc_en_gpio),398 "cannot get dcdc-en-gpio %d\n", ret);399 400 jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi);401 if (IS_ERR(jdi->backlight))402 return dev_err_probe(dev, PTR_ERR(jdi->backlight),403 "failed to register backlight %d\n", ret);404 405 drm_panel_init(&jdi->base, &jdi->dsi->dev, &jdi_panel_funcs,406 DRM_MODE_CONNECTOR_DSI);407 408 drm_panel_add(&jdi->base);409 410 return 0;411}412 413static void jdi_panel_del(struct jdi_panel *jdi)414{415 if (jdi->base.dev)416 drm_panel_remove(&jdi->base);417}418 419static int jdi_panel_probe(struct mipi_dsi_device *dsi)420{421 struct jdi_panel *jdi;422 int ret;423 424 dsi->lanes = 4;425 dsi->format = MIPI_DSI_FMT_RGB888;426 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |427 MIPI_DSI_CLOCK_NON_CONTINUOUS;428 429 jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);430 if (!jdi)431 return -ENOMEM;432 433 mipi_dsi_set_drvdata(dsi, jdi);434 435 jdi->dsi = dsi;436 437 ret = jdi_panel_add(jdi);438 if (ret < 0)439 return ret;440 441 ret = mipi_dsi_attach(dsi);442 if (ret < 0) {443 jdi_panel_del(jdi);444 return ret;445 }446 447 return 0;448}449 450static void jdi_panel_remove(struct mipi_dsi_device *dsi)451{452 struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);453 int ret;454 455 ret = mipi_dsi_detach(dsi);456 if (ret < 0)457 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",458 ret);459 460 jdi_panel_del(jdi);461}462 463static struct mipi_dsi_driver jdi_panel_driver = {464 .driver = {465 .name = "panel-jdi-lt070me05000",466 .of_match_table = jdi_of_match,467 },468 .probe = jdi_panel_probe,469 .remove = jdi_panel_remove,470};471module_mipi_dsi_driver(jdi_panel_driver);472 473MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");474MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");475MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA");476MODULE_LICENSE("GPL v2");477