panel-visionox-rm69299.c (7220B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2019, The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/delay.h> 7#include <linux/module.h> 8#include <linux/of_device.h> 9#include <linux/gpio/consumer.h> 10#include <linux/regulator/consumer.h> 11 12#include <video/mipi_display.h> 13 14#include <drm/drm_mipi_dsi.h> 15#include <drm/drm_modes.h> 16#include <drm/drm_panel.h> 17 18struct visionox_rm69299 { 19 struct drm_panel panel; 20 struct regulator_bulk_data supplies[2]; 21 struct gpio_desc *reset_gpio; 22 struct mipi_dsi_device *dsi; 23 bool prepared; 24 bool enabled; 25}; 26 27static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel) 28{ 29 return container_of(panel, struct visionox_rm69299, panel); 30} 31 32static int visionox_rm69299_power_on(struct visionox_rm69299 *ctx) 33{ 34 int ret; 35 36 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 37 if (ret < 0) 38 return ret; 39 40 /* 41 * Reset sequence of visionox panel requires the panel to be 42 * out of reset for 10ms, followed by being held in reset 43 * for 10ms and then out again 44 */ 45 gpiod_set_value(ctx->reset_gpio, 1); 46 usleep_range(10000, 20000); 47 gpiod_set_value(ctx->reset_gpio, 0); 48 usleep_range(10000, 20000); 49 gpiod_set_value(ctx->reset_gpio, 1); 50 usleep_range(10000, 20000); 51 52 return 0; 53} 54 55static int visionox_rm69299_power_off(struct visionox_rm69299 *ctx) 56{ 57 gpiod_set_value(ctx->reset_gpio, 0); 58 59 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 60} 61 62static int visionox_rm69299_unprepare(struct drm_panel *panel) 63{ 64 struct visionox_rm69299 *ctx = panel_to_ctx(panel); 65 int ret; 66 67 ctx->dsi->mode_flags = 0; 68 69 ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); 70 if (ret < 0) 71 dev_err(ctx->panel.dev, "set_display_off cmd failed ret = %d\n", ret); 72 73 /* 120ms delay required here as per DCS spec */ 74 msleep(120); 75 76 ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0); 77 if (ret < 0) { 78 dev_err(ctx->panel.dev, "enter_sleep cmd failed ret = %d\n", ret); 79 } 80 81 ret = visionox_rm69299_power_off(ctx); 82 83 ctx->prepared = false; 84 return ret; 85} 86 87static int visionox_rm69299_prepare(struct drm_panel *panel) 88{ 89 struct visionox_rm69299 *ctx = panel_to_ctx(panel); 90 int ret; 91 92 if (ctx->prepared) 93 return 0; 94 95 ret = visionox_rm69299_power_on(ctx); 96 if (ret < 0) 97 return ret; 98 99 ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM; 100 101 ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xfe, 0x00 }, 2); 102 if (ret < 0) { 103 dev_err(ctx->panel.dev, "cmd set tx 0 failed, ret = %d\n", ret); 104 goto power_off; 105 } 106 107 ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xc2, 0x08 }, 2); 108 if (ret < 0) { 109 dev_err(ctx->panel.dev, "cmd set tx 1 failed, ret = %d\n", ret); 110 goto power_off; 111 } 112 113 ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x35, 0x00 }, 2); 114 if (ret < 0) { 115 dev_err(ctx->panel.dev, "cmd set tx 2 failed, ret = %d\n", ret); 116 goto power_off; 117 } 118 119 ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x51, 0xff }, 2); 120 if (ret < 0) { 121 dev_err(ctx->panel.dev, "cmd set tx 3 failed, ret = %d\n", ret); 122 goto power_off; 123 } 124 125 ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); 126 if (ret < 0) { 127 dev_err(ctx->panel.dev, "exit_sleep_mode cmd failed ret = %d\n", ret); 128 goto power_off; 129 } 130 131 /* Per DSI spec wait 120ms after sending exit sleep DCS command */ 132 msleep(120); 133 134 ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); 135 if (ret < 0) { 136 dev_err(ctx->panel.dev, "set_display_on cmd failed ret = %d\n", ret); 137 goto power_off; 138 } 139 140 /* Per DSI spec wait 120ms after sending set_display_on DCS command */ 141 msleep(120); 142 143 ctx->prepared = true; 144 145 return 0; 146 147power_off: 148 return ret; 149} 150 151static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = { 152 .name = "1080x2248", 153 .clock = 158695, 154 .hdisplay = 1080, 155 .hsync_start = 1080 + 26, 156 .hsync_end = 1080 + 26 + 2, 157 .htotal = 1080 + 26 + 2 + 36, 158 .vdisplay = 2248, 159 .vsync_start = 2248 + 56, 160 .vsync_end = 2248 + 56 + 4, 161 .vtotal = 2248 + 56 + 4 + 4, 162 .flags = 0, 163}; 164 165static int visionox_rm69299_get_modes(struct drm_panel *panel, 166 struct drm_connector *connector) 167{ 168 struct visionox_rm69299 *ctx = panel_to_ctx(panel); 169 struct drm_display_mode *mode; 170 171 mode = drm_mode_duplicate(connector->dev, 172 &visionox_rm69299_1080x2248_60hz); 173 if (!mode) { 174 dev_err(ctx->panel.dev, "failed to create a new display mode\n"); 175 return 0; 176 } 177 178 connector->display_info.width_mm = 74; 179 connector->display_info.height_mm = 131; 180 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 181 drm_mode_probed_add(connector, mode); 182 183 return 1; 184} 185 186static const struct drm_panel_funcs visionox_rm69299_drm_funcs = { 187 .unprepare = visionox_rm69299_unprepare, 188 .prepare = visionox_rm69299_prepare, 189 .get_modes = visionox_rm69299_get_modes, 190}; 191 192static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) 193{ 194 struct device *dev = &dsi->dev; 195 struct visionox_rm69299 *ctx; 196 int ret; 197 198 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 199 if (!ctx) 200 return -ENOMEM; 201 202 mipi_dsi_set_drvdata(dsi, ctx); 203 204 ctx->panel.dev = dev; 205 ctx->dsi = dsi; 206 207 ctx->supplies[0].supply = "vdda"; 208 ctx->supplies[1].supply = "vdd3p3"; 209 210 ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies), 211 ctx->supplies); 212 if (ret < 0) 213 return ret; 214 215 ctx->reset_gpio = devm_gpiod_get(ctx->panel.dev, 216 "reset", GPIOD_OUT_LOW); 217 if (IS_ERR(ctx->reset_gpio)) { 218 dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio)); 219 return PTR_ERR(ctx->reset_gpio); 220 } 221 222 drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs, 223 DRM_MODE_CONNECTOR_DSI); 224 ctx->panel.dev = dev; 225 ctx->panel.funcs = &visionox_rm69299_drm_funcs; 226 drm_panel_add(&ctx->panel); 227 228 dsi->lanes = 4; 229 dsi->format = MIPI_DSI_FMT_RGB888; 230 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | 231 MIPI_DSI_CLOCK_NON_CONTINUOUS; 232 ret = mipi_dsi_attach(dsi); 233 if (ret < 0) { 234 dev_err(dev, "dsi attach failed ret = %d\n", ret); 235 goto err_dsi_attach; 236 } 237 238 ret = regulator_set_load(ctx->supplies[0].consumer, 32000); 239 if (ret) { 240 dev_err(dev, "regulator set load failed for vdda supply ret = %d\n", ret); 241 goto err_set_load; 242 } 243 244 ret = regulator_set_load(ctx->supplies[1].consumer, 13200); 245 if (ret) { 246 dev_err(dev, "regulator set load failed for vdd3p3 supply ret = %d\n", ret); 247 goto err_set_load; 248 } 249 250 return 0; 251 252err_set_load: 253 mipi_dsi_detach(dsi); 254err_dsi_attach: 255 drm_panel_remove(&ctx->panel); 256 return ret; 257} 258 259static int visionox_rm69299_remove(struct mipi_dsi_device *dsi) 260{ 261 struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi); 262 263 mipi_dsi_detach(ctx->dsi); 264 mipi_dsi_device_unregister(ctx->dsi); 265 266 drm_panel_remove(&ctx->panel); 267 return 0; 268} 269 270static const struct of_device_id visionox_rm69299_of_match[] = { 271 { .compatible = "visionox,rm69299-1080p-display", }, 272 { /* sentinel */ } 273}; 274MODULE_DEVICE_TABLE(of, visionox_rm69299_of_match); 275 276static struct mipi_dsi_driver visionox_rm69299_driver = { 277 .driver = { 278 .name = "panel-visionox-rm69299", 279 .of_match_table = visionox_rm69299_of_match, 280 }, 281 .probe = visionox_rm69299_probe, 282 .remove = visionox_rm69299_remove, 283}; 284module_mipi_dsi_driver(visionox_rm69299_driver); 285 286MODULE_DESCRIPTION("Visionox RM69299 DSI Panel Driver"); 287MODULE_LICENSE("GPL v2");