diff options
| -rw-r--r-- | drivers/video/da8xx-fb.c | 85 |
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 | |||
| 135 | static void __iomem *da8xx_fb_reg_base; | 138 | static void __iomem *da8xx_fb_reg_base; |
| 136 | static unsigned int lcd_revision; | 139 | static unsigned int lcd_revision; |
| 137 | static irq_handler_t lcdc_irq_handler; | 140 | static irq_handler_t lcdc_irq_handler; |
| @@ -684,38 +687,76 @@ static void da8xx_fb_lcd_reset(void) | |||
| 684 | } | 687 | } |
| 685 | } | 688 | } |
| 686 | 689 | ||
| 687 | static inline unsigned da8xx_fb_calc_clk_divider(struct da8xx_fb_par *par, | 690 | static 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 | |||
| 693 | static 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 | ||
| 702 | static 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 | |||
| 718 | static 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 | ||
| 713 | static inline void da8xx_fb_calc_config_clk_divider(struct da8xx_fb_par *par, | 743 | static 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 | |||
| 753 | static 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 | ||
| 721 | static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, | 762 | static 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) | |
