[RFC 4/4] drm/panel/ili9341: Support mi0283qt

Josef Luštický josef at lusticky.cz
Tue Jul 30 08:34:15 UTC 2019


Hi Noralf,
see comments bellow.

po 29. 7. 2019 v 21:55 odesílatel Noralf Trønnes <noralf at tronnes.org> napsal:
>
> 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
Keep the same format for values.
>
> +#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
Command_mode_only is a bit misleading name - when the MIPI_DBI
interface is used for commands and
image data then there are command and data transfers over the MIPI_DBI
interface.
So "command_mode_only" may confuse users with the usage of
Data/Command pin/bit in MIPI_DBI transfers.
Consider naming this like "serial_mode_only" or "serial_interface_only", or
"prgb_mode_supported" and set to "true" for dt024ctft.

>   */
>  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;
Similarly to the comment above, this should be named
"serial_input_mode" or (invertably) "prgb_input_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)
Consider naming this "ili9341_serial_prepare".

> +{
> +       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