blob: 37838a86d7fd1acfbd0acee4a9d7b954e6ef1fc2 [file] [log] [blame]
Stelian Pop39cf4802008-05-09 21:57:18 +02001/*
2 * Driver for AT91/AT32 LCD Controller
3 *
4 * Copyright (C) 2007 Atmel Corporation
5 *
Wolfgang Denk3765b3e2013-10-07 13:07:26 +02006 * SPDX-License-Identifier: GPL-2.0+
Stelian Pop39cf4802008-05-09 21:57:18 +02007 */
8
9#include <common.h>
Simon Glassd63ec262016-05-05 07:28:19 -060010#include <fdtdec.h>
Stelian Pop39cf4802008-05-09 21:57:18 +020011#include <asm/io.h>
Stelian Pop39cf4802008-05-09 21:57:18 +020012#include <asm/arch/gpio.h>
13#include <asm/arch/clk.h>
14#include <lcd.h>
Nikita Kiryanov0b29a892015-02-03 13:32:27 +020015#include <bmp_layout.h>
Stelian Pop39cf4802008-05-09 21:57:18 +020016#include <atmel_lcdc.h>
17
Stelian Pop39cf4802008-05-09 21:57:18 +020018/* configurable parameters */
19#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
20#define ATMEL_LCDC_DMA_BURST_LEN 8
Mark Jackson6bbced62009-06-29 15:59:10 +010021#ifndef ATMEL_LCDC_GUARD_TIME
22#define ATMEL_LCDC_GUARD_TIME 1
23#endif
Stelian Pop39cf4802008-05-09 21:57:18 +020024
Bo Shenc6941e12015-01-16 10:55:46 +080025#if defined(CONFIG_AT91SAM9263)
Stelian Pop39cf4802008-05-09 21:57:18 +020026#define ATMEL_LCDC_FIFO_SIZE 2048
27#else
28#define ATMEL_LCDC_FIFO_SIZE 512
29#endif
30
31#define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg))
32#define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg))
33
Nikita Kiryanov38b55082015-02-03 13:32:21 +020034ushort *configuration_get_cmap(void)
35{
36 return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
37}
38
Nikita Kiryanovb3d12e92015-02-03 13:32:22 +020039#if defined(CONFIG_BMP_16BPP) && defined(CONFIG_ATMEL_LCD_BGR555)
40void fb_put_word(uchar **fb, uchar **from)
41{
42 *(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03);
43 *(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2);
44 *from += 2;
45}
46#endif
47
Nikita Kiryanova02e9482015-02-03 13:32:24 +020048#ifdef CONFIG_LCD_LOGO
49#include <bmp_logo.h>
50void lcd_logo_set_cmap(void)
51{
52 int i;
53 uint lut_entry;
54 ushort colreg;
55 uint *cmap = (uint *)configuration_get_cmap();
56
57 for (i = 0; i < BMP_LOGO_COLORS; ++i) {
58 colreg = bmp_logo_palette[i];
59#ifdef CONFIG_ATMEL_LCD_BGR555
60 lut_entry = ((colreg & 0x000F) << 11) |
61 ((colreg & 0x00F0) << 2) |
62 ((colreg & 0x0F00) >> 7);
63#else
64 lut_entry = ((colreg & 0x000F) << 1) |
65 ((colreg & 0x00F0) << 3) |
66 ((colreg & 0x0F00) << 4);
67#endif
68 *(cmap + BMP_LOGO_OFFSET) = lut_entry;
69 cmap++;
70 }
71}
72#endif
73
Stelian Pop39cf4802008-05-09 21:57:18 +020074void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
75{
76#if defined(CONFIG_ATMEL_LCD_BGR555)
77 lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
78 (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7));
79#else
80 lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
81 (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8));
82#endif
83}
84
Simon Glass1c3dbe52015-05-13 07:02:27 -060085void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
Nikita Kiryanov0b29a892015-02-03 13:32:27 +020086{
87 int i;
88
89 for (i = 0; i < colors; ++i) {
Simon Glass1c3dbe52015-05-13 07:02:27 -060090 struct bmp_color_table_entry cte = bmp->color_table[i];
Nikita Kiryanov0b29a892015-02-03 13:32:27 +020091 lcd_setcolreg(i, cte.red, cte.green, cte.blue);
92 }
93}
94
Simon Glassd63ec262016-05-05 07:28:19 -060095static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
96 bool tft, bool cont_pol_low, ulong lcdbase)
Stelian Pop39cf4802008-05-09 21:57:18 +020097{
98 unsigned long value;
Simon Glassd63ec262016-05-05 07:28:19 -060099 void *reg = (void *)addr;
Stelian Pop39cf4802008-05-09 21:57:18 +0200100
101 /* Turn off the LCD controller and the DMA controller */
Simon Glassd63ec262016-05-05 07:28:19 -0600102 lcdc_writel(reg, ATMEL_LCDC_PWRCON,
Mark Jackson6bbced62009-06-29 15:59:10 +0100103 ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
Stelian Pop39cf4802008-05-09 21:57:18 +0200104
105 /* Wait for the LCDC core to become idle */
Simon Glassd63ec262016-05-05 07:28:19 -0600106 while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
Stelian Pop39cf4802008-05-09 21:57:18 +0200107 udelay(10);
108
Simon Glassd63ec262016-05-05 07:28:19 -0600109 lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
Stelian Pop39cf4802008-05-09 21:57:18 +0200110
111 /* Reset LCDC DMA */
Simon Glassd63ec262016-05-05 07:28:19 -0600112 lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
Stelian Pop39cf4802008-05-09 21:57:18 +0200113
114 /* ...set frame size and burst length = 8 words (?) */
Simon Glassd63ec262016-05-05 07:28:19 -0600115 value = (timing->hactive.typ * timing->vactive.typ *
116 (1 << bpix)) / 32;
Stelian Pop39cf4802008-05-09 21:57:18 +0200117 value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
Simon Glassd63ec262016-05-05 07:28:19 -0600118 lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200119
120 /* Set pixel clock */
Simon Glassd63ec262016-05-05 07:28:19 -0600121 value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
122 if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
Stelian Pop39cf4802008-05-09 21:57:18 +0200123 value++;
124 value = (value / 2) - 1;
125
126 if (!value) {
Simon Glassd63ec262016-05-05 07:28:19 -0600127 lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
Stelian Pop39cf4802008-05-09 21:57:18 +0200128 } else
Simon Glassd63ec262016-05-05 07:28:19 -0600129 lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
Stelian Pop39cf4802008-05-09 21:57:18 +0200130 value << ATMEL_LCDC_CLKVAL_OFFSET);
131
132 /* Initialize control register 2 */
Stefan Roesef2302d42008-08-06 14:05:38 +0200133#ifdef CONFIG_AVR32
134 value = ATMEL_LCDC_MEMOR_BIG | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
135#else
Stelian Pop39cf4802008-05-09 21:57:18 +0200136 value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
Stefan Roesef2302d42008-08-06 14:05:38 +0200137#endif
Simon Glassd63ec262016-05-05 07:28:19 -0600138 if (tft)
Stelian Pop39cf4802008-05-09 21:57:18 +0200139 value |= ATMEL_LCDC_DISTYPE_TFT;
140
Simon Glassd63ec262016-05-05 07:28:19 -0600141 if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
142 value |= ATMEL_LCDC_INVLINE_INVERTED;
143 if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
144 value |= ATMEL_LCDC_INVFRAME_INVERTED;
145 value |= bpix << 5;
146 lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200147
148 /* Vertical timing */
Simon Glassd63ec262016-05-05 07:28:19 -0600149 value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
150 value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
151 value |= timing->vfront_porch.typ;
152 /* Magic! (Datasheet says "Bit 31 must be written to 1") */
153 value |= 1U << 31;
154 lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200155
156 /* Horizontal timing */
Simon Glassd63ec262016-05-05 07:28:19 -0600157 value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
158 value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
159 value |= (timing->hback_porch.typ - 1);
160 lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200161
162 /* Display size */
Simon Glassd63ec262016-05-05 07:28:19 -0600163 value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
164 value |= timing->vactive.typ - 1;
165 lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200166
167 /* FIFO Threshold: Use formula from data sheet */
168 value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
Simon Glassd63ec262016-05-05 07:28:19 -0600169 lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
Stelian Pop39cf4802008-05-09 21:57:18 +0200170
171 /* Toggle LCD_MODE every frame */
Simon Glassd63ec262016-05-05 07:28:19 -0600172 lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
Stelian Pop39cf4802008-05-09 21:57:18 +0200173
174 /* Disable all interrupts */
Simon Glassd63ec262016-05-05 07:28:19 -0600175 lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
Stelian Pop39cf4802008-05-09 21:57:18 +0200176
177 /* Set contrast */
178 value = ATMEL_LCDC_PS_DIV8 |
Stelian Pop39cf4802008-05-09 21:57:18 +0200179 ATMEL_LCDC_ENA_PWMENABLE;
Simon Glassd63ec262016-05-05 07:28:19 -0600180 if (!cont_pol_low)
Alexander Steincdfcedb2010-07-20 08:55:40 +0200181 value |= ATMEL_LCDC_POL_POSITIVE;
Simon Glassd63ec262016-05-05 07:28:19 -0600182 lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
183 lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
Stelian Pop39cf4802008-05-09 21:57:18 +0200184
185 /* Set framebuffer DMA base address and pixel offset */
Simon Glassd63ec262016-05-05 07:28:19 -0600186 lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
Stelian Pop39cf4802008-05-09 21:57:18 +0200187
Simon Glassd63ec262016-05-05 07:28:19 -0600188 lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
189 lcdc_writel(reg, ATMEL_LCDC_PWRCON,
Mark Jackson6bbced62009-06-29 15:59:10 +0100190 (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
Stelian Pop39cf4802008-05-09 21:57:18 +0200191}
192
Simon Glassd63ec262016-05-05 07:28:19 -0600193void lcd_ctrl_init(void *lcdbase)
194{
195 struct display_timing timing;
196
197 timing.flags = 0;
198 if (!(panel_info.vl_sync & ATMEL_LCDC_INVLINE_INVERTED))
199 timing.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
200 if (!(panel_info.vl_sync & ATMEL_LCDC_INVFRAME_INVERTED))
201 timing.flags |= DISPLAY_FLAGS_VSYNC_LOW;
202 timing.pixelclock.typ = panel_info.vl_clk;
203
204 timing.hactive.typ = panel_info.vl_col;
205 timing.hfront_porch.typ = panel_info.vl_right_margin;
206 timing.hback_porch.typ = panel_info.vl_left_margin;
207 timing.hsync_len.typ = panel_info.vl_hsync_len;
208
209 timing.vactive.typ = panel_info.vl_row;
210 timing.vfront_porch.typ = panel_info.vl_clk;
211 timing.vback_porch.typ = panel_info.vl_clk;
212 timing.vsync_len.typ = panel_info.vl_clk;
213
214 atmel_fb_init(panel_info.mmio, &timing, panel_info.vl_bpix,
215 panel_info.vl_tft, panel_info.vl_cont_pol_low,
216 (ulong)lcdbase);
217}
218
Stelian Pop39cf4802008-05-09 21:57:18 +0200219ulong calc_fbsize(void)
220{
221 return ((panel_info.vl_col * panel_info.vl_row *
222 NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE;
223}