[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