Signed-off-by: Noralf Trønnes <nor...@tronnes.org>
---
 drivers/gpu/drm/panel/panel-ilitek-ili9341.c | 179 ++++++++++++++++++-
 1 file changed, 170 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c 
b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c
index a755f3e60111..dd333a642159 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c
@@ -21,10 +21,13 @@
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_graph.h>
 #include <linux/spi/spi.h>
 
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
@@ -32,10 +35,25 @@
 
 /* ILI9341 extended register set (Vendor Command Set) */
 #define ILI9341_IFMODE         0xB0 // clock polarity
+#define ILI9341_FRMCTR1        0xb1
+#define ILI9341_DISCTRL        0xb6
+#define ILI9341_ETMOD          0xb7
+#define ILI9341_PWCTRL1        0xc0
+#define ILI9341_PWCTRL2        0xc1
+#define ILI9341_VMCTRL1        0xc5
+#define ILI9341_VMCTRL2        0xc7
+#define ILI9341_PWCTRLA        0xcb
+#define ILI9341_PWCTRLB        0xcf
 #define ILI9341_IFCTL          0xF6 // interface conrol
 #define ILI9341_PGAMCTRL       0xE0 // positive gamma control
 #define ILI9341_NGAMCTRL       0xE1 // negative gamma control
+#define ILI9341_DTCTRLA        0xe8
+#define ILI9341_DTCTRLB        0xea
+#define ILI9341_PWRSEQ         0xed
+#define ILI9341_EN3GAM         0xf2
+#define ILI9341_PUMPCTRL       0xf7
 
+#define ILI9341_MADCTL_BGR     BIT(3)
 #define ILI9341_MADCTL_MV      BIT(5)
 #define ILI9341_MADCTL_MX      BIT(6)
 #define ILI9341_MADCTL_MY      BIT(7)
@@ -44,15 +62,18 @@
  * struct ili9341_config - the display specific configuration
  * @funcs: Panel functions
  * @mode: Display mode
+ * @command_mode_only: Panel only supports command mode
  */
 struct ili9341_config {
        const struct drm_panel_funcs *funcs;
        const struct drm_display_mode *mode;
+       bool command_mode_only;
 };
 
 struct ili9341 {
        struct drm_panel panel;
-       struct mipi_dbi dbi;
+       struct mipi_dbi_dev dbidev;
+       bool command_mode;
        struct backlight_device *backlight;
        const struct ili9341_config *conf;
 };
@@ -64,7 +85,7 @@ static inline struct ili9341 *panel_to_ili9341(struct 
drm_panel *panel)
 
 static int ili9341_deinit(struct drm_panel *panel, struct ili9341 *ili)
 {
-       struct mipi_dbi *dbi = &ili->dbi;
+       struct mipi_dbi *dbi = &ili->dbidev.dbi;
 
        mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
        mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
@@ -74,7 +95,7 @@ static int ili9341_deinit(struct drm_panel *panel, struct 
ili9341 *ili)
 
 static int dt024ctft_init(struct drm_panel *panel, struct ili9341 *ili)
 {
-       struct mipi_dbi *dbi = &ili->dbi;
+       struct mipi_dbi *dbi = &ili->dbidev.dbi;
 
        /* HW / SW Reset display and wait */
        mipi_dbi_hw_reset(dbi);
@@ -164,6 +185,7 @@ static const struct drm_display_mode prgb_240x320_mode = {
        .height_mm = 49,
 };
 
+/* This is not used in command mode */
 static int ili9341_get_modes(struct drm_panel *panel)
 {
        struct drm_connector *connector = panel->connector;
@@ -201,19 +223,125 @@ static const struct ili9341_config dt024ctft_data = {
        .mode = &prgb_240x320_mode,
 };
 
+static int mi0283qt_prepare(struct drm_panel *panel)
+{
+       struct ili9341 *ili = panel_to_ili9341(panel);
+       struct mipi_dbi *dbi = &ili->dbidev.dbi;
+       u8 addr_mode;
+       int ret;
+
+       DRM_DEBUG_KMS("\n");
+
+       ret = mipi_dbi_poweron_conditional_reset(&ili->dbidev);
+       if (ret < 0)
+               return ret;
+       if (ret == 1)
+               goto out_enable;
+       mipi_dbi_hw_reset(dbi);
+
+       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
+
+       mipi_dbi_command(dbi, ILI9341_PWCTRLB, 0x00, 0x83, 0x30);
+       mipi_dbi_command(dbi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81);
+       mipi_dbi_command(dbi, ILI9341_DTCTRLA, 0x85, 0x01, 0x79);
+       mipi_dbi_command(dbi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02);
+       mipi_dbi_command(dbi, ILI9341_PUMPCTRL, 0x20);
+       mipi_dbi_command(dbi, ILI9341_DTCTRLB, 0x00, 0x00);
+
+       /* Power Control */
+       mipi_dbi_command(dbi, ILI9341_PWCTRL1, 0x26);
+       mipi_dbi_command(dbi, ILI9341_PWCTRL2, 0x11);
+       /* VCOM */
+       mipi_dbi_command(dbi, ILI9341_VMCTRL1, 0x35, 0x3e);
+       mipi_dbi_command(dbi, ILI9341_VMCTRL2, 0xbe);
+
+       /* Memory Access Control */
+       mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 
MIPI_DCS_PIXEL_FMT_16BIT);
+
+       /* Frame Rate */
+       mipi_dbi_command(dbi, ILI9341_FRMCTR1, 0x00, 0x1b);
+
+       /* Gamma */
+       mipi_dbi_command(dbi, ILI9341_EN3GAM, 0x08);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
+       mipi_dbi_command(dbi, ILI9341_PGAMCTRL,
+                      0x1f, 0x1a, 0x18, 0x0a, 0x0f, 0x06, 0x45, 0x87,
+                      0x32, 0x0a, 0x07, 0x02, 0x07, 0x05, 0x00);
+       mipi_dbi_command(dbi, ILI9341_NGAMCTRL,
+                      0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3a, 0x78,
+                      0x4d, 0x05, 0x18, 0x0d, 0x38, 0x3a, 0x1f);
+
+       /* DDRAM */
+       mipi_dbi_command(dbi, ILI9341_ETMOD, 0x07);
+
+       /* Display */
+       mipi_dbi_command(dbi, ILI9341_DISCTRL, 0x0a, 0x82, 0x27, 0x00);
+       mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+       msleep(100);
+
+       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+       msleep(100);
+
+out_enable:
+       /* The PiTFT (ili9340) has a hardware reset circuit that
+        * resets only on power-on and not on each reboot through
+        * a gpio like the rpi-display does.
+        * As a result, we need to always apply the rotation value
+        * regardless of the display "on/off" state.
+        */
+       switch (ili->dbidev.rotation) {
+       default:
+               addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
+                           ILI9341_MADCTL_MX;
+               break;
+       case 90:
+               addr_mode = ILI9341_MADCTL_MY;
+               break;
+       case 180:
+               addr_mode = ILI9341_MADCTL_MV;
+               break;
+       case 270:
+               addr_mode = ILI9341_MADCTL_MX;
+               break;
+       }
+       addr_mode |= ILI9341_MADCTL_BGR;
+       mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+
+       return 0;
+}
+
+static const struct drm_panel_funcs mi0283qt_drm_funcs = {
+       .disable = ili9341_disable,
+       .unprepare = ili9341_unprepare,
+       .prepare = mi0283qt_prepare,
+       .enable = ili9341_enable,
+       .get_modes = ili9341_get_modes,
+};
+
+static const struct drm_display_mode mi0283qt_mode = {
+       DRM_SIMPLE_MODE(320, 240, 58, 43),
+};
+
+static const struct ili9341_config mi0283qt_data = {
+       .funcs = &mi0283qt_drm_funcs,
+       .mode = &mi0283qt_mode,
+       .command_mode_only = true,
+};
+
 static int ili9341_probe(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
        struct ili9341 *ili;
        struct mipi_dbi *dbi;
        struct gpio_desc *dc_gpio;
+       struct device_node *port;
        int ret;
 
-       ili = devm_kzalloc(dev, sizeof(*ili), GFP_KERNEL);
+       ili = kzalloc(sizeof(*ili), GFP_KERNEL);
        if (!ili)
                return -ENOMEM;
 
-       dbi = &ili->dbi;
+       dbi = &ili->dbidev.dbi;
 
        spi_set_drvdata(spi, ili);
 
@@ -255,23 +383,55 @@ static int ili9341_probe(struct spi_device *spi)
        ili->panel.dev = dev;
        ili->panel.funcs = ili->conf->funcs;
 
-       return drm_panel_add(&ili->panel);
+       port = of_get_child_by_name(dev->of_node, "port");
+       if (port)
+               of_node_put(port);
+       else
+               ili->command_mode = true;
+
+       if (ili->conf->command_mode_only)
+               ili->command_mode = true;
+
+       if (ili->command_mode)
+               ret = drm_mipi_dbi_panel_register(&ili->panel, &ili->dbidev, 
ili->conf->mode);
+       else
+               ret = drm_panel_add(&ili->panel);
+
+       return ret;
 }
 
 static int ili9341_remove(struct spi_device *spi)
 {
        struct ili9341 *ili = spi_get_drvdata(spi);
 
-       drm_panel_remove(&ili->panel);
+       if (ili->command_mode) {
+               struct drm_device *drm = &ili->dbidev.drm;
 
-       ili9341_disable(&ili->panel);
-       ili9341_unprepare(&ili->panel);
+               drm_dev_unplug(drm);
+               drm_atomic_helper_shutdown(drm);
+       } else {
+               drm_panel_remove(&ili->panel);
+
+               ili9341_disable(&ili->panel);
+               ili9341_unprepare(&ili->panel);
+               kfree(ili);
+       }
 
        return 0;
 }
 
+static void ili9341_shutdown(struct spi_device *spi)
+{
+       struct ili9341 *ili = spi_get_drvdata(spi);
+
+       if (ili->command_mode)
+               drm_atomic_helper_shutdown(&ili->dbidev.drm);
+}
+
 static const struct of_device_id ili9341_of_match[] = {
        { .compatible = "displaytech,dt024ctft", .data = &dt024ctft_data },
+/*     { .compatible = "multi-inno,mi0283qt", .data = &mi0283qt_data }, */
+       { .compatible = "mi,mi0283qt", .data = &mi0283qt_data },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, ili9341_of_match);
@@ -279,6 +439,7 @@ MODULE_DEVICE_TABLE(of, ili9341_of_match);
 static struct spi_driver ili9341_driver = {
        .probe = ili9341_probe,
        .remove = ili9341_remove,
+       .shutdown = ili9341_shutdown,
        .driver = {
                .name = "panel-ilitek-ili9341",
                .of_match_table = ili9341_of_match,
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to