Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * BOE BF060Y8M-AJ0 5.99" MIPI-DSI OLED Panel on SW43404 DriverIC
0004  *
0005  * Copyright (c) 2020 AngeloGioacchino Del Regno
0006  *                    <angelogioacchino.delregno@somainline.org>
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/of.h>
0014 #include <linux/regulator/consumer.h>
0015 #include <video/mipi_display.h>
0016 #include <drm/drm_mipi_dsi.h>
0017 #include <drm/drm_modes.h>
0018 #include <drm/drm_panel.h>
0019 
0020 #define DCS_ALLOW_HBM_RANGE     0x0c
0021 #define DCS_DISALLOW_HBM_RANGE      0x08
0022 
0023 enum boe_bf060y8m_aj0_supplies {
0024     BF060Y8M_VREG_VCC,
0025     BF060Y8M_VREG_VDDIO,
0026     BF060Y8M_VREG_VCI,
0027     BF060Y8M_VREG_EL_VDD,
0028     BF060Y8M_VREG_EL_VSS,
0029     BF060Y8M_VREG_MAX
0030 };
0031 
0032 struct boe_bf060y8m_aj0 {
0033     struct drm_panel panel;
0034     struct mipi_dsi_device *dsi;
0035     struct regulator_bulk_data vregs[BF060Y8M_VREG_MAX];
0036     struct gpio_desc *reset_gpio;
0037     bool prepared;
0038 };
0039 
0040 static inline
0041 struct boe_bf060y8m_aj0 *to_boe_bf060y8m_aj0(struct drm_panel *panel)
0042 {
0043     return container_of(panel, struct boe_bf060y8m_aj0, panel);
0044 }
0045 
0046 #define dsi_dcs_write_seq(dsi, seq...) do {             \
0047         static const u8 d[] = { seq };              \
0048         int ret;                        \
0049         ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
0050         if (ret < 0)                        \
0051             return ret;                 \
0052     } while (0)
0053 
0054 static void boe_bf060y8m_aj0_reset(struct boe_bf060y8m_aj0 *boe)
0055 {
0056     gpiod_set_value_cansleep(boe->reset_gpio, 0);
0057     usleep_range(2000, 3000);
0058     gpiod_set_value_cansleep(boe->reset_gpio, 1);
0059     usleep_range(15000, 16000);
0060     gpiod_set_value_cansleep(boe->reset_gpio, 0);
0061     usleep_range(5000, 6000);
0062 }
0063 
0064 static int boe_bf060y8m_aj0_on(struct boe_bf060y8m_aj0 *boe)
0065 {
0066     struct mipi_dsi_device *dsi = boe->dsi;
0067     struct device *dev = &dsi->dev;
0068     int ret;
0069 
0070     dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
0071     dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x4c);
0072     dsi_dcs_write_seq(dsi, MIPI_DCS_SET_3D_CONTROL, 0x10);
0073     dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, DCS_ALLOW_HBM_RANGE);
0074     dsi_dcs_write_seq(dsi, 0xf8,
0075               0x00, 0x08, 0x10, 0x00, 0x22, 0x00, 0x00, 0x2d);
0076 
0077     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0078     if (ret < 0) {
0079         dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
0080         return ret;
0081     }
0082     msleep(30);
0083 
0084     dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
0085     dsi_dcs_write_seq(dsi, 0xc0,
0086               0x08, 0x48, 0x65, 0x33, 0x33, 0x33,
0087               0x2a, 0x31, 0x39, 0x20, 0x09);
0088     dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x00, 0x00, 0x1f, 0x1f,
0089               0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
0090               0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
0091     dsi_dcs_write_seq(dsi, 0xe2, 0x20, 0x04, 0x10, 0x12, 0x92,
0092               0x4f, 0x8f, 0x44, 0x84, 0x83, 0x83, 0x83,
0093               0x5c, 0x5c, 0x5c);
0094     dsi_dcs_write_seq(dsi, 0xde, 0x01, 0x2c, 0x00, 0x77, 0x3e);
0095 
0096     msleep(30);
0097 
0098     ret = mipi_dsi_dcs_set_display_on(dsi);
0099     if (ret < 0) {
0100         dev_err(dev, "Failed to set display on: %d\n", ret);
0101         return ret;
0102     }
0103     msleep(50);
0104 
0105     return 0;
0106 }
0107 
0108 static int boe_bf060y8m_aj0_off(struct boe_bf060y8m_aj0 *boe)
0109 {
0110     struct mipi_dsi_device *dsi = boe->dsi;
0111     struct device *dev = &dsi->dev;
0112     int ret;
0113 
0114     /* OFF commands sent in HS mode */
0115     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
0116     ret = mipi_dsi_dcs_set_display_off(dsi);
0117     if (ret < 0) {
0118         dev_err(dev, "Failed to set display off: %d\n", ret);
0119         return ret;
0120     }
0121     msleep(20);
0122 
0123     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
0124     if (ret < 0) {
0125         dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
0126         return ret;
0127     }
0128     usleep_range(1000, 2000);
0129     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
0130 
0131     return 0;
0132 }
0133 
0134 static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel)
0135 {
0136     struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
0137     struct device *dev = &boe->dsi->dev;
0138     int ret;
0139 
0140     if (boe->prepared)
0141         return 0;
0142 
0143     /*
0144      * Enable EL Driving Voltage first - doing that at the beginning
0145      * or at the end of the power sequence doesn't matter, so enable
0146      * it here to avoid yet another usleep at the end.
0147      */
0148     ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
0149     if (ret)
0150         return ret;
0151     ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
0152     if (ret)
0153         goto err_elvss;
0154 
0155     ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
0156     if (ret)
0157         goto err_vcc;
0158     usleep_range(1000, 2000);
0159     ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
0160     if (ret)
0161         goto err_vddio;
0162     usleep_range(500, 1000);
0163     ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCI].consumer);
0164     if (ret)
0165         goto err_vci;
0166     usleep_range(2000, 3000);
0167 
0168     boe_bf060y8m_aj0_reset(boe);
0169 
0170     ret = boe_bf060y8m_aj0_on(boe);
0171     if (ret < 0) {
0172         dev_err(dev, "Failed to initialize panel: %d\n", ret);
0173         gpiod_set_value_cansleep(boe->reset_gpio, 1);
0174         return ret;
0175     }
0176 
0177     boe->prepared = true;
0178     return 0;
0179 
0180 err_vci:
0181     regulator_disable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
0182 err_vddio:
0183     regulator_disable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
0184 err_vcc:
0185     regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
0186 err_elvss:
0187     regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
0188     return ret;
0189 }
0190 
0191 static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel)
0192 {
0193     struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
0194     struct device *dev = &boe->dsi->dev;
0195     int ret;
0196 
0197     if (!boe->prepared)
0198         return 0;
0199 
0200     ret = boe_bf060y8m_aj0_off(boe);
0201     if (ret < 0)
0202         dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
0203 
0204     gpiod_set_value_cansleep(boe->reset_gpio, 1);
0205     ret = regulator_bulk_disable(ARRAY_SIZE(boe->vregs), boe->vregs);
0206 
0207     boe->prepared = false;
0208     return 0;
0209 }
0210 
0211 static const struct drm_display_mode boe_bf060y8m_aj0_mode = {
0212     .clock = 165268,
0213     .hdisplay = 1080,
0214     .hsync_start = 1080 + 36,
0215     .hsync_end = 1080 + 36 + 24,
0216     .htotal = 1080 + 36 + 24 + 96,
0217     .vdisplay = 2160,
0218     .vsync_start = 2160 + 16,
0219     .vsync_end = 2160 + 16 + 1,
0220     .vtotal = 2160 + 16 + 1 + 15,
0221     .width_mm = 68,   /* 68.04 mm */
0222     .height_mm = 136, /* 136.08 mm */
0223 };
0224 
0225 static int boe_bf060y8m_aj0_get_modes(struct drm_panel *panel,
0226                       struct drm_connector *connector)
0227 {
0228     struct drm_display_mode *mode;
0229 
0230     mode = drm_mode_duplicate(connector->dev, &boe_bf060y8m_aj0_mode);
0231     if (!mode)
0232         return -ENOMEM;
0233 
0234     drm_mode_set_name(mode);
0235 
0236     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0237     connector->display_info.width_mm = mode->width_mm;
0238     connector->display_info.height_mm = mode->height_mm;
0239     drm_mode_probed_add(connector, mode);
0240 
0241     return 1;
0242 }
0243 
0244 static const struct drm_panel_funcs boe_bf060y8m_aj0_panel_funcs = {
0245     .prepare = boe_bf060y8m_aj0_prepare,
0246     .unprepare = boe_bf060y8m_aj0_unprepare,
0247     .get_modes = boe_bf060y8m_aj0_get_modes,
0248 };
0249 
0250 static int boe_bf060y8m_aj0_bl_update_status(struct backlight_device *bl)
0251 {
0252     struct mipi_dsi_device *dsi = bl_get_data(bl);
0253     u16 brightness = backlight_get_brightness(bl);
0254     int ret;
0255 
0256     ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
0257     if (ret < 0)
0258         return ret;
0259 
0260     return 0;
0261 }
0262 
0263 static int boe_bf060y8m_aj0_bl_get_brightness(struct backlight_device *bl)
0264 {
0265     struct mipi_dsi_device *dsi = bl_get_data(bl);
0266     u16 brightness;
0267     int ret;
0268 
0269     ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
0270     if (ret < 0)
0271         return ret;
0272 
0273     return brightness & 0xff;
0274 }
0275 
0276 static const struct backlight_ops boe_bf060y8m_aj0_bl_ops = {
0277     .update_status = boe_bf060y8m_aj0_bl_update_status,
0278     .get_brightness = boe_bf060y8m_aj0_bl_get_brightness,
0279 };
0280 
0281 static struct backlight_device *
0282 boe_bf060y8m_aj0_create_backlight(struct mipi_dsi_device *dsi)
0283 {
0284     struct device *dev = &dsi->dev;
0285     const struct backlight_properties props = {
0286         .type = BACKLIGHT_RAW,
0287         .brightness = 127,
0288         .max_brightness = 255,
0289         .scale = BACKLIGHT_SCALE_NON_LINEAR,
0290     };
0291 
0292     return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
0293                           &boe_bf060y8m_aj0_bl_ops, &props);
0294 }
0295 
0296 static int boe_bf060y8m_aj0_init_vregs(struct boe_bf060y8m_aj0 *boe,
0297                        struct device *dev)
0298 {
0299     struct regulator *vreg;
0300     int ret;
0301 
0302     boe->vregs[BF060Y8M_VREG_VCC].supply = "vcc";
0303     boe->vregs[BF060Y8M_VREG_VDDIO].supply = "vddio";
0304     boe->vregs[BF060Y8M_VREG_VCI].supply = "vci";
0305     boe->vregs[BF060Y8M_VREG_EL_VDD].supply = "elvdd";
0306     boe->vregs[BF060Y8M_VREG_EL_VSS].supply = "elvss";
0307     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(boe->vregs),
0308                       boe->vregs);
0309     if (ret < 0) {
0310         dev_err(dev, "Failed to get regulators: %d\n", ret);
0311         return ret;
0312     }
0313 
0314     vreg = boe->vregs[BF060Y8M_VREG_VCC].consumer;
0315     ret = regulator_is_supported_voltage(vreg, 2700000, 3600000);
0316     if (!ret)
0317         return ret;
0318 
0319     vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
0320     ret = regulator_is_supported_voltage(vreg, 1620000, 1980000);
0321     if (!ret)
0322         return ret;
0323 
0324     vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
0325     ret = regulator_is_supported_voltage(vreg, 2600000, 3600000);
0326     if (!ret)
0327         return ret;
0328 
0329     vreg = boe->vregs[BF060Y8M_VREG_EL_VDD].consumer;
0330     ret = regulator_is_supported_voltage(vreg, 4400000, 4800000);
0331     if (!ret)
0332         return ret;
0333 
0334     /* ELVSS is negative: -5.00V to -1.40V */
0335     vreg = boe->vregs[BF060Y8M_VREG_EL_VSS].consumer;
0336     ret = regulator_is_supported_voltage(vreg, 1400000, 5000000);
0337     if (!ret)
0338         return ret;
0339 
0340     /*
0341      * Set min/max rated current, known only for VCI and VDDIO and,
0342      * in case of failure, just go on gracefully, as this step is not
0343      * guaranteed to succeed on all regulator HW but do a debug print
0344      * to inform the developer during debugging.
0345      * In any case, these two supplies are also optional, so they may
0346      * be fixed-regulator which, at the time of writing, does not
0347      * support fake current limiting.
0348      */
0349     vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
0350     ret = regulator_set_current_limit(vreg, 1500, 2500);
0351     if (ret)
0352         dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
0353             boe->vregs[1].supply, ret);
0354 
0355     vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
0356     ret = regulator_set_current_limit(vreg, 20000, 40000);
0357     if (ret)
0358         dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
0359             boe->vregs[2].supply, ret);
0360 
0361     return 0;
0362 }
0363 
0364 static int boe_bf060y8m_aj0_probe(struct mipi_dsi_device *dsi)
0365 {
0366     struct device *dev = &dsi->dev;
0367     struct boe_bf060y8m_aj0 *boe;
0368     int ret;
0369 
0370     boe = devm_kzalloc(dev, sizeof(*boe), GFP_KERNEL);
0371     if (!boe)
0372         return -ENOMEM;
0373 
0374     ret = boe_bf060y8m_aj0_init_vregs(boe, dev);
0375     if (ret)
0376         return dev_err_probe(dev, ret,
0377                      "Failed to initialize supplies.\n");
0378 
0379     boe->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
0380     if (IS_ERR(boe->reset_gpio))
0381         return dev_err_probe(dev, PTR_ERR(boe->reset_gpio),
0382                      "Failed to get reset-gpios\n");
0383 
0384     boe->dsi = dsi;
0385     mipi_dsi_set_drvdata(dsi, boe);
0386 
0387     dsi->lanes = 4;
0388     dsi->format = MIPI_DSI_FMT_RGB888;
0389     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
0390               MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
0391               MIPI_DSI_CLOCK_NON_CONTINUOUS |
0392               MIPI_DSI_MODE_LPM;
0393 
0394     drm_panel_init(&boe->panel, dev, &boe_bf060y8m_aj0_panel_funcs,
0395                DRM_MODE_CONNECTOR_DSI);
0396 
0397     boe->panel.backlight = boe_bf060y8m_aj0_create_backlight(dsi);
0398     if (IS_ERR(boe->panel.backlight))
0399         return dev_err_probe(dev, PTR_ERR(boe->panel.backlight),
0400                      "Failed to create backlight\n");
0401 
0402     drm_panel_add(&boe->panel);
0403 
0404     ret = mipi_dsi_attach(dsi);
0405     if (ret < 0) {
0406         dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
0407         return ret;
0408     }
0409 
0410     return 0;
0411 }
0412 
0413 static int boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi)
0414 {
0415     struct boe_bf060y8m_aj0 *boe = mipi_dsi_get_drvdata(dsi);
0416     int ret;
0417 
0418     ret = mipi_dsi_detach(dsi);
0419     if (ret < 0)
0420         dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
0421 
0422     drm_panel_remove(&boe->panel);
0423 
0424     return 0;
0425 }
0426 
0427 static const struct of_device_id boe_bf060y8m_aj0_of_match[] = {
0428     { .compatible = "boe,bf060y8m-aj0" },
0429     { /* sentinel */ }
0430 };
0431 MODULE_DEVICE_TABLE(of, boe_bf060y8m_aj0_of_match);
0432 
0433 static struct mipi_dsi_driver boe_bf060y8m_aj0_driver = {
0434     .probe = boe_bf060y8m_aj0_probe,
0435     .remove = boe_bf060y8m_aj0_remove,
0436     .driver = {
0437         .name = "panel-sw43404-boe-fhd-amoled",
0438         .of_match_table = boe_bf060y8m_aj0_of_match,
0439     },
0440 };
0441 module_mipi_dsi_driver(boe_bf060y8m_aj0_driver);
0442 
0443 MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");
0444 MODULE_DESCRIPTION("BOE BF060Y8M-AJ0 MIPI-DSI OLED panel");
0445 MODULE_LICENSE("GPL v2");