b0940b1dd3
Change-Id: Ie3fe223ff40abff38bb9b09f398eb5411fa0be4c
397 lines
No EOL
13 KiB
C
397 lines
No EOL
13 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;
|
|
static lcdif_irq_cb_t g_underflow_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);
|
|
}
|
|
if(BF_RD(LCDIF_CTRL1, UNDERFLOW_IRQ))
|
|
{
|
|
if(g_underflow_cb)
|
|
g_underflow_cb();
|
|
BF_CLR(LCDIF_CTRL1, UNDERFLOW_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(len == 0)
|
|
return;
|
|
#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);
|
|
#else
|
|
(void) buf;
|
|
(void) width;
|
|
(void) height;
|
|
panicf("Unimplemented");
|
|
#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;
|
|
}
|
|
|
|
void imx233_lcdif_enable_underflow_irq(bool en)
|
|
{
|
|
if(en)
|
|
BF_SET(LCDIF_CTRL1, UNDERFLOW_IRQ_EN);
|
|
else
|
|
BF_CLR(LCDIF_CTRL1, UNDERFLOW_IRQ_EN);
|
|
BF_CLR(LCDIF_CTRL1, UNDERFLOW_IRQ);
|
|
}
|
|
|
|
void imx233_lcdif_set_underflow_cb(lcdif_irq_cb_t cb)
|
|
{
|
|
g_underflow_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 |