Merge tag 'drm/panel/for-3.17-rc1' of git://anongit.freedesktop.org/tegra/linux into...
authorDave Airlie <airlied@gmail.com>
Thu, 7 Aug 2014 09:51:52 +0000 (19:51 +1000)
committerDave Airlie <airlied@gmail.com>
Thu, 7 Aug 2014 09:51:52 +0000 (19:51 +1000)
drm/panel: Changes for v3.17-rc1

Panels can now be more finely controlled via .prepare() and .unprepare()
callbacks in addition to .enable() and .disable(). New kerneldoc details
what they are supposed to do and when they should be called.

The simple panel driver gained support for a couple of new panels and it
is now possible to specify additional delays during power up and power
down sequences if panels require it.

DSI devices can now advertise that they support non-continuous clock
mode which will allow DSI host controllers to disable the high speed
clock after transmissions to save power.

* tag 'drm/panel/for-3.17-rc1' of git://anongit.freedesktop.org/tegra/linux: (30 commits)
  drm/panel: simple: Use devm_gpiod_get_optional()
  drm/dsi: Replace upcasting macro by function
  drm/panel: ld9040: Replace upcasting macro by function
  drm/exynos: dp: Modify driver to support drm_panel
  drm/exynos: Move DP setup into commit()
  drm/panel: simple: Add AUO B133HTN01 panel support
  drm/panel: simple: Support delays in panel functions
  drm/panel: simple: Add proper definition for prepare and unprepare
  drm/panel: s6e8aa0: Add proper definition for prepare and unprepare
  drm/panel: ld9040: Add proper definition for prepare and unprepare
  drm/tegra: Add support for panel prepare and unprepare routines
  drm/exynos: dsi: Add support for panel prepare and unprepare routines
  drm/exynos: dpi: Add support for panel prepare and unprepare routines
  drm/panel: simple: Add dummy prepare and unprepare routines
  drm/panel: s6e8aa0: Add dummy prepare and unprepare routines
  drm/panel: ld9040: Add dummy prepare and unprepare routines
  drm/panel: Provide convenience wrapper for .get_modes()
  drm/panel: add .prepare() and .unprepare() functions
  drm/panel: simple: Remove simple-panel compatible
  drm/panel: simple: Add Innolux N116BGE panel support
  ...

17 files changed:
Documentation/devicetree/bindings/panel/auo,b133htn01.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/innolux,n116bge.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt [new file with mode: 0644]
drivers/gpu/drm/drm_mipi_dsi.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_dp_core.c
drivers/gpu/drm/exynos/exynos_dp_core.h
drivers/gpu/drm/exynos/exynos_drm_dpi.c
drivers/gpu/drm/exynos/exynos_drm_dsi.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/panel-ld9040.c
drivers/gpu/drm/panel/panel-s6e8aa0.c
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/tegra/output.c
include/drm/drm_mipi_dsi.h
include/drm/drm_panel.h

diff --git a/Documentation/devicetree/bindings/panel/auo,b133htn01.txt b/Documentation/devicetree/bindings/panel/auo,b133htn01.txt
new file mode 100644 (file)
index 0000000..302226b
--- /dev/null
@@ -0,0 +1,7 @@
+AU Optronics Corporation 13.3" FHD (1920x1080) color TFT-LCD panel
+
+Required properties:
+- compatible: should be "auo,b133htn01"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt b/Documentation/devicetree/bindings/panel/foxlink,fl500wvr00-a0t.txt
new file mode 100644 (file)
index 0000000..b47f9d8
--- /dev/null
@@ -0,0 +1,7 @@
+Foxlink Group 5" WVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "foxlink,fl500wvr00-a0t"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/innolux,n116bge.txt b/Documentation/devicetree/bindings/panel/innolux,n116bge.txt
new file mode 100644 (file)
index 0000000..081bb93
--- /dev/null
@@ -0,0 +1,7 @@
+Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,n116bge"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt b/Documentation/devicetree/bindings/panel/innolux,n156bge-l21.txt
new file mode 100644 (file)
index 0000000..7825844
--- /dev/null
@@ -0,0 +1,7 @@
+InnoLux 15.6" WXGA TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,n156bge-l21"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
index e633df2..6aa6a9e 100644 (file)
@@ -201,16 +201,15 @@ EXPORT_SYMBOL(mipi_dsi_detach);
 /**
  * mipi_dsi_dcs_write - send DCS write command
  * @dsi: DSI device
- * @channel: virtual channel
  * @data: pointer to the command followed by parameters
  * @len: length of @data
  */
-int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
-                      const void *data, size_t len)
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
+                           size_t len)
 {
        const struct mipi_dsi_host_ops *ops = dsi->host->ops;
        struct mipi_dsi_msg msg = {
-               .channel = channel,
+               .channel = dsi->channel,
                .tx_buf = data,
                .tx_len = len
        };
@@ -239,19 +238,18 @@ EXPORT_SYMBOL(mipi_dsi_dcs_write);
 /**
  * mipi_dsi_dcs_read - send DCS read request command
  * @dsi: DSI device
- * @channel: virtual channel
  * @cmd: DCS read command
  * @data: pointer to read buffer
  * @len: length of @data
  *
  * Function returns number of read bytes or error code.
  */
-ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
-                         u8 cmd, void *data, size_t len)
+ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
+                         size_t len)
 {
        const struct mipi_dsi_host_ops *ops = dsi->host->ops;
        struct mipi_dsi_msg msg = {
-               .channel = channel,
+               .channel = dsi->channel,
                .type = MIPI_DSI_DCS_READ,
                .tx_buf = &cmd,
                .tx_len = 1,
index 9ba1aae..7f9f6f9 100644 (file)
@@ -53,6 +53,7 @@ config DRM_EXYNOS_DP
        bool "EXYNOS DRM DP driver support"
        depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
        default DRM_EXYNOS
+       select DRM_PANEL
        help
          This enables support for DP device.
 
index 31c3de9..4f3c7eb 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/gpio.h>
@@ -28,6 +27,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
 #include <drm/bridge/ptn3460.h>
 
 #include "exynos_drm_drv.h"
@@ -41,7 +41,7 @@ struct bridge_init {
        struct device_node *node;
 };
 
-static int exynos_dp_init_dp(struct exynos_dp_device *dp)
+static void exynos_dp_init_dp(struct exynos_dp_device *dp)
 {
        exynos_dp_reset(dp);
 
@@ -58,8 +58,6 @@ static int exynos_dp_init_dp(struct exynos_dp_device *dp)
 
        exynos_dp_init_hpd(dp);
        exynos_dp_init_aux(dp);
-
-       return 0;
 }
 
 static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
@@ -875,10 +873,24 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 static void exynos_dp_hotplug(struct work_struct *work)
 {
        struct exynos_dp_device *dp;
-       int ret;
 
        dp = container_of(work, struct exynos_dp_device, hotplug_work);
 
+       if (dp->drm_dev)
+               drm_helper_hpd_irq_event(dp->drm_dev);
+}
+
+static void exynos_dp_commit(struct exynos_drm_display *display)
+{
+       struct exynos_dp_device *dp = display->ctx;
+       int ret;
+
+       /* Keep the panel disabled while we configure video */
+       if (dp->panel) {
+               if (drm_panel_disable(dp->panel))
+                       DRM_ERROR("failed to disable the panel\n");
+       }
+
        ret = exynos_dp_detect_hpd(dp);
        if (ret) {
                /* Cable has been disconnected, we're done */
@@ -909,6 +921,12 @@ static void exynos_dp_hotplug(struct work_struct *work)
        ret = exynos_dp_config_video(dp);
        if (ret)
                dev_err(dp->dev, "unable to config video\n");
+
+       /* Safe to enable the panel now */
+       if (dp->panel) {
+               if (drm_panel_enable(dp->panel))
+                       DRM_ERROR("failed to enable the panel\n");
+       }
 }
 
 static enum drm_connector_status exynos_dp_detect(
@@ -933,15 +951,18 @@ static int exynos_dp_get_modes(struct drm_connector *connector)
        struct exynos_dp_device *dp = ctx_from_connector(connector);
        struct drm_display_mode *mode;
 
+       if (dp->panel)
+               return drm_panel_get_modes(dp->panel);
+
        mode = drm_mode_create(connector->dev);
        if (!mode) {
                DRM_ERROR("failed to create a new display mode.\n");
                return 0;
        }
 
-       drm_display_mode_from_videomode(&dp->panel.vm, mode);
-       mode->width_mm = dp->panel.width_mm;
-       mode->height_mm = dp->panel.height_mm;
+       drm_display_mode_from_videomode(&dp->priv.vm, mode);
+       mode->width_mm = dp->priv.width_mm;
+       mode->height_mm = dp->priv.height_mm;
        connector->display_info.width_mm = mode->width_mm;
        connector->display_info.height_mm = mode->height_mm;
 
@@ -1021,7 +1042,10 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
        drm_connector_register(connector);
        drm_mode_connector_attach_encoder(connector, encoder);
 
-       return 0;
+       if (dp->panel)
+               ret = drm_panel_attach(dp->panel, &dp->connector);
+
+       return ret;
 }
 
 static void exynos_dp_phy_init(struct exynos_dp_device *dp)
@@ -1050,26 +1074,50 @@ static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
        }
 }
 
-static void exynos_dp_poweron(struct exynos_dp_device *dp)
+static void exynos_dp_poweron(struct exynos_drm_display *display)
 {
+       struct exynos_dp_device *dp = display->ctx;
+
        if (dp->dpms_mode == DRM_MODE_DPMS_ON)
                return;
 
+       if (dp->panel) {
+               if (drm_panel_prepare(dp->panel)) {
+                       DRM_ERROR("failed to setup the panel\n");
+                       return;
+               }
+       }
+
        clk_prepare_enable(dp->clock);
        exynos_dp_phy_init(dp);
        exynos_dp_init_dp(dp);
        enable_irq(dp->irq);
+       exynos_dp_commit(display);
 }
 
-static void exynos_dp_poweroff(struct exynos_dp_device *dp)
+static void exynos_dp_poweroff(struct exynos_drm_display *display)
 {
+       struct exynos_dp_device *dp = display->ctx;
+
        if (dp->dpms_mode != DRM_MODE_DPMS_ON)
                return;
 
+       if (dp->panel) {
+               if (drm_panel_disable(dp->panel)) {
+                       DRM_ERROR("failed to disable the panel\n");
+                       return;
+               }
+       }
+
        disable_irq(dp->irq);
        flush_work(&dp->hotplug_work);
        exynos_dp_phy_exit(dp);
        clk_disable_unprepare(dp->clock);
+
+       if (dp->panel) {
+               if (drm_panel_unprepare(dp->panel))
+                       DRM_ERROR("failed to turnoff the panel\n");
+       }
 }
 
 static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
@@ -1078,12 +1126,12 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
-               exynos_dp_poweron(dp);
+               exynos_dp_poweron(display);
                break;
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
        case DRM_MODE_DPMS_OFF:
-               exynos_dp_poweroff(dp);
+               exynos_dp_poweroff(display);
                break;
        default:
                break;
@@ -1094,6 +1142,7 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
 static struct exynos_drm_display_ops exynos_dp_display_ops = {
        .create_connector = exynos_dp_create_connector,
        .dpms = exynos_dp_dpms,
+       .commit = exynos_dp_commit,
 };
 
 static struct exynos_drm_display exynos_dp_display = {
@@ -1201,7 +1250,7 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
 {
        int ret;
 
-       ret = of_get_videomode(dp->dev->of_node, &dp->panel.vm,
+       ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
                        OF_USE_NATIVE_MODE);
        if (ret) {
                DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
@@ -1215,16 +1264,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
        struct platform_device *pdev = to_platform_device(dev);
        struct drm_device *drm_dev = data;
        struct resource *res;
-       struct exynos_dp_device *dp;
+       struct exynos_dp_device *dp = exynos_dp_display.ctx;
        unsigned int irq_flags;
-
        int ret = 0;
 
-       dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
-                               GFP_KERNEL);
-       if (!dp)
-               return -ENOMEM;
-
        dp->dev = &pdev->dev;
        dp->dpms_mode = DRM_MODE_DPMS_OFF;
 
@@ -1236,9 +1279,11 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
        if (ret)
                return ret;
 
-       ret = exynos_dp_dt_parse_panel(dp);
-       if (ret)
-               return ret;
+       if (!dp->panel) {
+               ret = exynos_dp_dt_parse_panel(dp);
+               if (ret)
+                       return ret;
+       }
 
        dp->clock = devm_clk_get(&pdev->dev, "dp");
        if (IS_ERR(dp->clock)) {
@@ -1298,7 +1343,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
        disable_irq(dp->irq);
 
        dp->drm_dev = drm_dev;
-       exynos_dp_display.ctx = dp;
 
        platform_set_drvdata(pdev, &exynos_dp_display);
 
@@ -1325,6 +1369,9 @@ static const struct component_ops exynos_dp_ops = {
 
 static int exynos_dp_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
+       struct device_node *panel_node;
+       struct exynos_dp_device *dp;
        int ret;
 
        ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
@@ -1332,6 +1379,21 @@ static int exynos_dp_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
+                               GFP_KERNEL);
+       if (!dp)
+               return -ENOMEM;
+
+       panel_node = of_parse_phandle(dev->of_node, "panel", 0);
+       if (panel_node) {
+               dp->panel = of_drm_find_panel(panel_node);
+               of_node_put(panel_node);
+               if (!dp->panel)
+                       return -EPROBE_DEFER;
+       }
+
+       exynos_dp_display.ctx = dp;
+
        ret = component_add(&pdev->dev, &exynos_dp_ops);
        if (ret)
                exynos_drm_component_del(&pdev->dev,
index 02cc4f9..a1aee69 100644 (file)
@@ -149,6 +149,7 @@ struct exynos_dp_device {
        struct drm_device       *drm_dev;
        struct drm_connector    connector;
        struct drm_encoder      *encoder;
+       struct drm_panel        *panel;
        struct clk              *clock;
        unsigned int            irq;
        void __iomem            *reg_base;
@@ -162,7 +163,7 @@ struct exynos_dp_device {
        int                     dpms_mode;
        int                     hpd_gpio;
 
-       struct exynos_drm_panel_info panel;
+       struct exynos_drm_panel_info priv;
 };
 
 /* exynos_dp_reg.c */
index 3aa1c7e..fa08f05 100644 (file)
@@ -125,14 +125,18 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
 
 static void exynos_dpi_poweron(struct exynos_dpi *ctx)
 {
-       if (ctx->panel)
+       if (ctx->panel) {
+               drm_panel_prepare(ctx->panel);
                drm_panel_enable(ctx->panel);
+       }
 }
 
 static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
 {
-       if (ctx->panel)
+       if (ctx->panel) {
                drm_panel_disable(ctx->panel);
+               drm_panel_unprepare(ctx->panel);
+       }
 }
 
 static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
index 86aebd8..442aa2d 100644 (file)
@@ -1333,7 +1333,7 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
        if (ret < 0)
                return ret;
 
-       ret = drm_panel_enable(dsi->panel);
+       ret = drm_panel_prepare(dsi->panel);
        if (ret < 0) {
                exynos_dsi_poweroff(dsi);
                return ret;
@@ -1342,6 +1342,14 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
        exynos_dsi_set_display_mode(dsi);
        exynos_dsi_set_display_enable(dsi, true);
 
+       ret = drm_panel_enable(dsi->panel);
+       if (ret < 0) {
+               exynos_dsi_set_display_enable(dsi, false);
+               drm_panel_unprepare(dsi->panel);
+               exynos_dsi_poweroff(dsi);
+               return ret;
+       }
+
        dsi->state |= DSIM_STATE_ENABLED;
 
        return 0;
@@ -1352,8 +1360,9 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
        if (!(dsi->state & DSIM_STATE_ENABLED))
                return;
 
-       exynos_dsi_set_display_enable(dsi, false);
        drm_panel_disable(dsi->panel);
+       exynos_dsi_set_display_enable(dsi, false);
+       drm_panel_unprepare(dsi->panel);
        exynos_dsi_poweroff(dsi);
 
        dsi->state &= ~DSIM_STATE_ENABLED;
index 4ec874d..bee9f72 100644 (file)
@@ -5,7 +5,7 @@ config DRM_PANEL
          Panel registration and lookup framework.
 
 menu "Display Panels"
-       depends on DRM_PANEL
+       depends on DRM && DRM_PANEL
 
 config DRM_PANEL_SIMPLE
        tristate "support for simple panels"
@@ -18,14 +18,11 @@ config DRM_PANEL_SIMPLE
 
 config DRM_PANEL_LD9040
        tristate "LD9040 RGB/SPI panel"
-       depends on DRM && DRM_PANEL
-       depends on OF
-       select SPI
+       depends on OF && SPI
        select VIDEOMODE_HELPERS
 
 config DRM_PANEL_S6E8AA0
        tristate "S6E8AA0 DSI video mode panel"
-       depends on DRM && DRM_PANEL
        depends on OF
        select DRM_MIPI_DSI
        select VIDEOMODE_HELPERS
index db1601f..42ac67b 100644 (file)
@@ -110,7 +110,10 @@ struct ld9040 {
        int error;
 };
 
-#define panel_to_ld9040(p) container_of(p, struct ld9040, panel)
+static inline struct ld9040 *panel_to_ld9040(struct drm_panel *panel)
+{
+       return container_of(panel, struct ld9040, panel);
+}
 
 static int ld9040_clear_error(struct ld9040 *ctx)
 {
@@ -215,6 +218,11 @@ static int ld9040_power_off(struct ld9040 *ctx)
 }
 
 static int ld9040_disable(struct drm_panel *panel)
+{
+       return 0;
+}
+
+static int ld9040_unprepare(struct drm_panel *panel)
 {
        struct ld9040 *ctx = panel_to_ld9040(panel);
 
@@ -228,7 +236,7 @@ static int ld9040_disable(struct drm_panel *panel)
        return ld9040_power_off(ctx);
 }
 
-static int ld9040_enable(struct drm_panel *panel)
+static int ld9040_prepare(struct drm_panel *panel)
 {
        struct ld9040 *ctx = panel_to_ld9040(panel);
        int ret;
@@ -242,11 +250,16 @@ static int ld9040_enable(struct drm_panel *panel)
        ret = ld9040_clear_error(ctx);
 
        if (ret < 0)
-               ld9040_disable(panel);
+               ld9040_unprepare(panel);
 
        return ret;
 }
 
+static int ld9040_enable(struct drm_panel *panel)
+{
+       return 0;
+}
+
 static int ld9040_get_modes(struct drm_panel *panel)
 {
        struct drm_connector *connector = panel->connector;
@@ -273,6 +286,8 @@ static int ld9040_get_modes(struct drm_panel *panel)
 
 static const struct drm_panel_funcs ld9040_drm_funcs = {
        .disable = ld9040_disable,
+       .unprepare = ld9040_unprepare,
+       .prepare = ld9040_prepare,
        .enable = ld9040_enable,
        .get_modes = ld9040_get_modes,
 };
index 06e57a2..b5217fe 100644 (file)
@@ -120,7 +120,10 @@ struct s6e8aa0 {
        int error;
 };
 
-#define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel)
+static inline struct s6e8aa0 *panel_to_s6e8aa0(struct drm_panel *panel)
+{
+       return container_of(panel, struct s6e8aa0, panel);
+}
 
 static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
 {
@@ -133,14 +136,14 @@ static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
 static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
 {
        struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
-       int ret;
+       ssize_t ret;
 
        if (ctx->error < 0)
                return;
 
-       ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len);
+       ret = mipi_dsi_dcs_write(dsi, data, len);
        if (ret < 0) {
-               dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
+               dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len,
                        data);
                ctx->error = ret;
        }
@@ -154,7 +157,7 @@ static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len)
        if (ctx->error < 0)
                return ctx->error;
 
-       ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len);
+       ret = mipi_dsi_dcs_read(dsi, cmd, data, len);
        if (ret < 0) {
                dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd);
                ctx->error = ret;
@@ -888,6 +891,11 @@ static int s6e8aa0_power_off(struct s6e8aa0 *ctx)
 }
 
 static int s6e8aa0_disable(struct drm_panel *panel)
+{
+       return 0;
+}
+
+static int s6e8aa0_unprepare(struct drm_panel *panel)
 {
        struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
 
@@ -900,7 +908,7 @@ static int s6e8aa0_disable(struct drm_panel *panel)
        return s6e8aa0_power_off(ctx);
 }
 
-static int s6e8aa0_enable(struct drm_panel *panel)
+static int s6e8aa0_prepare(struct drm_panel *panel)
 {
        struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
        int ret;
@@ -913,11 +921,16 @@ static int s6e8aa0_enable(struct drm_panel *panel)
        ret = ctx->error;
 
        if (ret < 0)
-               s6e8aa0_disable(panel);
+               s6e8aa0_unprepare(panel);
 
        return ret;
 }
 
+static int s6e8aa0_enable(struct drm_panel *panel)
+{
+       return 0;
+}
+
 static int s6e8aa0_get_modes(struct drm_panel *panel)
 {
        struct drm_connector *connector = panel->connector;
@@ -944,6 +957,8 @@ static int s6e8aa0_get_modes(struct drm_panel *panel)
 
 static const struct drm_panel_funcs s6e8aa0_drm_funcs = {
        .disable = s6e8aa0_disable,
+       .unprepare = s6e8aa0_unprepare,
+       .prepare = s6e8aa0_prepare,
        .enable = s6e8aa0_enable,
        .get_modes = s6e8aa0_get_modes,
 };
index a251361..4ce1db0 100644 (file)
@@ -37,14 +37,35 @@ struct panel_desc {
        const struct drm_display_mode *modes;
        unsigned int num_modes;
 
+       unsigned int bpc;
+
        struct {
                unsigned int width;
                unsigned int height;
        } size;
+
+       /**
+        * @prepare: the time (in milliseconds) that it takes for the panel to
+        *           become ready and start receiving video data
+        * @enable: the time (in milliseconds) that it takes for the panel to
+        *          display the first valid frame after starting to receive
+        *          video data
+        * @disable: the time (in milliseconds) that it takes for the panel to
+        *           turn the display off (no content is visible)
+        * @unprepare: the time (in milliseconds) that it takes for the panel
+        *             to power itself down completely
+        */
+       struct {
+               unsigned int prepare;
+               unsigned int enable;
+               unsigned int disable;
+               unsigned int unprepare;
+       } delay;
 };
 
 struct panel_simple {
        struct drm_panel base;
+       bool prepared;
        bool enabled;
 
        const struct panel_desc *desc;
@@ -87,6 +108,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
                num++;
        }
 
+       connector->display_info.bpc = panel->desc->bpc;
        connector->display_info.width_mm = panel->desc->size.width;
        connector->display_info.height_mm = panel->desc->size.height;
 
@@ -105,21 +127,40 @@ static int panel_simple_disable(struct drm_panel *panel)
                backlight_update_status(p->backlight);
        }
 
+       if (p->desc->delay.disable)
+               msleep(p->desc->delay.disable);
+
+       p->enabled = false;
+
+       return 0;
+}
+
+static int panel_simple_unprepare(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+
+       if (!p->prepared)
+               return 0;
+
        if (p->enable_gpio)
                gpiod_set_value_cansleep(p->enable_gpio, 0);
 
        regulator_disable(p->supply);
-       p->enabled = false;
+
+       if (p->desc->delay.unprepare)
+               msleep(p->desc->delay.unprepare);
+
+       p->prepared = false;
 
        return 0;
 }
 
-static int panel_simple_enable(struct drm_panel *panel)
+static int panel_simple_prepare(struct drm_panel *panel)
 {
        struct panel_simple *p = to_panel_simple(panel);
        int err;
 
-       if (p->enabled)
+       if (p->prepared)
                return 0;
 
        err = regulator_enable(p->supply);
@@ -131,6 +172,24 @@ static int panel_simple_enable(struct drm_panel *panel)
        if (p->enable_gpio)
                gpiod_set_value_cansleep(p->enable_gpio, 1);
 
+       if (p->desc->delay.prepare)
+               msleep(p->desc->delay.prepare);
+
+       p->prepared = true;
+
+       return 0;
+}
+
+static int panel_simple_enable(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+
+       if (p->enabled)
+               return 0;
+
+       if (p->desc->delay.enable)
+               msleep(p->desc->delay.enable);
+
        if (p->backlight) {
                p->backlight->props.power = FB_BLANK_UNBLANK;
                backlight_update_status(p->backlight);
@@ -164,6 +223,8 @@ static int panel_simple_get_modes(struct drm_panel *panel)
 
 static const struct drm_panel_funcs panel_simple_funcs = {
        .disable = panel_simple_disable,
+       .unprepare = panel_simple_unprepare,
+       .prepare = panel_simple_prepare,
        .enable = panel_simple_enable,
        .get_modes = panel_simple_get_modes,
 };
@@ -179,22 +240,21 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
                return -ENOMEM;
 
        panel->enabled = false;
+       panel->prepared = false;
        panel->desc = desc;
 
        panel->supply = devm_regulator_get(dev, "power");
        if (IS_ERR(panel->supply))
                return PTR_ERR(panel->supply);
 
-       panel->enable_gpio = devm_gpiod_get(dev, "enable");
+       panel->enable_gpio = devm_gpiod_get_optional(dev, "enable");
        if (IS_ERR(panel->enable_gpio)) {
                err = PTR_ERR(panel->enable_gpio);
-               if (err != -ENOENT) {
-                       dev_err(dev, "failed to request GPIO: %d\n", err);
-                       return err;
-               }
+               dev_err(dev, "failed to request GPIO: %d\n", err);
+               return err;
+       }
 
-               panel->enable_gpio = NULL;
-       } else {
+       if (panel->enable_gpio) {
                err = gpiod_direction_output(panel->enable_gpio, 0);
                if (err < 0) {
                        dev_err(dev, "failed to setup GPIO: %d\n", err);
@@ -285,6 +345,7 @@ static const struct drm_display_mode auo_b101aw03_mode = {
 static const struct panel_desc auo_b101aw03 = {
        .modes = &auo_b101aw03_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 223,
                .height = 125,
@@ -307,12 +368,40 @@ static const struct drm_display_mode auo_b133xtn01_mode = {
 static const struct panel_desc auo_b133xtn01 = {
        .modes = &auo_b133xtn01_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 293,
                .height = 165,
        },
 };
 
+static const struct drm_display_mode auo_b133htn01_mode = {
+       .clock = 150660,
+       .hdisplay = 1920,
+       .hsync_start = 1920 + 172,
+       .hsync_end = 1920 + 172 + 80,
+       .htotal = 1920 + 172 + 80 + 60,
+       .vdisplay = 1080,
+       .vsync_start = 1080 + 25,
+       .vsync_end = 1080 + 25 + 10,
+       .vtotal = 1080 + 25 + 10 + 10,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b133htn01 = {
+       .modes = &auo_b133htn01_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 293,
+               .height = 165,
+       },
+       .delay = {
+               .prepare = 105,
+               .enable = 20,
+               .unprepare = 50,
+       },
+};
+
 static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
        .clock = 72070,
        .hdisplay = 1366,
@@ -329,6 +418,7 @@ static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
 static const struct panel_desc chunghwa_claa101wa01a = {
        .modes = &chunghwa_claa101wa01a_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 220,
                .height = 120,
@@ -351,6 +441,7 @@ static const struct drm_display_mode chunghwa_claa101wb01_mode = {
 static const struct panel_desc chunghwa_claa101wb01 = {
        .modes = &chunghwa_claa101wb01_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 223,
                .height = 125,
@@ -374,6 +465,7 @@ static const struct drm_display_mode edt_et057090dhu_mode = {
 static const struct panel_desc edt_et057090dhu = {
        .modes = &edt_et057090dhu_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 115,
                .height = 86,
@@ -397,12 +489,82 @@ static const struct drm_display_mode edt_etm0700g0dh6_mode = {
 static const struct panel_desc edt_etm0700g0dh6 = {
        .modes = &edt_etm0700g0dh6_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 152,
                .height = 91,
        },
 };
 
+static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
+       .clock = 32260,
+       .hdisplay = 800,
+       .hsync_start = 800 + 168,
+       .hsync_end = 800 + 168 + 64,
+       .htotal = 800 + 168 + 64 + 88,
+       .vdisplay = 480,
+       .vsync_start = 480 + 37,
+       .vsync_end = 480 + 37 + 2,
+       .vtotal = 480 + 37 + 2 + 8,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc foxlink_fl500wvr00_a0t = {
+       .modes = &foxlink_fl500wvr00_a0t_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 108,
+               .height = 65,
+       },
+};
+
+static const struct drm_display_mode innolux_n116bge_mode = {
+       .clock = 71000,
+       .hdisplay = 1366,
+       .hsync_start = 1366 + 64,
+       .hsync_end = 1366 + 64 + 6,
+       .htotal = 1366 + 64 + 6 + 64,
+       .vdisplay = 768,
+       .vsync_start = 768 + 8,
+       .vsync_end = 768 + 8 + 4,
+       .vtotal = 768 + 8 + 4 + 8,
+       .vrefresh = 60,
+       .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc innolux_n116bge = {
+       .modes = &innolux_n116bge_mode,
+       .num_modes = 1,
+       .bpc = 6,
+       .size = {
+               .width = 256,
+               .height = 144,
+       },
+};
+
+static const struct drm_display_mode innolux_n156bge_l21_mode = {
+       .clock = 69300,
+       .hdisplay = 1366,
+       .hsync_start = 1366 + 16,
+       .hsync_end = 1366 + 16 + 34,
+       .htotal = 1366 + 16 + 34 + 50,
+       .vdisplay = 768,
+       .vsync_start = 768 + 2,
+       .vsync_end = 768 + 2 + 6,
+       .vtotal = 768 + 2 + 6 + 12,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc innolux_n156bge_l21 = {
+       .modes = &innolux_n156bge_l21_mode,
+       .num_modes = 1,
+       .bpc = 6,
+       .size = {
+               .width = 344,
+               .height = 193,
+       },
+};
+
 static const struct drm_display_mode lg_lp129qe_mode = {
        .clock = 285250,
        .hdisplay = 2560,
@@ -419,6 +581,7 @@ static const struct drm_display_mode lg_lp129qe_mode = {
 static const struct panel_desc lg_lp129qe = {
        .modes = &lg_lp129qe_mode,
        .num_modes = 1,
+       .bpc = 8,
        .size = {
                .width = 272,
                .height = 181,
@@ -441,6 +604,7 @@ static const struct drm_display_mode samsung_ltn101nt05_mode = {
 static const struct panel_desc samsung_ltn101nt05 = {
        .modes = &samsung_ltn101nt05_mode,
        .num_modes = 1,
+       .bpc = 6,
        .size = {
                .width = 1024,
                .height = 600,
@@ -451,6 +615,9 @@ static const struct of_device_id platform_of_match[] = {
        {
                .compatible = "auo,b101aw03",
                .data = &auo_b101aw03,
+       }, {
+               .compatible = "auo,b133htn01",
+               .data = &auo_b133htn01,
        }, {
                .compatible = "auo,b133xtn01",
                .data = &auo_b133xtn01,
@@ -469,14 +636,21 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "edt,etm0700g0dh6",
                .data = &edt_etm0700g0dh6,
+       }, {
+               .compatible = "foxlink,fl500wvr00-a0t",
+               .data = &foxlink_fl500wvr00_a0t,
+       }, {
+               .compatible = "innolux,n116bge",
+               .data = &innolux_n116bge,
+       }, {
+               .compatible = "innolux,n156bge-l21",
+               .data = &innolux_n156bge_l21,
        }, {
                .compatible = "lg,lp129qe",
                .data = &lg_lp129qe,
        }, {
                .compatible = "samsung,ltn101nt05",
                .data = &samsung_ltn101nt05,
-       }, {
-               .compatible = "simple-panel",
        }, {
                /* sentinel */
        }
@@ -545,7 +719,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
                        .height = 151,
                },
        },
-       .flags = MIPI_DSI_MODE_VIDEO,
+       .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
        .format = MIPI_DSI_FMT_RGB888,
        .lanes = 4,
 };
@@ -599,7 +773,8 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
                        .height = 136,
                },
        },
-       .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+       .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+                MIPI_DSI_CLOCK_NON_CONTINUOUS,
        .format = MIPI_DSI_FMT_RGB888,
        .lanes = 4,
 };
index 446837e..0c67d7e 100644 (file)
@@ -140,7 +140,9 @@ static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
        if (mode != DRM_MODE_DPMS_ON) {
                drm_panel_disable(panel);
                tegra_output_disable(output);
+               drm_panel_unprepare(panel);
        } else {
+               drm_panel_prepare(panel);
                tegra_output_enable(output);
                drm_panel_enable(panel);
        }
index 944f33f..2bb55b8 100644 (file)
@@ -94,6 +94,8 @@ void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
 #define MIPI_DSI_MODE_VSYNC_FLUSH      BIT(8)
 /* disable EoT packets in HS mode */
 #define MIPI_DSI_MODE_EOT_PACKET       BIT(9)
+/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
+#define MIPI_DSI_CLOCK_NON_CONTINUOUS  BIT(10)
 
 enum mipi_dsi_pixel_format {
        MIPI_DSI_FMT_RGB888,
@@ -121,14 +123,17 @@ struct mipi_dsi_device {
        unsigned long mode_flags;
 };
 
-#define to_mipi_dsi_device(d) container_of(d, struct mipi_dsi_device, dev)
+static inline struct mipi_dsi_device *to_mipi_dsi_device(struct device *dev)
+{
+       return container_of(dev, struct mipi_dsi_device, dev);
+}
 
 int mipi_dsi_attach(struct mipi_dsi_device *dsi);
 int mipi_dsi_detach(struct mipi_dsi_device *dsi);
-int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
-                      const void *data, size_t len);
-ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
-                         u8 cmd, void *data, size_t len);
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
+                           size_t len);
+ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
+                         size_t len);
 
 /**
  * struct mipi_dsi_driver - DSI driver
@@ -144,7 +149,11 @@ struct mipi_dsi_driver {
        void (*shutdown)(struct mipi_dsi_device *dsi);
 };
 
-#define to_mipi_dsi_driver(d) container_of(d, struct mipi_dsi_driver, driver)
+static inline struct mipi_dsi_driver *
+to_mipi_dsi_driver(struct device_driver *driver)
+{
+       return container_of(driver, struct mipi_dsi_driver, driver);
+}
 
 static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dsi)
 {
index c2ab77a..1fbcc96 100644 (file)
@@ -30,8 +30,42 @@ struct drm_connector;
 struct drm_device;
 struct drm_panel;
 
+/**
+ * struct drm_panel_funcs - perform operations on a given panel
+ * @disable: disable panel (turn off back light, etc.)
+ * @unprepare: turn off panel
+ * @prepare: turn on panel and perform set up
+ * @enable: enable panel (turn on back light, etc.)
+ * @get_modes: add modes to the connector that the panel is attached to and
+ * return the number of modes added
+ *
+ * The .prepare() function is typically called before the display controller
+ * starts to transmit video data. Panel drivers can use this to turn the panel
+ * on and wait for it to become ready. If additional configuration is required
+ * (via a control bus such as I2C, SPI or DSI for example) this is a good time
+ * to do that.
+ *
+ * After the display controller has started transmitting video data, it's safe
+ * to call the .enable() function. This will typically enable the backlight to
+ * make the image on screen visible. Some panels require a certain amount of
+ * time or frames before the image is displayed. This function is responsible
+ * for taking this into account before enabling the backlight to avoid visual
+ * glitches.
+ *
+ * Before stopping video transmission from the display controller it can be
+ * necessary to turn off the panel to avoid visual glitches. This is done in
+ * the .disable() function. Analogously to .enable() this typically involves
+ * turning off the backlight and waiting for some time to make sure no image
+ * is visible on the panel. It is then safe for the display controller to
+ * cease transmission of video data.
+ *
+ * To save power when no video data is transmitted, a driver can power down
+ * the panel. This is the job of the .unprepare() function.
+ */
 struct drm_panel_funcs {
        int (*disable)(struct drm_panel *panel);
+       int (*unprepare)(struct drm_panel *panel);
+       int (*prepare)(struct drm_panel *panel);
        int (*enable)(struct drm_panel *panel);
        int (*get_modes)(struct drm_panel *panel);
 };
@@ -46,6 +80,14 @@ struct drm_panel {
        struct list_head list;
 };
 
+static inline int drm_panel_unprepare(struct drm_panel *panel)
+{
+       if (panel && panel->funcs && panel->funcs->unprepare)
+               return panel->funcs->unprepare(panel);
+
+       return panel ? -ENOSYS : -EINVAL;
+}
+
 static inline int drm_panel_disable(struct drm_panel *panel)
 {
        if (panel && panel->funcs && panel->funcs->disable)
@@ -54,6 +96,14 @@ static inline int drm_panel_disable(struct drm_panel *panel)
        return panel ? -ENOSYS : -EINVAL;
 }
 
+static inline int drm_panel_prepare(struct drm_panel *panel)
+{
+       if (panel && panel->funcs && panel->funcs->prepare)
+               return panel->funcs->prepare(panel);
+
+       return panel ? -ENOSYS : -EINVAL;
+}
+
 static inline int drm_panel_enable(struct drm_panel *panel)
 {
        if (panel && panel->funcs && panel->funcs->enable)
@@ -62,6 +112,14 @@ static inline int drm_panel_enable(struct drm_panel *panel)
        return panel ? -ENOSYS : -EINVAL;
 }
 
+static inline int drm_panel_get_modes(struct drm_panel *panel)
+{
+       if (panel && panel->funcs && panel->funcs->get_modes)
+               return panel->funcs->get_modes(panel);
+
+       return panel ? -ENOSYS : -EINVAL;
+}
+
 void drm_panel_init(struct drm_panel *panel);
 
 int drm_panel_add(struct drm_panel *panel);