summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/pl111/pl111_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/pl111/pl111_display.c')
-rw-r--r--drivers/gpu/drm/pl111/pl111_display.c103
1 files changed, 56 insertions, 47 deletions
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
index 754f6b25f265..3a9661b9b1fc 100644
--- a/drivers/gpu/drm/pl111/pl111_display.c
+++ b/drivers/gpu/drm/pl111/pl111_display.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
*
@@ -6,24 +7,21 @@
* Copyright (c) 2006-2008 Intel Corporation
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
* Copyright (C) 2011 Texas Instruments
- *
- * This program is free software and is provided to you under the terms of the
- * GNU General Public License version 2 as published by the Free Software
- * Foundation, and any use by you of this program is subject to the terms of
- * such GNU licence.
- *
*/
-#include <linux/amba/clcd-regs.h>
#include <linux/clk.h>
-#include <linux/version.h>
+#include <linux/delay.h>
#include <linux/dma-buf.h>
+#include <linux/media-bus-format.h>
#include <linux/of_graph.h>
-#include <drm/drmP.h>
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
#include "pl111_drm.h"
@@ -51,12 +49,12 @@ irqreturn_t pl111_irq(int irq, void *data)
}
static enum drm_mode_status
-pl111_mode_valid(struct drm_crtc *crtc,
+pl111_mode_valid(struct drm_simple_display_pipe *pipe,
const struct drm_display_mode *mode)
{
- struct drm_device *drm = crtc->dev;
+ struct drm_device *drm = pipe->crtc.dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
- u32 cpp = priv->variant->fb_bpp / 8;
+ u32 cpp = DIV_ROUND_UP(priv->variant->fb_depth, 8);
u64 bw;
/*
@@ -97,7 +95,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
return -EINVAL;
if (fb) {
- u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0);
+ u32 offset = drm_fb_dma_get_gem_addr(fb, pstate, 0);
/* FB base address must be dword aligned. */
if (offset & 3)
@@ -131,6 +129,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
struct drm_framebuffer *fb = plane->state->fb;
struct drm_connector *connector = priv->connector;
struct drm_bridge *bridge = priv->bridge;
+ bool grayscale = false;
u32 cntl;
u32 ppl, hsw, hfp, hbp;
u32 lpp, vsw, vfp, vbp;
@@ -188,8 +187,22 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
tim2 |= TIM2_IOE;
if (connector->display_info.bus_flags &
- DRM_BUS_FLAG_PIXDATA_NEGEDGE)
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
tim2 |= TIM2_IPC;
+
+ if (connector->display_info.num_bus_formats == 1 &&
+ connector->display_info.bus_formats[0] ==
+ MEDIA_BUS_FMT_Y8_1X8)
+ grayscale = true;
+
+ /*
+ * The AC pin bias frequency is set to max count when using
+ * grayscale so at least once in a while we will reverse
+ * polarity and get rid of any DC built up that could
+ * damage the display.
+ */
+ if (grayscale)
+ tim2 |= TIM2_ACB_MASK;
}
if (bridge) {
@@ -221,14 +234,24 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
writel(0, priv->regs + CLCD_TIM3);
- /* Hard-code TFT panel */
- cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+ /*
+ * Detect grayscale bus format. We do not support a grayscale mode
+ * toward userspace, instead we expose an RGB24 buffer and then the
+ * hardware will activate its grayscaler to convert to the grayscale
+ * format.
+ */
+ if (grayscale)
+ cntl = CNTL_LCDEN | CNTL_LCDMONO8;
+ else
+ /* Else we assume TFT display */
+ cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+
/* On the ST Micro variant, assume all 24 bits are connected */
if (priv->variant->st_bitmux_control)
cntl |= CNTL_ST_CDWID_24;
/*
- * Note that the the ARM hardware's format reader takes 'r' from
+ * Note that the ARM hardware's format reader takes 'r' from
* the low bit, while DRM formats list channels from high bit
* to low bit as you read left to right. The ST Micro version of
* the PL110 (LCDC) however uses the standard DRM format.
@@ -332,7 +355,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
drm_crtc_vblank_on(crtc);
}
-void pl111_display_disable(struct drm_simple_display_pipe *pipe)
+static void pl111_display_disable(struct drm_simple_display_pipe *pipe)
{
struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
@@ -376,7 +399,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe,
struct drm_framebuffer *fb = pstate->fb;
if (fb) {
- u32 addr = drm_fb_cma_get_gem_addr(fb, pstate, 0);
+ u32 addr = drm_fb_dma_get_gem_addr(fb, pstate, 0);
writel(addr, priv->regs + CLCD_UBAS);
}
@@ -419,7 +442,6 @@ static struct drm_simple_display_pipe_funcs pl111_display_funcs = {
.enable = pl111_display_enable,
.disable = pl111_display_disable,
.update = pl111_display_update,
- .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
};
static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
@@ -452,12 +474,15 @@ static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
return best_div;
}
-static long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int pl111_clk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- int div = pl111_clk_div_choose_div(hw, rate, prate, true);
+ int div = pl111_clk_div_choose_div(hw, req->rate,
+ &req->best_parent_rate, true);
- return DIV_ROUND_UP_ULL(*prate, div);
+ req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);
+
+ return 0;
}
static unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw,
@@ -507,7 +532,7 @@ static int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops pl111_clk_div_ops = {
.recalc_rate = pl111_clk_div_recalc_rate,
- .round_rate = pl111_clk_div_round_rate,
+ .determine_rate = pl111_clk_div_determine_rate,
.set_rate = pl111_clk_div_set_rate,
};
@@ -531,14 +556,15 @@ pl111_init_clock_divider(struct drm_device *drm)
dev_err(drm->dev, "CLCD: unable to get clcdclk.\n");
return PTR_ERR(parent);
}
+
+ spin_lock_init(&priv->tim2_lock);
+
/* If the clock divider is broken, use the parent directly */
if (priv->variant->broken_clockdivider) {
priv->clk = parent;
return 0;
}
parent_name = __clk_get_name(parent);
-
- spin_lock_init(&priv->tim2_lock);
div->init = &init;
ret = devm_clk_hw_register(drm->dev, div);
@@ -550,25 +576,8 @@ pl111_init_clock_divider(struct drm_device *drm)
int pl111_display_init(struct drm_device *drm)
{
struct pl111_drm_dev_private *priv = drm->dev_private;
- struct device *dev = drm->dev;
- struct device_node *endpoint;
- u32 tft_r0b0g0[3];
int ret;
- endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
- if (!endpoint)
- return -ENODEV;
-
- if (of_property_read_u32_array(endpoint,
- "arm,pl11x,tft-r0g0b0-pads",
- tft_r0b0g0,
- ARRAY_SIZE(tft_r0b0g0)) != 0) {
- dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
- of_node_put(endpoint);
- return -ENOENT;
- }
- of_node_put(endpoint);
-
ret = pl111_init_clock_divider(drm);
if (ret)
return ret;