Commit 0c6287ec authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm/panel/for-3.17-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

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
  ...
parents 21d70354 9746c619
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.
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.
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.
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.
......@@ -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,
......
......@@ -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.
......
......@@ -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,
......
......@@ -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 */
......
......@@ -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)
......
......@@ -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;
......
......@@ -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
......
......@@ -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,
};
......
......@@ -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,
};
......
......@@ -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;