Home | History | Annotate | Download | only in video
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Freescale i.MX23/i.MX28 LCDIF driver
      4  *
      5  * Copyright (C) 2011-2013 Marek Vasut <marex (at) denx.de>
      6  */
      7 #include <common.h>
      8 #include <malloc.h>
      9 #include <video_fb.h>
     10 
     11 #include <asm/arch/imx-regs.h>
     12 #include <asm/arch/clock.h>
     13 #include <asm/arch/sys_proto.h>
     14 #include <linux/errno.h>
     15 #include <asm/io.h>
     16 
     17 #include <asm/mach-imx/dma.h>
     18 
     19 #include "videomodes.h"
     20 
     21 #define	PS2KHZ(ps)	(1000000000UL / (ps))
     22 
     23 static GraphicDevice panel;
     24 struct mxs_dma_desc desc;
     25 
     26 /**
     27  * mxsfb_system_setup() - Fine-tune LCDIF configuration
     28  *
     29  * This function is used to adjust the LCDIF configuration. This is usually
     30  * needed when driving the controller in System-Mode to operate an 8080 or
     31  * 6800 connected SmartLCD.
     32  */
     33 __weak void mxsfb_system_setup(void)
     34 {
     35 }
     36 
     37 /*
     38  * ARIES M28EVK:
     39  * setenv videomode
     40  * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066,
     41  *       le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0
     42  *
     43  * Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel:
     44  * setenv videomode
     45  * video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851,
     46  * 	 le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0
     47  */
     48 
     49 static void mxs_lcd_init(GraphicDevice *panel,
     50 			struct ctfb_res_modes *mode, int bpp)
     51 {
     52 	struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
     53 	uint32_t word_len = 0, bus_width = 0;
     54 	uint8_t valid_data = 0;
     55 
     56 	/* Kick in the LCDIF clock */
     57 	mxs_set_lcdclk(MXS_LCDIF_BASE, PS2KHZ(mode->pixclock));
     58 
     59 	/* Restart the LCDIF block */
     60 	mxs_reset_block(&regs->hw_lcdif_ctrl_reg);
     61 
     62 	switch (bpp) {
     63 	case 24:
     64 		word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
     65 		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT;
     66 		valid_data = 0x7;
     67 		break;
     68 	case 18:
     69 		word_len = LCDIF_CTRL_WORD_LENGTH_24BIT;
     70 		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT;
     71 		valid_data = 0x7;
     72 		break;
     73 	case 16:
     74 		word_len = LCDIF_CTRL_WORD_LENGTH_16BIT;
     75 		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT;
     76 		valid_data = 0xf;
     77 		break;
     78 	case 8:
     79 		word_len = LCDIF_CTRL_WORD_LENGTH_8BIT;
     80 		bus_width = LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT;
     81 		valid_data = 0xf;
     82 		break;
     83 	}
     84 
     85 	writel(bus_width | word_len | LCDIF_CTRL_DOTCLK_MODE |
     86 		LCDIF_CTRL_BYPASS_COUNT | LCDIF_CTRL_LCDIF_MASTER,
     87 		&regs->hw_lcdif_ctrl);
     88 
     89 	writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET,
     90 		&regs->hw_lcdif_ctrl1);
     91 
     92 	mxsfb_system_setup();
     93 
     94 	writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres,
     95 		&regs->hw_lcdif_transfer_count);
     96 
     97 	writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL |
     98 		LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
     99 		LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
    100 		mode->vsync_len, &regs->hw_lcdif_vdctrl0);
    101 	writel(mode->upper_margin + mode->lower_margin +
    102 		mode->vsync_len + mode->yres,
    103 		&regs->hw_lcdif_vdctrl1);
    104 	writel((mode->hsync_len << LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH_OFFSET) |
    105 		(mode->left_margin + mode->right_margin +
    106 		mode->hsync_len + mode->xres),
    107 		&regs->hw_lcdif_vdctrl2);
    108 	writel(((mode->left_margin + mode->hsync_len) <<
    109 		LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT_OFFSET) |
    110 		(mode->upper_margin + mode->vsync_len),
    111 		&regs->hw_lcdif_vdctrl3);
    112 	writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres,
    113 		&regs->hw_lcdif_vdctrl4);
    114 
    115 	writel(panel->frameAdrs, &regs->hw_lcdif_cur_buf);
    116 	writel(panel->frameAdrs, &regs->hw_lcdif_next_buf);
    117 
    118 	/* Flush FIFO first */
    119 	writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_set);
    120 
    121 #ifndef CONFIG_VIDEO_MXS_MODE_SYSTEM
    122 	/* Sync signals ON */
    123 	setbits_le32(&regs->hw_lcdif_vdctrl4, LCDIF_VDCTRL4_SYNC_SIGNALS_ON);
    124 #endif
    125 
    126 	/* FIFO cleared */
    127 	writel(LCDIF_CTRL1_FIFO_CLEAR, &regs->hw_lcdif_ctrl1_clr);
    128 
    129 	/* RUN! */
    130 	writel(LCDIF_CTRL_RUN, &regs->hw_lcdif_ctrl_set);
    131 }
    132 
    133 void lcdif_power_down(void)
    134 {
    135 	struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
    136 	int timeout = 1000000;
    137 
    138 	if (!panel.frameAdrs)
    139 		return;
    140 
    141 	writel(panel.frameAdrs, &regs->hw_lcdif_cur_buf_reg);
    142 	writel(panel.frameAdrs, &regs->hw_lcdif_next_buf_reg);
    143 	writel(LCDIF_CTRL1_VSYNC_EDGE_IRQ, &regs->hw_lcdif_ctrl1_clr);
    144 	while (--timeout) {
    145 		if (readl(&regs->hw_lcdif_ctrl1_reg) &
    146 		    LCDIF_CTRL1_VSYNC_EDGE_IRQ)
    147 			break;
    148 		udelay(1);
    149 	}
    150 	mxs_reset_block((struct mxs_register_32 *)&regs->hw_lcdif_ctrl_reg);
    151 }
    152 
    153 void *video_hw_init(void)
    154 {
    155 	int bpp = -1;
    156 	char *penv;
    157 	void *fb;
    158 	struct ctfb_res_modes mode;
    159 
    160 	puts("Video: ");
    161 
    162 	/* Suck display configuration from "videomode" variable */
    163 	penv = env_get("videomode");
    164 	if (!penv) {
    165 		puts("MXSFB: 'videomode' variable not set!\n");
    166 		return NULL;
    167 	}
    168 
    169 	bpp = video_get_params(&mode, penv);
    170 
    171 	/* fill in Graphic device struct */
    172 	sprintf(panel.modeIdent, "%dx%dx%d",
    173 			mode.xres, mode.yres, bpp);
    174 
    175 	panel.winSizeX = mode.xres;
    176 	panel.winSizeY = mode.yres;
    177 	panel.plnSizeX = mode.xres;
    178 	panel.plnSizeY = mode.yres;
    179 
    180 	switch (bpp) {
    181 	case 24:
    182 	case 18:
    183 		panel.gdfBytesPP = 4;
    184 		panel.gdfIndex = GDF_32BIT_X888RGB;
    185 		break;
    186 	case 16:
    187 		panel.gdfBytesPP = 2;
    188 		panel.gdfIndex = GDF_16BIT_565RGB;
    189 		break;
    190 	case 8:
    191 		panel.gdfBytesPP = 1;
    192 		panel.gdfIndex = GDF__8BIT_INDEX;
    193 		break;
    194 	default:
    195 		printf("MXSFB: Invalid BPP specified! (bpp = %i)\n", bpp);
    196 		return NULL;
    197 	}
    198 
    199 	panel.memSize = mode.xres * mode.yres * panel.gdfBytesPP;
    200 
    201 	/* Allocate framebuffer */
    202 	fb = memalign(ARCH_DMA_MINALIGN,
    203 		      roundup(panel.memSize, ARCH_DMA_MINALIGN));
    204 	if (!fb) {
    205 		printf("MXSFB: Error allocating framebuffer!\n");
    206 		return NULL;
    207 	}
    208 
    209 	/* Wipe framebuffer */
    210 	memset(fb, 0, panel.memSize);
    211 
    212 	panel.frameAdrs = (u32)fb;
    213 
    214 	printf("%s\n", panel.modeIdent);
    215 
    216 	/* Start framebuffer */
    217 	mxs_lcd_init(&panel, &mode, bpp);
    218 
    219 #ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM
    220 	/*
    221 	 * If the LCD runs in system mode, the LCD refresh has to be triggered
    222 	 * manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid
    223 	 * having to set this bit manually after every single change in the
    224 	 * framebuffer memory, we set up specially crafted circular DMA, which
    225 	 * sets the RUN bit, then waits until it gets cleared and repeats this
    226 	 * infinitelly. This way, we get smooth continuous updates of the LCD.
    227 	 */
    228 	struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE;
    229 
    230 	memset(&desc, 0, sizeof(struct mxs_dma_desc));
    231 	desc.address = (dma_addr_t)&desc;
    232 	desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
    233 			MXS_DMA_DESC_WAIT4END |
    234 			(1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
    235 	desc.cmd.pio_words[0] = readl(&regs->hw_lcdif_ctrl) | LCDIF_CTRL_RUN;
    236 	desc.cmd.next = (uint32_t)&desc.cmd;
    237 
    238 	/* Execute the DMA chain. */
    239 	mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc);
    240 #endif
    241 
    242 	return (void *)&panel;
    243 }
    244