[RFC 4/4] drm/panel/ili9341: Support mi0283qt
Noralf Trønnes
noralf at tronnes.org
Mon Jul 29 19:55:26 UTC 2019
Signed-off-by: Noralf Trønnes <noralf at 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
More information about the dri-devel
mailing list