/*************************************************************************** * __________ __ ___. * 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" #include "regs/lcdif.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) { BF_WR_ALL(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(LCDIF_CTRL, WORD_LENGTH_V(8_BIT)); break; case 16: BF_WR(LCDIF_CTRL, WORD_LENGTH_V(16_BIT)); break; #if IMX233_SUBTARGET >= 3780 case 18: BF_WR(LCDIF_CTRL, WORD_LENGTH_V(18_BIT)); break; case 24: BF_WR(LCDIF_CTRL, WORD_LENGTH_V(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 BF_WR_ALL(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; BF_WR_ALL(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) { BF_WR_ALL(LCDIF_VDCTRL0, ENABLE_PRESENT(enable_present), VSYNC_PERIOD_UNIT(1), VSYNC_PULSE_WIDTH_UNIT(1), DOTCLK_V_VALID_DATA_CNT(v_active)); BF_WR_ALL(LCDIF_VDCTRL1, VSYNC_PERIOD(v_period), VSYNC_PULSE_WIDTH(v_pulse_width)); BF_WR_ALL(LCDIF_VDCTRL2, HSYNC_PULSE_WIDTH(h_pulse_width), HSYNC_PERIOD(h_period), DOTCLK_H_VALID_DATA_CNT(h_active)); BF_WR_ALL(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 BF_SET(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(LCDIF_CTRL, LCD_DATABUS_WIDTH_V(8_BIT)); break; case 16: BF_WR(LCDIF_CTRL, LCD_DATABUS_WIDTH_V(16_BIT)); break; case 18: BF_WR(LCDIF_CTRL, LCD_DATABUS_WIDTH_V(18_BIT)); break; case 24: BF_WR(LCDIF_CTRL, LCD_DATABUS_WIDTH_V(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