summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/vc4/vc4_dsi.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-05-29 06:04:39 +1000
committerDave Airlie <airlied@redhat.com>2017-05-29 06:04:39 +1000
commit1afc45445d15493f3aaadbe2b549b37eaffcc407 (patch)
tree471cdf0619cc75a44a992c0916beb498b3c894d9 /drivers/gpu/drm/vc4/vc4_dsi.c
parente98c58e55f68f8785aebfab1f8c9a03d8de0afe1 (diff)
parent71ebc9a3795818eab52e81bbcbdfae130ee35d9e (diff)
downloadlinux-1afc45445d15493f3aaadbe2b549b37eaffcc407.tar.gz
linux-1afc45445d15493f3aaadbe2b549b37eaffcc407.tar.xz
Merge tag 'drm-misc-next-2017-05-26' of git://anongit.freedesktop.org/git/drm-misc into drm-next
UAPI Changes: - Add DRM_MODE_ROTATE_ and DRM_MODE_REFLECT_ defines to the UAPI (Robert) Cross-subsystem Changes: - Standardize sync_file.txt documentation format (Mauro) Core Changes: - Turf drm_[cm]alloc functions for kvmalloc alternatives (Michal) - Add optional mode_valid() hook to crtc/encoder/bridge (Jose) - Improve documentation around mode validation/alteration (Daniel) - Reduce sync_file construction time by deferring name creation (Chris) Driver Changes: - pl111: Wire up the clock divider and add debugfs (Eric) - various: Fix include notation and remove -Iinclude/drm (Masahiro) - stm: Add Benjamin Gaignard and Vincent Abriou as STM maintainers (Vincent) - various: Miscellaneous trivial fixes to pl111/stm/vgem/vc4 Cc: Michal Hocko <mhocko@suse.com> Cc: Eric Anholt <eric@anholt.net> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Robert Foss <robert.foss@collabora.com> Cc: Vincent Abriou <vincent.abriou@st.com> Cc: Jose Abreu <Jose.Abreu@synopsys.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Mauro Carvalho Chehab <mchehab@s-opensource.com> * tag 'drm-misc-next-2017-05-26' of git://anongit.freedesktop.org/git/drm-misc: (55 commits) dma-buf/sync-file: Defer creation of sync_file->name sync_file.txt: standardize document format gpu: drm: gma500: remove two more dead variable drm/doc: Clarify mode_fixup vs. atomic_check a bit more drm/doc: Document adjusted/request modes a bit better drm: Add crtc/encoder/bridge->mode_valid() callbacks MAINTAINERS: update drm/stm maintainers list drm/stm: ltdc: fix duplicated arguments drm/pl111: Fix return value check in pl111_amba_probe() drm/amd: include <linux/delay.h> instead of "linux/delay.h" drm: Add DRM_MODE_ROTATE_ and DRM_MODE_REFLECT_ to UAPI drm/vgem: Fix return value check in vgem_init() drm/blend: Fix comment typ-o drm/stm: remove unneeded -Iinclude/drm compiler flag drm/vc4: fix include notation and remove -Iinclude/drm flag drm/pl111: Add a debugfs node to dump our registers. drm/pl111: make structure mode_config_funcs static drm/pl111: make structure pl111_display_funcs static drm/pl111: Register the clock divider and use it. drm: drop drm_[cm]alloc* helpers ...
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_dsi.c')
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c140
1 files changed, 100 insertions, 40 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 160f981d1cf4..0ef41df3915f 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -29,20 +29,20 @@
* hopefully present.
*/
-#include "drm_atomic_helper.h"
-#include "drm_crtc_helper.h"
-#include "drm_edid.h"
-#include "drm_mipi_dsi.h"
-#include "drm_panel.h"
-#include "linux/clk.h"
-#include "linux/clk-provider.h"
-#include "linux/completion.h"
-#include "linux/component.h"
-#include "linux/dmaengine.h"
-#include "linux/i2c.h"
-#include "linux/of_address.h"
-#include "linux/of_platform.h"
-#include "linux/pm_runtime.h"
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/completion.h>
+#include <linux/component.h>
+#include <linux/dmaengine.h>
+#include <linux/i2c.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include "vc4_drv.h"
#include "vc4_regs.h"
@@ -519,7 +519,8 @@ struct vc4_dsi {
/* DSI channel for the panel we're connected to. */
u32 channel;
u32 lanes;
- enum mipi_dsi_pixel_format format;
+ u32 format;
+ u32 divider;
u32 mode_flags;
/* Input clock from CPRMAN to the digital PHY, for the DSI
@@ -906,13 +907,67 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
pm_runtime_put(dev);
}
+/* Extends the mode's blank intervals to handle BCM2835's integer-only
+ * DSI PLL divider.
+ *
+ * On 2835, PLLD is set to 2Ghz, and may not be changed by the display
+ * driver since most peripherals are hanging off of the PLLD_PER
+ * divider. PLLD_DSI1, which drives our DSI bit clock (and therefore
+ * the pixel clock), only has an integer divider off of DSI.
+ *
+ * To get our panel mode to refresh at the expected 60Hz, we need to
+ * extend the horizontal blank time. This means we drive a
+ * higher-than-expected clock rate to the panel, but that's what the
+ * firmware does too.
+ */
+static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
+ struct vc4_dsi *dsi = vc4_encoder->dsi;
+ struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
+ unsigned long parent_rate = clk_get_rate(phy_parent);
+ unsigned long pixel_clock_hz = mode->clock * 1000;
+ unsigned long pll_clock = pixel_clock_hz * dsi->divider;
+ int divider;
+
+ /* Find what divider gets us a faster clock than the requested
+ * pixel clock.
+ */
+ for (divider = 1; divider < 8; divider++) {
+ if (parent_rate / divider < pll_clock) {
+ divider--;
+ break;
+ }
+ }
+
+ /* Now that we've picked a PLL divider, calculate back to its
+ * pixel clock.
+ */
+ pll_clock = parent_rate / divider;
+ pixel_clock_hz = pll_clock / dsi->divider;
+
+ /* Round up the clk_set_rate() request slightly, since
+ * PLLD_DSI1 is an integer divider and its rate selection will
+ * never round up.
+ */
+ adjusted_mode->clock = pixel_clock_hz / 1000 + 1;
+
+ /* Given the new pixel clock, adjust HFP to keep vrefresh the same. */
+ adjusted_mode->htotal = pixel_clock_hz / (mode->vrefresh * mode->vtotal);
+ adjusted_mode->hsync_end += adjusted_mode->htotal - mode->htotal;
+ adjusted_mode->hsync_start += adjusted_mode->htotal - mode->htotal;
+
+ return true;
+}
+
static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
{
- struct drm_display_mode *mode = &encoder->crtc->mode;
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
struct vc4_dsi *dsi = vc4_encoder->dsi;
struct device *dev = &dsi->pdev->dev;
- u32 format = 0, divider = 0;
bool debug_dump_regs = false;
unsigned long hs_clock;
u32 ui_ns;
@@ -940,26 +995,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
vc4_dsi_dump_regs(dsi);
}
- switch (dsi->format) {
- case MIPI_DSI_FMT_RGB888:
- format = DSI_PFORMAT_RGB888;
- divider = 24 / dsi->lanes;
- break;
- case MIPI_DSI_FMT_RGB666:
- format = DSI_PFORMAT_RGB666;
- divider = 24 / dsi->lanes;
- break;
- case MIPI_DSI_FMT_RGB666_PACKED:
- format = DSI_PFORMAT_RGB666_PACKED;
- divider = 18 / dsi->lanes;
- break;
- case MIPI_DSI_FMT_RGB565:
- format = DSI_PFORMAT_RGB565;
- divider = 16 / dsi->lanes;
- break;
- }
-
- phy_clock = pixel_clock_hz * divider;
+ phy_clock = pixel_clock_hz * dsi->divider;
ret = clk_set_rate(dsi->pll_phy_clock, phy_clock);
if (ret) {
dev_err(&dsi->pdev->dev,
@@ -1134,8 +1170,9 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
DSI_PORT_WRITE(DISP0_CTRL,
- VC4_SET_FIELD(divider, DSI_DISP0_PIX_CLK_DIV) |
- VC4_SET_FIELD(format, DSI_DISP0_PFORMAT) |
+ VC4_SET_FIELD(dsi->divider,
+ DSI_DISP0_PIX_CLK_DIV) |
+ VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) |
VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME,
DSI_DISP0_LP_STOP_CTRL) |
DSI_DISP0_ST_END |
@@ -1347,9 +1384,31 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
dsi->lanes = device->lanes;
dsi->channel = device->channel;
- dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
+ switch (device->format) {
+ case MIPI_DSI_FMT_RGB888:
+ dsi->format = DSI_PFORMAT_RGB888;
+ dsi->divider = 24 / dsi->lanes;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ dsi->format = DSI_PFORMAT_RGB666;
+ dsi->divider = 24 / dsi->lanes;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ dsi->format = DSI_PFORMAT_RGB666_PACKED;
+ dsi->divider = 18 / dsi->lanes;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ dsi->format = DSI_PFORMAT_RGB565;
+ dsi->divider = 16 / dsi->lanes;
+ break;
+ default:
+ dev_err(&dsi->pdev->dev, "Unknown DSI format: %d.\n",
+ dsi->format);
+ return 0;
+ }
+
if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
dev_err(&dsi->pdev->dev,
"Only VIDEO mode panels supported currently.\n");
@@ -1397,6 +1456,7 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
.disable = vc4_dsi_encoder_disable,
.enable = vc4_dsi_encoder_enable,
+ .mode_fixup = vc4_dsi_encoder_mode_fixup,
};
static const struct of_device_id vc4_dsi_dt_match[] = {