arch/arm/src/am335x/am335x_lcdc.c: Add logic to get/set the module reference clock. Still one more clocking interface to to.

This commit is contained in:
Gregory Nutt 2019-07-08 14:52:32 -06:00
parent e16c73957d
commit 6991ac37fe

View File

@ -56,13 +56,22 @@
#include <nuttx/video/fb.h>
#include "up_arch.h"
#include "hardware/am335x_cm.h"
#include "am335x_pinmux.h"
#include "am335x_config.h"
#include "am335x_gpio.h"
#include "am335x_sysclk.h"
#include "am335x_lcdc.h"
#include <arch/board/board.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DPLL_MAX_MUL 0x800
#define DPLL_MAX_DIV 0x80
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@ -101,7 +110,7 @@ static int am335x_setcursor(FAR struct fb_vtable_s *vtable,
/* Miscellaneous internal functions */
static int am335x_lcd_interrupt(int irq, void *context, void *arg);
static uint32_t am335x_lcd_divisor(uint32_t reference, uint32_t freq);
static uint32_t am335x_lcd_divisor(uint32_t reference, uint32_t frequency);
/****************************************************************************
* Private Data
@ -325,21 +334,21 @@ done:
* Name: am335x_lcd_divisor
****************************************************************************/
static uint32_t am335x_lcd_divisor(uint32_t reference, uint32_t freq)
static uint32_t am335x_lcd_divisor(uint32_t reference, uint32_t frequency)
{
uint32_t div;
uint32_t delta;
uint32_t mindelta;
int i;
mindelta = freq;
mindelta = frequency;
div = 255;
/* Raster mode case: divisors are in range from 2 to 255 */
for (i = 2; i < 255; i++)
{
delta = reference / i - freq;
delta = reference / i - frequency;
if (delta < 0)
{
delta = -delta;
@ -355,6 +364,103 @@ static uint32_t am335x_lcd_divisor(uint32_t reference, uint32_t freq)
return div;
}
/****************************************************************************
* Name: am335x_set_refclk
****************************************************************************/
static int am335x_set_refclk(uint32_t frequency)
{
uint32_t sysclk;
uint32_t mul;
uint32_t div;
uint32_t delta;
uint32_t mindelta;
int timeout;
int i;
int j;
sysclk = am335x_get_sysclk();
/* Bypass mode */
putreg32(AM335X_CM_WKUP_CLKMODE_DPLL_DISP, 0x4);
/* Make sure it's in bypass mode */
while ((getreg32(AM335X_CM_WKUP_IDLEST_DPLL_DISP) & (1 << 8)) == 0)
{
up_udelay(10);
}
/* Dumb and non-optimal implementation */
mul = 0;
div = 0;
mindelta = frequency;
for (i = 1; i < DPLL_MAX_MUL; i++)
{
for (j = 1; j < DPLL_MAX_DIV; j++)
{
delta = frequency - i * (sysclk / j);
if (delta < 0)
{
delta = -delta;
}
if (delta < mindelta)
{
mul = i;
div = j;
mindelta = delta;
}
if (mindelta == 0)
{
break;
}
}
}
putreg32(AM335X_CM_WKUP_CLKSEL_DPLL_DISP, (mul << 8) | (div - 1));
/* Locked mode */
putreg32(AM335X_CM_WKUP_CLKMODE_DPLL_DISP, 0x7);
timeout = 10000;
while ((getreg32(AM335X_CM_WKUP_IDLEST_DPLL_DISP) & (1 << 0)) == 0 &&
timeout-- > 0)
{
up_udelay(10);
}
return timeout > 0 ? OK : -ETIMEDOUT;
}
/****************************************************************************
* Name: am335x_get_refclk
****************************************************************************/
static int am335x_get_refclk(uint32_t *frequency)
{
uint32_t regval;
uint32_t sysclk;
regval = getreg32(AM335X_CM_WKUP_CLKMODE_DPLL_DISP);
/* Check if we are running in bypass */
if ((regval & (1 << 23)) != 0)
{
return -ENXIO;
}
sysclk = am335x_get_sysclk();
*frequency = ((regval >> 8) & 0x7ff) * (sysclk / ((regval & 0x7f) + 1));
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -494,7 +600,12 @@ int am335x_lcd_initialize(FAR const struct am335x_panel_info_s *panel)
lcderr("ERROR: Failed to attach LCDC interrupt.");
}
/* Enable clocking to the LCD peripheral */
/* Enable clocking to the LCD peripheral. Set the initial reference clock
* to twice the VGA pixel clock for now.
*/
(void)am335x_set_refclk(2 * 25175000);
/* REVISIT: Need to (1) set the initial pixel clock and (2) set
* LCDC related bits in PRCM to enable clocking to the LCDC.
* Reference: http://fxr.watson.org/fxr/source/arm/ti/am335x/am335x_prcm.c#L819
@ -506,30 +617,24 @@ int am335x_lcd_initialize(FAR const struct am335x_panel_info_s *panel)
#endif
#warning Missing logic
/* Adjust clock to get double of requested pixel clock frequency
/* Adjust reference clock to get double of requested pixel clock frequency
* HDMI/DVI displays are very sensitive to error in frequency value.
* Reference: http://fxr.watson.org/fxr/source/arm/ti/am335x/am335x_prcm.c#L718
*/
#warning Missing logic
#if 0 /* FIXEME */
if (ti_prcm_clk_set_source_freq(LCDC_CLK, priv->panel.pixclk * 2))
#endif
ret = am335x_set_refclk(2 * priv->panel.pixclk);
if (ret < 0)
{
lcderr("ERROR: Can't set source frequency\n");
return -ENXIO;
return ret;
}
/* Read back the actual reference clock frequency */
/* Reference: http://fxr.watson.org/fxr/source/arm/ti/am335x/am335x_prcm.c#L701 */
#warning Missing logic
#if 0 /* FIXEME */
if (ti_prcm_clk_get_source_freq(LCDC_CLK, &reffreq))
#endif
ret = am335x_get_refclk(&reffreq);
if (ret < 0)
{
lcderr("ERROR: Can't get reference frequency\n");
return -ENXIO;
return ret;
}
/* Panel initialization */