Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2019, The Linux Foundation. All rights reserved.
0004  */
0005 
0006 #include <linux/delay.h>
0007 #include <linux/module.h>
0008 #include <linux/of_device.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/regulator/consumer.h>
0011 
0012 #include <video/mipi_display.h>
0013 
0014 #include <drm/drm_mipi_dsi.h>
0015 #include <drm/drm_modes.h>
0016 #include <drm/drm_panel.h>
0017 
0018 struct visionox_rm69299 {
0019     struct drm_panel panel;
0020     struct regulator_bulk_data supplies[2];
0021     struct gpio_desc *reset_gpio;
0022     struct mipi_dsi_device *dsi;
0023     bool prepared;
0024     bool enabled;
0025 };
0026 
0027 static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel)
0028 {
0029     return container_of(panel, struct visionox_rm69299, panel);
0030 }
0031 
0032 static int visionox_rm69299_power_on(struct visionox_rm69299 *ctx)
0033 {
0034     int ret;
0035 
0036     ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0037     if (ret < 0)
0038         return ret;
0039 
0040     /*
0041      * Reset sequence of visionox panel requires the panel to be
0042      * out of reset for 10ms, followed by being held in reset
0043      * for 10ms and then out again
0044      */
0045     gpiod_set_value(ctx->reset_gpio, 1);
0046     usleep_range(10000, 20000);
0047     gpiod_set_value(ctx->reset_gpio, 0);
0048     usleep_range(10000, 20000);
0049     gpiod_set_value(ctx->reset_gpio, 1);
0050     usleep_range(10000, 20000);
0051 
0052     return 0;
0053 }
0054 
0055 static int visionox_rm69299_power_off(struct visionox_rm69299 *ctx)
0056 {
0057     gpiod_set_value(ctx->reset_gpio, 0);
0058 
0059     return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0060 }
0061 
0062 static int visionox_rm69299_unprepare(struct drm_panel *panel)
0063 {
0064     struct visionox_rm69299 *ctx = panel_to_ctx(panel);
0065     int ret;
0066 
0067     ctx->dsi->mode_flags = 0;
0068 
0069     ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
0070     if (ret < 0)
0071         dev_err(ctx->panel.dev, "set_display_off cmd failed ret = %d\n", ret);
0072 
0073     /* 120ms delay required here as per DCS spec */
0074     msleep(120);
0075 
0076     ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
0077     if (ret < 0) {
0078         dev_err(ctx->panel.dev, "enter_sleep cmd failed ret = %d\n", ret);
0079     }
0080 
0081     ret = visionox_rm69299_power_off(ctx);
0082 
0083     ctx->prepared = false;
0084     return ret;
0085 }
0086 
0087 static int visionox_rm69299_prepare(struct drm_panel *panel)
0088 {
0089     struct visionox_rm69299 *ctx = panel_to_ctx(panel);
0090     int ret;
0091 
0092     if (ctx->prepared)
0093         return 0;
0094 
0095     ret = visionox_rm69299_power_on(ctx);
0096     if (ret < 0)
0097         return ret;
0098 
0099     ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
0100 
0101     ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xfe, 0x00 }, 2);
0102     if (ret < 0) {
0103         dev_err(ctx->panel.dev, "cmd set tx 0 failed, ret = %d\n", ret);
0104         goto power_off;
0105     }
0106 
0107     ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xc2, 0x08 }, 2);
0108     if (ret < 0) {
0109         dev_err(ctx->panel.dev, "cmd set tx 1 failed, ret = %d\n", ret);
0110         goto power_off;
0111     }
0112 
0113     ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x35, 0x00 }, 2);
0114     if (ret < 0) {
0115         dev_err(ctx->panel.dev, "cmd set tx 2 failed, ret = %d\n", ret);
0116         goto power_off;
0117     }
0118 
0119     ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x51, 0xff }, 2);
0120     if (ret < 0) {
0121         dev_err(ctx->panel.dev, "cmd set tx 3 failed, ret = %d\n", ret);
0122         goto power_off;
0123     }
0124 
0125     ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
0126     if (ret < 0) {
0127         dev_err(ctx->panel.dev, "exit_sleep_mode cmd failed ret = %d\n", ret);
0128         goto power_off;
0129     }
0130 
0131     /* Per DSI spec wait 120ms after sending exit sleep DCS command */
0132     msleep(120);
0133 
0134     ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
0135     if (ret < 0) {
0136         dev_err(ctx->panel.dev, "set_display_on cmd failed ret = %d\n", ret);
0137         goto power_off;
0138     }
0139 
0140     /* Per DSI spec wait 120ms after sending set_display_on DCS command */
0141     msleep(120);
0142 
0143     ctx->prepared = true;
0144 
0145     return 0;
0146 
0147 power_off:
0148     return ret;
0149 }
0150 
0151 static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = {
0152     .name = "1080x2248",
0153     .clock = 158695,
0154     .hdisplay = 1080,
0155     .hsync_start = 1080 + 26,
0156     .hsync_end = 1080 + 26 + 2,
0157     .htotal = 1080 + 26 + 2 + 36,
0158     .vdisplay = 2248,
0159     .vsync_start = 2248 + 56,
0160     .vsync_end = 2248 + 56 + 4,
0161     .vtotal = 2248 + 56 + 4 + 4,
0162     .flags = 0,
0163 };
0164 
0165 static int visionox_rm69299_get_modes(struct drm_panel *panel,
0166                       struct drm_connector *connector)
0167 {
0168     struct visionox_rm69299 *ctx = panel_to_ctx(panel);
0169     struct drm_display_mode *mode;
0170 
0171     mode = drm_mode_duplicate(connector->dev,
0172                   &visionox_rm69299_1080x2248_60hz);
0173     if (!mode) {
0174         dev_err(ctx->panel.dev, "failed to create a new display mode\n");
0175         return 0;
0176     }
0177 
0178     connector->display_info.width_mm = 74;
0179     connector->display_info.height_mm = 131;
0180     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0181     drm_mode_probed_add(connector, mode);
0182 
0183     return 1;
0184 }
0185 
0186 static const struct drm_panel_funcs visionox_rm69299_drm_funcs = {
0187     .unprepare = visionox_rm69299_unprepare,
0188     .prepare = visionox_rm69299_prepare,
0189     .get_modes = visionox_rm69299_get_modes,
0190 };
0191 
0192 static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
0193 {
0194     struct device *dev = &dsi->dev;
0195     struct visionox_rm69299 *ctx;
0196     int ret;
0197 
0198     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0199     if (!ctx)
0200         return -ENOMEM;
0201 
0202     mipi_dsi_set_drvdata(dsi, ctx);
0203 
0204     ctx->panel.dev = dev;
0205     ctx->dsi = dsi;
0206 
0207     ctx->supplies[0].supply = "vdda";
0208     ctx->supplies[1].supply = "vdd3p3";
0209 
0210     ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies),
0211                       ctx->supplies);
0212     if (ret < 0)
0213         return ret;
0214 
0215     ctx->reset_gpio = devm_gpiod_get(ctx->panel.dev,
0216                      "reset", GPIOD_OUT_LOW);
0217     if (IS_ERR(ctx->reset_gpio)) {
0218         dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio));
0219         return PTR_ERR(ctx->reset_gpio);
0220     }
0221 
0222     drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs,
0223                DRM_MODE_CONNECTOR_DSI);
0224     ctx->panel.dev = dev;
0225     ctx->panel.funcs = &visionox_rm69299_drm_funcs;
0226     drm_panel_add(&ctx->panel);
0227 
0228     dsi->lanes = 4;
0229     dsi->format = MIPI_DSI_FMT_RGB888;
0230     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
0231               MIPI_DSI_CLOCK_NON_CONTINUOUS;
0232     ret = mipi_dsi_attach(dsi);
0233     if (ret < 0) {
0234         dev_err(dev, "dsi attach failed ret = %d\n", ret);
0235         goto err_dsi_attach;
0236     }
0237 
0238     ret = regulator_set_load(ctx->supplies[0].consumer, 32000);
0239     if (ret) {
0240         dev_err(dev, "regulator set load failed for vdda supply ret = %d\n", ret);
0241         goto err_set_load;
0242     }
0243 
0244     ret = regulator_set_load(ctx->supplies[1].consumer, 13200);
0245     if (ret) {
0246         dev_err(dev, "regulator set load failed for vdd3p3 supply ret = %d\n", ret);
0247         goto err_set_load;
0248     }
0249 
0250     return 0;
0251 
0252 err_set_load:
0253     mipi_dsi_detach(dsi);
0254 err_dsi_attach:
0255     drm_panel_remove(&ctx->panel);
0256     return ret;
0257 }
0258 
0259 static int visionox_rm69299_remove(struct mipi_dsi_device *dsi)
0260 {
0261     struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi);
0262 
0263     mipi_dsi_detach(ctx->dsi);
0264     mipi_dsi_device_unregister(ctx->dsi);
0265 
0266     drm_panel_remove(&ctx->panel);
0267     return 0;
0268 }
0269 
0270 static const struct of_device_id visionox_rm69299_of_match[] = {
0271     { .compatible = "visionox,rm69299-1080p-display", },
0272     { /* sentinel */ }
0273 };
0274 MODULE_DEVICE_TABLE(of, visionox_rm69299_of_match);
0275 
0276 static struct mipi_dsi_driver visionox_rm69299_driver = {
0277     .driver = {
0278         .name = "panel-visionox-rm69299",
0279         .of_match_table = visionox_rm69299_of_match,
0280     },
0281     .probe = visionox_rm69299_probe,
0282     .remove = visionox_rm69299_remove,
0283 };
0284 module_mipi_dsi_driver(visionox_rm69299_driver);
0285 
0286 MODULE_DESCRIPTION("Visionox RM69299 DSI Panel Driver");
0287 MODULE_LICENSE("GPL v2");