Last active
May 6, 2023 06:24
-
-
Save jbg/6d9818b24f30c3149e569f0821f5cf9d to your computer and use it in GitHub Desktop.
DSI driver for the nexus 5x
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0-only | |
// Copyright (c) 2019, The Linux Foundation. All rights reserved. | |
#include <drm/drm_mipi_dsi.h> | |
#include <drm/drm_modes.h> | |
#include <drm/drm_panel.h> | |
#include <linux/backlight.h> | |
#include <linux/delay.h> | |
#include <linux/gpio/consumer.h> | |
#include <linux/module.h> | |
#include <linux/of.h> | |
struct jd35695b { | |
struct drm_panel panel; | |
struct mipi_dsi_device *dsi; | |
struct backlight_device *backlight; | |
struct gpio_desc *reset_gpio; | |
bool prepared; | |
bool enabled; | |
}; | |
static inline struct jd35695b *to_jd35695b(struct drm_panel *panel) | |
{ | |
return container_of(panel, struct jd35695b, panel); | |
} | |
#define dsi_generic_write_seq(dsi, seq...) do { \ | |
static const u8 d[] = { seq }; \ | |
int ret; \ | |
ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ | |
if (ret < 0) \ | |
return ret; \ | |
} while (0) | |
#define dsi_dcs_write_seq(dsi, seq...) do { \ | |
static const u8 d[] = { seq }; \ | |
int ret; \ | |
ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \ | |
if (ret < 0) \ | |
return ret; \ | |
} while (0) | |
static void jd35695b_reset(struct jd35695b *ctx) | |
{ | |
gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
usleep_range(10000, 11000); | |
gpiod_set_value_cansleep(ctx->reset_gpio, 1); | |
usleep_range(10000, 11000); | |
gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
usleep_range(1000, 2000); | |
gpiod_set_value_cansleep(ctx->reset_gpio, 1); | |
} | |
static int jd35695b_on(struct jd35695b *ctx) | |
{ | |
struct mipi_dsi_device *dsi = ctx->dsi; | |
struct device *dev = &dsi->dev; | |
int ret; | |
dsi->mode_flags |= MIPI_DSI_MODE_LPM; | |
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set tear on: %d\n", ret); | |
return ret; | |
} | |
ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x0778); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set tear scanline: %d\n", ret); | |
return ret; | |
} | |
dsi_dcs_write_seq(dsi, 0xc9, | |
0x49, 0x02, 0x05, 0x00, 0x0f, 0x06, 0x67, 0x03, 0x2e, | |
0x10, 0xf0); | |
ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 0x0437); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set column address: %d\n", ret); | |
return ret; | |
} | |
ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 0x077f); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set page address: %d\n", ret); | |
return ret; | |
} | |
dsi_generic_write_seq(dsi, 0xfb, 0x01); | |
ret = mipi_dsi_dcs_exit_sleep_mode(dsi); | |
if (ret < 0) { | |
dev_err(dev, "Failed to exit sleep mode: %d\n", ret); | |
return ret; | |
} | |
msleep(101); | |
ret = mipi_dsi_dcs_set_display_on(dsi); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set display on: %d\n", ret); | |
return ret; | |
} | |
msleep(41); | |
return 0; | |
} | |
static int jd35695b_off(struct jd35695b *ctx) | |
{ | |
struct mipi_dsi_device *dsi = ctx->dsi; | |
struct device *dev = &dsi->dev; | |
int ret; | |
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; | |
ret = mipi_dsi_dcs_set_display_off(dsi); | |
if (ret < 0) { | |
dev_err(dev, "Failed to set display off: %d\n", ret); | |
return ret; | |
} | |
ret = mipi_dsi_dcs_enter_sleep_mode(dsi); | |
if (ret < 0) { | |
dev_err(dev, "Failed to enter sleep mode: %d\n", ret); | |
return ret; | |
} | |
msleep(61); | |
return 0; | |
} | |
static int jd35695b_prepare(struct drm_panel *panel) | |
{ | |
struct jd35695b *ctx = to_jd35695b(panel); | |
struct device *dev = &ctx->dsi->dev; | |
int ret; | |
if (ctx->prepared) | |
return 0; | |
jd35695b_reset(ctx); | |
ret = jd35695b_on(ctx); | |
if (ret < 0) { | |
dev_err(dev, "Failed to initialize panel: %d\n", ret); | |
gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
return ret; | |
} | |
ctx->prepared = true; | |
return 0; | |
} | |
static int jd35695b_unprepare(struct drm_panel *panel) | |
{ | |
struct jd35695b *ctx = to_jd35695b(panel); | |
struct device *dev = &ctx->dsi->dev; | |
int ret; | |
if (!ctx->prepared) | |
return 0; | |
ret = jd35695b_off(ctx); | |
if (ret < 0) | |
dev_err(dev, "Failed to un-initialize panel: %d\n", ret); | |
gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
ctx->prepared = false; | |
return 0; | |
} | |
static int jd35695b_enable(struct drm_panel *panel) | |
{ | |
struct jd35695b *ctx = to_jd35695b(panel); | |
int ret; | |
if (ctx->enabled) | |
return 0; | |
ret = backlight_enable(ctx->backlight); | |
if (ret < 0) { | |
dev_err(&ctx->dsi->dev, "Failed to enable backlight: %d\n", ret); | |
return ret; | |
} | |
ctx->enabled = true; | |
return 0; | |
} | |
static int jd35695b_disable(struct drm_panel *panel) | |
{ | |
struct jd35695b *ctx = to_jd35695b(panel); | |
int ret; | |
if (!ctx->enabled) | |
return 0; | |
ret = backlight_disable(ctx->backlight); | |
if (ret < 0) { | |
dev_err(&ctx->dsi->dev, "Failed to disable backlight: %d\n", ret); | |
return ret; | |
} | |
ctx->enabled = false; | |
return 0; | |
} | |
static const struct drm_display_mode jd35695b_mode = { | |
.clock = (1080 + 120 + 4 + 76) * (1920 + 8 + 1 + 7) * 60 / 1000, | |
.hdisplay = 1080, | |
.hsync_start = 1080 + 120, | |
.hsync_end = 1080 + 120 + 4, | |
.htotal = 1080 + 120 + 4 + 76, | |
.vdisplay = 1920, | |
.vsync_start = 1920 + 8, | |
.vsync_end = 1920 + 8 + 1, | |
.vtotal = 1920 + 8 + 1 + 7, | |
.vrefresh = 60, | |
.width_mm = 65, | |
.height_mm = 115, | |
}; | |
static int jd35695b_get_modes(struct drm_panel *panel) | |
{ | |
struct drm_display_mode *mode; | |
mode = drm_mode_duplicate(panel->drm, &jd35695b_mode); | |
if (!mode) | |
return -ENOMEM; | |
drm_mode_set_name(mode); | |
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | |
panel->connector->display_info.width_mm = mode->width_mm; | |
panel->connector->display_info.height_mm = mode->height_mm; | |
drm_mode_probed_add(panel->connector, mode); | |
return 1; | |
} | |
static const struct drm_panel_funcs jd35695b_panel_funcs = { | |
.disable = jd35695b_disable, | |
.unprepare = jd35695b_unprepare, | |
.prepare = jd35695b_prepare, | |
.enable = jd35695b_enable, | |
.get_modes = jd35695b_get_modes, | |
}; | |
static int jd35695b_probe(struct mipi_dsi_device *dsi) | |
{ | |
struct device *dev = &dsi->dev; | |
struct jd35695b *ctx; | |
int ret; | |
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | |
if (!ctx) | |
return -ENOMEM; | |
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); | |
if (IS_ERR(ctx->reset_gpio)) { | |
ret = PTR_ERR(ctx->reset_gpio); | |
dev_err(dev, "Failed to get reset-gpios: %d\n", ret); | |
return ret; | |
} | |
ctx->backlight = devm_of_find_backlight(dev); | |
if (IS_ERR(ctx->backlight)) { | |
ret = PTR_ERR(ctx->backlight); | |
dev_err(dev, "Failed to get backlight: %d\n", ret); | |
return ret; | |
} | |
ctx->dsi = dsi; | |
mipi_dsi_set_drvdata(dsi, ctx); | |
dsi->lanes = 4; | |
dsi->format = MIPI_DSI_FMT_RGB888; | |
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_EOT_PACKET | | |
MIPI_DSI_CLOCK_NON_CONTINUOUS; | |
drm_panel_init(&ctx->panel); | |
ctx->panel.dev = dev; | |
ctx->panel.funcs = &jd35695b_panel_funcs; | |
ret = drm_panel_add(&ctx->panel); | |
if (ret < 0) { | |
dev_err(dev, "Failed to add panel: %d\n", ret); | |
return ret; | |
} | |
ret = mipi_dsi_attach(dsi); | |
if (ret < 0) { | |
dev_err(dev, "Failed to attach to DSI host: %d\n", ret); | |
return ret; | |
} | |
return 0; | |
} | |
static int jd35695b_remove(struct mipi_dsi_device *dsi) | |
{ | |
struct jd35695b *ctx = mipi_dsi_get_drvdata(dsi); | |
int ret; | |
ret = mipi_dsi_detach(dsi); | |
if (ret < 0) | |
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); | |
drm_panel_remove(&ctx->panel); | |
return 0; | |
} | |
static const struct of_device_id jd35695b_of_match[] = { | |
{ .compatible = "mdss,jd35695b" }, // FIXME | |
{ } | |
}; | |
MODULE_DEVICE_TABLE(of, jd35695b_of_match); | |
static struct mipi_dsi_driver jd35695b_driver = { | |
.probe = jd35695b_probe, | |
.remove = jd35695b_remove, | |
.driver = { | |
.name = "panel-jd35695b", | |
.of_match_table = jd35695b_of_match, | |
}, | |
}; | |
module_mipi_dsi_driver(jd35695b_driver); | |
MODULE_AUTHOR("Jasper Hugo <[email protected]>"); | |
MODULE_DESCRIPTION("DRM driver for jd35695b 1080p cmd mode dsi panel"); | |
MODULE_LICENSE("GPL v2"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment