rockbox/firmware/target/arm/imx233/lcdif-imx233.c

369 lines
12 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (c) 2011 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 "lcdif-imx233.h"
#include "pinctrl-imx233.h"
#include "icoll-imx233.h"
#if IMX233_SUBTARGET >= 3700
static lcdif_irq_cb_t g_cur_frame_cb = NULL;
static lcdif_irq_cb_t g_vsync_edge_cb = NULL;
#endif
/* for some crazy reason, all "non-dma" interrupts are routed to the ERROR irq */
#if IMX233_SUBTARGET >= 3700
void INT_LCDIF_ERROR(void)
{
if(BF_RD(LCDIF_CTRL1, CUR_FRAME_DONE_IRQ))
{
if(g_cur_frame_cb)
g_cur_frame_cb();
BF_CLR(LCDIF_CTRL1, CUR_FRAME_DONE_IRQ);
}
if(BF_RD(LCDIF_CTRL1, VSYNC_EDGE_IRQ))
{
if(g_vsync_edge_cb)
g_vsync_edge_cb();
BF_CLR(LCDIF_CTRL1, VSYNC_EDGE_IRQ);
}
}
#endif
void imx233_lcdif_enable(bool enable)
{
if(enable)
BF_CLR(LCDIF_CTRL, CLKGATE);
else
BF_SET(LCDIF_CTRL, CLKGATE);
}
void imx233_lcdif_reset_lcd(bool enable)
{
#if IMX233_SUBTARGET < 3700
if(enable)
BF_SET(LCDIF_CTRL, RESET);
else
BF_CLR(LCDIF_CTRL, RESET);
#else
if(enable)
BF_SET(LCDIF_CTRL1, RESET);
else
BF_CLR(LCDIF_CTRL1, RESET);
#endif
}
void imx233_lcdif_init(void)
{
imx233_reset_block(&HW_LCDIF_CTRL);
#if IMX233_SUBTARGET >= 3700
imx233_icoll_enable_interrupt(INT_SRC_LCDIF_ERROR, true);
#endif
}
void imx233_lcdif_set_timings(unsigned data_setup, unsigned data_hold,
unsigned cmd_setup, unsigned cmd_hold)
{
HW_LCDIF_TIMING = BF_OR4(LCDIF_TIMING, DATA_SETUP(data_setup),
DATA_HOLD(data_hold), CMD_SETUP(cmd_setup), CMD_HOLD(cmd_hold));
}
void imx233_lcdif_set_word_length(unsigned word_length)
{
switch(word_length)
{
case 8: BF_WR_V(LCDIF_CTRL, WORD_LENGTH, 8_BIT); break;
case 16: BF_WR_V(LCDIF_CTRL, WORD_LENGTH, 16_BIT); break;
#if IMX233_SUBTARGET >= 3780
case 18: BF_WR_V(LCDIF_CTRL, WORD_LENGTH, 18_BIT); break;
case 24: BF_WR_V(LCDIF_CTRL, WORD_LENGTH, 24_BIT); break;
#endif
default:
panicf("this chip cannot handle a lcd word length of %d", word_length);
break;
}
}
void imx233_lcdif_wait_ready(void)
{
while(BF_RD(LCDIF_CTRL, RUN));
}
void imx233_lcdif_set_data_swizzle(unsigned swizzle)
{
#if IMX233_SUBTARGET >= 3780
BF_WR(LCDIF_CTRL, INPUT_DATA_SWIZZLE, swizzle);
#else
BF_WR(LCDIF_CTRL, DATA_SWIZZLE, swizzle);
#endif
}
void imx233_lcdif_wait_fifo(void)
{
#if IMX233_SUBTARGET >= 3700
while(BF_RD(LCDIF_STAT, TXFIFO_FULL));
#else
while(!BF_RD(LCDIF_CTRL, FIFO_STATUS));
#endif
}
/* The following function set byte packing often, ifdefing everytime is painful */
#if IMX233_SUBTARGET < 3700
#define imx233_lcdif_set_byte_packing_format(a)
#endif
// bbp = bytes per pixel
static void pio_send(unsigned len, unsigned bpp, uint8_t *buf)
{
/* WARNING: the imx233 has a limitation on count wrt to byte packing, the
* count must be a multiple of 2 with maximum packing when word-length is
* 16-bit!
* On the other hand, 8-bit word length doesn't seem to have any limitations,
* for example one can send 3 bytes with a packing format of 0xf
* WARNING for this function to work properly with any swizzle, we have to
* make sure we pack as many 32-bits as possible even when the data is not
* word-aligned */
imx233_lcdif_set_byte_packing_format(0xf);
/* compute shift between buf and next word-aligned pointer */
int shift = 0;
uint32_t temp_buf = 0;
int count = len * bpp; // number of bytes
while(0x3 & (intptr_t)buf)
{
temp_buf = temp_buf | *buf++ << shift;
shift += 8;
count--;
}
/* starting from now, all read are 32-bit */
uint32_t *wbuf = (void *)buf;
#if IMX233_SUBTARGET >= 3780
HW_LCDIF_TRANSFER_COUNT = BF_OR2(LCDIF_TRANSFER_COUNT, V_COUNT(1), H_COUNT(len));
#else
BF_WR(LCDIF_CTRL, COUNT, len);
#endif
BF_SET(LCDIF_CTRL, RUN);
while(count > 0)
{
uint32_t val = *wbuf++;
imx233_lcdif_wait_fifo();
HW_LCDIF_DATA = temp_buf | val << shift;
if(shift != 0)
temp_buf = val >> (32 - shift);
count -= 4;
}
/* send remaining bytes if any */
if(shift != 0)
{
imx233_lcdif_wait_fifo();
HW_LCDIF_DATA = temp_buf;
}
imx233_lcdif_wait_ready();
}
void imx233_lcdif_pio_send(bool data_mode, unsigned len, void *buf)
{
imx233_lcdif_wait_ready();
#if IMX233_SUBTARGET >= 3780
imx233_lcdif_enable_bus_master(false);
#endif
if(data_mode)
BF_SET(LCDIF_CTRL, DATA_SELECT);
else
BF_CLR(LCDIF_CTRL, DATA_SELECT);
switch(BF_RD(LCDIF_CTRL, WORD_LENGTH))
{
case BV_LCDIF_CTRL_WORD_LENGTH__8_BIT: pio_send(len, 1, buf); break;
case BV_LCDIF_CTRL_WORD_LENGTH__16_BIT: pio_send(len, 2, buf); break;
#if IMX233_SUBTARGET >= 3780
case BV_LCDIF_CTRL_WORD_LENGTH__18_BIT: pio_send(len, 4, buf); break;
#endif
default: panicf("Don't know how to handle this word length");
}
}
void imx233_lcdif_dma_send(void *buf, unsigned width, unsigned height)
{
#if IMX233_SUBTARGET >= 3780
imx233_lcdif_enable_bus_master(true);
HW_LCDIF_CUR_BUF = (uint32_t)buf;
HW_LCDIF_TRANSFER_COUNT = BF_OR2(LCDIF_TRANSFER_COUNT, V_COUNT(height), H_COUNT(width));
BF_SET(LCDIF_CTRL, DATA_SELECT);
BF_SET(LCDIF_CTRL, RUN);
#endif
}
static void setup_data_pins(unsigned bus_width)
{
imx233_pinctrl_setup_vpin(VPIN_LCD_D0, "lcd d0", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D1, "lcd d1", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D2, "lcd d2", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D3, "lcd d3", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D4, "lcd d4", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D5, "lcd d5", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D6, "lcd d6", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D7, "lcd d7", PINCTRL_DRIVE_4mA, false);
if(bus_width >= 16)
{
imx233_pinctrl_setup_vpin(VPIN_LCD_D8, "lcd d8", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D9, "lcd d9", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D10, "lcd d10", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D11, "lcd d11", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D12, "lcd d12", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D13, "lcd d13", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D14, "lcd d14", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D15, "lcd d15", PINCTRL_DRIVE_4mA, false);
}
#if IMX233_SUBTARGET >= 3780
if(bus_width >= 18)
{
imx233_pinctrl_setup_vpin(VPIN_LCD_D16, "lcd d16", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_D17, "lcd d17", PINCTRL_DRIVE_4mA, false);
}
#endif
}
void imx233_lcdif_setup_system_pins(unsigned bus_width)
{
imx233_pinctrl_setup_vpin(VPIN_LCD_RESET, "lcd reset", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_RS, "lcd rs", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_WR, "lcd wr", PINCTRL_DRIVE_4mA, false);
#ifdef VPIN_LCD_RD
imx233_pinctrl_setup_vpin(VPIN_LCD_RD, "lcd rd", PINCTRL_DRIVE_4mA, false);
#endif
imx233_pinctrl_setup_vpin(VPIN_LCD_CS, "lcd cs", PINCTRL_DRIVE_4mA, false);
setup_data_pins(bus_width);
}
#if IMX233_SUBTARGET >= 3700
void imx233_lcdif_setup_dotclk_pins(unsigned bus_width, bool have_enable)
{
if(have_enable)
imx233_pinctrl_setup_vpin(VPIN_LCD_ENABLE, "lcd enable", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_RESET, "lcd reset", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_HSYNC, "lcd hsync", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_VSYNC, "lcd vsync", PINCTRL_DRIVE_4mA, false);
imx233_pinctrl_setup_vpin(VPIN_LCD_DOTCLK, "lcd dotclk", PINCTRL_DRIVE_4mA, false);
setup_data_pins(bus_width);
}
void imx233_lcdif_set_byte_packing_format(unsigned byte_packing)
{
BF_WR(LCDIF_CTRL1, BYTE_PACKING_FORMAT, byte_packing);
}
#endif
#if IMX233_SUBTARGET >= 3700 && IMX233_SUBTARGET < 3780
void imx233_lcdif_enable_sync_signals(bool en)
{
BF_WR(LCDIF_VDCTRL3, SYNC_SIGNALS_ON, en);
}
void imx233_lcdif_setup_dotclk(unsigned v_pulse_width, unsigned v_period,
unsigned v_wait_cnt, unsigned v_active, unsigned h_pulse_width,
unsigned h_period, unsigned h_wait_cnt, unsigned h_active, bool enable_present)
{
HW_LCDIF_VDCTRL0 = BF_OR4(LCDIF_VDCTRL0, ENABLE_PRESENT(enable_present),
VSYNC_PERIOD_UNIT(1), VSYNC_PULSE_WIDTH_UNIT(1),
DOTCLK_V_VALID_DATA_CNT(v_active));
HW_LCDIF_VDCTRL1 = BF_OR2(LCDIF_VDCTRL1, VSYNC_PERIOD(v_period),
VSYNC_PULSE_WIDTH(v_pulse_width));
HW_LCDIF_VDCTRL2 = BF_OR3(LCDIF_VDCTRL2, HSYNC_PULSE_WIDTH(h_pulse_width),
HSYNC_PERIOD(h_period), DOTCLK_H_VALID_DATA_CNT(h_active));
HW_LCDIF_VDCTRL3 = BF_OR2(LCDIF_VDCTRL3, VERTICAL_WAIT_CNT(v_wait_cnt),
HORIZONTAL_WAIT_CNT(h_wait_cnt));
// setup dotclk mode, always bypass count, apparently data select is needed
HW_LCDIF_CTRL_SET = BM_OR3(LCDIF_CTRL, DOTCLK_MODE, BYPASS_COUNT, DATA_SELECT);
}
void imx233_lcdif_setup_dotclk_ex(unsigned v_pulse_width, unsigned v_back_porch,
unsigned v_front_porch, unsigned h_pulse_width, unsigned h_back_porch,
unsigned h_front_porch, unsigned width, unsigned height, unsigned clk_per_pix,
bool enable_present)
{
unsigned h_active = clk_per_pix * width;
unsigned h_period = h_active + h_back_porch + h_front_porch;
unsigned v_active = height;
unsigned v_period = v_active + v_back_porch + v_front_porch;
imx233_lcdif_setup_dotclk(v_pulse_width, v_period, v_back_porch, v_active,
h_pulse_width, h_period, h_back_porch, h_active, enable_present);
}
void imx233_lcdif_enable_frame_done_irq(bool en)
{
if(en)
BF_SET(LCDIF_CTRL1, CUR_FRAME_DONE_IRQ_EN);
else
BF_CLR(LCDIF_CTRL1, CUR_FRAME_DONE_IRQ_EN);
BF_CLR(LCDIF_CTRL1, CUR_FRAME_DONE_IRQ);
}
void imx233_lcdif_set_frame_done_cb(lcdif_irq_cb_t cb)
{
g_cur_frame_cb = cb;
}
void imx233_lcdif_enable_vsync_edge_irq(bool en)
{
if(en)
BF_SET(LCDIF_CTRL1, VSYNC_EDGE_IRQ_EN);
else
BF_CLR(LCDIF_CTRL1, VSYNC_EDGE_IRQ_EN);
BF_CLR(LCDIF_CTRL1, VSYNC_EDGE_IRQ);
}
void imx233_lcdif_set_vsync_edge_cb(lcdif_irq_cb_t cb)
{
g_vsync_edge_cb = cb;
}
#endif
#if IMX233_SUBTARGET >= 3780
void imx233_lcdif_set_lcd_databus_width(unsigned width)
{
switch(width)
{
case 8: BF_WR_V(LCDIF_CTRL, LCD_DATABUS_WIDTH, 8_BIT); break;
case 16: BF_WR_V(LCDIF_CTRL, LCD_DATABUS_WIDTH, 16_BIT); break;
case 18: BF_WR_V(LCDIF_CTRL, LCD_DATABUS_WIDTH, 18_BIT); break;
case 24: BF_WR_V(LCDIF_CTRL, LCD_DATABUS_WIDTH, 24_BIT); break;
default:
panicf("this chip cannot handle a lcd bus width of %d", width);
break;
}
}
void imx233_lcdif_enable_underflow_recover(bool enable)
{
if(enable)
BF_SET(LCDIF_CTRL1, RECOVER_ON_UNDERFLOW);
else
BF_CLR(LCDIF_CTRL1, RECOVER_ON_UNDERFLOW);
}
void imx233_lcdif_enable_bus_master(bool enable)
{
if(enable)
BF_SET(LCDIF_CTRL, LCDIF_MASTER);
else
BF_CLR(LCDIF_CTRL, LCDIF_MASTER);
}
#endif