drm/panel: Move OMAP's DSI command mode panel driver
authorSebastian Reichel <sebastian.reichel@collabora.com>
Tue, 15 Dec 2020 10:46:12 +0000 (12:46 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Tue, 15 Dec 2020 14:08:25 +0000 (16:08 +0200)
The panel driver is no longer using any OMAP specific APIs, so
let's move it into the generic panel directory.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-40-tomi.valkeinen@ti.com
drivers/gpu/drm/omapdrm/Kconfig
drivers/gpu/drm/omapdrm/Makefile
drivers/gpu/drm/omapdrm/displays/Kconfig [deleted file]
drivers/gpu/drm/omapdrm/displays/Makefile [deleted file]
drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c [deleted file]
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-dsi-cm.c [new file with mode: 0644]

index 5417e7a47072b28c8048bab669e5da5ff3388bc5..cea3f44ea6d434d773b9444a49230ab529d9f92b 100644 (file)
@@ -12,6 +12,5 @@ config DRM_OMAP
 if DRM_OMAP
 
 source "drivers/gpu/drm/omapdrm/dss/Kconfig"
-source "drivers/gpu/drm/omapdrm/displays/Kconfig"
 
 endif
index f115253115c59abc542a206ed168cfaf5b9995de..66a73eae6f7cc16413ddf061f588badfa1704f40 100644 (file)
@@ -5,7 +5,6 @@
 #
 
 obj-y += dss/
-obj-y += displays/
 
 omapdrm-y := omap_drv.o \
        omap_irq.o \
diff --git a/drivers/gpu/drm/omapdrm/displays/Kconfig b/drivers/gpu/drm/omapdrm/displays/Kconfig
deleted file mode 100644 (file)
index f2be594..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-menu "OMAPDRM External Display Device Drivers"
-
-config DRM_OMAP_PANEL_DSI_CM
-       tristate "Generic DSI Command Mode Panel"
-       depends on BACKLIGHT_CLASS_DEVICE
-       help
-         Driver for generic DSI command mode panels.
-
-endmenu
diff --git a/drivers/gpu/drm/omapdrm/displays/Makefile b/drivers/gpu/drm/omapdrm/displays/Makefile
deleted file mode 100644 (file)
index 488ddf1..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
deleted file mode 100644 (file)
index 21df199..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Generic DSI Command Mode panel driver
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/
- * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
- */
-
-#include <linux/backlight.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/regulator/consumer.h>
-
-#include <drm/drm_connector.h>
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_panel.h>
-
-#include <video/display_timing.h>
-#include <video/mipi_display.h>
-#include <video/of_display_timing.h>
-#include <video/videomode.h>
-
-#define DCS_READ_NUM_ERRORS    0x05
-#define DCS_GET_ID1            0xda
-#define DCS_GET_ID2            0xdb
-#define DCS_GET_ID3            0xdc
-
-#define DCS_REGULATOR_SUPPLY_NUM 2
-
-struct panel_drv_data {
-       struct mipi_dsi_device *dsi;
-       struct drm_panel panel;
-       struct drm_display_mode mode;
-
-       struct mutex lock;
-
-       struct backlight_device *bldev;
-       struct backlight_device *extbldev;
-
-       unsigned long   hw_guard_end;   /* next value of jiffies when we can
-                                        * issue the next sleep in/out command
-                                        */
-       unsigned long   hw_guard_wait;  /* max guard time in jiffies */
-
-       /* panel HW configuration from DT or platform data */
-       struct gpio_desc *reset_gpio;
-
-       struct regulator_bulk_data supplies[DCS_REGULATOR_SUPPLY_NUM];
-
-       bool use_dsi_backlight;
-
-       int width_mm;
-       int height_mm;
-
-       /* runtime variables */
-       bool enabled;
-
-       bool intro_printed;
-};
-
-static inline struct panel_drv_data *panel_to_ddata(struct drm_panel *panel)
-{
-       return container_of(panel, struct panel_drv_data, panel);
-}
-
-static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
-
-static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
-{
-       struct backlight_device *backlight;
-
-       if (ddata->bldev)
-               backlight = ddata->bldev;
-       else if (ddata->extbldev)
-               backlight = ddata->extbldev;
-       else
-               return;
-
-       if (enable) {
-               backlight->props.fb_blank = FB_BLANK_UNBLANK;
-               backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
-               backlight->props.power = FB_BLANK_UNBLANK;
-       } else {
-               backlight->props.fb_blank = FB_BLANK_NORMAL;
-               backlight->props.power = FB_BLANK_POWERDOWN;
-               backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
-       }
-
-       backlight_update_status(backlight);
-}
-
-static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
-{
-       ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
-       ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
-}
-
-static void hw_guard_wait(struct panel_drv_data *ddata)
-{
-       unsigned long wait = ddata->hw_guard_end - jiffies;
-
-       if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(wait);
-       }
-}
-
-static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
-{
-       return mipi_dsi_dcs_read(ddata->dsi, dcs_cmd, data, 1);
-}
-
-static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
-{
-       return mipi_dsi_dcs_write(ddata->dsi, dcs_cmd, &param, 1);
-}
-
-static int dsicm_sleep_in(struct panel_drv_data *ddata)
-
-{
-       int r;
-
-       hw_guard_wait(ddata);
-
-       r = mipi_dsi_dcs_enter_sleep_mode(ddata->dsi);
-       if (r)
-               return r;
-
-       hw_guard_start(ddata, 120);
-
-       usleep_range(5000, 10000);
-
-       return 0;
-}
-
-static int dsicm_sleep_out(struct panel_drv_data *ddata)
-{
-       int r;
-
-       hw_guard_wait(ddata);
-
-       r = mipi_dsi_dcs_exit_sleep_mode(ddata->dsi);
-       if (r)
-               return r;
-
-       hw_guard_start(ddata, 120);
-
-       usleep_range(5000, 10000);
-
-       return 0;
-}
-
-static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
-{
-       int r;
-
-       r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
-       if (r)
-               return r;
-       r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
-       if (r)
-               return r;
-       r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
-       if (r)
-               return r;
-
-       return 0;
-}
-
-static int dsicm_bl_update_status(struct backlight_device *dev)
-{
-       struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
-       int r = 0;
-       int level;
-
-       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
-                       dev->props.power == FB_BLANK_UNBLANK)
-               level = dev->props.brightness;
-       else
-               level = 0;
-
-       dev_dbg(&ddata->dsi->dev, "update brightness to %d\n", level);
-
-       mutex_lock(&ddata->lock);
-
-       if (ddata->enabled) {
-               if (!r)
-                       r = dsicm_dcs_write_1(
-                               ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, level);
-       }
-
-       mutex_unlock(&ddata->lock);
-
-       return r;
-}
-
-static int dsicm_bl_get_intensity(struct backlight_device *dev)
-{
-       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
-                       dev->props.power == FB_BLANK_UNBLANK)
-               return dev->props.brightness;
-
-       return 0;
-}
-
-static const struct backlight_ops dsicm_bl_ops = {
-       .get_brightness = dsicm_bl_get_intensity,
-       .update_status  = dsicm_bl_update_status,
-};
-
-static ssize_t num_dsi_errors_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct panel_drv_data *ddata = dev_get_drvdata(dev);
-       u8 errors = 0;
-       int r = -ENODEV;
-
-       mutex_lock(&ddata->lock);
-
-       if (ddata->enabled)
-               r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, &errors);
-
-       mutex_unlock(&ddata->lock);
-
-       if (r)
-               return r;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", errors);
-}
-
-static ssize_t hw_revision_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct panel_drv_data *ddata = dev_get_drvdata(dev);
-       u8 id1, id2, id3;
-       int r = -ENODEV;
-
-       mutex_lock(&ddata->lock);
-
-       if (ddata->enabled)
-               r = dsicm_get_id(ddata, &id1, &id2, &id3);
-
-       mutex_unlock(&ddata->lock);
-
-       if (r)
-               return r;
-
-       return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
-}
-
-static DEVICE_ATTR_RO(num_dsi_errors);
-static DEVICE_ATTR_RO(hw_revision);
-
-static struct attribute *dsicm_attrs[] = {
-       &dev_attr_num_dsi_errors.attr,
-       &dev_attr_hw_revision.attr,
-       NULL,
-};
-
-static const struct attribute_group dsicm_attr_group = {
-       .attrs = dsicm_attrs,
-};
-
-static void dsicm_hw_reset(struct panel_drv_data *ddata)
-{
-       gpiod_set_value(ddata->reset_gpio, 1);
-       udelay(10);
-       /* reset the panel */
-       gpiod_set_value(ddata->reset_gpio, 0);
-       /* assert reset */
-       udelay(10);
-       gpiod_set_value(ddata->reset_gpio, 1);
-       /* wait after releasing reset */
-       usleep_range(5000, 10000);
-}
-
-static int dsicm_power_on(struct panel_drv_data *ddata)
-{
-       u8 id1, id2, id3;
-       int r;
-
-       dsicm_hw_reset(ddata);
-
-       ddata->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
-
-       r = dsicm_sleep_out(ddata);
-       if (r)
-               goto err;
-
-       r = dsicm_get_id(ddata, &id1, &id2, &id3);
-       if (r)
-               goto err;
-
-       r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
-       if (r)
-               goto err;
-
-       r = dsicm_dcs_write_1(ddata, MIPI_DCS_WRITE_CONTROL_DISPLAY,
-                       (1<<2) | (1<<5));       /* BL | BCTRL */
-       if (r)
-               goto err;
-
-       r = mipi_dsi_dcs_set_pixel_format(ddata->dsi, MIPI_DCS_PIXEL_FMT_24BIT);
-       if (r)
-               goto err;
-
-       r = mipi_dsi_dcs_set_display_on(ddata->dsi);
-       if (r)
-               goto err;
-
-       r = _dsicm_enable_te(ddata, true);
-       if (r)
-               goto err;
-
-       ddata->enabled = true;
-
-       if (!ddata->intro_printed) {
-               dev_info(&ddata->dsi->dev, "panel revision %02x.%02x.%02x\n",
-                       id1, id2, id3);
-               ddata->intro_printed = true;
-       }
-
-       ddata->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
-
-       return 0;
-err:
-       dev_err(&ddata->dsi->dev, "error while enabling panel, issuing HW reset\n");
-
-       dsicm_hw_reset(ddata);
-
-       return r;
-}
-
-static int dsicm_power_off(struct panel_drv_data *ddata)
-{
-       int r;
-
-       ddata->enabled = false;
-
-       r = mipi_dsi_dcs_set_display_off(ddata->dsi);
-       if (!r)
-               r = dsicm_sleep_in(ddata);
-
-       if (r) {
-               dev_err(&ddata->dsi->dev,
-                               "error disabling panel, issuing HW reset\n");
-               dsicm_hw_reset(ddata);
-       }
-
-       return r;
-}
-
-static int dsicm_prepare(struct drm_panel *panel)
-{
-       struct panel_drv_data *ddata = panel_to_ddata(panel);
-       int r;
-
-       r = regulator_bulk_enable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
-       if (r)
-               dev_err(&ddata->dsi->dev, "failed to enable supplies: %d\n", r);
-
-       return r;
-}
-
-static int dsicm_enable(struct drm_panel *panel)
-{
-       struct panel_drv_data *ddata = panel_to_ddata(panel);
-       int r;
-
-       mutex_lock(&ddata->lock);
-
-       r = dsicm_power_on(ddata);
-       if (r)
-               goto err;
-
-       mutex_unlock(&ddata->lock);
-
-       dsicm_bl_power(ddata, true);
-
-       return 0;
-err:
-       dev_err(&ddata->dsi->dev, "enable failed (%d)\n", r);
-       mutex_unlock(&ddata->lock);
-       return r;
-}
-
-static int dsicm_unprepare(struct drm_panel *panel)
-{
-       struct panel_drv_data *ddata = panel_to_ddata(panel);
-       int r;
-
-       r = regulator_bulk_disable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
-       if (r)
-               dev_err(&ddata->dsi->dev, "failed to disable supplies: %d\n", r);
-
-       return r;
-}
-
-static int dsicm_disable(struct drm_panel *panel)
-{
-       struct panel_drv_data *ddata = panel_to_ddata(panel);
-       int r;
-
-       dsicm_bl_power(ddata, false);
-
-       mutex_lock(&ddata->lock);
-
-       r = dsicm_power_off(ddata);
-
-       mutex_unlock(&ddata->lock);
-
-       return r;
-}
-
-static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
-{
-       struct mipi_dsi_device *dsi = ddata->dsi;
-       int r;
-
-       if (enable)
-               r = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
-       else
-               r = mipi_dsi_dcs_set_tear_off(dsi);
-
-       /* possible panel bug */
-       msleep(100);
-
-       return r;
-}
-
-static int dsicm_get_modes(struct drm_panel *panel,
-                          struct drm_connector *connector)
-{
-       struct panel_drv_data *ddata = panel_to_ddata(panel);
-       struct drm_display_mode *mode;
-
-       mode = drm_mode_duplicate(connector->dev, &ddata->mode);
-       if (!mode) {
-               dev_err(&ddata->dsi->dev, "failed to add mode %ux%ux@%u kHz\n",
-                       ddata->mode.hdisplay, ddata->mode.vdisplay,
-                       ddata->mode.clock);
-               return -ENOMEM;
-       }
-
-       drm_mode_set_name(mode);
-       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-
-       connector->display_info.width_mm = ddata->width_mm;
-       connector->display_info.height_mm = ddata->height_mm;
-
-       drm_mode_probed_add(connector, mode);
-
-       return 1;
-}
-
-static const struct drm_panel_funcs dsicm_panel_funcs = {
-       .unprepare = dsicm_unprepare,
-       .disable = dsicm_disable,
-       .prepare = dsicm_prepare,
-       .enable = dsicm_enable,
-       .get_modes = dsicm_get_modes,
-};
-
-static int dsicm_probe_of(struct mipi_dsi_device *dsi)
-{
-       struct device_node *node = dsi->dev.of_node;
-       struct backlight_device *backlight;
-       struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
-       struct display_timing timing;
-       struct videomode vm = {
-               .hactive = 864,
-               .vactive = 480,
-       };
-       int err;
-
-       ddata->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
-       if (IS_ERR(ddata->reset_gpio)) {
-               err = PTR_ERR(ddata->reset_gpio);
-               dev_err(&dsi->dev, "reset gpio request failed: %d", err);
-               return err;
-       }
-
-       err = of_get_display_timing(node, "panel-timing", &timing);
-       if (!err) {
-               videomode_from_timing(&timing, &vm);
-       } else {
-               dev_warn(&dsi->dev,
-                        "failed to get video timing, using defaults\n");
-       }
-
-       if (!vm.pixelclock)
-               vm.pixelclock = vm.hactive * vm.vactive * 60;
-       drm_display_mode_from_videomode(&vm, &ddata->mode);
-
-       ddata->width_mm = 0;
-       of_property_read_u32(node, "width-mm", &ddata->width_mm);
-
-       ddata->height_mm = 0;
-       of_property_read_u32(node, "height-mm", &ddata->height_mm);
-
-       ddata->supplies[0].supply = "vpnl";
-       ddata->supplies[1].supply = "vddi";
-       err = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ddata->supplies),
-                                     ddata->supplies);
-       if (err)
-               return err;
-
-       backlight = devm_of_find_backlight(&dsi->dev);
-       if (IS_ERR(backlight))
-               return PTR_ERR(backlight);
-
-       /* If no backlight device is found assume native backlight support */
-       if (backlight)
-               ddata->extbldev = backlight;
-       else
-               ddata->use_dsi_backlight = true;
-
-       return 0;
-}
-
-static int dsicm_probe(struct mipi_dsi_device *dsi)
-{
-       struct panel_drv_data *ddata;
-       struct backlight_device *bldev = NULL;
-       struct device *dev = &dsi->dev;
-       int r;
-
-       dev_dbg(dev, "probe\n");
-
-       ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
-       if (!ddata)
-               return -ENOMEM;
-
-       mipi_dsi_set_drvdata(dsi, ddata);
-       ddata->dsi = dsi;
-
-       r = dsicm_probe_of(dsi);
-       if (r)
-               return r;
-
-       mutex_init(&ddata->lock);
-
-       dsicm_hw_reset(ddata);
-
-       drm_panel_init(&ddata->panel, dev, &dsicm_panel_funcs,
-                      DRM_MODE_CONNECTOR_DSI);
-
-       if (ddata->use_dsi_backlight) {
-               struct backlight_properties props = { 0 };
-               props.max_brightness = 255;
-               props.type = BACKLIGHT_RAW;
-
-               bldev = devm_backlight_device_register(dev, dev_name(dev),
-                       dev, ddata, &dsicm_bl_ops, &props);
-               if (IS_ERR(bldev)) {
-                       r = PTR_ERR(bldev);
-                       goto err_bl;
-               }
-
-               ddata->bldev = bldev;
-       }
-
-       r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
-       if (r) {
-               dev_err(dev, "failed to create sysfs files\n");
-               goto err_bl;
-       }
-
-       dsi->lanes = 2;
-       dsi->format = MIPI_DSI_FMT_RGB888;
-       dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
-                         MIPI_DSI_MODE_EOT_PACKET;
-       dsi->hs_rate = 300000000;
-       dsi->lp_rate = 10000000;
-
-       drm_panel_add(&ddata->panel);
-
-       r = mipi_dsi_attach(dsi);
-       if (r < 0)
-               goto err_dsi_attach;
-
-       return 0;
-
-err_dsi_attach:
-       drm_panel_remove(&ddata->panel);
-       sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
-err_bl:
-       if (ddata->extbldev)
-               put_device(&ddata->extbldev->dev);
-
-       return r;
-}
-
-static int dsicm_remove(struct mipi_dsi_device *dsi)
-{
-       struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
-
-       dev_dbg(&dsi->dev, "remove\n");
-
-       mipi_dsi_detach(dsi);
-
-       drm_panel_remove(&ddata->panel);
-
-       sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
-
-       if (ddata->extbldev)
-               put_device(&ddata->extbldev->dev);
-
-       return 0;
-}
-
-static const struct of_device_id dsicm_of_match[] = {
-       { .compatible = "panel-dsi-cm", },
-       {},
-};
-
-MODULE_DEVICE_TABLE(of, dsicm_of_match);
-
-static struct mipi_dsi_driver dsicm_driver = {
-       .probe = dsicm_probe,
-       .remove = dsicm_remove,
-       .driver = {
-               .name = "panel-dsi-cm",
-               .of_match_table = dsicm_of_match,
-       },
-};
-module_mipi_dsi_driver(dsicm_driver);
-
-MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
-MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
-MODULE_LICENSE("GPL");
index 8fec45b2ce02aa8e54606ba7635e2fc5bd6df531..4894913936e9712b88dc1fdfca2ed7e9450327e0 100644 (file)
@@ -57,6 +57,15 @@ config DRM_PANEL_BOE_TV101WUM_NL6
          Say Y here if you want to support for BOE TV101WUM and AUO KD101N80
          45NA WUXGA PANEL DSI Video Mode panel
 
+config DRM_PANEL_DSI_CM
+       tristate "Generic DSI command mode panels"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         DRM panel driver for DSI command mode panels with support for
+         embedded and external backlights.
+
 config DRM_PANEL_LVDS
        tristate "Generic LVDS panel driver"
        depends on OF
index 03496695e03f0317f081121a838b34651bf6f365..cae4d976c06959ef53b64d1e5ea1d2823228926e 100644 (file)
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
 obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
 obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
+obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
 obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
 obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
diff --git a/drivers/gpu/drm/panel/panel-dsi-cm.c b/drivers/gpu/drm/panel/panel-dsi-cm.c
new file mode 100644 (file)
index 0000000..21df199
--- /dev/null
@@ -0,0 +1,637 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic DSI Command Mode panel driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <video/display_timing.h>
+#include <video/mipi_display.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
+#define DCS_READ_NUM_ERRORS    0x05
+#define DCS_GET_ID1            0xda
+#define DCS_GET_ID2            0xdb
+#define DCS_GET_ID3            0xdc
+
+#define DCS_REGULATOR_SUPPLY_NUM 2
+
+struct panel_drv_data {
+       struct mipi_dsi_device *dsi;
+       struct drm_panel panel;
+       struct drm_display_mode mode;
+
+       struct mutex lock;
+
+       struct backlight_device *bldev;
+       struct backlight_device *extbldev;
+
+       unsigned long   hw_guard_end;   /* next value of jiffies when we can
+                                        * issue the next sleep in/out command
+                                        */
+       unsigned long   hw_guard_wait;  /* max guard time in jiffies */
+
+       /* panel HW configuration from DT or platform data */
+       struct gpio_desc *reset_gpio;
+
+       struct regulator_bulk_data supplies[DCS_REGULATOR_SUPPLY_NUM];
+
+       bool use_dsi_backlight;
+
+       int width_mm;
+       int height_mm;
+
+       /* runtime variables */
+       bool enabled;
+
+       bool intro_printed;
+};
+
+static inline struct panel_drv_data *panel_to_ddata(struct drm_panel *panel)
+{
+       return container_of(panel, struct panel_drv_data, panel);
+}
+
+static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
+
+static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
+{
+       struct backlight_device *backlight;
+
+       if (ddata->bldev)
+               backlight = ddata->bldev;
+       else if (ddata->extbldev)
+               backlight = ddata->extbldev;
+       else
+               return;
+
+       if (enable) {
+               backlight->props.fb_blank = FB_BLANK_UNBLANK;
+               backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
+               backlight->props.power = FB_BLANK_UNBLANK;
+       } else {
+               backlight->props.fb_blank = FB_BLANK_NORMAL;
+               backlight->props.power = FB_BLANK_POWERDOWN;
+               backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
+       }
+
+       backlight_update_status(backlight);
+}
+
+static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
+{
+       ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
+       ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct panel_drv_data *ddata)
+{
+       unsigned long wait = ddata->hw_guard_end - jiffies;
+
+       if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(wait);
+       }
+}
+
+static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
+{
+       return mipi_dsi_dcs_read(ddata->dsi, dcs_cmd, data, 1);
+}
+
+static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
+{
+       return mipi_dsi_dcs_write(ddata->dsi, dcs_cmd, &param, 1);
+}
+
+static int dsicm_sleep_in(struct panel_drv_data *ddata)
+
+{
+       int r;
+
+       hw_guard_wait(ddata);
+
+       r = mipi_dsi_dcs_enter_sleep_mode(ddata->dsi);
+       if (r)
+               return r;
+
+       hw_guard_start(ddata, 120);
+
+       usleep_range(5000, 10000);
+
+       return 0;
+}
+
+static int dsicm_sleep_out(struct panel_drv_data *ddata)
+{
+       int r;
+
+       hw_guard_wait(ddata);
+
+       r = mipi_dsi_dcs_exit_sleep_mode(ddata->dsi);
+       if (r)
+               return r;
+
+       hw_guard_start(ddata, 120);
+
+       usleep_range(5000, 10000);
+
+       return 0;
+}
+
+static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
+{
+       int r;
+
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
+       if (r)
+               return r;
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
+       if (r)
+               return r;
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int dsicm_bl_update_status(struct backlight_device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
+       int r = 0;
+       int level;
+
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               level = dev->props.brightness;
+       else
+               level = 0;
+
+       dev_dbg(&ddata->dsi->dev, "update brightness to %d\n", level);
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled) {
+               if (!r)
+                       r = dsicm_dcs_write_1(
+                               ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, level);
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       return r;
+}
+
+static int dsicm_bl_get_intensity(struct backlight_device *dev)
+{
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               return dev->props.brightness;
+
+       return 0;
+}
+
+static const struct backlight_ops dsicm_bl_ops = {
+       .get_brightness = dsicm_bl_get_intensity,
+       .update_status  = dsicm_bl_update_status,
+};
+
+static ssize_t num_dsi_errors_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       u8 errors = 0;
+       int r = -ENODEV;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled)
+               r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, &errors);
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", errors);
+}
+
+static ssize_t hw_revision_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       u8 id1, id2, id3;
+       int r = -ENODEV;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled)
+               r = dsicm_get_id(ddata, &id1, &id2, &id3);
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
+}
+
+static DEVICE_ATTR_RO(num_dsi_errors);
+static DEVICE_ATTR_RO(hw_revision);
+
+static struct attribute *dsicm_attrs[] = {
+       &dev_attr_num_dsi_errors.attr,
+       &dev_attr_hw_revision.attr,
+       NULL,
+};
+
+static const struct attribute_group dsicm_attr_group = {
+       .attrs = dsicm_attrs,
+};
+
+static void dsicm_hw_reset(struct panel_drv_data *ddata)
+{
+       gpiod_set_value(ddata->reset_gpio, 1);
+       udelay(10);
+       /* reset the panel */
+       gpiod_set_value(ddata->reset_gpio, 0);
+       /* assert reset */
+       udelay(10);
+       gpiod_set_value(ddata->reset_gpio, 1);
+       /* wait after releasing reset */
+       usleep_range(5000, 10000);
+}
+
+static int dsicm_power_on(struct panel_drv_data *ddata)
+{
+       u8 id1, id2, id3;
+       int r;
+
+       dsicm_hw_reset(ddata);
+
+       ddata->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       r = dsicm_sleep_out(ddata);
+       if (r)
+               goto err;
+
+       r = dsicm_get_id(ddata, &id1, &id2, &id3);
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_1(ddata, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+                       (1<<2) | (1<<5));       /* BL | BCTRL */
+       if (r)
+               goto err;
+
+       r = mipi_dsi_dcs_set_pixel_format(ddata->dsi, MIPI_DCS_PIXEL_FMT_24BIT);
+       if (r)
+               goto err;
+
+       r = mipi_dsi_dcs_set_display_on(ddata->dsi);
+       if (r)
+               goto err;
+
+       r = _dsicm_enable_te(ddata, true);
+       if (r)
+               goto err;
+
+       ddata->enabled = true;
+
+       if (!ddata->intro_printed) {
+               dev_info(&ddata->dsi->dev, "panel revision %02x.%02x.%02x\n",
+                       id1, id2, id3);
+               ddata->intro_printed = true;
+       }
+
+       ddata->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+       return 0;
+err:
+       dev_err(&ddata->dsi->dev, "error while enabling panel, issuing HW reset\n");
+
+       dsicm_hw_reset(ddata);
+
+       return r;
+}
+
+static int dsicm_power_off(struct panel_drv_data *ddata)
+{
+       int r;
+
+       ddata->enabled = false;
+
+       r = mipi_dsi_dcs_set_display_off(ddata->dsi);
+       if (!r)
+               r = dsicm_sleep_in(ddata);
+
+       if (r) {
+               dev_err(&ddata->dsi->dev,
+                               "error disabling panel, issuing HW reset\n");
+               dsicm_hw_reset(ddata);
+       }
+
+       return r;
+}
+
+static int dsicm_prepare(struct drm_panel *panel)
+{
+       struct panel_drv_data *ddata = panel_to_ddata(panel);
+       int r;
+
+       r = regulator_bulk_enable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
+       if (r)
+               dev_err(&ddata->dsi->dev, "failed to enable supplies: %d\n", r);
+
+       return r;
+}
+
+static int dsicm_enable(struct drm_panel *panel)
+{
+       struct panel_drv_data *ddata = panel_to_ddata(panel);
+       int r;
+
+       mutex_lock(&ddata->lock);
+
+       r = dsicm_power_on(ddata);
+       if (r)
+               goto err;
+
+       mutex_unlock(&ddata->lock);
+
+       dsicm_bl_power(ddata, true);
+
+       return 0;
+err:
+       dev_err(&ddata->dsi->dev, "enable failed (%d)\n", r);
+       mutex_unlock(&ddata->lock);
+       return r;
+}
+
+static int dsicm_unprepare(struct drm_panel *panel)
+{
+       struct panel_drv_data *ddata = panel_to_ddata(panel);
+       int r;
+
+       r = regulator_bulk_disable(ARRAY_SIZE(ddata->supplies), ddata->supplies);
+       if (r)
+               dev_err(&ddata->dsi->dev, "failed to disable supplies: %d\n", r);
+
+       return r;
+}
+
+static int dsicm_disable(struct drm_panel *panel)
+{
+       struct panel_drv_data *ddata = panel_to_ddata(panel);
+       int r;
+
+       dsicm_bl_power(ddata, false);
+
+       mutex_lock(&ddata->lock);
+
+       r = dsicm_power_off(ddata);
+
+       mutex_unlock(&ddata->lock);
+
+       return r;
+}
+
+static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
+{
+       struct mipi_dsi_device *dsi = ddata->dsi;
+       int r;
+
+       if (enable)
+               r = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+       else
+               r = mipi_dsi_dcs_set_tear_off(dsi);
+
+       /* possible panel bug */
+       msleep(100);
+
+       return r;
+}
+
+static int dsicm_get_modes(struct drm_panel *panel,
+                          struct drm_connector *connector)
+{
+       struct panel_drv_data *ddata = panel_to_ddata(panel);
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(connector->dev, &ddata->mode);
+       if (!mode) {
+               dev_err(&ddata->dsi->dev, "failed to add mode %ux%ux@%u kHz\n",
+                       ddata->mode.hdisplay, ddata->mode.vdisplay,
+                       ddata->mode.clock);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+       connector->display_info.width_mm = ddata->width_mm;
+       connector->display_info.height_mm = ddata->height_mm;
+
+       drm_mode_probed_add(connector, mode);
+
+       return 1;
+}
+
+static const struct drm_panel_funcs dsicm_panel_funcs = {
+       .unprepare = dsicm_unprepare,
+       .disable = dsicm_disable,
+       .prepare = dsicm_prepare,
+       .enable = dsicm_enable,
+       .get_modes = dsicm_get_modes,
+};
+
+static int dsicm_probe_of(struct mipi_dsi_device *dsi)
+{
+       struct device_node *node = dsi->dev.of_node;
+       struct backlight_device *backlight;
+       struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
+       struct display_timing timing;
+       struct videomode vm = {
+               .hactive = 864,
+               .vactive = 480,
+       };
+       int err;
+
+       ddata->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ddata->reset_gpio)) {
+               err = PTR_ERR(ddata->reset_gpio);
+               dev_err(&dsi->dev, "reset gpio request failed: %d", err);
+               return err;
+       }
+
+       err = of_get_display_timing(node, "panel-timing", &timing);
+       if (!err) {
+               videomode_from_timing(&timing, &vm);
+       } else {
+               dev_warn(&dsi->dev,
+                        "failed to get video timing, using defaults\n");
+       }
+
+       if (!vm.pixelclock)
+               vm.pixelclock = vm.hactive * vm.vactive * 60;
+       drm_display_mode_from_videomode(&vm, &ddata->mode);
+
+       ddata->width_mm = 0;
+       of_property_read_u32(node, "width-mm", &ddata->width_mm);
+
+       ddata->height_mm = 0;
+       of_property_read_u32(node, "height-mm", &ddata->height_mm);
+
+       ddata->supplies[0].supply = "vpnl";
+       ddata->supplies[1].supply = "vddi";
+       err = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ddata->supplies),
+                                     ddata->supplies);
+       if (err)
+               return err;
+
+       backlight = devm_of_find_backlight(&dsi->dev);
+       if (IS_ERR(backlight))
+               return PTR_ERR(backlight);
+
+       /* If no backlight device is found assume native backlight support */
+       if (backlight)
+               ddata->extbldev = backlight;
+       else
+               ddata->use_dsi_backlight = true;
+
+       return 0;
+}
+
+static int dsicm_probe(struct mipi_dsi_device *dsi)
+{
+       struct panel_drv_data *ddata;
+       struct backlight_device *bldev = NULL;
+       struct device *dev = &dsi->dev;
+       int r;
+
+       dev_dbg(dev, "probe\n");
+
+       ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       mipi_dsi_set_drvdata(dsi, ddata);
+       ddata->dsi = dsi;
+
+       r = dsicm_probe_of(dsi);
+       if (r)
+               return r;
+
+       mutex_init(&ddata->lock);
+
+       dsicm_hw_reset(ddata);
+
+       drm_panel_init(&ddata->panel, dev, &dsicm_panel_funcs,
+                      DRM_MODE_CONNECTOR_DSI);
+
+       if (ddata->use_dsi_backlight) {
+               struct backlight_properties props = { 0 };
+               props.max_brightness = 255;
+               props.type = BACKLIGHT_RAW;
+
+               bldev = devm_backlight_device_register(dev, dev_name(dev),
+                       dev, ddata, &dsicm_bl_ops, &props);
+               if (IS_ERR(bldev)) {
+                       r = PTR_ERR(bldev);
+                       goto err_bl;
+               }
+
+               ddata->bldev = bldev;
+       }
+
+       r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
+       if (r) {
+               dev_err(dev, "failed to create sysfs files\n");
+               goto err_bl;
+       }
+
+       dsi->lanes = 2;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |
+                         MIPI_DSI_MODE_EOT_PACKET;
+       dsi->hs_rate = 300000000;
+       dsi->lp_rate = 10000000;
+
+       drm_panel_add(&ddata->panel);
+
+       r = mipi_dsi_attach(dsi);
+       if (r < 0)
+               goto err_dsi_attach;
+
+       return 0;
+
+err_dsi_attach:
+       drm_panel_remove(&ddata->panel);
+       sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
+err_bl:
+       if (ddata->extbldev)
+               put_device(&ddata->extbldev->dev);
+
+       return r;
+}
+
+static int dsicm_remove(struct mipi_dsi_device *dsi)
+{
+       struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi);
+
+       dev_dbg(&dsi->dev, "remove\n");
+
+       mipi_dsi_detach(dsi);
+
+       drm_panel_remove(&ddata->panel);
+
+       sysfs_remove_group(&dsi->dev.kobj, &dsicm_attr_group);
+
+       if (ddata->extbldev)
+               put_device(&ddata->extbldev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id dsicm_of_match[] = {
+       { .compatible = "panel-dsi-cm", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, dsicm_of_match);
+
+static struct mipi_dsi_driver dsicm_driver = {
+       .probe = dsicm_probe,
+       .remove = dsicm_remove,
+       .driver = {
+               .name = "panel-dsi-cm",
+               .of_match_table = dsicm_of_match,
+       },
+};
+module_mipi_dsi_driver(dsicm_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
+MODULE_LICENSE("GPL");