aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2019-01-16 18:11:37 -0500
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2019-02-07 19:25:56 -0500
commit02f2b30032c12b1b91abe5f2bd0d74ba1f700ea1 (patch)
tree43d2758068f601af5999236e6f26ab6907ca8c8e /drivers/gpu
parent6e1f8557818f3c2476404ba1d4a5157b823b79f0 (diff)
drm: rcar-du: lvds: Add API to enable/disable clock output
On the D3 and E3 platforms, the LVDS internal PLL supplies the pixel clock to the DU. This works automatically for LVDS outputs as the LVDS encoder is enabled through the bridge API, enabling the internal PLL and clock output. However, when using the DU DPAD output with the LVDS outputs turned off, the LVDS PLL needs to be controlled manually. Add an API to do so, to be called by the DU driver. The drivers/gpu/drm/rcar-du/ directory has to be treated as obj-y unconditionally, as the LVDS driver could be built-in while the DU driver is compiled as a module. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig1
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.c77
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.h27
4 files changed, 96 insertions, 11 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f0c1f8731a27..1ac55c65eac0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -81,7 +81,7 @@ obj-$(CONFIG_DRM_UDL) += udl/
81obj-$(CONFIG_DRM_AST) += ast/ 81obj-$(CONFIG_DRM_AST) += ast/
82obj-$(CONFIG_DRM_ARMADA) += armada/ 82obj-$(CONFIG_DRM_ARMADA) += armada/
83obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ 83obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/
84obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ 84obj-y += rcar-du/
85obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 85obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
86obj-y += omapdrm/ 86obj-y += omapdrm/
87obj-$(CONFIG_DRM_SUN4I) += sun4i/ 87obj-$(CONFIG_DRM_SUN4I) += sun4i/
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 225141656e19..7c36e2777a15 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -4,6 +4,7 @@ config DRM_RCAR_DU
4 depends on DRM && OF 4 depends on DRM && OF
5 depends on ARM || ARM64 5 depends on ARM || ARM64
6 depends on ARCH_RENESAS || COMPILE_TEST 6 depends on ARCH_RENESAS || COMPILE_TEST
7 imply DRM_RCAR_LVDS
7 select DRM_KMS_HELPER 8 select DRM_KMS_HELPER
8 select DRM_KMS_CMA_HELPER 9 select DRM_KMS_CMA_HELPER
9 select DRM_GEM_CMA_HELPER 10 select DRM_GEM_CMA_HELPER
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
index a7789c5b3880..7ef97b2a6eda 100644
--- a/drivers/gpu/drm/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
@@ -23,6 +23,7 @@
23#include <drm/drm_panel.h> 23#include <drm/drm_panel.h>
24#include <drm/drm_probe_helper.h> 24#include <drm/drm_probe_helper.h>
25 25
26#include "rcar_lvds.h"
26#include "rcar_lvds_regs.h" 27#include "rcar_lvds_regs.h"
27 28
28struct rcar_lvds; 29struct rcar_lvds;
@@ -183,8 +184,9 @@ struct pll_info {
183 184
184static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, 185static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
185 unsigned long target, struct pll_info *pll, 186 unsigned long target, struct pll_info *pll,
186 u32 clksel) 187 u32 clksel, bool dot_clock_only)
187{ 188{
189 unsigned int div7 = dot_clock_only ? 1 : 7;
188 unsigned long output; 190 unsigned long output;
189 unsigned long fin; 191 unsigned long fin;
190 unsigned int m_min; 192 unsigned int m_min;
@@ -218,9 +220,9 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
218 * `------------> | | 220 * `------------> | |
219 * |/ 221 * |/
220 * 222 *
221 * The /7 divider is optional when the LVDS PLL is used to generate a 223 * The /7 divider is optional, it is enabled when the LVDS PLL is used
222 * dot clock for the DU RGB output, without using the LVDS encoder. We 224 * to drive the LVDS encoder, and disabled when used to generate a dot
223 * don't support this configuration yet. 225 * clock for the DU RGB output, without using the LVDS encoder.
224 * 226 *
225 * The PLL allowed input frequency range is 12 MHz to 192 MHz. 227 * The PLL allowed input frequency range is 12 MHz to 192 MHz.
226 */ 228 */
@@ -280,7 +282,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
280 * the PLL, followed by a an optional fixed /7 282 * the PLL, followed by a an optional fixed /7
281 * divider. 283 * divider.
282 */ 284 */
283 fout = fvco / (1 << e) / 7; 285 fout = fvco / (1 << e) / div7;
284 div = DIV_ROUND_CLOSEST(fout, target); 286 div = DIV_ROUND_CLOSEST(fout, target);
285 diff = abs(fout / div - target); 287 diff = abs(fout / div - target);
286 288
@@ -301,7 +303,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk,
301 303
302done: 304done:
303 output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) 305 output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e)
304 / 7 / pll->div; 306 / div7 / pll->div;
305 error = (long)(output - target) * 10000 / (long)target; 307 error = (long)(output - target) * 10000 / (long)target;
306 308
307 dev_dbg(lvds->dev, 309 dev_dbg(lvds->dev,
@@ -311,17 +313,18 @@ done:
311 pll->pll_m, pll->pll_n, pll->pll_e, pll->div); 313 pll->pll_m, pll->pll_n, pll->pll_e, pll->div);
312} 314}
313 315
314static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) 316static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds,
317 unsigned int freq, bool dot_clock_only)
315{ 318{
316 struct pll_info pll = { .diff = (unsigned long)-1 }; 319 struct pll_info pll = { .diff = (unsigned long)-1 };
317 u32 lvdpllcr; 320 u32 lvdpllcr;
318 321
319 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, 322 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll,
320 LVDPLLCR_CKSEL_DU_DOTCLKIN(0)); 323 LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only);
321 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, 324 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll,
322 LVDPLLCR_CKSEL_DU_DOTCLKIN(1)); 325 LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only);
323 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, 326 rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll,
324 LVDPLLCR_CKSEL_EXTAL); 327 LVDPLLCR_CKSEL_EXTAL, dot_clock_only);
325 328
326 lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT 329 lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT
327 | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); 330 | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1);
@@ -330,6 +333,9 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq)
330 lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL 333 lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL
331 | LVDPLLCR_PLLE(pll.pll_e - 1); 334 | LVDPLLCR_PLLE(pll.pll_e - 1);
332 335
336 if (dot_clock_only)
337 lvdpllcr |= LVDPLLCR_OCKSEL;
338
333 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); 339 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
334 340
335 if (pll.div > 1) 341 if (pll.div > 1)
@@ -343,6 +349,57 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq)
343 rcar_lvds_write(lvds, LVDDIV, 0); 349 rcar_lvds_write(lvds, LVDDIV, 0);
344} 350}
345 351
352static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq)
353{
354 __rcar_lvds_pll_setup_d3_e3(lvds, freq, false);
355}
356
357/* -----------------------------------------------------------------------------
358 * Clock - D3/E3 only
359 */
360
361int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq)
362{
363 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
364 int ret;
365
366 if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
367 return -ENODEV;
368
369 dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq);
370
371 WARN_ON(lvds->enabled);
372
373 ret = clk_prepare_enable(lvds->clocks.mod);
374 if (ret < 0)
375 return ret;
376
377 __rcar_lvds_pll_setup_d3_e3(lvds, freq, true);
378
379 lvds->enabled = true;
380 return 0;
381}
382EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable);
383
384void rcar_lvds_clk_disable(struct drm_bridge *bridge)
385{
386 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
387
388 if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)))
389 return;
390
391 dev_dbg(lvds->dev, "disabling LVDS PLL\n");
392
393 WARN_ON(!lvds->enabled);
394
395 rcar_lvds_write(lvds, LVDPLLCR, 0);
396
397 clk_disable_unprepare(lvds->clocks.mod);
398
399 lvds->enabled = false;
400}
401EXPORT_SYMBOL_GPL(rcar_lvds_clk_disable);
402
346/* ----------------------------------------------------------------------------- 403/* -----------------------------------------------------------------------------
347 * Bridge 404 * Bridge
348 */ 405 */
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.h b/drivers/gpu/drm/rcar-du/rcar_lvds.h
new file mode 100644
index 000000000000..a709cae1bc32
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.h
@@ -0,0 +1,27 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * rcar_lvds.h -- R-Car LVDS Encoder
4 *
5 * Copyright (C) 2013-2018 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10#ifndef __RCAR_LVDS_H__
11#define __RCAR_LVDS_H__
12
13struct drm_bridge;
14
15#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
16int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq);
17void rcar_lvds_clk_disable(struct drm_bridge *bridge);
18#else
19static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge,
20 unsigned long freq)
21{
22 return -ENOSYS;
23}
24static inline void rcar_lvds_clk_disable(struct drm_bridge *bridge) { }
25#endif /* CONFIG_DRM_RCAR_LVDS */
26
27#endif /* __RCAR_LVDS_H__ */