summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDana Conrad <dconrad@fastmail.com>2024-08-07 00:52:56 +0000
committerSolomon Peachy <pizza@shaftnet.org>2024-11-13 18:26:30 -0500
commit1fb906500a44110e3f41ca4ef2aa347f9485fd58 (patch)
tree7c1cbade1fbf48888e61fd449ca9e62aebb89b6e
parentd0aaa37d33ce1e64d335d7de4d096895881c6c53 (diff)
downloadrockbox-1fb906500a.tar.gz
rockbox-1fb906500a.zip
x1000: LCD DMA fix
Credit ZappBranigan2972 on the forums Change-Id: Ia4d498d215d8f365156f15965b715832202bec8a
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.c156
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h4
2 files changed, 117 insertions, 43 deletions
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
index 979febf066..58c4bd6a61 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
@@ -69,11 +69,42 @@ static volatile int fbcopy_done;
/* True if we're in sleep mode */
static bool lcd_sleeping = false;
#endif
+static bool lcd_on = false;
/* Check if running with interrupts disabled (eg: panic screen) */
#define lcd_panic_mode \
UNLIKELY((read_c0_status() & 1) == 0)
+typedef enum {
+ LCD_SEND0, LCD_SEND1,
+ WAIT_FRAME, SLEEP_FRAME,
+ WAIT_EOF, SLEEP_EOF,
+ WAIT_QD,
+ WAIT_FBCPF, WAIT_FBCPP
+} Lcd_ID;
+
+
+// TODO: This may need a yield?
+static void __attribute__ ((noinline)) lcd_wait(Lcd_ID id)
+{
+ uint32_t i, done = 0;
+
+ for(i=0; i<LCD_WAIT_STEPS; i++) {
+ if (done) break;
+ switch (id) {
+ case LCD_SEND0:
+ case LCD_SEND1:
+ case WAIT_FRAME:
+ case SLEEP_FRAME: done = !jz_readf(LCD_MSTATE, BUSY); break;
+ case WAIT_EOF:
+ case SLEEP_EOF: done = jz_readf(LCD_STATE, EOF) != 0; break;
+ case WAIT_QD: done = jz_readf(LCD_STATE, QD) != 0; break;
+ case WAIT_FBCPF:
+ case WAIT_FBCPP: done = fbcopy_done != 0; break;
+ }
+ }
+}
+
static void lcd_init_controller(const struct lcd_tgt_config* cfg)
{
/* Set MCFG/MCFG_NEW according to target interface settings */
@@ -125,7 +156,7 @@ static void lcd_init_controller(const struct lcd_tgt_config* cfg)
/* DMA settings */
jz_writef(LCD_CTRL, ENABLE(0), BURST_V(64WORD),
- EOFM(1), SOFM(0), IFUM(0), QDM(0),
+ EOFM(0), SOFM(0), IFUM(0), QDM(0),
BEDN(cfg->big_endian), PEDN(0));
jz_write(LCD_DAH, LCD_WIDTH);
jz_write(LCD_DAV, LCD_HEIGHT);
@@ -185,7 +216,7 @@ static void lcd_fbcopy_dma_cb(int evt)
fbcopy_done = 1;
}
-static void lcd_fbcopy_dma_run(dma_desc* d)
+static void lcd_fbcopy_dma_run(dma_desc* d, Lcd_ID id)
{
if(lcd_panic_mode) {
/* Can't use DMA if interrupts are off, so just do a memcpy().
@@ -205,7 +236,7 @@ static void lcd_fbcopy_dma_run(dma_desc* d)
jz_set(DMA_DB, 1 << DMA_CHANNEL_FBCOPY);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_FBCOPY), CTE(1));
- while(!fbcopy_done);
+ lcd_wait(id);
}
static void lcd_fbcopy_dma_full(void)
@@ -221,7 +252,7 @@ static void lcd_fbcopy_dma_full(void)
d.rt = jz_orf(DMA_CHN_RT, TYPE_V(AUTO));
d.pad0 = 0;
d.pad1 = 0;
- lcd_fbcopy_dma_run(&d);
+ lcd_fbcopy_dma_run(&d, WAIT_FBCPF);
}
/* NOTE: DMA stride mode can only transfer up to 255 blocks at once.
@@ -254,7 +285,7 @@ static void lcd_fbcopy_dma_partial1(int x, int y, int width, int height)
d.tc = width * height * sizeof(fb_data);
}
- lcd_fbcopy_dma_run(&d);
+ lcd_fbcopy_dma_run(&d, WAIT_FBCPP);
}
#if STRIDE_MAIN(LCD_HEIGHT, LCD_WIDTH) > 255
@@ -274,7 +305,12 @@ static void lcd_fbcopy_dma_partial(int x, int y, int width, int height)
# define lcd_fbcopy_dma_partial lcd_fbcopy_dma_partial1
#endif
-static void lcd_dma_start(void)
+typedef enum {
+ MODE_INIT,
+ MODE_SLEEP
+} DMA_Mode;
+
+static void lcd_dma_start(DMA_Mode mode)
{
/* Set format conversion bit, seems necessary for DMA mode.
* Must set DTIMES here if we use an 8-bit bus type. */
@@ -286,44 +322,72 @@ static void lcd_dma_start(void)
TE_INV(lcd_tgt_config.te_polarity ? 0 : 1),
NOT_USE_TE(lcd_tgt_config.te_enable ? 0 : 1));
+ /* ref PM sec. 4.9.1 DMA operation,
+ * as well as sec. 4.5.2 Enabling the Controller.
+ * I think we're effectively doing the last bit
+ * of enabling the controller here, and then
+ * doing dma start.
+ */
+ jz_writef(LCD_MCTRL, DMA_MODE(1), DMA_START(1));
+
+ while(jz_readf(LCD_MSTATE, BUSY));
+
+ jz_writef(LCD_MCTRL, DMA_TX_EN(1));
+
+ /* clear any old flags */
+ jz_write(LCD_STATE, 0);
+
/* Begin DMA transfer. Need to start a dummy frame or else we will
* not be able to pass lcd_wait_frame() at the first lcd_update(). */
- jz_write(LCD_STATE, 0);
+ // if (mode == MODE_INIT) { // Run this only once on Startup
jz_write(LCD_DA, PHYSADDR(&lcd_dma_desc[0]));
- jz_writef(LCD_MCTRL, DMA_MODE(1), DMA_START(1), DMA_TX_EN(1));
jz_writef(LCD_CTRL, ENABLE(1));
+ // }
+
+ lcd_on = true;
}
static bool lcd_wait_frame(void)
{
/* Bail out if DMA is not enabled */
- int irq = disable_irq_save();
- int bit = jz_readf(LCD_CTRL, ENABLE);
- restore_irq(irq);
- if(!bit)
+ if(!lcd_active())
return false;
/* Usual case -- wait for EOF, wait for FIFO to drain, clear EOF */
- while(jz_readf(LCD_STATE, EOF) == 0);
- while(jz_readf(LCD_MSTATE, BUSY));
+ lcd_wait(WAIT_EOF);
+ lcd_wait(WAIT_FRAME);
jz_writef(LCD_STATE, EOF(0));
return true;
}
-static void lcd_dma_stop(void)
+static void lcd_dma_stop(DMA_Mode mode)
{
+ int irq = disable_irq_save();
+ lcd_on = false;
+ restore_irq(irq);
+
+ if (mode == MODE_INIT) { // Run this only once when endinng
#ifdef LCD_X1000_DMA_WAIT_FOR_FRAME
- /* Wait for frame to finish to avoid misaligning the write pointer */
- lcd_wait_frame();
+ /* Wait for frame to finish to avoid misaligning the write pointer */
+ lcd_wait_frame();
#endif
-
- /* Stop the DMA transfer */
- jz_writef(LCD_CTRL, ENABLE(0));
- jz_writef(LCD_MCTRL, DMA_TX_EN(0));
-
- /* Wait for disable to take effect */
- while(jz_readf(LCD_STATE, QD) == 0);
- jz_writef(LCD_STATE, QD(0));
+ /* Stop the DMA transfer */
+ jz_writef(LCD_CTRL, ENABLE(0));
+ jz_writef(LCD_MCTRL, DMA_TX_EN(0));
+
+ /* Wait for disable to take effect */
+ lcd_wait(WAIT_QD);
+ } else { // MODE_SLEEP
+ jz_writef(LCD_CTRL, ENABLE(0));
+
+ /* Usual case -- wait for EOF, wait for FIFO to drain */
+ lcd_wait(SLEEP_EOF);
+ lcd_wait(SLEEP_FRAME);
+
+ /* Stop the DMA transfer */
+ jz_writef(LCD_MCTRL, DMA_TX_EN(0));
+ }
+ jz_write(LCD_STATE, 0); // clear State
/* Clear format conversion bit, disable vsync */
jz_writef(LCD_MCFG_NEW, FMT_CONV(0), DTIMES(0));
@@ -332,7 +396,7 @@ static void lcd_dma_stop(void)
static void lcd_send(uint32_t d)
{
- while(jz_readf(LCD_MSTATE, BUSY));
+ lcd_wait(LCD_SEND0);
REG_LCD_MDATA = d;
}
@@ -370,6 +434,7 @@ void lcd_exec_commands(const uint32_t* cmdseq)
break;
}
}
+ lcd_wait(LCD_SEND1);
}
void lcd_init_device(void)
@@ -381,7 +446,7 @@ void lcd_init_device(void)
lcd_tgt_enable(true);
- lcd_dma_start();
+ lcd_dma_start(MODE_INIT);
}
#ifdef HAVE_LCD_SHUTDOWN
@@ -389,8 +454,8 @@ void lcd_shutdown(void)
{
if(lcd_sleeping)
lcd_tgt_sleep(false);
- else if(jz_readf(LCD_CTRL, ENABLE))
- lcd_dma_stop();
+ else if(lcd_active())
+ lcd_dma_stop(MODE_INIT);
lcd_tgt_enable(false);
jz_writef(CPM_CLKGR, LCD(1));
@@ -400,37 +465,42 @@ void lcd_shutdown(void)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
bool lcd_active(void)
{
- return jz_readf(LCD_CTRL, ENABLE);
+ return lcd_on;
}
void lcd_enable(bool en)
{
- /* Must disable IRQs to turn off the running LCD */
- int irq = disable_irq_save();
- int bit = jz_readf(LCD_CTRL, ENABLE);
- if(bit && !en)
- lcd_dma_stop();
- restore_irq(irq);
+ bool state = lcd_active();
+ if(state && !en)
+#if defined(BOOTLOADER)
+ lcd_dma_stop(MODE_INIT);
+#else
+ lcd_dma_stop(MODE_SLEEP);
+#endif
/* Deal with sleep mode */
#if defined(HAVE_LCD_SLEEP) || defined(LCD_X1000_FASTSLEEP)
#if defined(LCD_X1000_FASTSLEEP)
- if(bit && !en) {
+ if(state && !en) {
lcd_tgt_sleep(true);
lcd_sleeping = true;
} else
#endif
- if(!bit && en && lcd_sleeping) {
+ if(!state && en && lcd_sleeping) {
lcd_tgt_sleep(false);
lcd_sleeping = false;
}
#endif
/* Handle turning the LCD back on */
- if(!bit && en)
+ if(!state && en)
{
send_event(LCD_EVENT_ACTIVATION, NULL);
- lcd_dma_start();
+#if defined(BOOTLOADER)
+ lcd_dma_start(MODE_INIT);
+#else
+ lcd_dma_start(MODE_SLEEP);
+#endif
}
}
#endif
@@ -466,6 +536,9 @@ void lcd_update(void)
* its entirety to the LCD through a different DMA process. */
void lcd_update_rect(int x, int y, int width, int height)
{
+ if(!lcd_wait_frame())
+ return;
+
/* Clamp the coordinates */
if(x < 0) {
width += x;
@@ -486,9 +559,6 @@ void lcd_update_rect(int x, int y, int width, int height)
if(width < 0 || height < 0)
return;
- if(!lcd_wait_frame())
- return;
-
commit_dcache();
lcd_fbcopy_dma_partial(x, y, width, height);
jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1));
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.h b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
index e88a8733ed..17ae06df2b 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
@@ -77,6 +77,10 @@ struct lcd_tgt_config {
size_t dma_wr_cmd_size;
};
+#define LCD_WAIT_MAX_US 65500 /* µS max. Waittime */
+#define LCD_WAIT_US 1 /* µS per Step */
+#define LCD_WAIT_STEPS (LCD_WAIT_MAX_US / LCD_WAIT_US)
+
/* Static configuration for the target's LCD, must be defined by target. */
extern const struct lcd_tgt_config lcd_tgt_config;