aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorDarren Etheridge <detheridge@ti.com>2013-08-05 18:02:36 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2013-08-09 07:02:43 -0400
commit2dfa77a2010fc65de131ef7f7454844df9182735 (patch)
tree5a6aceac93f0d55147a47a9e31716dbc77b6ea8a /drivers/video
parent2bdff0680f00ef4d2448b8d20d61a3554b368c24 (diff)
video: da8xx-fb: set upstream clock rate (if reqd)
Based on original patch by: Afzal Mohammed <afzal@ti.com> LCDC IP has a clock divider to adjust pixel clock, this limits pixel clock range to fck/255 - fck/2(fck - rate of input clock to LCDC IP). In the case of AM335x, where this IP is present, default fck is not sufficient to provide normal pixel clock rates, hence rendering this driver unusable on AM335x. If input clock too is configurable, allowable range of pixel clock would increase. Here initially it is checked whether with present fck, divider in IP could be configured to obtain required rate, if not, fck is adjusted. This makes it usable on AM335x. Note: Another solution would be to model an inherited basic clock divider of CCF, an advantage would be a better possible resolution for pixel clk. And trying to instantiate a CCF clock would mean that to be consistent, 3 bits being turned on to enable clocks of LCDC IP would have to be modeled as gate clocks. Now that would bring in a total of 4 clocks, including necessity to create a new inherited divider clock, and that mean a branch of clock tree would be present in LCDC driver. This would add complexity to LCDC driver bringing in considerable amount of clock handling code, and this would not bring in much advantage for existing use cases other than providing a higher resolution of pixel clock. And existing use cases work without relying on clock modeling. Another fact is that out of the two platform's using this driver DaVinci is not yet converted to CCF. In future if higher resolution of pixel clock is required, and probably after DaVinci is CCF'ed, modeling clock nodes inside driver may be considered. v2: purely cosmetic changes to try and clarify what variables are being used for in the clock rate / divider calculations Signed-off-by: Darren Etheridge <detheridge@ti.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/da8xx-fb.c85
1 files changed, 65 insertions, 20 deletions
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index d8b295ab85f5..dfe757254e26 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -132,6 +132,9 @@
132#define WSI_TIMEOUT 50 132#define WSI_TIMEOUT 50
133#define PALETTE_SIZE 256 133#define PALETTE_SIZE 256
134 134
135#define CLK_MIN_DIV 2
136#define CLK_MAX_DIV 255
137
135static void __iomem *da8xx_fb_reg_base; 138static void __iomem *da8xx_fb_reg_base;
136static unsigned int lcd_revision; 139static unsigned int lcd_revision;
137static irq_handler_t lcdc_irq_handler; 140static irq_handler_t lcdc_irq_handler;
@@ -684,38 +687,76 @@ static void da8xx_fb_lcd_reset(void)
684 } 687 }
685} 688}
686 689
687static inline unsigned da8xx_fb_calc_clk_divider(struct da8xx_fb_par *par, 690static int da8xx_fb_config_clk_divider(struct da8xx_fb_par *par,
688 unsigned pixclock) 691 unsigned lcdc_clk_div,
689{ 692 unsigned lcdc_clk_rate)
690 return par->lcd_fck_rate / (PICOS2KHZ(pixclock) * 1000);
691}
692
693static inline unsigned da8xx_fb_round_clk(struct da8xx_fb_par *par,
694 unsigned pixclock)
695{ 693{
696 unsigned div; 694 int ret;
697 695
698 div = da8xx_fb_calc_clk_divider(par, pixclock); 696 if (par->lcd_fck_rate != lcdc_clk_rate) {
699 return KHZ2PICOS(par->lcd_fck_rate / (1000 * div)); 697 ret = clk_set_rate(par->lcdc_clk, lcdc_clk_rate);
700} 698 if (IS_ERR_VALUE(ret)) {
699 dev_err(par->dev,
700 "unable to set clock rate at %u\n",
701 lcdc_clk_rate);
702 return ret;
703 }
704 par->lcd_fck_rate = clk_get_rate(par->lcdc_clk);
705 }
701 706
702static inline void da8xx_fb_config_clk_divider(unsigned div)
703{
704 /* Configure the LCD clock divisor. */ 707 /* Configure the LCD clock divisor. */
705 lcdc_write(LCD_CLK_DIVISOR(div) | 708 lcdc_write(LCD_CLK_DIVISOR(lcdc_clk_div) |
706 (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); 709 (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
707 710
708 if (lcd_revision == LCD_VERSION_2) 711 if (lcd_revision == LCD_VERSION_2)
709 lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN | 712 lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN |
710 LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG); 713 LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG);
714
715 return 0;
716}
717
718static unsigned int da8xx_fb_calc_clk_divider(struct da8xx_fb_par *par,
719 unsigned pixclock,
720 unsigned *lcdc_clk_rate)
721{
722 unsigned lcdc_clk_div;
723
724 pixclock = PICOS2KHZ(pixclock) * 1000;
725
726 *lcdc_clk_rate = par->lcd_fck_rate;
727
728 if (pixclock < (*lcdc_clk_rate / CLK_MAX_DIV)) {
729 *lcdc_clk_rate = clk_round_rate(par->lcdc_clk,
730 pixclock * CLK_MAX_DIV);
731 lcdc_clk_div = CLK_MAX_DIV;
732 } else if (pixclock > (*lcdc_clk_rate / CLK_MIN_DIV)) {
733 *lcdc_clk_rate = clk_round_rate(par->lcdc_clk,
734 pixclock * CLK_MIN_DIV);
735 lcdc_clk_div = CLK_MIN_DIV;
736 } else {
737 lcdc_clk_div = *lcdc_clk_rate / pixclock;
738 }
739
740 return lcdc_clk_div;
711} 741}
712 742
713static inline void da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par, 743static int da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par,
714 struct fb_videomode *mode) 744 struct fb_videomode *mode)
715{ 745{
716 unsigned div = da8xx_fb_calc_clk_divider(par, mode->pixclock); 746 unsigned lcdc_clk_rate;
747 unsigned lcdc_clk_div = da8xx_fb_calc_clk_divider(par, mode->pixclock,
748 &lcdc_clk_rate);
717 749
718 da8xx_fb_config_clk_divider(div); 750 return da8xx_fb_config_clk_divider(par, lcdc_clk_div, lcdc_clk_rate);
751}
752
753static inline unsigned da8xx_fb_round_clk(struct da8xx_fb_par *par,
754 unsigned pixclock)
755{
756 unsigned lcdc_clk_div, lcdc_clk_rate;
757
758 lcdc_clk_div = da8xx_fb_calc_clk_divider(par, pixclock, &lcdc_clk_rate);
759 return KHZ2PICOS(lcdc_clk_rate / (1000 * lcdc_clk_div));
719} 760}
720 761
721static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, 762static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
@@ -724,7 +765,11 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
724 u32 bpp; 765 u32 bpp;
725 int ret = 0; 766 int ret = 0;
726 767
727 da8xx_fb_calc_config_clk_divider(par, panel); 768 ret = da8xx_fb_calc_config_clk_divider(par, panel);
769 if (IS_ERR_VALUE(ret)) {
770 dev_err(par->dev, "unable to configure clock\n");
771 return ret;
772 }
728 773
729 if (panel->sync & FB_SYNC_CLK_INVERT) 774 if (panel->sync & FB_SYNC_CLK_INVERT)
730 lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) | 775 lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) |