rockbox/firmware/target/arm/imx233/creative-zen/lcd-zen.c

351 lines
9.2 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* 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 "regs/regs-uartdbg.h"
#include "logf.h"
#ifndef BOOTLOADER
#include "button.h"
#include "font.h"
#include "action.h"
#endif
static bool lcd_on;
/**
* DMA
*/
/* Used for DMA */
struct lcdif_dma_command_t
{
struct apb_dma_command_t dma;
uint32_t pad;
} __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_power(bool en)
{
imx233_pinctrl_set_gpio(1, 8, en);
mdelay(10);
}
static void lcd_power_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)
{
/* NOTE I don't understand why I have to use BGR, logic would say I should not */
spi_write_reg(0x1, 0x2b1d);// inversion
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, 0xd040);// polarity (OF uses 0xc040, seems incorrect)
spi_write_reg(0x8, 0); // vsync back porch (0=3H)
spi_write_reg(0x9, 0); // hsync back porhc (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
*/
bool lcd_active(void)
{
return lcd_on;
}
void lcd_enable(bool enable)
{
if(lcd_on == enable)
return;
lcd_on = enable;
if(lcd_on)
{
// 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_power(true);
// setup registers
imx233_lcdif_enable_sync_signals(true); // we need frame signals during init
lcd_power_seq();
lcd_init_seq();
lcd_display_on_seq();
imx233_dma_reset_channel(APB_LCDIF);
imx233_dma_start_command(APB_LCDIF, &lcdif_dma[0].dma);
BF_SET(LCDIF_CTRL, DOTCLK_MODE);
BF_SET(LCDIF_CTRL, RUN);
}
else
{
// power down
lcd_display_off_seq();
lcd_power(false);
// stop lcdif
BF_CLR(LCDIF_CTRL, DOTCLK_MODE);
// disable spi
spi_enable(false);
}
}
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_power");
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, 2);
imx233_clkctrl_set_bypass(CLK_PIX, true); /* use XTAL */
imx233_clkctrl_enable(CLK_PIX, true);
imx233_dma_reset_channel(APB_LCDIF);
imx233_dma_clkgate_channel(APB_LCDIF, true);
imx233_lcdif_init();
imx233_lcdif_setup_dotclk_pins(8, false);
imx233_lcdif_set_word_length(8);
/** Datasheet states:
* 257H >= VBP >= 3H, VBP > VLW, VFP > 1H
* 1533clk >= HBP >= 24clk, HBP > HLW, HFP >= 4clk
*
* Take VLW=1H, VBP=3H, VFP=2H, 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);
// 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_OR3(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;
}
// enable
lcd_enable(true);
}
void lcd_update(void)
{
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
}
void lcd_update_rect(int x, int y, int w, int h)
{
#ifdef HAVE_LCD_ENABLE
if(!lcd_on)
return;
#endif
for(int yy = y; yy < y + h; yy++)
{
uint16_t *pix = FBADDR(x, yy);
uint8_t *p = 3 * (yy * LCD_WIDTH + x) + (uint8_t *)FRAME;
for(int xx = 0; xx < w; xx++, pix++)
{
*p++ = RGB_UNPACK_RED(*pix);
*p++ = RGB_UNPACK_GREEN(*pix);
*p++ = RGB_UNPACK_BLUE(*pix);
}
}
}