diff options
| -rw-r--r-- | drivers/gpu/drm/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds.c | 77 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds.h | 27 |
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/ | |||
| 81 | obj-$(CONFIG_DRM_AST) += ast/ | 81 | obj-$(CONFIG_DRM_AST) += ast/ |
| 82 | obj-$(CONFIG_DRM_ARMADA) += armada/ | 82 | obj-$(CONFIG_DRM_ARMADA) += armada/ |
| 83 | obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ | 83 | obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ |
| 84 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ | 84 | obj-y += rcar-du/ |
| 85 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | 85 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
| 86 | obj-y += omapdrm/ | 86 | obj-y += omapdrm/ |
| 87 | obj-$(CONFIG_DRM_SUN4I) += sun4i/ | 87 | obj-$(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 | ||
| 28 | struct rcar_lvds; | 29 | struct rcar_lvds; |
| @@ -183,8 +184,9 @@ struct pll_info { | |||
| 183 | 184 | ||
| 184 | static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, | 185 | static 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 | ||
| 302 | done: | 304 | done: |
| 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 | ||
| 314 | static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) | 316 | static 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 | ||
| 352 | static 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 | |||
| 361 | int 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 | } | ||
| 382 | EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable); | ||
| 383 | |||
| 384 | void 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 | } | ||
| 401 | EXPORT_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 | |||
| 13 | struct drm_bridge; | ||
| 14 | |||
| 15 | #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) | ||
| 16 | int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq); | ||
| 17 | void rcar_lvds_clk_disable(struct drm_bridge *bridge); | ||
| 18 | #else | ||
| 19 | static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge, | ||
| 20 | unsigned long freq) | ||
| 21 | { | ||
| 22 | return -ENOSYS; | ||
| 23 | } | ||
| 24 | static inline void rcar_lvds_clk_disable(struct drm_bridge *bridge) { } | ||
| 25 | #endif /* CONFIG_DRM_RCAR_LVDS */ | ||
| 26 | |||
| 27 | #endif /* __RCAR_LVDS_H__ */ | ||
