Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) STMicroelectronics SA 2017
0004  *
0005  * Authors: Philippe Cornu <philippe.cornu@st.com>
0006  *          Yannick Fertre <yannick.fertre@st.com>
0007  */
0008 
0009 #include <linux/backlight.h>
0010 #include <linux/delay.h>
0011 #include <linux/gpio/consumer.h>
0012 #include <linux/module.h>
0013 #include <linux/regulator/consumer.h>
0014 
0015 #include <video/mipi_display.h>
0016 
0017 #include <drm/drm_mipi_dsi.h>
0018 #include <drm/drm_modes.h>
0019 #include <drm/drm_panel.h>
0020 
0021 #define OTM8009A_BACKLIGHT_DEFAULT  240
0022 #define OTM8009A_BACKLIGHT_MAX      255
0023 
0024 /* Manufacturer Command Set */
0025 #define MCS_ADRSFT  0x0000  /* Address Shift Function */
0026 #define MCS_PANSET  0xB3A6  /* Panel Type Setting */
0027 #define MCS_SD_CTRL 0xC0A2  /* Source Driver Timing Setting */
0028 #define MCS_P_DRV_M 0xC0B4  /* Panel Driving Mode */
0029 #define MCS_OSC_ADJ 0xC181  /* Oscillator Adjustment for Idle/Normal mode */
0030 #define MCS_RGB_VID_SET 0xC1A1  /* RGB Video Mode Setting */
0031 #define MCS_SD_PCH_CTRL 0xC480  /* Source Driver Precharge Control */
0032 #define MCS_NO_DOC1 0xC48A  /* Command not documented */
0033 #define MCS_PWR_CTRL1   0xC580  /* Power Control Setting 1 */
0034 #define MCS_PWR_CTRL2   0xC590  /* Power Control Setting 2 for Normal Mode */
0035 #define MCS_PWR_CTRL4   0xC5B0  /* Power Control Setting 4 for DC Voltage */
0036 #define MCS_PANCTRLSET1 0xCB80  /* Panel Control Setting 1 */
0037 #define MCS_PANCTRLSET2 0xCB90  /* Panel Control Setting 2 */
0038 #define MCS_PANCTRLSET3 0xCBA0  /* Panel Control Setting 3 */
0039 #define MCS_PANCTRLSET4 0xCBB0  /* Panel Control Setting 4 */
0040 #define MCS_PANCTRLSET5 0xCBC0  /* Panel Control Setting 5 */
0041 #define MCS_PANCTRLSET6 0xCBD0  /* Panel Control Setting 6 */
0042 #define MCS_PANCTRLSET7 0xCBE0  /* Panel Control Setting 7 */
0043 #define MCS_PANCTRLSET8 0xCBF0  /* Panel Control Setting 8 */
0044 #define MCS_PANU2D1 0xCC80  /* Panel U2D Setting 1 */
0045 #define MCS_PANU2D2 0xCC90  /* Panel U2D Setting 2 */
0046 #define MCS_PANU2D3 0xCCA0  /* Panel U2D Setting 3 */
0047 #define MCS_PAND2U1 0xCCB0  /* Panel D2U Setting 1 */
0048 #define MCS_PAND2U2 0xCCC0  /* Panel D2U Setting 2 */
0049 #define MCS_PAND2U3 0xCCD0  /* Panel D2U Setting 3 */
0050 #define MCS_GOAVST  0xCE80  /* GOA VST Setting */
0051 #define MCS_GOACLKA1    0xCEA0  /* GOA CLKA1 Setting */
0052 #define MCS_GOACLKA3    0xCEB0  /* GOA CLKA3 Setting */
0053 #define MCS_GOAECLK 0xCFC0  /* GOA ECLK Setting */
0054 #define MCS_NO_DOC2 0xCFD0  /* Command not documented */
0055 #define MCS_GVDDSET 0xD800  /* GVDD/NGVDD */
0056 #define MCS_VCOMDC  0xD900  /* VCOM Voltage Setting */
0057 #define MCS_GMCT2_2P    0xE100  /* Gamma Correction 2.2+ Setting */
0058 #define MCS_GMCT2_2N    0xE200  /* Gamma Correction 2.2- Setting */
0059 #define MCS_NO_DOC3 0xF5B6  /* Command not documented */
0060 #define MCS_CMD2_ENA1   0xFF00  /* Enable Access Command2 "CMD2" */
0061 #define MCS_CMD2_ENA2   0xFF80  /* Enable Access Orise Command2 */
0062 
0063 #define OTM8009A_HDISPLAY   480
0064 #define OTM8009A_VDISPLAY   800
0065 
0066 struct otm8009a {
0067     struct device *dev;
0068     struct drm_panel panel;
0069     struct backlight_device *bl_dev;
0070     struct gpio_desc *reset_gpio;
0071     struct regulator *supply;
0072     bool prepared;
0073     bool enabled;
0074 };
0075 
0076 static const struct drm_display_mode modes[] = {
0077     { /* 50 Hz, preferred */
0078         .clock = 29700,
0079         .hdisplay = 480,
0080         .hsync_start = 480 + 98,
0081         .hsync_end = 480 + 98 + 32,
0082         .htotal = 480 + 98 + 32 + 98,
0083         .vdisplay = 800,
0084         .vsync_start = 800 + 15,
0085         .vsync_end = 800 + 15 + 10,
0086         .vtotal = 800 + 15 + 10 + 14,
0087         .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0088         .width_mm = 52,
0089         .height_mm = 86,
0090     },
0091     { /* 60 Hz */
0092         .clock = 33000,
0093         .hdisplay = 480,
0094         .hsync_start = 480 + 70,
0095         .hsync_end = 480 + 70 + 32,
0096         .htotal = 480 + 70 + 32 + 72,
0097         .vdisplay = 800,
0098         .vsync_start = 800 + 15,
0099         .vsync_end = 800 + 15 + 10,
0100         .vtotal = 800 + 15 + 10 + 16,
0101         .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0102         .width_mm = 52,
0103         .height_mm = 86,
0104     },
0105 };
0106 
0107 static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
0108 {
0109     return container_of(panel, struct otm8009a, panel);
0110 }
0111 
0112 static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
0113                    size_t len)
0114 {
0115     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0116 
0117     if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
0118         dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n");
0119 }
0120 
0121 #define dcs_write_seq(ctx, seq...)          \
0122 ({                          \
0123     static const u8 d[] = { seq };          \
0124     otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d));  \
0125 })
0126 
0127 #define dcs_write_cmd_at(ctx, cmd, seq...)      \
0128 ({                          \
0129     dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF);   \
0130     dcs_write_seq(ctx, (cmd) >> 8, seq);        \
0131 })
0132 
0133 static int otm8009a_init_sequence(struct otm8009a *ctx)
0134 {
0135     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0136     int ret;
0137 
0138     /* Enter CMD2 */
0139     dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
0140 
0141     /* Enter Orise Command2 */
0142     dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09);
0143 
0144     dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30);
0145     mdelay(10);
0146 
0147     dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40);
0148     mdelay(10);
0149 
0150     dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9);
0151     dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34);
0152     dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50);
0153     dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E);
0154     dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
0155     dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01);
0156     dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34);
0157     dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33);
0158     dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79);
0159     dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B);
0160     dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83);
0161     dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83);
0162     dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E);
0163     dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01);
0164 
0165     dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
0166     dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
0167              0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
0168     dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
0169              0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
0170     dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
0171              0x01, 0x02, 0x00, 0x00);
0172 
0173     dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00);
0174 
0175     dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
0176     dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0177              0, 0, 0, 0, 0);
0178     dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0179              0, 0, 0, 0, 0);
0180     dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
0181     dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
0182              0, 0, 0, 0, 0);
0183     dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
0184              4, 0, 0, 0, 0);
0185     dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
0186     dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0187              0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
0188 
0189     dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
0190              0x00, 0x00, 0x00, 0x00);
0191     dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0192              0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
0193     dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
0194              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
0195     dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
0196              0x00, 0x00, 0x00, 0x00);
0197     dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0198              0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
0199     dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
0200              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
0201 
0202     dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66);
0203 
0204     dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06);
0205 
0206     dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
0207              0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
0208              0x01);
0209     dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
0210              0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
0211              0x01);
0212 
0213     /* Exit CMD2 */
0214     dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
0215 
0216     ret = mipi_dsi_dcs_nop(dsi);
0217     if (ret)
0218         return ret;
0219 
0220     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0221     if (ret)
0222         return ret;
0223 
0224     /* Wait for sleep out exit */
0225     mdelay(120);
0226 
0227     /* Default portrait 480x800 rgb24 */
0228     dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
0229 
0230     ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1);
0231     if (ret)
0232         return ret;
0233 
0234     ret = mipi_dsi_dcs_set_page_address(dsi, 0, OTM8009A_VDISPLAY - 1);
0235     if (ret)
0236         return ret;
0237 
0238     /* See otm8009a driver documentation for pixel format descriptions */
0239     ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
0240                         MIPI_DCS_PIXEL_FMT_24BIT << 4);
0241     if (ret)
0242         return ret;
0243 
0244     /* Disable CABC feature */
0245     dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
0246 
0247     ret = mipi_dsi_dcs_set_display_on(dsi);
0248     if (ret)
0249         return ret;
0250 
0251     ret = mipi_dsi_dcs_nop(dsi);
0252     if (ret)
0253         return ret;
0254 
0255     /* Send Command GRAM memory write (no parameters) */
0256     dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START);
0257 
0258     /* Wait a short while to let the panel be ready before the 1st frame */
0259     mdelay(10);
0260 
0261     return 0;
0262 }
0263 
0264 static int otm8009a_disable(struct drm_panel *panel)
0265 {
0266     struct otm8009a *ctx = panel_to_otm8009a(panel);
0267     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0268     int ret;
0269 
0270     if (!ctx->enabled)
0271         return 0; /* This is not an issue so we return 0 here */
0272 
0273     backlight_disable(ctx->bl_dev);
0274 
0275     ret = mipi_dsi_dcs_set_display_off(dsi);
0276     if (ret)
0277         return ret;
0278 
0279     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0280     if (ret)
0281         return ret;
0282 
0283     msleep(120);
0284 
0285     ctx->enabled = false;
0286 
0287     return 0;
0288 }
0289 
0290 static int otm8009a_unprepare(struct drm_panel *panel)
0291 {
0292     struct otm8009a *ctx = panel_to_otm8009a(panel);
0293 
0294     if (!ctx->prepared)
0295         return 0;
0296 
0297     if (ctx->reset_gpio) {
0298         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0299         msleep(20);
0300     }
0301 
0302     regulator_disable(ctx->supply);
0303 
0304     ctx->prepared = false;
0305 
0306     return 0;
0307 }
0308 
0309 static int otm8009a_prepare(struct drm_panel *panel)
0310 {
0311     struct otm8009a *ctx = panel_to_otm8009a(panel);
0312     int ret;
0313 
0314     if (ctx->prepared)
0315         return 0;
0316 
0317     ret = regulator_enable(ctx->supply);
0318     if (ret < 0) {
0319         dev_err(panel->dev, "failed to enable supply: %d\n", ret);
0320         return ret;
0321     }
0322 
0323     if (ctx->reset_gpio) {
0324         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0325         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0326         msleep(20);
0327         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0328         msleep(100);
0329     }
0330 
0331     ret = otm8009a_init_sequence(ctx);
0332     if (ret)
0333         return ret;
0334 
0335     ctx->prepared = true;
0336 
0337     return 0;
0338 }
0339 
0340 static int otm8009a_enable(struct drm_panel *panel)
0341 {
0342     struct otm8009a *ctx = panel_to_otm8009a(panel);
0343 
0344     if (ctx->enabled)
0345         return 0;
0346 
0347     backlight_enable(ctx->bl_dev);
0348 
0349     ctx->enabled = true;
0350 
0351     return 0;
0352 }
0353 
0354 static int otm8009a_get_modes(struct drm_panel *panel,
0355                   struct drm_connector *connector)
0356 {
0357     struct drm_display_mode *mode;
0358     unsigned int num_modes = ARRAY_SIZE(modes);
0359     unsigned int i;
0360 
0361     for (i = 0; i < num_modes; i++) {
0362         mode = drm_mode_duplicate(connector->dev, &modes[i]);
0363         if (!mode) {
0364             dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0365                 modes[i].hdisplay,
0366                 modes[i].vdisplay,
0367                 drm_mode_vrefresh(&modes[i]));
0368             return -ENOMEM;
0369         }
0370 
0371         mode->type = DRM_MODE_TYPE_DRIVER;
0372 
0373         /* Setting first mode as preferred */
0374         if (!i)
0375             mode->type |=  DRM_MODE_TYPE_PREFERRED;
0376 
0377         drm_mode_set_name(mode);
0378         drm_mode_probed_add(connector, mode);
0379     }
0380 
0381     connector->display_info.width_mm = mode->width_mm;
0382     connector->display_info.height_mm = mode->height_mm;
0383 
0384     return num_modes;
0385 }
0386 
0387 static const struct drm_panel_funcs otm8009a_drm_funcs = {
0388     .disable   = otm8009a_disable,
0389     .unprepare = otm8009a_unprepare,
0390     .prepare   = otm8009a_prepare,
0391     .enable    = otm8009a_enable,
0392     .get_modes = otm8009a_get_modes,
0393 };
0394 
0395 /*
0396  * DSI-BASED BACKLIGHT
0397  */
0398 
0399 static int otm8009a_backlight_update_status(struct backlight_device *bd)
0400 {
0401     struct otm8009a *ctx = bl_get_data(bd);
0402     u8 data[2];
0403 
0404     if (!ctx->prepared) {
0405         dev_dbg(&bd->dev, "lcd not ready yet for setting its backlight!\n");
0406         return -ENXIO;
0407     }
0408 
0409     if (bd->props.power <= FB_BLANK_NORMAL) {
0410         /* Power on the backlight with the requested brightness
0411          * Note We can not use mipi_dsi_dcs_set_display_brightness()
0412          * as otm8009a driver support only 8-bit brightness (1 param).
0413          */
0414         data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
0415         data[1] = bd->props.brightness;
0416         otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
0417 
0418         /* set Brightness Control & Backlight on */
0419         data[1] = 0x24;
0420 
0421     } else {
0422         /* Power off the backlight: set Brightness Control & Bl off */
0423         data[1] = 0;
0424     }
0425 
0426     /* Update Brightness Control & Backlight */
0427     data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
0428     otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
0429 
0430     return 0;
0431 }
0432 
0433 static const struct backlight_ops otm8009a_backlight_ops = {
0434     .update_status = otm8009a_backlight_update_status,
0435 };
0436 
0437 static int otm8009a_probe(struct mipi_dsi_device *dsi)
0438 {
0439     struct device *dev = &dsi->dev;
0440     struct otm8009a *ctx;
0441     int ret;
0442 
0443     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0444     if (!ctx)
0445         return -ENOMEM;
0446 
0447     ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
0448     if (IS_ERR(ctx->reset_gpio)) {
0449         dev_err(dev, "cannot get reset-gpio\n");
0450         return PTR_ERR(ctx->reset_gpio);
0451     }
0452 
0453     ctx->supply = devm_regulator_get(dev, "power");
0454     if (IS_ERR(ctx->supply)) {
0455         ret = PTR_ERR(ctx->supply);
0456         if (ret != -EPROBE_DEFER)
0457             dev_err(dev, "failed to request regulator: %d\n", ret);
0458         return ret;
0459     }
0460 
0461     mipi_dsi_set_drvdata(dsi, ctx);
0462 
0463     ctx->dev = dev;
0464 
0465     dsi->lanes = 2;
0466     dsi->format = MIPI_DSI_FMT_RGB888;
0467     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
0468               MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS;
0469 
0470     drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs,
0471                DRM_MODE_CONNECTOR_DSI);
0472 
0473     ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev),
0474                              dsi->host->dev, ctx,
0475                              &otm8009a_backlight_ops,
0476                              NULL);
0477     if (IS_ERR(ctx->bl_dev)) {
0478         ret = PTR_ERR(ctx->bl_dev);
0479         dev_err(dev, "failed to register backlight: %d\n", ret);
0480         return ret;
0481     }
0482 
0483     ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX;
0484     ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT;
0485     ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
0486     ctx->bl_dev->props.type = BACKLIGHT_RAW;
0487 
0488     drm_panel_add(&ctx->panel);
0489 
0490     ret = mipi_dsi_attach(dsi);
0491     if (ret < 0) {
0492         dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n");
0493         drm_panel_remove(&ctx->panel);
0494         return ret;
0495     }
0496 
0497     return 0;
0498 }
0499 
0500 static int otm8009a_remove(struct mipi_dsi_device *dsi)
0501 {
0502     struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi);
0503 
0504     mipi_dsi_detach(dsi);
0505     drm_panel_remove(&ctx->panel);
0506 
0507     return 0;
0508 }
0509 
0510 static const struct of_device_id orisetech_otm8009a_of_match[] = {
0511     { .compatible = "orisetech,otm8009a" },
0512     { }
0513 };
0514 MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match);
0515 
0516 static struct mipi_dsi_driver orisetech_otm8009a_driver = {
0517     .probe  = otm8009a_probe,
0518     .remove = otm8009a_remove,
0519     .driver = {
0520         .name = "panel-orisetech-otm8009a",
0521         .of_match_table = orisetech_otm8009a_of_match,
0522     },
0523 };
0524 module_mipi_dsi_driver(orisetech_otm8009a_driver);
0525 
0526 MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
0527 MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
0528 MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel");
0529 MODULE_LICENSE("GPL v2");