cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lcdc.c (17967B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * OMAP1 internal LCD controller
      4 *
      5 * Copyright (C) 2004 Nokia Corporation
      6 * Author: Imre Deak <imre.deak@nokia.com>
      7 */
      8#include <linux/module.h>
      9#include <linux/device.h>
     10#include <linux/interrupt.h>
     11#include <linux/spinlock.h>
     12#include <linux/err.h>
     13#include <linux/mm.h>
     14#include <linux/fb.h>
     15#include <linux/dma-mapping.h>
     16#include <linux/vmalloc.h>
     17#include <linux/clk.h>
     18#include <linux/gfp.h>
     19
     20#include <linux/soc/ti/omap1-io.h>
     21#include <linux/soc/ti/omap1-soc.h>
     22#include <linux/omap-dma.h>
     23
     24#include <asm/mach-types.h>
     25
     26#include "omapfb.h"
     27
     28#include "lcdc.h"
     29#include "lcd_dma.h"
     30
     31#define MODULE_NAME			"lcdc"
     32
     33#define MAX_PALETTE_SIZE		PAGE_SIZE
     34
     35enum lcdc_load_mode {
     36	OMAP_LCDC_LOAD_PALETTE,
     37	OMAP_LCDC_LOAD_FRAME,
     38	OMAP_LCDC_LOAD_PALETTE_AND_FRAME
     39};
     40
     41static struct omap_lcd_controller {
     42	enum omapfb_update_mode	update_mode;
     43	int			ext_mode;
     44
     45	unsigned long		frame_offset;
     46	int			screen_width;
     47	int			xres;
     48	int			yres;
     49
     50	enum omapfb_color_format	color_mode;
     51	int			bpp;
     52	void			*palette_virt;
     53	dma_addr_t		palette_phys;
     54	int			palette_code;
     55	int			palette_size;
     56
     57	unsigned int		irq_mask;
     58	struct completion	last_frame_complete;
     59	struct completion	palette_load_complete;
     60	struct clk		*lcd_ck;
     61	struct omapfb_device	*fbdev;
     62
     63	void			(*dma_callback)(void *data);
     64	void			*dma_callback_data;
     65
     66	dma_addr_t		vram_phys;
     67	void			*vram_virt;
     68	unsigned long		vram_size;
     69} lcdc;
     70
     71static inline void enable_irqs(int mask)
     72{
     73	lcdc.irq_mask |= mask;
     74}
     75
     76static inline void disable_irqs(int mask)
     77{
     78	lcdc.irq_mask &= ~mask;
     79}
     80
     81static void set_load_mode(enum lcdc_load_mode mode)
     82{
     83	u32 l;
     84
     85	l = omap_readl(OMAP_LCDC_CONTROL);
     86	l &= ~(3 << 20);
     87	switch (mode) {
     88	case OMAP_LCDC_LOAD_PALETTE:
     89		l |= 1 << 20;
     90		break;
     91	case OMAP_LCDC_LOAD_FRAME:
     92		l |= 2 << 20;
     93		break;
     94	case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:
     95		break;
     96	default:
     97		BUG();
     98	}
     99	omap_writel(l, OMAP_LCDC_CONTROL);
    100}
    101
    102static void enable_controller(void)
    103{
    104	u32 l;
    105
    106	l = omap_readl(OMAP_LCDC_CONTROL);
    107	l |= OMAP_LCDC_CTRL_LCD_EN;
    108	l &= ~OMAP_LCDC_IRQ_MASK;
    109	l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE;	/* enabled IRQs */
    110	omap_writel(l, OMAP_LCDC_CONTROL);
    111}
    112
    113static void disable_controller_async(void)
    114{
    115	u32 l;
    116	u32 mask;
    117
    118	l = omap_readl(OMAP_LCDC_CONTROL);
    119	mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;
    120	/*
    121	 * Preserve the DONE mask, since we still want to get the
    122	 * final DONE irq. It will be disabled in the IRQ handler.
    123	 */
    124	mask &= ~OMAP_LCDC_IRQ_DONE;
    125	l &= ~mask;
    126	omap_writel(l, OMAP_LCDC_CONTROL);
    127}
    128
    129static void disable_controller(void)
    130{
    131	init_completion(&lcdc.last_frame_complete);
    132	disable_controller_async();
    133	if (!wait_for_completion_timeout(&lcdc.last_frame_complete,
    134				msecs_to_jiffies(500)))
    135		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
    136}
    137
    138static void reset_controller(u32 status)
    139{
    140	static unsigned long reset_count;
    141	static unsigned long last_jiffies;
    142
    143	disable_controller_async();
    144	reset_count++;
    145	if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) {
    146		dev_err(lcdc.fbdev->dev,
    147			  "resetting (status %#010x,reset count %lu)\n",
    148			  status, reset_count);
    149		last_jiffies = jiffies;
    150	}
    151	if (reset_count < 100) {
    152		enable_controller();
    153	} else {
    154		reset_count = 0;
    155		dev_err(lcdc.fbdev->dev,
    156			"too many reset attempts, giving up.\n");
    157	}
    158}
    159
    160/*
    161 * Configure the LCD DMA according to the current mode specified by parameters
    162 * in lcdc.fbdev and fbdev->var.
    163 */
    164static void setup_lcd_dma(void)
    165{
    166	static const int dma_elem_type[] = {
    167		0,
    168		OMAP_DMA_DATA_TYPE_S8,
    169		OMAP_DMA_DATA_TYPE_S16,
    170		0,
    171		OMAP_DMA_DATA_TYPE_S32,
    172	};
    173	struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par;
    174	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
    175	unsigned long	src;
    176	int		esize, xelem, yelem;
    177
    178	src = lcdc.vram_phys + lcdc.frame_offset;
    179
    180	switch (var->rotate) {
    181	case 0:
    182		if (plane->info.mirror || (src & 3) ||
    183		    lcdc.color_mode == OMAPFB_COLOR_YUV420 ||
    184		    (lcdc.xres & 1))
    185			esize = 2;
    186		else
    187			esize = 4;
    188		xelem = lcdc.xres * lcdc.bpp / 8 / esize;
    189		yelem = lcdc.yres;
    190		break;
    191	case 90:
    192	case 180:
    193	case 270:
    194		if (cpu_is_omap15xx()) {
    195			BUG();
    196		}
    197		esize = 2;
    198		xelem = lcdc.yres * lcdc.bpp / 16;
    199		yelem = lcdc.xres;
    200		break;
    201	default:
    202		BUG();
    203		return;
    204	}
    205#ifdef VERBOSE
    206	dev_dbg(lcdc.fbdev->dev,
    207		 "setup_dma: src %#010lx esize %d xelem %d yelem %d\n",
    208		 src, esize, xelem, yelem);
    209#endif
    210	omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);
    211	if (!cpu_is_omap15xx()) {
    212		int bpp = lcdc.bpp;
    213
    214		/*
    215		 * YUV support is only for external mode when we have the
    216		 * YUV window embedded in a 16bpp frame buffer.
    217		 */
    218		if (lcdc.color_mode == OMAPFB_COLOR_YUV420)
    219			bpp = 16;
    220		/* Set virtual xres elem size */
    221		omap_set_lcd_dma_b1_vxres(
    222			lcdc.screen_width * bpp / 8 / esize);
    223		/* Setup transformations */
    224		omap_set_lcd_dma_b1_rotation(var->rotate);
    225		omap_set_lcd_dma_b1_mirror(plane->info.mirror);
    226	}
    227	omap_setup_lcd_dma();
    228}
    229
    230static irqreturn_t lcdc_irq_handler(int irq, void *dev_id)
    231{
    232	u32 status;
    233
    234	status = omap_readl(OMAP_LCDC_STATUS);
    235
    236	if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))
    237		reset_controller(status);
    238	else {
    239		if (status & OMAP_LCDC_STAT_DONE) {
    240			u32 l;
    241
    242			/*
    243			 * Disable IRQ_DONE. The status bit will be cleared
    244			 * only when the controller is reenabled and we don't
    245			 * want to get more interrupts.
    246			 */
    247			l = omap_readl(OMAP_LCDC_CONTROL);
    248			l &= ~OMAP_LCDC_IRQ_DONE;
    249			omap_writel(l, OMAP_LCDC_CONTROL);
    250			complete(&lcdc.last_frame_complete);
    251		}
    252		if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {
    253			disable_controller_async();
    254			complete(&lcdc.palette_load_complete);
    255		}
    256	}
    257
    258	/*
    259	 * Clear these interrupt status bits.
    260	 * Sync_lost, FUF bits were cleared by disabling the LCD controller
    261	 * LOADED_PALETTE can be cleared this way only in palette only
    262	 * load mode. In other load modes it's cleared by disabling the
    263	 * controller.
    264	 */
    265	status &= ~(OMAP_LCDC_STAT_VSYNC |
    266		    OMAP_LCDC_STAT_LOADED_PALETTE |
    267		    OMAP_LCDC_STAT_ABC |
    268		    OMAP_LCDC_STAT_LINE_INT);
    269	omap_writel(status, OMAP_LCDC_STATUS);
    270	return IRQ_HANDLED;
    271}
    272
    273/*
    274 * Change to a new video mode. We defer this to a later time to avoid any
    275 * flicker and not to mess up the current LCD DMA context. For this we disable
    276 * the LCD controller, which will generate a DONE irq after the last frame has
    277 * been transferred. Then it'll be safe to reconfigure both the LCD controller
    278 * as well as the LCD DMA.
    279 */
    280static int omap_lcdc_setup_plane(int plane, int channel_out,
    281				 unsigned long offset, int screen_width,
    282				 int pos_x, int pos_y, int width, int height,
    283				 int color_mode)
    284{
    285	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
    286	struct lcd_panel *panel = lcdc.fbdev->panel;
    287	int rot_x, rot_y;
    288
    289	if (var->rotate == 0) {
    290		rot_x = panel->x_res;
    291		rot_y = panel->y_res;
    292	} else {
    293		rot_x = panel->y_res;
    294		rot_y = panel->x_res;
    295	}
    296	if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||
    297	    width > rot_x || height > rot_y) {
    298#ifdef VERBOSE
    299		dev_dbg(lcdc.fbdev->dev,
    300			"invalid plane params plane %d pos_x %d pos_y %d "
    301			"w %d h %d\n", plane, pos_x, pos_y, width, height);
    302#endif
    303		return -EINVAL;
    304	}
    305
    306	lcdc.frame_offset = offset;
    307	lcdc.xres = width;
    308	lcdc.yres = height;
    309	lcdc.screen_width = screen_width;
    310	lcdc.color_mode = color_mode;
    311
    312	switch (color_mode) {
    313	case OMAPFB_COLOR_CLUT_8BPP:
    314		lcdc.bpp = 8;
    315		lcdc.palette_code = 0x3000;
    316		lcdc.palette_size = 512;
    317		break;
    318	case OMAPFB_COLOR_RGB565:
    319		lcdc.bpp = 16;
    320		lcdc.palette_code = 0x4000;
    321		lcdc.palette_size = 32;
    322		break;
    323	case OMAPFB_COLOR_RGB444:
    324		lcdc.bpp = 16;
    325		lcdc.palette_code = 0x4000;
    326		lcdc.palette_size = 32;
    327		break;
    328	case OMAPFB_COLOR_YUV420:
    329		if (lcdc.ext_mode) {
    330			lcdc.bpp = 12;
    331			break;
    332		}
    333		fallthrough;
    334	case OMAPFB_COLOR_YUV422:
    335		if (lcdc.ext_mode) {
    336			lcdc.bpp = 16;
    337			break;
    338		}
    339		fallthrough;
    340	default:
    341		/* FIXME: other BPPs.
    342		 * bpp1: code  0,     size 256
    343		 * bpp2: code  0x1000 size 256
    344		 * bpp4: code  0x2000 size 256
    345		 * bpp12: code 0x4000 size 32
    346		 */
    347		dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode);
    348		BUG();
    349		return -1;
    350	}
    351
    352	if (lcdc.ext_mode) {
    353		setup_lcd_dma();
    354		return 0;
    355	}
    356
    357	if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
    358		disable_controller();
    359		omap_stop_lcd_dma();
    360		setup_lcd_dma();
    361		enable_controller();
    362	}
    363
    364	return 0;
    365}
    366
    367static int omap_lcdc_enable_plane(int plane, int enable)
    368{
    369	dev_dbg(lcdc.fbdev->dev,
    370		"plane %d enable %d update_mode %d ext_mode %d\n",
    371		plane, enable, lcdc.update_mode, lcdc.ext_mode);
    372	if (plane != OMAPFB_PLANE_GFX)
    373		return -EINVAL;
    374
    375	return 0;
    376}
    377
    378/*
    379 * Configure the LCD DMA for a palette load operation and do the palette
    380 * downloading synchronously. We don't use the frame+palette load mode of
    381 * the controller, since the palette can always be downloaded separately.
    382 */
    383static void load_palette(void)
    384{
    385	u16	*palette;
    386
    387	palette = (u16 *)lcdc.palette_virt;
    388
    389	*(u16 *)palette &= 0x0fff;
    390	*(u16 *)palette |= lcdc.palette_code;
    391
    392	omap_set_lcd_dma_b1(lcdc.palette_phys,
    393		lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
    394
    395	omap_set_lcd_dma_single_transfer(1);
    396	omap_setup_lcd_dma();
    397
    398	init_completion(&lcdc.palette_load_complete);
    399	enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
    400	set_load_mode(OMAP_LCDC_LOAD_PALETTE);
    401	enable_controller();
    402	if (!wait_for_completion_timeout(&lcdc.palette_load_complete,
    403				msecs_to_jiffies(500)))
    404		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
    405	/* The controller gets disabled in the irq handler */
    406	disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
    407	omap_stop_lcd_dma();
    408
    409	omap_set_lcd_dma_single_transfer(lcdc.ext_mode);
    410}
    411
    412/* Used only in internal controller mode */
    413static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
    414			       u16 transp, int update_hw_pal)
    415{
    416	u16 *palette;
    417
    418	if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
    419		return -EINVAL;
    420
    421	palette = (u16 *)lcdc.palette_virt;
    422
    423	palette[regno] &= ~0x0fff;
    424	palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
    425			   (blue >> 12);
    426
    427	if (update_hw_pal) {
    428		disable_controller();
    429		omap_stop_lcd_dma();
    430		load_palette();
    431		setup_lcd_dma();
    432		set_load_mode(OMAP_LCDC_LOAD_FRAME);
    433		enable_controller();
    434	}
    435
    436	return 0;
    437}
    438
    439static void calc_ck_div(int is_tft, int pck, int *pck_div)
    440{
    441	unsigned long lck;
    442
    443	pck = max(1, pck);
    444	lck = clk_get_rate(lcdc.lcd_ck);
    445	*pck_div = (lck + pck - 1) / pck;
    446	if (is_tft)
    447		*pck_div = max(2, *pck_div);
    448	else
    449		*pck_div = max(3, *pck_div);
    450	if (*pck_div > 255) {
    451		/* FIXME: try to adjust logic clock divider as well */
    452		*pck_div = 255;
    453		dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n",
    454			 pck / 1000);
    455	}
    456}
    457
    458static inline void setup_regs(void)
    459{
    460	u32 l;
    461	struct lcd_panel *panel = lcdc.fbdev->panel;
    462	int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
    463	unsigned long lck;
    464	int pcd;
    465
    466	l = omap_readl(OMAP_LCDC_CONTROL);
    467	l &= ~OMAP_LCDC_CTRL_LCD_TFT;
    468	l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0;
    469#ifdef CONFIG_MACH_OMAP_PALMTE
    470/* FIXME:if (machine_is_omap_palmte()) { */
    471		/* PalmTE uses alternate TFT setting in 8BPP mode */
    472		l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0;
    473/*	} */
    474#endif
    475	omap_writel(l, OMAP_LCDC_CONTROL);
    476
    477	l = omap_readl(OMAP_LCDC_TIMING2);
    478	l &= ~(((1 << 6) - 1) << 20);
    479	l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20;
    480	omap_writel(l, OMAP_LCDC_TIMING2);
    481
    482	l = panel->x_res - 1;
    483	l |= (panel->hsw - 1) << 10;
    484	l |= (panel->hfp - 1) << 16;
    485	l |= (panel->hbp - 1) << 24;
    486	omap_writel(l, OMAP_LCDC_TIMING0);
    487
    488	l = panel->y_res - 1;
    489	l |= (panel->vsw - 1) << 10;
    490	l |= panel->vfp << 16;
    491	l |= panel->vbp << 24;
    492	omap_writel(l, OMAP_LCDC_TIMING1);
    493
    494	l = omap_readl(OMAP_LCDC_TIMING2);
    495	l &= ~0xff;
    496
    497	lck = clk_get_rate(lcdc.lcd_ck);
    498
    499	if (!panel->pcd)
    500		calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd);
    501	else {
    502		dev_warn(lcdc.fbdev->dev,
    503		    "Pixel clock divider value is obsolete.\n"
    504		    "Try to set pixel_clock to %lu and pcd to 0 "
    505		    "in drivers/video/omap/lcd_%s.c and submit a patch.\n",
    506			lck / panel->pcd / 1000, panel->name);
    507
    508		pcd = panel->pcd;
    509	}
    510	l |= pcd & 0xff;
    511	l |= panel->acb << 8;
    512	omap_writel(l, OMAP_LCDC_TIMING2);
    513
    514	/* update panel info with the exact clock */
    515	panel->pixel_clock = lck / pcd / 1000;
    516}
    517
    518/*
    519 * Configure the LCD controller, download the color palette and start a looped
    520 * DMA transfer of the frame image data. Called only in internal
    521 * controller mode.
    522 */
    523static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode)
    524{
    525	int r = 0;
    526
    527	if (mode != lcdc.update_mode) {
    528		switch (mode) {
    529		case OMAPFB_AUTO_UPDATE:
    530			setup_regs();
    531			load_palette();
    532
    533			/* Setup and start LCD DMA */
    534			setup_lcd_dma();
    535
    536			set_load_mode(OMAP_LCDC_LOAD_FRAME);
    537			enable_irqs(OMAP_LCDC_IRQ_DONE);
    538			/* This will start the actual DMA transfer */
    539			enable_controller();
    540			lcdc.update_mode = mode;
    541			break;
    542		case OMAPFB_UPDATE_DISABLED:
    543			disable_controller();
    544			omap_stop_lcd_dma();
    545			lcdc.update_mode = mode;
    546			break;
    547		default:
    548			r = -EINVAL;
    549		}
    550	}
    551
    552	return r;
    553}
    554
    555static enum omapfb_update_mode omap_lcdc_get_update_mode(void)
    556{
    557	return lcdc.update_mode;
    558}
    559
    560/* PM code called only in internal controller mode */
    561static void omap_lcdc_suspend(void)
    562{
    563	omap_lcdc_set_update_mode(OMAPFB_UPDATE_DISABLED);
    564}
    565
    566static void omap_lcdc_resume(void)
    567{
    568	omap_lcdc_set_update_mode(OMAPFB_AUTO_UPDATE);
    569}
    570
    571static void omap_lcdc_get_caps(int plane, struct omapfb_caps *caps)
    572{
    573	return;
    574}
    575
    576int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data)
    577{
    578	BUG_ON(callback == NULL);
    579
    580	if (lcdc.dma_callback)
    581		return -EBUSY;
    582	else {
    583		lcdc.dma_callback = callback;
    584		lcdc.dma_callback_data = data;
    585	}
    586	return 0;
    587}
    588EXPORT_SYMBOL(omap_lcdc_set_dma_callback);
    589
    590void omap_lcdc_free_dma_callback(void)
    591{
    592	lcdc.dma_callback = NULL;
    593}
    594EXPORT_SYMBOL(omap_lcdc_free_dma_callback);
    595
    596static void lcdc_dma_handler(u16 status, void *data)
    597{
    598	if (lcdc.dma_callback)
    599		lcdc.dma_callback(lcdc.dma_callback_data);
    600}
    601
    602static int alloc_palette_ram(void)
    603{
    604	lcdc.palette_virt = dma_alloc_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE,
    605					 &lcdc.palette_phys, GFP_KERNEL);
    606	if (lcdc.palette_virt == NULL) {
    607		dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n");
    608		return -ENOMEM;
    609	}
    610	memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE);
    611
    612	return 0;
    613}
    614
    615static void free_palette_ram(void)
    616{
    617	dma_free_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE, lcdc.palette_virt,
    618		    lcdc.palette_phys);
    619}
    620
    621static int alloc_fbmem(struct omapfb_mem_region *region)
    622{
    623	int bpp;
    624	int frame_size;
    625	struct lcd_panel *panel = lcdc.fbdev->panel;
    626
    627	bpp = panel->bpp;
    628	if (bpp == 12)
    629		bpp = 16;
    630	frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res);
    631	if (region->size > frame_size)
    632		frame_size = region->size;
    633	lcdc.vram_size = frame_size;
    634	lcdc.vram_virt = dma_alloc_wc(lcdc.fbdev->dev, lcdc.vram_size,
    635				      &lcdc.vram_phys, GFP_KERNEL);
    636	if (lcdc.vram_virt == NULL) {
    637		dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n");
    638		return -ENOMEM;
    639	}
    640	region->size = frame_size;
    641	region->paddr = lcdc.vram_phys;
    642	region->vaddr = lcdc.vram_virt;
    643	region->alloc = 1;
    644
    645	memset(lcdc.vram_virt, 0, lcdc.vram_size);
    646
    647	return 0;
    648}
    649
    650static void free_fbmem(void)
    651{
    652	dma_free_wc(lcdc.fbdev->dev, lcdc.vram_size, lcdc.vram_virt,
    653		    lcdc.vram_phys);
    654}
    655
    656static int setup_fbmem(struct omapfb_mem_desc *req_md)
    657{
    658	if (!req_md->region_cnt) {
    659		dev_err(lcdc.fbdev->dev, "no memory regions defined\n");
    660		return -EINVAL;
    661	}
    662
    663	if (req_md->region_cnt > 1) {
    664		dev_err(lcdc.fbdev->dev, "only one plane is supported\n");
    665		req_md->region_cnt = 1;
    666	}
    667
    668	return alloc_fbmem(&req_md->region[0]);
    669}
    670
    671static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
    672			  struct omapfb_mem_desc *req_vram)
    673{
    674	int r;
    675	u32 l;
    676	int rate;
    677	struct clk *tc_ck;
    678
    679	lcdc.irq_mask = 0;
    680
    681	lcdc.fbdev = fbdev;
    682	lcdc.ext_mode = ext_mode;
    683
    684	l = 0;
    685	omap_writel(l, OMAP_LCDC_CONTROL);
    686
    687	/* FIXME:
    688	 * According to errata some platforms have a clock rate limitiation
    689	 */
    690	lcdc.lcd_ck = clk_get(fbdev->dev, "lcd_ck");
    691	if (IS_ERR(lcdc.lcd_ck)) {
    692		dev_err(fbdev->dev, "unable to access LCD clock\n");
    693		r = PTR_ERR(lcdc.lcd_ck);
    694		goto fail0;
    695	}
    696
    697	tc_ck = clk_get(fbdev->dev, "tc_ck");
    698	if (IS_ERR(tc_ck)) {
    699		dev_err(fbdev->dev, "unable to access TC clock\n");
    700		r = PTR_ERR(tc_ck);
    701		goto fail1;
    702	}
    703
    704	rate = clk_get_rate(tc_ck);
    705	clk_put(tc_ck);
    706
    707	if (machine_is_ams_delta())
    708		rate /= 4;
    709	if (machine_is_omap_h3())
    710		rate /= 3;
    711	r = clk_set_rate(lcdc.lcd_ck, rate);
    712	if (r) {
    713		dev_err(fbdev->dev, "failed to adjust LCD rate\n");
    714		goto fail1;
    715	}
    716	clk_prepare_enable(lcdc.lcd_ck);
    717
    718	r = request_irq(fbdev->int_irq, lcdc_irq_handler, 0, MODULE_NAME, fbdev);
    719	if (r) {
    720		dev_err(fbdev->dev, "unable to get IRQ\n");
    721		goto fail2;
    722	}
    723
    724	r = omap_request_lcd_dma(lcdc_dma_handler, NULL);
    725	if (r) {
    726		dev_err(fbdev->dev, "unable to get LCD DMA\n");
    727		goto fail3;
    728	}
    729
    730	omap_set_lcd_dma_single_transfer(ext_mode);
    731	omap_set_lcd_dma_ext_controller(ext_mode);
    732
    733	if (!ext_mode)
    734		if ((r = alloc_palette_ram()) < 0)
    735			goto fail4;
    736
    737	if ((r = setup_fbmem(req_vram)) < 0)
    738		goto fail5;
    739
    740	pr_info("omapfb: LCDC initialized\n");
    741
    742	return 0;
    743fail5:
    744	if (!ext_mode)
    745		free_palette_ram();
    746fail4:
    747	omap_free_lcd_dma();
    748fail3:
    749	free_irq(fbdev->int_irq, lcdc.fbdev);
    750fail2:
    751	clk_disable_unprepare(lcdc.lcd_ck);
    752fail1:
    753	clk_put(lcdc.lcd_ck);
    754fail0:
    755	return r;
    756}
    757
    758static void omap_lcdc_cleanup(void)
    759{
    760	if (!lcdc.ext_mode)
    761		free_palette_ram();
    762	free_fbmem();
    763	omap_free_lcd_dma();
    764	free_irq(lcdc.fbdev->int_irq, lcdc.fbdev);
    765	clk_disable_unprepare(lcdc.lcd_ck);
    766	clk_put(lcdc.lcd_ck);
    767}
    768
    769const struct lcd_ctrl omap1_int_ctrl = {
    770	.name			= "internal",
    771	.init			= omap_lcdc_init,
    772	.cleanup		= omap_lcdc_cleanup,
    773	.get_caps		= omap_lcdc_get_caps,
    774	.set_update_mode	= omap_lcdc_set_update_mode,
    775	.get_update_mode	= omap_lcdc_get_update_mode,
    776	.update_window		= NULL,
    777	.suspend		= omap_lcdc_suspend,
    778	.resume			= omap_lcdc_resume,
    779	.setup_plane		= omap_lcdc_setup_plane,
    780	.enable_plane		= omap_lcdc_enable_plane,
    781	.setcolreg		= omap_lcdc_setcolreg,
    782};