mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-09 13:12:37 -05:00
By default STMP3700 launches from negative edge for capture on positive edge. If we leave DPL (bit 13 of reg 0x13) to 0, it will be captured on falling edge, resulting in a potentially undefined behavior. Some make sure LCD captures on rising edge. Alternatively we could change the dotclk polarity in VDCTRL0. Change-Id: I4ceb2f5a9be88e07f0af9bf493b7881883320fda
338 lines
9.4 KiB
C
338 lines
9.4 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 2013 by Amaury Pouly
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <sys/types.h> /* off_t */
|
|
#include <string.h>
|
|
#include "cpu.h"
|
|
#include "system.h"
|
|
#include "kernel.h"
|
|
#include "backlight-target.h"
|
|
#include "lcd.h"
|
|
#include "lcdif-imx233.h"
|
|
#include "clkctrl-imx233.h"
|
|
#include "pinctrl-imx233.h"
|
|
#include "dma-imx233.h"
|
|
#include "logf.h"
|
|
#include "lcd-target.h"
|
|
#ifndef BOOTLOADER
|
|
#include "button.h"
|
|
#include "font.h"
|
|
#include "action.h"
|
|
#endif
|
|
|
|
#include "regs/lcdif.h"
|
|
|
|
/**
|
|
* DMA
|
|
*/
|
|
|
|
/* Used for DMA */
|
|
struct lcdif_dma_command_t
|
|
{
|
|
struct apb_dma_command_t dma;
|
|
uint32_t ctrl;
|
|
} __attribute__((packed)) CACHEALIGN_ATTR;
|
|
|
|
__ENSURE_STRUCT_CACHE_FRIENDLY(struct lcdif_dma_command_t)
|
|
|
|
#define NR_CMDS ((IMX233_FRAMEBUFFER_SIZE + IMX233_MAX_SINGLE_DMA_XFER_SIZE - 1) / IMX233_MAX_SINGLE_DMA_XFER_SIZE)
|
|
|
|
struct lcdif_dma_command_t lcdif_dma[NR_CMDS];
|
|
|
|
/**
|
|
* Utils
|
|
*/
|
|
static int g_wait_nr_frame = 0;
|
|
static struct semaphore g_wait_sema;
|
|
|
|
static void wait_frames_cb(void)
|
|
{
|
|
if(--g_wait_nr_frame == 0)
|
|
semaphore_release(&g_wait_sema);
|
|
}
|
|
|
|
static void wait_nr_frames(int nr)
|
|
{
|
|
g_wait_nr_frame = 2 + nr; // +1 because we want entire frames, +1 to be safe
|
|
imx233_lcdif_set_vsync_edge_cb(wait_frames_cb);
|
|
imx233_lcdif_enable_vsync_edge_irq(true);
|
|
semaphore_wait(&g_wait_sema, TIMEOUT_BLOCK);
|
|
imx233_lcdif_enable_vsync_edge_irq(false);
|
|
}
|
|
|
|
/**
|
|
* SPI
|
|
*/
|
|
|
|
#define SPI_CS(v) imx233_pinctrl_set_gpio(1, 11, v)
|
|
#define SPI_SCL(v) imx233_pinctrl_set_gpio(1, 10, v)
|
|
#define SPI_SDO(v) imx233_pinctrl_set_gpio(1, 9, v)
|
|
|
|
#define DEV_ID 0x74
|
|
#define RS 0x2
|
|
#define RW 0x1
|
|
|
|
static void spi_enable(bool en)
|
|
{
|
|
imx233_pinctrl_set_gpio(1, 9, en);
|
|
imx233_pinctrl_set_gpio(1, 10, en);
|
|
imx233_pinctrl_set_gpio(1, 11, en);
|
|
imx233_pinctrl_enable_gpio(1, 9, en);
|
|
imx233_pinctrl_enable_gpio(1, 10, en);
|
|
imx233_pinctrl_enable_gpio(1, 11, en);
|
|
mdelay(1);
|
|
}
|
|
|
|
static void spi_delay(void)
|
|
{
|
|
udelay(1);
|
|
}
|
|
|
|
static void spi_begin(void)
|
|
{
|
|
SPI_CS(false);
|
|
spi_delay();
|
|
}
|
|
|
|
static void spi_write(unsigned char b)
|
|
{
|
|
for(int i = 7; i >= 0; i--)
|
|
{
|
|
SPI_SCL(false);
|
|
spi_delay();
|
|
SPI_SDO((b >> i) & 1);
|
|
spi_delay();
|
|
SPI_SCL(true);
|
|
spi_delay();
|
|
}
|
|
}
|
|
|
|
static void spi_end(void)
|
|
{
|
|
SPI_CS(true);
|
|
spi_delay();
|
|
}
|
|
|
|
static void spi_write_reg(uint8_t reg, uint16_t value)
|
|
{
|
|
spi_begin();
|
|
spi_write(DEV_ID);
|
|
spi_write(0);
|
|
spi_write(reg);
|
|
spi_end();
|
|
spi_begin();
|
|
spi_write(DEV_ID | RS);
|
|
spi_write(value >> 8);
|
|
spi_write(value & 0xff);
|
|
spi_end();
|
|
}
|
|
|
|
/**
|
|
* LCD control
|
|
*/
|
|
|
|
static void lcd_something(bool en)
|
|
{
|
|
/* I don't know what this pin does */
|
|
imx233_pinctrl_set_gpio(1, 8, en);
|
|
mdelay(10);
|
|
}
|
|
|
|
static void lcd_something_seq(void)
|
|
{
|
|
spi_write_reg(0x7, 0);
|
|
mdelay(10);
|
|
spi_write_reg(0x12, 0x1618);
|
|
spi_write_reg(0x11, 0x2227);
|
|
spi_write_reg(0x13, 0x61d1);
|
|
spi_write_reg(0x10, 0x550c);
|
|
wait_nr_frames(5);
|
|
spi_write_reg(0x12, 0x0c58);
|
|
}
|
|
|
|
static void lcd_init_seq(void)
|
|
{
|
|
spi_write_reg(0x1, 0x231d);// no BGR inversion (OF uses BGR)
|
|
spi_write_reg(0x2, 0x300);
|
|
/* NOTE by default stmp3700 has vsync/hsync active low and data launch
|
|
* at negative edge of dotclk, reflect this in the polarity settings */
|
|
spi_write_reg(0x3, 0xf040);// OF uses 0xc040, but DS says bit 12 must be set
|
|
spi_write_reg(0x8, 0); // vsync back porch (0=3H)
|
|
spi_write_reg(0x9, 0); // hsync back porch (0=24clk)
|
|
spi_write_reg(0x76, 0x2213);
|
|
spi_write_reg(0xb, 0x33e1);
|
|
spi_write_reg(0xc, 0x23);
|
|
spi_write_reg(0x76, 0);
|
|
spi_write_reg(0xd, 7);
|
|
spi_write_reg(0xe, 0);
|
|
spi_write_reg(0x15, 0x803);
|
|
spi_write_reg(0x14, 0);
|
|
spi_write_reg(0x16, 0);
|
|
spi_write_reg(0x30, 0x706);
|
|
spi_write_reg(0x31, 0x406);
|
|
spi_write_reg(0x32, 0xc09);
|
|
spi_write_reg(0x33, 0x606);
|
|
spi_write_reg(0x34, 0x706);
|
|
spi_write_reg(0x35, 0x406);
|
|
spi_write_reg(0x36, 0xc06);
|
|
spi_write_reg(0x37, 0x601);
|
|
spi_write_reg(0x38, 0x504);
|
|
spi_write_reg(0x39, 0x504);
|
|
}
|
|
|
|
static void lcd_display_on_seq(void)
|
|
{
|
|
spi_write_reg(0x7, 1);
|
|
wait_nr_frames(1);
|
|
spi_write_reg(0x7, 0x101);
|
|
wait_nr_frames(2);
|
|
spi_write_reg(0x76, 0x2213);
|
|
spi_write_reg(0x1c, 0x6650);
|
|
spi_write_reg(0xb, 0x33e1);
|
|
spi_write_reg(0x76, 0);
|
|
spi_write_reg(0x7, 0x103);
|
|
}
|
|
|
|
static void lcd_display_off_seq(void)
|
|
{
|
|
spi_write_reg(0xb, 0x30e1);
|
|
spi_write_reg(0x7, 0x102);
|
|
wait_nr_frames(2);
|
|
spi_write_reg(0x7, 0);
|
|
spi_write_reg(0x12, 0);
|
|
spi_write_reg(0x10, 0x100);
|
|
}
|
|
|
|
/**
|
|
* Rockbox
|
|
*/
|
|
|
|
void lcd_enable(bool enable)
|
|
{
|
|
if(lcd_active() == enable)
|
|
return;
|
|
|
|
lcd_set_active(enable);
|
|
if(lcd_active())
|
|
{
|
|
// enable spi
|
|
spi_enable(true);
|
|
// reset
|
|
imx233_lcdif_reset_lcd(true);
|
|
imx233_lcdif_reset_lcd(false);
|
|
mdelay(1);
|
|
imx233_lcdif_reset_lcd(true);
|
|
mdelay(1);
|
|
// "power" on
|
|
lcd_something(true);
|
|
// setup registers
|
|
lcd_something_seq();
|
|
lcd_init_seq();
|
|
lcd_display_on_seq();
|
|
|
|
imx233_dma_reset_channel(APB_LCDIF);
|
|
imx233_dma_start_command(APB_LCDIF, &lcdif_dma[0].dma);
|
|
}
|
|
else
|
|
{
|
|
// power down
|
|
lcd_display_off_seq();
|
|
lcd_something(false);
|
|
// stop lcdif
|
|
BF_CLR(LCDIF_CTRL, DOTCLK_MODE);
|
|
/* stmp37xx errata: clearing DOTCLK_MODE won't clear RUN: wait until
|
|
* fifo is empty and then clear manually */
|
|
while(!BF_RD(LCDIF_STAT, TXFIFO_EMPTY));
|
|
BF_CLR(LCDIF_CTRL, RUN);
|
|
// disable spi
|
|
spi_enable(false);
|
|
}
|
|
}
|
|
|
|
static void lcd_underflow(void)
|
|
{
|
|
/* on underflow, current frame is dead so stop lcdif and prepare for next frame
|
|
* don't bother with the errata, fifo is empty since we are underflowing ! */
|
|
BF_CLR(LCDIF_CTRL, DOTCLK_MODE);
|
|
imx233_dma_reset_channel(APB_LCDIF);
|
|
imx233_dma_start_command(APB_LCDIF, &lcdif_dma[0].dma);
|
|
}
|
|
|
|
void lcd_init_device(void)
|
|
{
|
|
semaphore_init(&g_wait_sema, 1, 0);
|
|
/* I'm not really sure this pin is related to power, it does not seem to do anything */
|
|
imx233_pinctrl_acquire(1, 8, "lcd_something");
|
|
imx233_pinctrl_acquire(1, 9, "lcd_spi_sdo");
|
|
imx233_pinctrl_acquire(1, 10, "lcd_spi_scl");
|
|
imx233_pinctrl_acquire(1, 11, "lcd_spi_cs");
|
|
imx233_pinctrl_set_function(1, 9, PINCTRL_FUNCTION_GPIO);
|
|
imx233_pinctrl_set_function(1, 10, PINCTRL_FUNCTION_GPIO);
|
|
imx233_pinctrl_set_function(1, 11, PINCTRL_FUNCTION_GPIO);
|
|
imx233_pinctrl_set_function(1, 8, PINCTRL_FUNCTION_GPIO);
|
|
imx233_pinctrl_enable_gpio(1, 8, true);
|
|
/** lcd is 320x240, data bus is 8-bit, depth is 24-bit so we need 3clk/pix
|
|
* by running PIX clock at 24MHz we can sustain ~100 fps */
|
|
imx233_clkctrl_enable(CLK_PIX, false);
|
|
imx233_clkctrl_set_div(CLK_PIX, 1);
|
|
imx233_clkctrl_set_bypass(CLK_PIX, true); /* use XTAL */
|
|
imx233_clkctrl_enable(CLK_PIX, true);
|
|
imx233_lcdif_init();
|
|
imx233_lcdif_setup_dotclk_pins(8, false);
|
|
imx233_lcdif_set_word_length(8);
|
|
imx233_lcdif_set_underflow_cb(&lcd_underflow);
|
|
imx233_lcdif_enable_underflow_irq(true);
|
|
imx233_dma_clkgate_channel(APB_LCDIF, true);
|
|
imx233_dma_reset_channel(APB_LCDIF);
|
|
/** Datasheet states:
|
|
* 257H >= VBP >= 3H, VBP > VLW, VFP >= 1H
|
|
* 1533clk >= HBP >= 24clk, HBP > HLW, HFP >= 4clk
|
|
*
|
|
* Take VLW=1H, VBP=3H, VFP=1H, HLW=8, HBP=24, HFP=4
|
|
* Take 3clk/pix because we send 24-bit/pix with 8-bit data bus
|
|
* Keep consistent with register setting in lcd_init_seq
|
|
*/
|
|
imx233_lcdif_setup_dotclk_ex(/*v_pulse_width*/1, /*v_back_porch*/3,
|
|
/*v_front_porch*/1, /*h_pulse_width*/8, /*h_back_porch*/24,
|
|
/*h_front_porch*/4, LCD_WIDTH, LCD_HEIGHT, /*clk_per_pix*/3,
|
|
/*enable_present*/false);
|
|
imx233_lcdif_set_byte_packing_format(0xf);
|
|
imx233_lcdif_enable_sync_signals(true); // we need frame signals during init
|
|
// setup dma
|
|
unsigned size = IMX233_FRAMEBUFFER_SIZE;
|
|
uint8_t *frame_p = FRAME;
|
|
for(int i = 0; i < NR_CMDS; i++)
|
|
{
|
|
unsigned xfer = MIN(IMX233_MAX_SINGLE_DMA_XFER_SIZE, size);
|
|
lcdif_dma[i].dma.next = &lcdif_dma[(i + 1) % NR_CMDS].dma;
|
|
lcdif_dma[i].dma.cmd = BF_OR(APB_CHx_CMD, CHAIN(1),
|
|
COMMAND(BV_APB_CHx_CMD_COMMAND__READ), XFER_COUNT(xfer));
|
|
lcdif_dma[i].dma.buffer = frame_p;
|
|
size -= xfer;
|
|
frame_p += xfer;
|
|
}
|
|
// first transfer: enable run, dotclk and so on
|
|
lcdif_dma[0].dma.cmd |= BF_OR(APB_CHx_CMD, CMDWORDS(1));
|
|
lcdif_dma[0].ctrl = BF_OR(LCDIF_CTRL, BYPASS_COUNT(1), DOTCLK_MODE(1),
|
|
RUN(1), WORD_LENGTH(1));
|
|
// enable
|
|
lcd_enable(true);
|
|
}
|