@@ -36,6 +36,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/lcm.h>
+#include <linux/clk-provider.h>
#include <video/of_display_timing.h>
#include <video/da8xx-fb.h>
#include <asm/div64.h>
@@ -133,6 +134,10 @@
#define WSI_TIMEOUT 50
#define PALETTE_SIZE 256
+#define LCD_CLK_SHIFT 8
+#define LCD_CLK_WIDTH 8
+#define LCD_CLK_MIN_DIV 2
+
static void __iomem *da8xx_fb_reg_base;
static struct resource *lcdc_regs;
static unsigned int lcd_revision;
@@ -181,6 +186,9 @@ struct da8xx_fb_par {
u32 pseudo_palette[16];
struct fb_videomode mode;
struct lcd_ctrl_config cfg;
+#ifdef CONFIG_COMMON_CLK
+ struct clk *child_clk;
+#endif
};
static struct fb_var_screeninfo da8xx_fb_var;
@@ -689,6 +697,19 @@ static inline unsigned da8xx_fb_calc_clk_divider(struct da8xx_fb_par *par,
return par->lcd_fck_rate / (PICOS2KHZ(pixclock) * 1000);
}
+#ifdef CONFIG_COMMON_CLK
+static inline unsigned da8xx_fb_round_clk(struct da8xx_fb_par *par,
+ unsigned pixclock)
+{
+ unsigned long rate;
+
+ rate = PICOS2KHZ(pixclock) * 1000;
+ rate = clk_round_rate(par->child_clk, rate);
+ rate = KHZ2PICOS(rate / 1000);
+
+ return rate;
+}
+#else
static inline unsigned da8xx_fb_round_clk(struct da8xx_fb_par *par,
unsigned pixclock)
{
@@ -697,25 +718,49 @@ static inline unsigned da8xx_fb_round_clk(struct da8xx_fb_par *par,
div = da8xx_fb_calc_clk_divider(par, pixclock);
return KHZ2PICOS(par->lcd_fck_rate / (1000 * div));
}
+#endif
static inline void da8xx_fb_config_clk_divider(unsigned div)
{
/* Configure the LCD clock divisor. */
lcdc_write(LCD_CLK_DIVISOR(div) |
(LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
+}
+static inline void da8xx_fb_clkc_enable(void)
+{
if (lcd_revision == LCD_VERSION_2)
lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN |
LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG);
}
-static inline void da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par,
+#ifdef CONFIG_COMMON_CLK
+static inline int da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par,
+ struct fb_videomode *mode)
+{
+ int ret;
+
+ ret = clk_set_rate(par->child_clk, PICOS2KHZ(mode->pixclock) * 1000);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(par->dev, "unable to setup pixel clock of %u ps",
+ mode->pixclock);
+ return ret;
+ }
+ da8xx_fb_clkc_enable();
+ return 0;
+}
+#else
+static inline int da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par,
struct fb_videomode *mode)
{
unsigned div = da8xx_fb_calc_clk_divider(par, mode->pixclock);
da8xx_fb_config_clk_divider(div);
+ da8xx_fb_clkc_enable();
+
+ return 0;
}
+#endif
static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
struct fb_videomode *panel)
@@ -723,7 +768,9 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
u32 bpp;
int ret = 0;
- da8xx_fb_calc_config_clk_divider(par, panel);
+ ret = da8xx_fb_calc_config_clk_divider(par, panel);
+ if (IS_ERR_VALUE(ret))
+ return ret;
if (panel->sync & FB_SYNC_CLK_INVERT)
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) |
@@ -1406,6 +1453,22 @@ static int fb_probe(struct platform_device *device)
da8xx_fb_lcd_reset();
+#ifdef CONFIG_COMMON_CLK
+ lcdc_write(LCD_RASTER_MODE | LCD_CLK_DIVISOR(0x2), LCD_CTRL_REG);
+ par->child_clk = clk_register_divider(NULL, "da8xx_fb_clk",
+ __clk_get_name(fb_clk),
+ CLK_SET_RATE_PARENT,
+ da8xx_fb_reg_base + LCD_CTRL_REG,
+ LCD_CLK_SHIFT, LCD_CLK_WIDTH,
+ LCD_CLK_MIN_DIV,
+ CLK_DIVIDER_ONE_BASED, NULL);
+ if (IS_ERR(par->child_clk)) {
+ dev_err(&device->dev, "error registering clk\n");
+ ret = -ENODEV;
+ goto err_release_fb;
+ }
+#endif
+
/* allocate frame buffer */
par->vram_size = lcdc_info->xres * lcdc_info->yres * lcd_cfg->bpp;
ulcm = lcm((lcdc_info->xres * lcd_cfg->bpp)/8, PAGE_SIZE);
Common clock framework provides a basic clock divider. Make use of it to handle clock configuration in the LCDC IP, wherever applicable; out of two platforms having this IP, only am335x is converted to use CCF, DaVinci is not yet converted. Hence wrap the modification such that it will come into effect only if CCF is selected, otherwise, prgram dividers as earlier. Once DaVinci is converted to use CCF, this ifdef'ery can be removed. Divider clock instantiated is made as a one that allows the rate propogation to it's parent, that provides more options w.r.t pixel clock rates that could be configured. Signed-off-by: Afzal Mohammed <afzal@ti.com> --- v3: model CCF clock divider with parent propogation if CCF selected v2: new patch drivers/video/da8xx-fb.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-)