summaryrefslogtreecommitdiff
path: root/firmware/target/arm/rk27xx/lcdif-rk27xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/rk27xx/lcdif-rk27xx.c')
-rw-r--r--firmware/target/arm/rk27xx/lcdif-rk27xx.c252
1 files changed, 178 insertions, 74 deletions
diff --git a/firmware/target/arm/rk27xx/lcdif-rk27xx.c b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
index affc49b213..01b19603fd 100644
--- a/firmware/target/arm/rk27xx/lcdif-rk27xx.c
+++ b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
@@ -5,9 +5,9 @@
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$
9 * 8 *
10 * Copyright (C) 2011 Marcin Bukat 9 * Copyright (C) 2011 Marcin Bukat
10 * Copyright (C) 2012 Andrew Ryabinin
11 * 11 *
12 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -25,9 +25,28 @@
25#include "system.h" 25#include "system.h"
26#include "cpu.h" 26#include "cpu.h"
27#include "lcdif-rk27xx.h" 27#include "lcdif-rk27xx.h"
28#include "lcd-target.h"
29#include <string.h>
28 30
31/* dwdma linked list struct - hw defined
32 * we don't use __attribute__((packed)) just because
33 * all fields are 32bits and so are aligned
34 */
35struct llp_t {
36 uint32_t sar;
37 uint32_t dar;
38 struct llp_t *llp;
39 uint32_t ctl_l;
40 uint32_t ctl_h;
41 uint32_t dstat;
42};
43
44/* array of structs which describes full screen update
45 * each struct describes dma transfer for single line
46 */
47static struct llp_t scr_llp[LCD_HEIGHT];
29 48
30unsigned int lcd_data_transform(unsigned int data) 49static uint32_t lcd_data_transform(uint32_t data)
31{ 50{
32 unsigned int r, g, b; 51 unsigned int r, g, b;
33 52
@@ -49,38 +68,10 @@ unsigned int lcd_data_transform(unsigned int data)
49 return (r | g | b); 68 return (r | g | b);
50} 69}
51 70
52void lcd_cmd(unsigned int cmd)
53{
54 LCD_COMMAND = lcd_data_transform(cmd);
55}
56
57void lcd_data(unsigned int data)
58{
59 LCD_DATA = lcd_data_transform(data);
60}
61
62void lcd_write_reg(unsigned int reg, unsigned int val)
63{
64 lcd_cmd(reg);
65 lcd_data(val);
66}
67
68static void lcdctrl_bypass(unsigned int on_off)
69{
70 while (!(LCDC_STA & LCDC_MCU_IDLE));
71
72 if (on_off)
73 MCU_CTRL |= MCU_CTRL_BYPASS;
74 else
75 MCU_CTRL &= ~MCU_CTRL_BYPASS;
76}
77
78/* This part is unclear. I am unable to use buffered/FIFO based writes
79 * to lcd. Depending on settings of IF I get various patterns on display
80 * but not what I want to display apparently.
81 */
82static void lcdctrl_init(void) 71static void lcdctrl_init(void)
83{ 72{
73 int i;
74
84 /* alpha b111 75 /* alpha b111
85 * stop at current frame complete 76 * stop at current frame complete
86 * MCU mode 77 * MCU mode
@@ -89,37 +80,43 @@ static void lcdctrl_init(void)
89 LCDC_CTRL = ALPHA(7) | LCDC_STOP | LCDC_MCU | RGB24B; 80 LCDC_CTRL = ALPHA(7) | LCDC_STOP | LCDC_MCU | RGB24B;
90 MCU_CTRL = ALPHA_BASE(0x3f) | MCU_CTRL_BYPASS; 81 MCU_CTRL = ALPHA_BASE(0x3f) | MCU_CTRL_BYPASS;
91 82
92 HOR_ACT = LCD_WIDTH + 3; /* define horizonatal active region */ 83 /* Warning: datasheet addresses and OF addresses
93 VERT_ACT = LCD_HEIGHT; /* define vertical active region */ 84 * don't match for HOR_ACT and VERT_ACT
94 VERT_PERIOD = 0xfff; /* CSn/WEn/RDn signal timings */ 85 */
95 86 HOR_ACT = LCD_WIDTH + 3; /* define horizonatal active region */
96 LINE0_YADDR = LINE_ALPHA_EN | 0x7fe; 87 VERT_ACT = LCD_HEIGHT; /* define vertical active region */
97 LINE1_YADDR = LINE_ALPHA_EN | ((1 * LCD_WIDTH) - 2); 88 VERT_PERIOD = (1<<7)|(1<<5)|1; /* CSn/WEn/RDn signal timings */
98 LINE2_YADDR = LINE_ALPHA_EN | ((2 * LCD_WIDTH) - 2);
99 LINE3_YADDR = LINE_ALPHA_EN | ((3 * LCD_WIDTH) - 2);
100 89
101 LINE0_UVADDR = 0x7fe + 1; 90 lcd_display_init();
102 LINE1_UVADDR = ((1 * LCD_WIDTH) - 2 + 1); 91 lcdctrl_bypass(0);
103 LINE2_UVADDR = ((2 * LCD_WIDTH) - 2 + 1);
104 LINE3_UVADDR = ((3 * LCD_WIDTH) - 2 + 1);
105 92
106#if 0 93 /* This lines define layout of data in lcdif internal buffer
94 * LINEx_UVADDR = LINEx_YADDR + 1
95 * buffer is organized as 2048 x 32bit
96 * we use RGB565 (16 bits per pixel) so we pack 2 pixels
97 * in every lcdbuffer mem cell
98 */
107 LINE0_YADDR = 0; 99 LINE0_YADDR = 0;
108 LINE1_YADDR = (1 * LCD_WIDTH); 100 LINE1_YADDR = 1 * LCD_WIDTH/2;
109 LINE2_YADDR = (2 * LCD_WIDTH); 101 LINE2_YADDR = 2 * LCD_WIDTH/2;
110 LINE3_YADDR = (3 * LCD_WIDTH); 102 LINE3_YADDR = 3 * LCD_WIDTH/2;
111 103
112 LINE0_UVADDR = 1; 104 LINE0_UVADDR = 1;
113 LINE1_UVADDR = (1 * LCD_WIDTH) + 1; 105 LINE1_UVADDR = (1 * LCD_WIDTH/2) + 1;
114 LINE2_UVADDR = (2 * LCD_WIDTH) + 1; 106 LINE2_UVADDR = (2 * LCD_WIDTH/2) + 1;
115 LINE3_UVADDR = (3 * LCD_WIDTH) + 1; 107 LINE3_UVADDR = (3 * LCD_WIDTH/2) + 1;
116 108
117 START_X = 0; 109 LCDC_INTR_MASK = INTR_MASK_EVENLINE;
118 START_Y = 0; 110
119 DELTA_X = 0x200; /* no scaling */ 111 /* This loop seems to fix strange glitch where
120 DELTA_Y = 0x200; /* no scaling */ 112 * whole lcd content was shifted ~4 lines verticaly
121#endif 113 * on second lcd_update call
122 LCDC_INTR_MASK = INTR_MASK_LINE; /* INTR_MASK_EVENLINE; */ 114 */
115 for (i=0; i<2048; i++)
116 *((uint32_t *)LCD_BUFF + i) = 0;
117
118 /* Setup buffered writes to lcd controler */
119 MCU_CTRL = MCU_CTRL_RS_HIGH|MCU_CTRL_BUFF_WRITE|MCU_CTRL_BUFF_START;
123} 120}
124 121
125/* configure pins to drive lcd in 18bit mode (16bit mode for HiFiMAN's) */ 122/* configure pins to drive lcd in 18bit mode (16bit mode for HiFiMAN's) */
@@ -138,26 +135,133 @@ static void iomux_lcd(enum lcdif_mode_t mode)
138 SCU_IOMUXB_CON |= IOMUX_LCD_D815; 135 SCU_IOMUXB_CON |= IOMUX_LCD_D815;
139} 136}
140 137
141void lcdif_init(enum lcdif_mode_t mode) 138static void dwdma_init(void)
142{ 139{
143 iomux_lcd(mode); /* setup pins for lcd interface */ 140 DWDMA_DMA_CHEN = 0xf00;
144 lcdctrl_init(); /* basic lcdc module configuration */ 141 DWDMA_CLEAR_BLOCK = 0x0f;
145 lcdctrl_bypass(1); /* run in bypass mode - all writes goes directly to lcd controller */ 142 DWDMA_DMA_CFG = 1; /* global enable */
146} 143}
147 144
148/* This is ugly hack. We drive lcd in bypass mode 145static void llp_setup(void *src, void *dst, struct llp_t *llp, uint32_t size)
149 * where all writes goes directly to lcd controller. 146{
150 * This is suboptimal at best. IF module povides 147 llp->sar = (uint32_t)src;
151 * FIFO, internal sram buffer, hardware scaller, 148 llp->dar = (uint32_t)dst;
152 * DMA signals, hardware alpha blending and more. 149 llp->llp = llp + 1;
153 * BUT the fact is that I have no idea how to use 150 llp->ctl_h = size;
154 * this modes. Datasheet floating around is very 151 llp->ctl_l = (1<<20) |
155 * unclean in this regard and OF uses ackward 152 (1<<23) |
156 * lcd update routines which are hard to understand. 153 (1<<17) |
157 * Moreover OF sets some bits in IF module registers 154 (2<<1) |
158 * which are referred as reseved in datasheet. 155 (2<<4) |
156 (3<<11) |
157 (3<<14) |
158 (1<<27) |
159 (1<<28) ;
160}
161
162static void llp_end(struct llp_t *llp)
163{
164 llp->ctl_l &= ~((1<<27)|(1<<28));
165}
166
167
168static void dwdma_start(uint8_t ch, struct llp_t *llp, uint8_t handshake)
169{
170 DWDMA_SAR(ch) = 0;
171 DWDMA_DAR(ch) = 0;
172 DWDMA_LLP(ch) = (uint32_t)llp;
173 DWDMA_CTL_L(ch) = (1<<20) |
174 (1<<23) |
175 (1<<17) |
176 (2<<1) |
177 (2<<4) |
178 (3<<11) |
179 (3<<14) |
180 (1<<27) |
181 (1<<28) ;
182
183 DWDMA_CTL_H(ch) = 1;
184 DWDMA_CFG_L(ch) = (7<<5);
185 DWDMA_CFG_H(ch) = (handshake<<11)|(1<<2);
186 DWDMA_SGR(ch) = (13<<20);
187 DWDMA_DMA_CHEN = (0x101<<ch);
188}
189
190static void create_llp(void)
191{
192 int i;
193
194 /* build LLPs */
195 for (i=0; i<LCD_HEIGHT; i++)
196 llp_setup((void *)FBADDR(0,i),
197 (void*)(LCD_BUFF+((i%4)*4*LCD_WIDTH/2)),
198 &(scr_llp[i]),
199 LCD_WIDTH/2);
200
201 llp_end(&scr_llp[LCD_HEIGHT-1]);
202}
203
204/* Public functions */
205
206/* Switch between lcdif bypass mode and buffered mode
207 * used in private implementations of lcd_set_gram_area()
208 */
209void lcdctrl_bypass(unsigned int on_off)
210{
211 while (!(LCDC_STA & LCDC_MCU_IDLE));
212
213 if (on_off)
214 MCU_CTRL |= MCU_CTRL_BYPASS;
215 else
216 MCU_CTRL &= ~MCU_CTRL_BYPASS;
217}
218
219/* Helper functions used to write commands
220 * to lcd controler in bypass mode
221 * used in controler specific implementations of:
222 * lcd_sleep()
223 * lcd_display_init()
159 */ 224 */
225void lcd_cmd(unsigned int cmd)
226{
227 LCD_COMMAND = lcd_data_transform(cmd);
228}
229
230void lcd_data(unsigned int data)
231{
232 LCD_DATA = lcd_data_transform(data);
233}
234
235void lcd_write_reg(unsigned int reg, unsigned int val)
236{
237 lcd_cmd(reg);
238 lcd_data(val);
239}
240
241/* rockbox API functions */
242void lcd_init_device(void)
243{
244 iomux_lcd(LCD_DATABUS_WIDTH); /* setup pins for lcd interface */
245 dwdma_init(); /* init dwdma module */
246 create_llp(); /* build LLPs for screen update dma */
247 lcdctrl_init(); /* basic lcdc module configuration */
248}
249
160void lcd_update() 250void lcd_update()
161{ 251{
162 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); 252 lcd_set_gram_area(0, 0, LCD_WIDTH, LCD_HEIGHT);
253 lcdctrl_bypass(0);
254
255 commit_discard_dcache_range(FBADDR(0,0), 2*LCD_WIDTH*LCD_HEIGHT);
256
257 while (!(LCDC_STA & LCDC_MCU_IDLE));
258
259 dwdma_start(0, scr_llp, 6);
260 udelay(10);
261
262 /* Setup buffered writes to lcd controler */
263 MCU_CTRL = MCU_CTRL_RS_HIGH|MCU_CTRL_BUFF_WRITE|MCU_CTRL_BUFF_START;
264
265 /* Wait for DMA transfer to finish */
266 while (DWDMA_CTL_L(0) & (1<<27));
163} 267}