Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2019, Huaqin Telecom Technology Co., Ltd
0004  *
0005  * Author: Jerry Han <jerry.han.hq@gmail.com>
0006  *
0007  */
0008 
0009 #include <linux/delay.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 
0015 #include <linux/gpio/consumer.h>
0016 #include <linux/regulator/consumer.h>
0017 
0018 #include <drm/drm_device.h>
0019 #include <drm/drm_mipi_dsi.h>
0020 #include <drm/drm_modes.h>
0021 #include <drm/drm_panel.h>
0022 
0023 #include <video/mipi_display.h>
0024 
0025 struct panel_cmd {
0026     char cmd;
0027     char data;
0028 };
0029 
0030 struct panel_desc {
0031     const struct drm_display_mode *display_mode;
0032     unsigned int bpc;
0033     unsigned int width_mm;
0034     unsigned int height_mm;
0035 
0036     unsigned long mode_flags;
0037     enum mipi_dsi_pixel_format format;
0038     unsigned int lanes;
0039     const struct panel_cmd *on_cmds;
0040     unsigned int on_cmds_num;
0041 };
0042 
0043 struct panel_info {
0044     struct drm_panel base;
0045     struct mipi_dsi_device *link;
0046     const struct panel_desc *desc;
0047 
0048     struct gpio_desc *enable_gpio;
0049     struct gpio_desc *pp33_gpio;
0050     struct gpio_desc *pp18_gpio;
0051 
0052     bool prepared;
0053     bool enabled;
0054 };
0055 
0056 static inline struct panel_info *to_panel_info(struct drm_panel *panel)
0057 {
0058     return container_of(panel, struct panel_info, base);
0059 }
0060 
0061 static void disable_gpios(struct panel_info *pinfo)
0062 {
0063     gpiod_set_value(pinfo->enable_gpio, 0);
0064     gpiod_set_value(pinfo->pp33_gpio, 0);
0065     gpiod_set_value(pinfo->pp18_gpio, 0);
0066 }
0067 
0068 static int send_mipi_cmds(struct drm_panel *panel, const struct panel_cmd *cmds)
0069 {
0070     struct panel_info *pinfo = to_panel_info(panel);
0071     unsigned int i = 0;
0072     int err;
0073 
0074     for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
0075         err = mipi_dsi_dcs_write_buffer(pinfo->link, &cmds[i],
0076                         sizeof(struct panel_cmd));
0077 
0078         if (err < 0)
0079             return err;
0080     }
0081 
0082     return 0;
0083 }
0084 
0085 static int boe_panel_disable(struct drm_panel *panel)
0086 {
0087     struct panel_info *pinfo = to_panel_info(panel);
0088     int err;
0089 
0090     if (!pinfo->enabled)
0091         return 0;
0092 
0093     err = mipi_dsi_dcs_set_display_off(pinfo->link);
0094     if (err < 0) {
0095         dev_err(panel->dev, "failed to set display off: %d\n", err);
0096         return err;
0097     }
0098 
0099     pinfo->enabled = false;
0100 
0101     return 0;
0102 }
0103 
0104 static int boe_panel_unprepare(struct drm_panel *panel)
0105 {
0106     struct panel_info *pinfo = to_panel_info(panel);
0107     int err;
0108 
0109     if (!pinfo->prepared)
0110         return 0;
0111 
0112     err = mipi_dsi_dcs_set_display_off(pinfo->link);
0113     if (err < 0)
0114         dev_err(panel->dev, "failed to set display off: %d\n", err);
0115 
0116     err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
0117     if (err < 0)
0118         dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
0119 
0120     /* sleep_mode_delay: 1ms - 2ms */
0121     usleep_range(1000, 2000);
0122 
0123     disable_gpios(pinfo);
0124 
0125     pinfo->prepared = false;
0126 
0127     return 0;
0128 }
0129 
0130 static int boe_panel_prepare(struct drm_panel *panel)
0131 {
0132     struct panel_info *pinfo = to_panel_info(panel);
0133     int err;
0134 
0135     if (pinfo->prepared)
0136         return 0;
0137 
0138     gpiod_set_value(pinfo->pp18_gpio, 1);
0139     /* T1: 5ms - 6ms */
0140     usleep_range(5000, 6000);
0141     gpiod_set_value(pinfo->pp33_gpio, 1);
0142 
0143     /* reset sequence */
0144     /* T2: 14ms - 15ms */
0145     usleep_range(14000, 15000);
0146     gpiod_set_value(pinfo->enable_gpio, 1);
0147 
0148     /* T3: 1ms - 2ms */
0149     usleep_range(1000, 2000);
0150     gpiod_set_value(pinfo->enable_gpio, 0);
0151 
0152     /* T4: 1ms - 2ms */
0153     usleep_range(1000, 2000);
0154     gpiod_set_value(pinfo->enable_gpio, 1);
0155 
0156     /* T5: 5ms - 6ms */
0157     usleep_range(5000, 6000);
0158 
0159     /* send init code */
0160     err = send_mipi_cmds(panel, pinfo->desc->on_cmds);
0161     if (err < 0) {
0162         dev_err(panel->dev, "failed to send DCS Init Code: %d\n", err);
0163         goto poweroff;
0164     }
0165 
0166     err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
0167     if (err < 0) {
0168         dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
0169         goto poweroff;
0170     }
0171 
0172     /* T6: 120ms - 121ms */
0173     usleep_range(120000, 121000);
0174 
0175     err = mipi_dsi_dcs_set_display_on(pinfo->link);
0176     if (err < 0) {
0177         dev_err(panel->dev, "failed to set display on: %d\n", err);
0178         goto poweroff;
0179     }
0180 
0181     /* T7: 20ms - 21ms */
0182     usleep_range(20000, 21000);
0183 
0184     pinfo->prepared = true;
0185 
0186     return 0;
0187 
0188 poweroff:
0189     disable_gpios(pinfo);
0190     return err;
0191 }
0192 
0193 static int boe_panel_enable(struct drm_panel *panel)
0194 {
0195     struct panel_info *pinfo = to_panel_info(panel);
0196     int ret;
0197 
0198     if (pinfo->enabled)
0199         return 0;
0200 
0201     usleep_range(120000, 121000);
0202 
0203     ret = mipi_dsi_dcs_set_display_on(pinfo->link);
0204     if (ret < 0) {
0205         dev_err(panel->dev, "failed to set display on: %d\n", ret);
0206         return ret;
0207     }
0208 
0209     pinfo->enabled = true;
0210 
0211     return 0;
0212 }
0213 
0214 static int boe_panel_get_modes(struct drm_panel *panel,
0215                    struct drm_connector *connector)
0216 {
0217     struct panel_info *pinfo = to_panel_info(panel);
0218     const struct drm_display_mode *m = pinfo->desc->display_mode;
0219     struct drm_display_mode *mode;
0220 
0221     mode = drm_mode_duplicate(connector->dev, m);
0222     if (!mode) {
0223         dev_err(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
0224             m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
0225         return -ENOMEM;
0226     }
0227 
0228     drm_mode_set_name(mode);
0229 
0230     drm_mode_probed_add(connector, mode);
0231 
0232     connector->display_info.width_mm = pinfo->desc->width_mm;
0233     connector->display_info.height_mm = pinfo->desc->height_mm;
0234     connector->display_info.bpc = pinfo->desc->bpc;
0235 
0236     return 1;
0237 }
0238 
0239 static const struct drm_panel_funcs panel_funcs = {
0240     .disable = boe_panel_disable,
0241     .unprepare = boe_panel_unprepare,
0242     .prepare = boe_panel_prepare,
0243     .enable = boe_panel_enable,
0244     .get_modes = boe_panel_get_modes,
0245 };
0246 
0247 static const struct drm_display_mode default_display_mode = {
0248     .clock = 159420,
0249     .hdisplay = 1200,
0250     .hsync_start = 1200 + 80,
0251     .hsync_end = 1200 + 80 + 60,
0252     .htotal = 1200 + 80 + 60 + 24,
0253     .vdisplay = 1920,
0254     .vsync_start = 1920 + 10,
0255     .vsync_end = 1920 + 10 + 14,
0256     .vtotal = 1920 + 10 + 14 + 4,
0257 };
0258 
0259 /* 8 inch */
0260 static const struct panel_cmd boe_himax8279d8p_on_cmds[] = {
0261     { 0xB0, 0x05 },
0262     { 0xB1, 0xE5 },
0263     { 0xB3, 0x52 },
0264     { 0xC0, 0x00 },
0265     { 0xC2, 0x57 },
0266     { 0xD9, 0x85 },
0267     { 0xB0, 0x01 },
0268     { 0xC8, 0x00 },
0269     { 0xC9, 0x00 },
0270     { 0xCC, 0x26 },
0271     { 0xCD, 0x26 },
0272     { 0xDC, 0x00 },
0273     { 0xDD, 0x00 },
0274     { 0xE0, 0x26 },
0275     { 0xE1, 0x26 },
0276     { 0xB0, 0x03 },
0277     { 0xC3, 0x2A },
0278     { 0xE7, 0x2A },
0279     { 0xC5, 0x2A },
0280     { 0xDE, 0x2A },
0281     { 0xBC, 0x02 },
0282     { 0xCB, 0x02 },
0283     { 0xB0, 0x00 },
0284     { 0xB6, 0x03 },
0285     { 0xBA, 0x8B },
0286     { 0xBF, 0x15 },
0287     { 0xC0, 0x18 },
0288     { 0xC2, 0x14 },
0289     { 0xC3, 0x02 },
0290     { 0xC4, 0x14 },
0291     { 0xC5, 0x02 },
0292     { 0xCC, 0x0A },
0293     { 0xB0, 0x06 },
0294     { 0xC0, 0xA5 },
0295     { 0xD5, 0x20 },
0296     { 0xC0, 0x00 },
0297     { 0xB0, 0x02 },
0298     { 0xC0, 0x00 },
0299     { 0xC1, 0x02 },
0300     { 0xC2, 0x06 },
0301     { 0xC3, 0x16 },
0302     { 0xC4, 0x0E },
0303     { 0xC5, 0x18 },
0304     { 0xC6, 0x26 },
0305     { 0xC7, 0x32 },
0306     { 0xC8, 0x3F },
0307     { 0xC9, 0x3F },
0308     { 0xCA, 0x3F },
0309     { 0xCB, 0x3F },
0310     { 0xCC, 0x3D },
0311     { 0xCD, 0x2F },
0312     { 0xCE, 0x2F },
0313     { 0xCF, 0x2F },
0314     { 0xD0, 0x07 },
0315     { 0xD2, 0x00 },
0316     { 0xD3, 0x02 },
0317     { 0xD4, 0x06 },
0318     { 0xD5, 0x12 },
0319     { 0xD6, 0x0A },
0320     { 0xD7, 0x14 },
0321     { 0xD8, 0x22 },
0322     { 0xD9, 0x2E },
0323     { 0xDA, 0x3D },
0324     { 0xDB, 0x3F },
0325     { 0xDC, 0x3F },
0326     { 0xDD, 0x3F },
0327     { 0xDE, 0x3D },
0328     { 0xDF, 0x2F },
0329     { 0xE0, 0x2F },
0330     { 0xE1, 0x2F },
0331     { 0xE2, 0x07 },
0332     { 0xB0, 0x07 },
0333     { 0xB1, 0x18 },
0334     { 0xB2, 0x19 },
0335     { 0xB3, 0x2E },
0336     { 0xB4, 0x52 },
0337     { 0xB5, 0x72 },
0338     { 0xB6, 0x8C },
0339     { 0xB7, 0xBD },
0340     { 0xB8, 0xEB },
0341     { 0xB9, 0x47 },
0342     { 0xBA, 0x96 },
0343     { 0xBB, 0x1E },
0344     { 0xBC, 0x90 },
0345     { 0xBD, 0x93 },
0346     { 0xBE, 0xFA },
0347     { 0xBF, 0x56 },
0348     { 0xC0, 0x8C },
0349     { 0xC1, 0xB7 },
0350     { 0xC2, 0xCC },
0351     { 0xC3, 0xDF },
0352     { 0xC4, 0xE8 },
0353     { 0xC5, 0xF0 },
0354     { 0xC6, 0xF8 },
0355     { 0xC7, 0xFA },
0356     { 0xC8, 0xFC },
0357     { 0xC9, 0x00 },
0358     { 0xCA, 0x00 },
0359     { 0xCB, 0x5A },
0360     { 0xCC, 0xAF },
0361     { 0xCD, 0xFF },
0362     { 0xCE, 0xFF },
0363     { 0xB0, 0x08 },
0364     { 0xB1, 0x04 },
0365     { 0xB2, 0x15 },
0366     { 0xB3, 0x2D },
0367     { 0xB4, 0x51 },
0368     { 0xB5, 0x72 },
0369     { 0xB6, 0x8D },
0370     { 0xB7, 0xBE },
0371     { 0xB8, 0xED },
0372     { 0xB9, 0x4A },
0373     { 0xBA, 0x9A },
0374     { 0xBB, 0x23 },
0375     { 0xBC, 0x95 },
0376     { 0xBD, 0x98 },
0377     { 0xBE, 0xFF },
0378     { 0xBF, 0x59 },
0379     { 0xC0, 0x8E },
0380     { 0xC1, 0xB9 },
0381     { 0xC2, 0xCD },
0382     { 0xC3, 0xDF },
0383     { 0xC4, 0xE8 },
0384     { 0xC5, 0xF0 },
0385     { 0xC6, 0xF8 },
0386     { 0xC7, 0xFA },
0387     { 0xC8, 0xFC },
0388     { 0xC9, 0x00 },
0389     { 0xCA, 0x00 },
0390     { 0xCB, 0x5A },
0391     { 0xCC, 0xAF },
0392     { 0xCD, 0xFF },
0393     { 0xCE, 0xFF },
0394     { 0xB0, 0x09 },
0395     { 0xB1, 0x04 },
0396     { 0xB2, 0x2C },
0397     { 0xB3, 0x36 },
0398     { 0xB4, 0x53 },
0399     { 0xB5, 0x73 },
0400     { 0xB6, 0x8E },
0401     { 0xB7, 0xC0 },
0402     { 0xB8, 0xEF },
0403     { 0xB9, 0x4C },
0404     { 0xBA, 0x9D },
0405     { 0xBB, 0x25 },
0406     { 0xBC, 0x96 },
0407     { 0xBD, 0x9A },
0408     { 0xBE, 0x01 },
0409     { 0xBF, 0x59 },
0410     { 0xC0, 0x8E },
0411     { 0xC1, 0xB9 },
0412     { 0xC2, 0xCD },
0413     { 0xC3, 0xDF },
0414     { 0xC4, 0xE8 },
0415     { 0xC5, 0xF0 },
0416     { 0xC6, 0xF8 },
0417     { 0xC7, 0xFA },
0418     { 0xC8, 0xFC },
0419     { 0xC9, 0x00 },
0420     { 0xCA, 0x00 },
0421     { 0xCB, 0x5A },
0422     { 0xCC, 0xBF },
0423     { 0xCD, 0xFF },
0424     { 0xCE, 0xFF },
0425     { 0xB0, 0x0A },
0426     { 0xB1, 0x18 },
0427     { 0xB2, 0x19 },
0428     { 0xB3, 0x2E },
0429     { 0xB4, 0x52 },
0430     { 0xB5, 0x72 },
0431     { 0xB6, 0x8C },
0432     { 0xB7, 0xBD },
0433     { 0xB8, 0xEB },
0434     { 0xB9, 0x47 },
0435     { 0xBA, 0x96 },
0436     { 0xBB, 0x1E },
0437     { 0xBC, 0x90 },
0438     { 0xBD, 0x93 },
0439     { 0xBE, 0xFA },
0440     { 0xBF, 0x56 },
0441     { 0xC0, 0x8C },
0442     { 0xC1, 0xB7 },
0443     { 0xC2, 0xCC },
0444     { 0xC3, 0xDF },
0445     { 0xC4, 0xE8 },
0446     { 0xC5, 0xF0 },
0447     { 0xC6, 0xF8 },
0448     { 0xC7, 0xFA },
0449     { 0xC8, 0xFC },
0450     { 0xC9, 0x00 },
0451     { 0xCA, 0x00 },
0452     { 0xCB, 0x5A },
0453     { 0xCC, 0xAF },
0454     { 0xCD, 0xFF },
0455     { 0xCE, 0xFF },
0456     { 0xB0, 0x0B },
0457     { 0xB1, 0x04 },
0458     { 0xB2, 0x15 },
0459     { 0xB3, 0x2D },
0460     { 0xB4, 0x51 },
0461     { 0xB5, 0x72 },
0462     { 0xB6, 0x8D },
0463     { 0xB7, 0xBE },
0464     { 0xB8, 0xED },
0465     { 0xB9, 0x4A },
0466     { 0xBA, 0x9A },
0467     { 0xBB, 0x23 },
0468     { 0xBC, 0x95 },
0469     { 0xBD, 0x98 },
0470     { 0xBE, 0xFF },
0471     { 0xBF, 0x59 },
0472     { 0xC0, 0x8E },
0473     { 0xC1, 0xB9 },
0474     { 0xC2, 0xCD },
0475     { 0xC3, 0xDF },
0476     { 0xC4, 0xE8 },
0477     { 0xC5, 0xF0 },
0478     { 0xC6, 0xF8 },
0479     { 0xC7, 0xFA },
0480     { 0xC8, 0xFC },
0481     { 0xC9, 0x00 },
0482     { 0xCA, 0x00 },
0483     { 0xCB, 0x5A },
0484     { 0xCC, 0xAF },
0485     { 0xCD, 0xFF },
0486     { 0xCE, 0xFF },
0487     { 0xB0, 0x0C },
0488     { 0xB1, 0x04 },
0489     { 0xB2, 0x2C },
0490     { 0xB3, 0x36 },
0491     { 0xB4, 0x53 },
0492     { 0xB5, 0x73 },
0493     { 0xB6, 0x8E },
0494     { 0xB7, 0xC0 },
0495     { 0xB8, 0xEF },
0496     { 0xB9, 0x4C },
0497     { 0xBA, 0x9D },
0498     { 0xBB, 0x25 },
0499     { 0xBC, 0x96 },
0500     { 0xBD, 0x9A },
0501     { 0xBE, 0x01 },
0502     { 0xBF, 0x59 },
0503     { 0xC0, 0x8E },
0504     { 0xC1, 0xB9 },
0505     { 0xC2, 0xCD },
0506     { 0xC3, 0xDF },
0507     { 0xC4, 0xE8 },
0508     { 0xC5, 0xF0 },
0509     { 0xC6, 0xF8 },
0510     { 0xC7, 0xFA },
0511     { 0xC8, 0xFC },
0512     { 0xC9, 0x00 },
0513     { 0xCA, 0x00 },
0514     { 0xCB, 0x5A },
0515     { 0xCC, 0xBF },
0516     { 0xCD, 0xFF },
0517     { 0xCE, 0xFF },
0518     { 0xB0, 0x04 },
0519     { 0xB5, 0x02 },
0520     { 0xB6, 0x01 },
0521 };
0522 
0523 static const struct panel_desc boe_himax8279d8p_panel_desc = {
0524     .display_mode = &default_display_mode,
0525     .bpc = 8,
0526     .width_mm = 107,
0527     .height_mm = 172,
0528     .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
0529             MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
0530     .format = MIPI_DSI_FMT_RGB888,
0531     .lanes = 4,
0532     .on_cmds = boe_himax8279d8p_on_cmds,
0533     .on_cmds_num = 260,
0534 };
0535 
0536 /* 10 inch */
0537 static const struct panel_cmd boe_himax8279d10p_on_cmds[] = {
0538     { 0xB0, 0x05 },
0539     { 0xB1, 0xE5 },
0540     { 0xB3, 0x52 },
0541     { 0xB0, 0x00 },
0542     { 0xB6, 0x03 },
0543     { 0xBA, 0x8B },
0544     { 0xBF, 0x1A },
0545     { 0xC0, 0x0F },
0546     { 0xC2, 0x0C },
0547     { 0xC3, 0x02 },
0548     { 0xC4, 0x0C },
0549     { 0xC5, 0x02 },
0550     { 0xB0, 0x01 },
0551     { 0xE0, 0x26 },
0552     { 0xE1, 0x26 },
0553     { 0xDC, 0x00 },
0554     { 0xDD, 0x00 },
0555     { 0xCC, 0x26 },
0556     { 0xCD, 0x26 },
0557     { 0xC8, 0x00 },
0558     { 0xC9, 0x00 },
0559     { 0xD2, 0x03 },
0560     { 0xD3, 0x03 },
0561     { 0xE6, 0x04 },
0562     { 0xE7, 0x04 },
0563     { 0xC4, 0x09 },
0564     { 0xC5, 0x09 },
0565     { 0xD8, 0x0A },
0566     { 0xD9, 0x0A },
0567     { 0xC2, 0x0B },
0568     { 0xC3, 0x0B },
0569     { 0xD6, 0x0C },
0570     { 0xD7, 0x0C },
0571     { 0xC0, 0x05 },
0572     { 0xC1, 0x05 },
0573     { 0xD4, 0x06 },
0574     { 0xD5, 0x06 },
0575     { 0xCA, 0x07 },
0576     { 0xCB, 0x07 },
0577     { 0xDE, 0x08 },
0578     { 0xDF, 0x08 },
0579     { 0xB0, 0x02 },
0580     { 0xC0, 0x00 },
0581     { 0xC1, 0x0D },
0582     { 0xC2, 0x17 },
0583     { 0xC3, 0x26 },
0584     { 0xC4, 0x31 },
0585     { 0xC5, 0x1C },
0586     { 0xC6, 0x2C },
0587     { 0xC7, 0x33 },
0588     { 0xC8, 0x31 },
0589     { 0xC9, 0x37 },
0590     { 0xCA, 0x37 },
0591     { 0xCB, 0x37 },
0592     { 0xCC, 0x39 },
0593     { 0xCD, 0x2E },
0594     { 0xCE, 0x2F },
0595     { 0xCF, 0x2F },
0596     { 0xD0, 0x07 },
0597     { 0xD2, 0x00 },
0598     { 0xD3, 0x0D },
0599     { 0xD4, 0x17 },
0600     { 0xD5, 0x26 },
0601     { 0xD6, 0x31 },
0602     { 0xD7, 0x3F },
0603     { 0xD8, 0x3F },
0604     { 0xD9, 0x3F },
0605     { 0xDA, 0x3F },
0606     { 0xDB, 0x37 },
0607     { 0xDC, 0x37 },
0608     { 0xDD, 0x37 },
0609     { 0xDE, 0x39 },
0610     { 0xDF, 0x2E },
0611     { 0xE0, 0x2F },
0612     { 0xE1, 0x2F },
0613     { 0xE2, 0x07 },
0614     { 0xB0, 0x03 },
0615     { 0xC8, 0x0B },
0616     { 0xC9, 0x07 },
0617     { 0xC3, 0x00 },
0618     { 0xE7, 0x00 },
0619     { 0xC5, 0x2A },
0620     { 0xDE, 0x2A },
0621     { 0xCA, 0x43 },
0622     { 0xC9, 0x07 },
0623     { 0xE4, 0xC0 },
0624     { 0xE5, 0x0D },
0625     { 0xCB, 0x01 },
0626     { 0xBC, 0x01 },
0627     { 0xB0, 0x06 },
0628     { 0xB8, 0xA5 },
0629     { 0xC0, 0xA5 },
0630     { 0xC7, 0x0F },
0631     { 0xD5, 0x32 },
0632     { 0xB8, 0x00 },
0633     { 0xC0, 0x00 },
0634     { 0xBC, 0x00 },
0635     { 0xB0, 0x07 },
0636     { 0xB1, 0x00 },
0637     { 0xB2, 0x05 },
0638     { 0xB3, 0x10 },
0639     { 0xB4, 0x22 },
0640     { 0xB5, 0x36 },
0641     { 0xB6, 0x4A },
0642     { 0xB7, 0x6C },
0643     { 0xB8, 0x9A },
0644     { 0xB9, 0xD7 },
0645     { 0xBA, 0x17 },
0646     { 0xBB, 0x92 },
0647     { 0xBC, 0x15 },
0648     { 0xBD, 0x18 },
0649     { 0xBE, 0x8C },
0650     { 0xBF, 0x00 },
0651     { 0xC0, 0x3A },
0652     { 0xC1, 0x72 },
0653     { 0xC2, 0x8C },
0654     { 0xC3, 0xA5 },
0655     { 0xC4, 0xB1 },
0656     { 0xC5, 0xBE },
0657     { 0xC6, 0xCA },
0658     { 0xC7, 0xD1 },
0659     { 0xC8, 0xD4 },
0660     { 0xC9, 0x00 },
0661     { 0xCA, 0x00 },
0662     { 0xCB, 0x16 },
0663     { 0xCC, 0xAF },
0664     { 0xCD, 0xFF },
0665     { 0xCE, 0xFF },
0666     { 0xB0, 0x08 },
0667     { 0xB1, 0x04 },
0668     { 0xB2, 0x05 },
0669     { 0xB3, 0x11 },
0670     { 0xB4, 0x24 },
0671     { 0xB5, 0x39 },
0672     { 0xB6, 0x4E },
0673     { 0xB7, 0x72 },
0674     { 0xB8, 0xA3 },
0675     { 0xB9, 0xE1 },
0676     { 0xBA, 0x25 },
0677     { 0xBB, 0xA8 },
0678     { 0xBC, 0x2E },
0679     { 0xBD, 0x32 },
0680     { 0xBE, 0xAD },
0681     { 0xBF, 0x28 },
0682     { 0xC0, 0x63 },
0683     { 0xC1, 0x9B },
0684     { 0xC2, 0xB5 },
0685     { 0xC3, 0xCF },
0686     { 0xC4, 0xDB },
0687     { 0xC5, 0xE8 },
0688     { 0xC6, 0xF5 },
0689     { 0xC7, 0xFA },
0690     { 0xC8, 0xFC },
0691     { 0xC9, 0x00 },
0692     { 0xCA, 0x00 },
0693     { 0xCB, 0x16 },
0694     { 0xCC, 0xAF },
0695     { 0xCD, 0xFF },
0696     { 0xCE, 0xFF },
0697     { 0xB0, 0x09 },
0698     { 0xB1, 0x04 },
0699     { 0xB2, 0x04 },
0700     { 0xB3, 0x0F },
0701     { 0xB4, 0x22 },
0702     { 0xB5, 0x37 },
0703     { 0xB6, 0x4D },
0704     { 0xB7, 0x71 },
0705     { 0xB8, 0xA2 },
0706     { 0xB9, 0xE1 },
0707     { 0xBA, 0x26 },
0708     { 0xBB, 0xA9 },
0709     { 0xBC, 0x2F },
0710     { 0xBD, 0x33 },
0711     { 0xBE, 0xAC },
0712     { 0xBF, 0x24 },
0713     { 0xC0, 0x5D },
0714     { 0xC1, 0x94 },
0715     { 0xC2, 0xAC },
0716     { 0xC3, 0xC5 },
0717     { 0xC4, 0xD1 },
0718     { 0xC5, 0xDC },
0719     { 0xC6, 0xE8 },
0720     { 0xC7, 0xED },
0721     { 0xC8, 0xF0 },
0722     { 0xC9, 0x00 },
0723     { 0xCA, 0x00 },
0724     { 0xCB, 0x16 },
0725     { 0xCC, 0xAF },
0726     { 0xCD, 0xFF },
0727     { 0xCE, 0xFF },
0728     { 0xB0, 0x0A },
0729     { 0xB1, 0x00 },
0730     { 0xB2, 0x05 },
0731     { 0xB3, 0x10 },
0732     { 0xB4, 0x22 },
0733     { 0xB5, 0x36 },
0734     { 0xB6, 0x4A },
0735     { 0xB7, 0x6C },
0736     { 0xB8, 0x9A },
0737     { 0xB9, 0xD7 },
0738     { 0xBA, 0x17 },
0739     { 0xBB, 0x92 },
0740     { 0xBC, 0x15 },
0741     { 0xBD, 0x18 },
0742     { 0xBE, 0x8C },
0743     { 0xBF, 0x00 },
0744     { 0xC0, 0x3A },
0745     { 0xC1, 0x72 },
0746     { 0xC2, 0x8C },
0747     { 0xC3, 0xA5 },
0748     { 0xC4, 0xB1 },
0749     { 0xC5, 0xBE },
0750     { 0xC6, 0xCA },
0751     { 0xC7, 0xD1 },
0752     { 0xC8, 0xD4 },
0753     { 0xC9, 0x00 },
0754     { 0xCA, 0x00 },
0755     { 0xCB, 0x16 },
0756     { 0xCC, 0xAF },
0757     { 0xCD, 0xFF },
0758     { 0xCE, 0xFF },
0759     { 0xB0, 0x0B },
0760     { 0xB1, 0x04 },
0761     { 0xB2, 0x05 },
0762     { 0xB3, 0x11 },
0763     { 0xB4, 0x24 },
0764     { 0xB5, 0x39 },
0765     { 0xB6, 0x4E },
0766     { 0xB7, 0x72 },
0767     { 0xB8, 0xA3 },
0768     { 0xB9, 0xE1 },
0769     { 0xBA, 0x25 },
0770     { 0xBB, 0xA8 },
0771     { 0xBC, 0x2E },
0772     { 0xBD, 0x32 },
0773     { 0xBE, 0xAD },
0774     { 0xBF, 0x28 },
0775     { 0xC0, 0x63 },
0776     { 0xC1, 0x9B },
0777     { 0xC2, 0xB5 },
0778     { 0xC3, 0xCF },
0779     { 0xC4, 0xDB },
0780     { 0xC5, 0xE8 },
0781     { 0xC6, 0xF5 },
0782     { 0xC7, 0xFA },
0783     { 0xC8, 0xFC },
0784     { 0xC9, 0x00 },
0785     { 0xCA, 0x00 },
0786     { 0xCB, 0x16 },
0787     { 0xCC, 0xAF },
0788     { 0xCD, 0xFF },
0789     { 0xCE, 0xFF },
0790     { 0xB0, 0x0C },
0791     { 0xB1, 0x04 },
0792     { 0xB2, 0x04 },
0793     { 0xB3, 0x0F },
0794     { 0xB4, 0x22 },
0795     { 0xB5, 0x37 },
0796     { 0xB6, 0x4D },
0797     { 0xB7, 0x71 },
0798     { 0xB8, 0xA2 },
0799     { 0xB9, 0xE1 },
0800     { 0xBA, 0x26 },
0801     { 0xBB, 0xA9 },
0802     { 0xBC, 0x2F },
0803     { 0xBD, 0x33 },
0804     { 0xBE, 0xAC },
0805     { 0xBF, 0x24 },
0806     { 0xC0, 0x5D },
0807     { 0xC1, 0x94 },
0808     { 0xC2, 0xAC },
0809     { 0xC3, 0xC5 },
0810     { 0xC4, 0xD1 },
0811     { 0xC5, 0xDC },
0812     { 0xC6, 0xE8 },
0813     { 0xC7, 0xED },
0814     { 0xC8, 0xF0 },
0815     { 0xC9, 0x00 },
0816     { 0xCA, 0x00 },
0817     { 0xCB, 0x16 },
0818     { 0xCC, 0xAF },
0819     { 0xCD, 0xFF },
0820     { 0xCE, 0xFF },
0821 };
0822 
0823 static const struct panel_desc boe_himax8279d10p_panel_desc = {
0824     .display_mode = &default_display_mode,
0825     .bpc = 8,
0826     .width_mm = 135,
0827     .height_mm = 216,
0828     .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
0829             MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
0830     .format = MIPI_DSI_FMT_RGB888,
0831     .lanes = 4,
0832     .on_cmds = boe_himax8279d10p_on_cmds,
0833     .on_cmds_num = 283,
0834 };
0835 
0836 static const struct of_device_id panel_of_match[] = {
0837     {
0838         .compatible = "boe,himax8279d8p",
0839         .data = &boe_himax8279d8p_panel_desc,
0840     },
0841     {
0842         .compatible = "boe,himax8279d10p",
0843         .data = &boe_himax8279d10p_panel_desc,
0844     },
0845     {
0846         /* sentinel */
0847     }
0848 };
0849 MODULE_DEVICE_TABLE(of, panel_of_match);
0850 
0851 static int panel_add(struct panel_info *pinfo)
0852 {
0853     struct device *dev = &pinfo->link->dev;
0854     int ret;
0855 
0856     pinfo->pp18_gpio = devm_gpiod_get(dev, "pp18", GPIOD_OUT_HIGH);
0857     if (IS_ERR(pinfo->pp18_gpio)) {
0858         ret = PTR_ERR(pinfo->pp18_gpio);
0859         if (ret != -EPROBE_DEFER)
0860             dev_err(dev, "failed to get pp18 gpio: %d\n", ret);
0861         return ret;
0862     }
0863 
0864     pinfo->pp33_gpio = devm_gpiod_get(dev, "pp33", GPIOD_OUT_HIGH);
0865     if (IS_ERR(pinfo->pp33_gpio)) {
0866         ret = PTR_ERR(pinfo->pp33_gpio);
0867         if (ret != -EPROBE_DEFER)
0868             dev_err(dev, "failed to get pp33 gpio: %d\n", ret);
0869         return ret;
0870     }
0871 
0872     pinfo->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
0873     if (IS_ERR(pinfo->enable_gpio)) {
0874         ret = PTR_ERR(pinfo->enable_gpio);
0875         if (ret != -EPROBE_DEFER)
0876             dev_err(dev, "failed to get enable gpio: %d\n", ret);
0877         return ret;
0878     }
0879 
0880     drm_panel_init(&pinfo->base, dev, &panel_funcs,
0881                DRM_MODE_CONNECTOR_DSI);
0882 
0883     ret = drm_panel_of_backlight(&pinfo->base);
0884     if (ret)
0885         return ret;
0886 
0887     drm_panel_add(&pinfo->base);
0888 
0889     return 0;
0890 }
0891 
0892 static int panel_probe(struct mipi_dsi_device *dsi)
0893 {
0894     struct panel_info *pinfo;
0895     const struct panel_desc *desc;
0896     int err;
0897 
0898     pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
0899     if (!pinfo)
0900         return -ENOMEM;
0901 
0902     desc = of_device_get_match_data(&dsi->dev);
0903     dsi->mode_flags = desc->mode_flags;
0904     dsi->format = desc->format;
0905     dsi->lanes = desc->lanes;
0906     pinfo->desc = desc;
0907 
0908     pinfo->link = dsi;
0909     mipi_dsi_set_drvdata(dsi, pinfo);
0910 
0911     err = panel_add(pinfo);
0912     if (err < 0)
0913         return err;
0914 
0915     err = mipi_dsi_attach(dsi);
0916     if (err < 0)
0917         drm_panel_remove(&pinfo->base);
0918 
0919     return err;
0920 }
0921 
0922 static int panel_remove(struct mipi_dsi_device *dsi)
0923 {
0924     struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
0925     int err;
0926 
0927     err = boe_panel_disable(&pinfo->base);
0928     if (err < 0)
0929         dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
0930 
0931     err = boe_panel_unprepare(&pinfo->base);
0932     if (err < 0)
0933         dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
0934 
0935     err = mipi_dsi_detach(dsi);
0936     if (err < 0)
0937         dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
0938 
0939     drm_panel_remove(&pinfo->base);
0940 
0941     return 0;
0942 }
0943 
0944 static void panel_shutdown(struct mipi_dsi_device *dsi)
0945 {
0946     struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
0947 
0948     boe_panel_disable(&pinfo->base);
0949     boe_panel_unprepare(&pinfo->base);
0950 }
0951 
0952 static struct mipi_dsi_driver panel_driver = {
0953     .driver = {
0954         .name = "panel-boe-himax8279d",
0955         .of_match_table = panel_of_match,
0956     },
0957     .probe = panel_probe,
0958     .remove = panel_remove,
0959     .shutdown = panel_shutdown,
0960 };
0961 module_mipi_dsi_driver(panel_driver);
0962 
0963 MODULE_AUTHOR("Jerry Han <jerry.han.hq@gmail.com>");
0964 MODULE_DESCRIPTION("Boe Himax8279d driver");
0965 MODULE_LICENSE("GPL v2");