162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP1 internal LCD controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Nokia Corporation 662306a36Sopenharmony_ci * Author: Imre Deak <imre.deak@nokia.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/fb.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/gfp.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/soc/ti/omap1-io.h> 2162306a36Sopenharmony_ci#include <linux/soc/ti/omap1-soc.h> 2262306a36Sopenharmony_ci#include <linux/omap-dma.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/mach-types.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "omapfb.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "lcdc.h" 2962306a36Sopenharmony_ci#include "lcd_dma.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MODULE_NAME "lcdc" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MAX_PALETTE_SIZE PAGE_SIZE 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum lcdc_load_mode { 3662306a36Sopenharmony_ci OMAP_LCDC_LOAD_PALETTE, 3762306a36Sopenharmony_ci OMAP_LCDC_LOAD_FRAME, 3862306a36Sopenharmony_ci OMAP_LCDC_LOAD_PALETTE_AND_FRAME 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic struct omap_lcd_controller { 4262306a36Sopenharmony_ci enum omapfb_update_mode update_mode; 4362306a36Sopenharmony_ci int ext_mode; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci unsigned long frame_offset; 4662306a36Sopenharmony_ci int screen_width; 4762306a36Sopenharmony_ci int xres; 4862306a36Sopenharmony_ci int yres; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci enum omapfb_color_format color_mode; 5162306a36Sopenharmony_ci int bpp; 5262306a36Sopenharmony_ci void *palette_virt; 5362306a36Sopenharmony_ci dma_addr_t palette_phys; 5462306a36Sopenharmony_ci int palette_code; 5562306a36Sopenharmony_ci int palette_size; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci unsigned int irq_mask; 5862306a36Sopenharmony_ci struct completion last_frame_complete; 5962306a36Sopenharmony_ci struct completion palette_load_complete; 6062306a36Sopenharmony_ci struct clk *lcd_ck; 6162306a36Sopenharmony_ci struct omapfb_device *fbdev; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci void (*dma_callback)(void *data); 6462306a36Sopenharmony_ci void *dma_callback_data; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci dma_addr_t vram_phys; 6762306a36Sopenharmony_ci void *vram_virt; 6862306a36Sopenharmony_ci unsigned long vram_size; 6962306a36Sopenharmony_ci} lcdc; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline void enable_irqs(int mask) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci lcdc.irq_mask |= mask; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void disable_irqs(int mask) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci lcdc.irq_mask &= ~mask; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void set_load_mode(enum lcdc_load_mode mode) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u32 l; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_CONTROL); 8662306a36Sopenharmony_ci l &= ~(3 << 20); 8762306a36Sopenharmony_ci switch (mode) { 8862306a36Sopenharmony_ci case OMAP_LCDC_LOAD_PALETTE: 8962306a36Sopenharmony_ci l |= 1 << 20; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case OMAP_LCDC_LOAD_FRAME: 9262306a36Sopenharmony_ci l |= 2 << 20; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case OMAP_LCDC_LOAD_PALETTE_AND_FRAME: 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci default: 9762306a36Sopenharmony_ci BUG(); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void enable_controller(void) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u32 l; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_CONTROL); 10762306a36Sopenharmony_ci l |= OMAP_LCDC_CTRL_LCD_EN; 10862306a36Sopenharmony_ci l &= ~OMAP_LCDC_IRQ_MASK; 10962306a36Sopenharmony_ci l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */ 11062306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic void disable_controller_async(void) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 l; 11662306a36Sopenharmony_ci u32 mask; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_CONTROL); 11962306a36Sopenharmony_ci mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK; 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Preserve the DONE mask, since we still want to get the 12262306a36Sopenharmony_ci * final DONE irq. It will be disabled in the IRQ handler. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci mask &= ~OMAP_LCDC_IRQ_DONE; 12562306a36Sopenharmony_ci l &= ~mask; 12662306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void disable_controller(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci init_completion(&lcdc.last_frame_complete); 13262306a36Sopenharmony_ci disable_controller_async(); 13362306a36Sopenharmony_ci if (!wait_for_completion_timeout(&lcdc.last_frame_complete, 13462306a36Sopenharmony_ci msecs_to_jiffies(500))) 13562306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n"); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void reset_controller(u32 status) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci static unsigned long reset_count; 14162306a36Sopenharmony_ci static unsigned long last_jiffies; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci disable_controller_async(); 14462306a36Sopenharmony_ci reset_count++; 14562306a36Sopenharmony_ci if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) { 14662306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, 14762306a36Sopenharmony_ci "resetting (status %#010x,reset count %lu)\n", 14862306a36Sopenharmony_ci status, reset_count); 14962306a36Sopenharmony_ci last_jiffies = jiffies; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (reset_count < 100) { 15262306a36Sopenharmony_ci enable_controller(); 15362306a36Sopenharmony_ci } else { 15462306a36Sopenharmony_ci reset_count = 0; 15562306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, 15662306a36Sopenharmony_ci "too many reset attempts, giving up.\n"); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * Configure the LCD DMA according to the current mode specified by parameters 16262306a36Sopenharmony_ci * in lcdc.fbdev and fbdev->var. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic void setup_lcd_dma(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci static const int dma_elem_type[] = { 16762306a36Sopenharmony_ci 0, 16862306a36Sopenharmony_ci OMAP_DMA_DATA_TYPE_S8, 16962306a36Sopenharmony_ci OMAP_DMA_DATA_TYPE_S16, 17062306a36Sopenharmony_ci 0, 17162306a36Sopenharmony_ci OMAP_DMA_DATA_TYPE_S32, 17262306a36Sopenharmony_ci }; 17362306a36Sopenharmony_ci struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par; 17462306a36Sopenharmony_ci struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var; 17562306a36Sopenharmony_ci unsigned long src; 17662306a36Sopenharmony_ci int esize, xelem, yelem; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci src = lcdc.vram_phys + lcdc.frame_offset; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci switch (var->rotate) { 18162306a36Sopenharmony_ci case 0: 18262306a36Sopenharmony_ci if (plane->info.mirror || (src & 3) || 18362306a36Sopenharmony_ci lcdc.color_mode == OMAPFB_COLOR_YUV420 || 18462306a36Sopenharmony_ci (lcdc.xres & 1)) 18562306a36Sopenharmony_ci esize = 2; 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci esize = 4; 18862306a36Sopenharmony_ci xelem = lcdc.xres * lcdc.bpp / 8 / esize; 18962306a36Sopenharmony_ci yelem = lcdc.yres; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case 90: 19262306a36Sopenharmony_ci case 180: 19362306a36Sopenharmony_ci case 270: 19462306a36Sopenharmony_ci if (cpu_is_omap15xx()) { 19562306a36Sopenharmony_ci BUG(); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci esize = 2; 19862306a36Sopenharmony_ci xelem = lcdc.yres * lcdc.bpp / 16; 19962306a36Sopenharmony_ci yelem = lcdc.xres; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci default: 20262306a36Sopenharmony_ci BUG(); 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci#ifdef VERBOSE 20662306a36Sopenharmony_ci dev_dbg(lcdc.fbdev->dev, 20762306a36Sopenharmony_ci "setup_dma: src %#010lx esize %d xelem %d yelem %d\n", 20862306a36Sopenharmony_ci src, esize, xelem, yelem); 20962306a36Sopenharmony_ci#endif 21062306a36Sopenharmony_ci omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]); 21162306a36Sopenharmony_ci if (!cpu_is_omap15xx()) { 21262306a36Sopenharmony_ci int bpp = lcdc.bpp; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * YUV support is only for external mode when we have the 21662306a36Sopenharmony_ci * YUV window embedded in a 16bpp frame buffer. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci if (lcdc.color_mode == OMAPFB_COLOR_YUV420) 21962306a36Sopenharmony_ci bpp = 16; 22062306a36Sopenharmony_ci /* Set virtual xres elem size */ 22162306a36Sopenharmony_ci omap_set_lcd_dma_b1_vxres( 22262306a36Sopenharmony_ci lcdc.screen_width * bpp / 8 / esize); 22362306a36Sopenharmony_ci /* Setup transformations */ 22462306a36Sopenharmony_ci omap_set_lcd_dma_b1_rotation(var->rotate); 22562306a36Sopenharmony_ci omap_set_lcd_dma_b1_mirror(plane->info.mirror); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci omap_setup_lcd_dma(); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic irqreturn_t lcdc_irq_handler(int irq, void *dev_id) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u32 status; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci status = omap_readl(OMAP_LCDC_STATUS); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST)) 23762306a36Sopenharmony_ci reset_controller(status); 23862306a36Sopenharmony_ci else { 23962306a36Sopenharmony_ci if (status & OMAP_LCDC_STAT_DONE) { 24062306a36Sopenharmony_ci u32 l; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* 24362306a36Sopenharmony_ci * Disable IRQ_DONE. The status bit will be cleared 24462306a36Sopenharmony_ci * only when the controller is reenabled and we don't 24562306a36Sopenharmony_ci * want to get more interrupts. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_CONTROL); 24862306a36Sopenharmony_ci l &= ~OMAP_LCDC_IRQ_DONE; 24962306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 25062306a36Sopenharmony_ci complete(&lcdc.last_frame_complete); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (status & OMAP_LCDC_STAT_LOADED_PALETTE) { 25362306a36Sopenharmony_ci disable_controller_async(); 25462306a36Sopenharmony_ci complete(&lcdc.palette_load_complete); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Clear these interrupt status bits. 26062306a36Sopenharmony_ci * Sync_lost, FUF bits were cleared by disabling the LCD controller 26162306a36Sopenharmony_ci * LOADED_PALETTE can be cleared this way only in palette only 26262306a36Sopenharmony_ci * load mode. In other load modes it's cleared by disabling the 26362306a36Sopenharmony_ci * controller. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci status &= ~(OMAP_LCDC_STAT_VSYNC | 26662306a36Sopenharmony_ci OMAP_LCDC_STAT_LOADED_PALETTE | 26762306a36Sopenharmony_ci OMAP_LCDC_STAT_ABC | 26862306a36Sopenharmony_ci OMAP_LCDC_STAT_LINE_INT); 26962306a36Sopenharmony_ci omap_writel(status, OMAP_LCDC_STATUS); 27062306a36Sopenharmony_ci return IRQ_HANDLED; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* 27462306a36Sopenharmony_ci * Change to a new video mode. We defer this to a later time to avoid any 27562306a36Sopenharmony_ci * flicker and not to mess up the current LCD DMA context. For this we disable 27662306a36Sopenharmony_ci * the LCD controller, which will generate a DONE irq after the last frame has 27762306a36Sopenharmony_ci * been transferred. Then it'll be safe to reconfigure both the LCD controller 27862306a36Sopenharmony_ci * as well as the LCD DMA. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic int omap_lcdc_setup_plane(int plane, int channel_out, 28162306a36Sopenharmony_ci unsigned long offset, int screen_width, 28262306a36Sopenharmony_ci int pos_x, int pos_y, int width, int height, 28362306a36Sopenharmony_ci int color_mode) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var; 28662306a36Sopenharmony_ci struct lcd_panel *panel = lcdc.fbdev->panel; 28762306a36Sopenharmony_ci int rot_x, rot_y; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (var->rotate == 0) { 29062306a36Sopenharmony_ci rot_x = panel->x_res; 29162306a36Sopenharmony_ci rot_y = panel->y_res; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci rot_x = panel->y_res; 29462306a36Sopenharmony_ci rot_y = panel->x_res; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 || 29762306a36Sopenharmony_ci width > rot_x || height > rot_y) { 29862306a36Sopenharmony_ci#ifdef VERBOSE 29962306a36Sopenharmony_ci dev_dbg(lcdc.fbdev->dev, 30062306a36Sopenharmony_ci "invalid plane params plane %d pos_x %d pos_y %d " 30162306a36Sopenharmony_ci "w %d h %d\n", plane, pos_x, pos_y, width, height); 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci lcdc.frame_offset = offset; 30762306a36Sopenharmony_ci lcdc.xres = width; 30862306a36Sopenharmony_ci lcdc.yres = height; 30962306a36Sopenharmony_ci lcdc.screen_width = screen_width; 31062306a36Sopenharmony_ci lcdc.color_mode = color_mode; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci switch (color_mode) { 31362306a36Sopenharmony_ci case OMAPFB_COLOR_CLUT_8BPP: 31462306a36Sopenharmony_ci lcdc.bpp = 8; 31562306a36Sopenharmony_ci lcdc.palette_code = 0x3000; 31662306a36Sopenharmony_ci lcdc.palette_size = 512; 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci case OMAPFB_COLOR_RGB565: 31962306a36Sopenharmony_ci lcdc.bpp = 16; 32062306a36Sopenharmony_ci lcdc.palette_code = 0x4000; 32162306a36Sopenharmony_ci lcdc.palette_size = 32; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case OMAPFB_COLOR_RGB444: 32462306a36Sopenharmony_ci lcdc.bpp = 16; 32562306a36Sopenharmony_ci lcdc.palette_code = 0x4000; 32662306a36Sopenharmony_ci lcdc.palette_size = 32; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci case OMAPFB_COLOR_YUV420: 32962306a36Sopenharmony_ci if (lcdc.ext_mode) { 33062306a36Sopenharmony_ci lcdc.bpp = 12; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci fallthrough; 33462306a36Sopenharmony_ci case OMAPFB_COLOR_YUV422: 33562306a36Sopenharmony_ci if (lcdc.ext_mode) { 33662306a36Sopenharmony_ci lcdc.bpp = 16; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci fallthrough; 34062306a36Sopenharmony_ci default: 34162306a36Sopenharmony_ci /* FIXME: other BPPs. 34262306a36Sopenharmony_ci * bpp1: code 0, size 256 34362306a36Sopenharmony_ci * bpp2: code 0x1000 size 256 34462306a36Sopenharmony_ci * bpp4: code 0x2000 size 256 34562306a36Sopenharmony_ci * bpp12: code 0x4000 size 32 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode); 34862306a36Sopenharmony_ci BUG(); 34962306a36Sopenharmony_ci return -1; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (lcdc.ext_mode) { 35362306a36Sopenharmony_ci setup_lcd_dma(); 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) { 35862306a36Sopenharmony_ci disable_controller(); 35962306a36Sopenharmony_ci omap_stop_lcd_dma(); 36062306a36Sopenharmony_ci setup_lcd_dma(); 36162306a36Sopenharmony_ci enable_controller(); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int omap_lcdc_enable_plane(int plane, int enable) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci dev_dbg(lcdc.fbdev->dev, 37062306a36Sopenharmony_ci "plane %d enable %d update_mode %d ext_mode %d\n", 37162306a36Sopenharmony_ci plane, enable, lcdc.update_mode, lcdc.ext_mode); 37262306a36Sopenharmony_ci if (plane != OMAPFB_PLANE_GFX) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * Configure the LCD DMA for a palette load operation and do the palette 38062306a36Sopenharmony_ci * downloading synchronously. We don't use the frame+palette load mode of 38162306a36Sopenharmony_ci * the controller, since the palette can always be downloaded separately. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic void load_palette(void) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci u16 *palette; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci palette = (u16 *)lcdc.palette_virt; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci *(u16 *)palette &= 0x0fff; 39062306a36Sopenharmony_ci *(u16 *)palette |= lcdc.palette_code; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci omap_set_lcd_dma_b1(lcdc.palette_phys, 39362306a36Sopenharmony_ci lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci omap_set_lcd_dma_single_transfer(1); 39662306a36Sopenharmony_ci omap_setup_lcd_dma(); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci init_completion(&lcdc.palette_load_complete); 39962306a36Sopenharmony_ci enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); 40062306a36Sopenharmony_ci set_load_mode(OMAP_LCDC_LOAD_PALETTE); 40162306a36Sopenharmony_ci enable_controller(); 40262306a36Sopenharmony_ci if (!wait_for_completion_timeout(&lcdc.palette_load_complete, 40362306a36Sopenharmony_ci msecs_to_jiffies(500))) 40462306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n"); 40562306a36Sopenharmony_ci /* The controller gets disabled in the irq handler */ 40662306a36Sopenharmony_ci disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); 40762306a36Sopenharmony_ci omap_stop_lcd_dma(); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci omap_set_lcd_dma_single_transfer(lcdc.ext_mode); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/* Used only in internal controller mode */ 41362306a36Sopenharmony_cistatic int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue, 41462306a36Sopenharmony_ci u16 transp, int update_hw_pal) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci u16 *palette; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255) 41962306a36Sopenharmony_ci return -EINVAL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci palette = (u16 *)lcdc.palette_virt; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci palette[regno] &= ~0x0fff; 42462306a36Sopenharmony_ci palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) | 42562306a36Sopenharmony_ci (blue >> 12); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (update_hw_pal) { 42862306a36Sopenharmony_ci disable_controller(); 42962306a36Sopenharmony_ci omap_stop_lcd_dma(); 43062306a36Sopenharmony_ci load_palette(); 43162306a36Sopenharmony_ci setup_lcd_dma(); 43262306a36Sopenharmony_ci set_load_mode(OMAP_LCDC_LOAD_FRAME); 43362306a36Sopenharmony_ci enable_controller(); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void calc_ck_div(int is_tft, int pck, int *pck_div) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci unsigned long lck; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pck = max(1, pck); 44462306a36Sopenharmony_ci lck = clk_get_rate(lcdc.lcd_ck); 44562306a36Sopenharmony_ci *pck_div = (lck + pck - 1) / pck; 44662306a36Sopenharmony_ci if (is_tft) 44762306a36Sopenharmony_ci *pck_div = max(2, *pck_div); 44862306a36Sopenharmony_ci else 44962306a36Sopenharmony_ci *pck_div = max(3, *pck_div); 45062306a36Sopenharmony_ci if (*pck_div > 255) { 45162306a36Sopenharmony_ci /* FIXME: try to adjust logic clock divider as well */ 45262306a36Sopenharmony_ci *pck_div = 255; 45362306a36Sopenharmony_ci dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n", 45462306a36Sopenharmony_ci pck / 1000); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic inline void setup_regs(void) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci u32 l; 46162306a36Sopenharmony_ci struct lcd_panel *panel = lcdc.fbdev->panel; 46262306a36Sopenharmony_ci int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; 46362306a36Sopenharmony_ci unsigned long lck; 46462306a36Sopenharmony_ci int pcd; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_CONTROL); 46762306a36Sopenharmony_ci l &= ~OMAP_LCDC_CTRL_LCD_TFT; 46862306a36Sopenharmony_ci l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0; 46962306a36Sopenharmony_ci#ifdef CONFIG_MACH_OMAP_PALMTE 47062306a36Sopenharmony_ci/* FIXME:if (machine_is_omap_palmte()) { */ 47162306a36Sopenharmony_ci /* PalmTE uses alternate TFT setting in 8BPP mode */ 47262306a36Sopenharmony_ci l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0; 47362306a36Sopenharmony_ci/* } */ 47462306a36Sopenharmony_ci#endif 47562306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_TIMING2); 47862306a36Sopenharmony_ci l &= ~(((1 << 6) - 1) << 20); 47962306a36Sopenharmony_ci l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20; 48062306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_TIMING2); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci l = panel->x_res - 1; 48362306a36Sopenharmony_ci l |= (panel->hsw - 1) << 10; 48462306a36Sopenharmony_ci l |= (panel->hfp - 1) << 16; 48562306a36Sopenharmony_ci l |= (panel->hbp - 1) << 24; 48662306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_TIMING0); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci l = panel->y_res - 1; 48962306a36Sopenharmony_ci l |= (panel->vsw - 1) << 10; 49062306a36Sopenharmony_ci l |= panel->vfp << 16; 49162306a36Sopenharmony_ci l |= panel->vbp << 24; 49262306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_TIMING1); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci l = omap_readl(OMAP_LCDC_TIMING2); 49562306a36Sopenharmony_ci l &= ~0xff; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci lck = clk_get_rate(lcdc.lcd_ck); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!panel->pcd) 50062306a36Sopenharmony_ci calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd); 50162306a36Sopenharmony_ci else { 50262306a36Sopenharmony_ci dev_warn(lcdc.fbdev->dev, 50362306a36Sopenharmony_ci "Pixel clock divider value is obsolete.\n" 50462306a36Sopenharmony_ci "Try to set pixel_clock to %lu and pcd to 0 " 50562306a36Sopenharmony_ci "in drivers/video/omap/lcd_%s.c and submit a patch.\n", 50662306a36Sopenharmony_ci lck / panel->pcd / 1000, panel->name); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci pcd = panel->pcd; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci l |= pcd & 0xff; 51162306a36Sopenharmony_ci l |= panel->acb << 8; 51262306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_TIMING2); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* update panel info with the exact clock */ 51562306a36Sopenharmony_ci panel->pixel_clock = lck / pcd / 1000; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* 51962306a36Sopenharmony_ci * Configure the LCD controller, download the color palette and start a looped 52062306a36Sopenharmony_ci * DMA transfer of the frame image data. Called only in internal 52162306a36Sopenharmony_ci * controller mode. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_cistatic int omap_lcdc_set_update_mode(enum omapfb_update_mode mode) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int r = 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (mode != lcdc.update_mode) { 52862306a36Sopenharmony_ci switch (mode) { 52962306a36Sopenharmony_ci case OMAPFB_AUTO_UPDATE: 53062306a36Sopenharmony_ci setup_regs(); 53162306a36Sopenharmony_ci load_palette(); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Setup and start LCD DMA */ 53462306a36Sopenharmony_ci setup_lcd_dma(); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci set_load_mode(OMAP_LCDC_LOAD_FRAME); 53762306a36Sopenharmony_ci enable_irqs(OMAP_LCDC_IRQ_DONE); 53862306a36Sopenharmony_ci /* This will start the actual DMA transfer */ 53962306a36Sopenharmony_ci enable_controller(); 54062306a36Sopenharmony_ci lcdc.update_mode = mode; 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci case OMAPFB_UPDATE_DISABLED: 54362306a36Sopenharmony_ci disable_controller(); 54462306a36Sopenharmony_ci omap_stop_lcd_dma(); 54562306a36Sopenharmony_ci lcdc.update_mode = mode; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci default: 54862306a36Sopenharmony_ci r = -EINVAL; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return r; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic enum omapfb_update_mode omap_lcdc_get_update_mode(void) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci return lcdc.update_mode; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* PM code called only in internal controller mode */ 56162306a36Sopenharmony_cistatic void omap_lcdc_suspend(void) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci omap_lcdc_set_update_mode(OMAPFB_UPDATE_DISABLED); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void omap_lcdc_resume(void) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci omap_lcdc_set_update_mode(OMAPFB_AUTO_UPDATE); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic void omap_lcdc_get_caps(int plane, struct omapfb_caps *caps) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci return; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciint omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci BUG_ON(callback == NULL); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (lcdc.dma_callback) 58162306a36Sopenharmony_ci return -EBUSY; 58262306a36Sopenharmony_ci else { 58362306a36Sopenharmony_ci lcdc.dma_callback = callback; 58462306a36Sopenharmony_ci lcdc.dma_callback_data = data; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_lcdc_set_dma_callback); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_civoid omap_lcdc_free_dma_callback(void) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci lcdc.dma_callback = NULL; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ciEXPORT_SYMBOL(omap_lcdc_free_dma_callback); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void lcdc_dma_handler(u16 status, void *data) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci if (lcdc.dma_callback) 59962306a36Sopenharmony_ci lcdc.dma_callback(lcdc.dma_callback_data); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int alloc_palette_ram(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci lcdc.palette_virt = dma_alloc_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE, 60562306a36Sopenharmony_ci &lcdc.palette_phys, GFP_KERNEL); 60662306a36Sopenharmony_ci if (lcdc.palette_virt == NULL) { 60762306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n"); 60862306a36Sopenharmony_ci return -ENOMEM; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic void free_palette_ram(void) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci dma_free_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE, lcdc.palette_virt, 61862306a36Sopenharmony_ci lcdc.palette_phys); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int alloc_fbmem(struct omapfb_mem_region *region) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int bpp; 62462306a36Sopenharmony_ci int frame_size; 62562306a36Sopenharmony_ci struct lcd_panel *panel = lcdc.fbdev->panel; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci bpp = panel->bpp; 62862306a36Sopenharmony_ci if (bpp == 12) 62962306a36Sopenharmony_ci bpp = 16; 63062306a36Sopenharmony_ci frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res); 63162306a36Sopenharmony_ci if (region->size > frame_size) 63262306a36Sopenharmony_ci frame_size = region->size; 63362306a36Sopenharmony_ci lcdc.vram_size = frame_size; 63462306a36Sopenharmony_ci lcdc.vram_virt = dma_alloc_wc(lcdc.fbdev->dev, lcdc.vram_size, 63562306a36Sopenharmony_ci &lcdc.vram_phys, GFP_KERNEL); 63662306a36Sopenharmony_ci if (lcdc.vram_virt == NULL) { 63762306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n"); 63862306a36Sopenharmony_ci return -ENOMEM; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci region->size = frame_size; 64162306a36Sopenharmony_ci region->paddr = lcdc.vram_phys; 64262306a36Sopenharmony_ci region->vaddr = lcdc.vram_virt; 64362306a36Sopenharmony_ci region->alloc = 1; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci memset(lcdc.vram_virt, 0, lcdc.vram_size); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void free_fbmem(void) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci dma_free_wc(lcdc.fbdev->dev, lcdc.vram_size, lcdc.vram_virt, 65362306a36Sopenharmony_ci lcdc.vram_phys); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int setup_fbmem(struct omapfb_mem_desc *req_md) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci if (!req_md->region_cnt) { 65962306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "no memory regions defined\n"); 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (req_md->region_cnt > 1) { 66462306a36Sopenharmony_ci dev_err(lcdc.fbdev->dev, "only one plane is supported\n"); 66562306a36Sopenharmony_ci req_md->region_cnt = 1; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return alloc_fbmem(&req_md->region[0]); 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, 67262306a36Sopenharmony_ci struct omapfb_mem_desc *req_vram) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci int r; 67562306a36Sopenharmony_ci u32 l; 67662306a36Sopenharmony_ci int rate; 67762306a36Sopenharmony_ci struct clk *tc_ck; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci lcdc.irq_mask = 0; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci lcdc.fbdev = fbdev; 68262306a36Sopenharmony_ci lcdc.ext_mode = ext_mode; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci l = 0; 68562306a36Sopenharmony_ci omap_writel(l, OMAP_LCDC_CONTROL); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* FIXME: 68862306a36Sopenharmony_ci * According to errata some platforms have a clock rate limitiation 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci lcdc.lcd_ck = clk_get(fbdev->dev, "lcd_ck"); 69162306a36Sopenharmony_ci if (IS_ERR(lcdc.lcd_ck)) { 69262306a36Sopenharmony_ci dev_err(fbdev->dev, "unable to access LCD clock\n"); 69362306a36Sopenharmony_ci r = PTR_ERR(lcdc.lcd_ck); 69462306a36Sopenharmony_ci goto fail0; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci tc_ck = clk_get(fbdev->dev, "tc_ck"); 69862306a36Sopenharmony_ci if (IS_ERR(tc_ck)) { 69962306a36Sopenharmony_ci dev_err(fbdev->dev, "unable to access TC clock\n"); 70062306a36Sopenharmony_ci r = PTR_ERR(tc_ck); 70162306a36Sopenharmony_ci goto fail1; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci rate = clk_get_rate(tc_ck); 70562306a36Sopenharmony_ci clk_put(tc_ck); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (machine_is_ams_delta()) 70862306a36Sopenharmony_ci rate /= 4; 70962306a36Sopenharmony_ci r = clk_set_rate(lcdc.lcd_ck, rate); 71062306a36Sopenharmony_ci if (r) { 71162306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to adjust LCD rate\n"); 71262306a36Sopenharmony_ci goto fail1; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci clk_prepare_enable(lcdc.lcd_ck); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci r = request_irq(fbdev->int_irq, lcdc_irq_handler, 0, MODULE_NAME, fbdev); 71762306a36Sopenharmony_ci if (r) { 71862306a36Sopenharmony_ci dev_err(fbdev->dev, "unable to get IRQ\n"); 71962306a36Sopenharmony_ci goto fail2; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci r = omap_request_lcd_dma(lcdc_dma_handler, NULL); 72362306a36Sopenharmony_ci if (r) { 72462306a36Sopenharmony_ci dev_err(fbdev->dev, "unable to get LCD DMA\n"); 72562306a36Sopenharmony_ci goto fail3; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci omap_set_lcd_dma_single_transfer(ext_mode); 72962306a36Sopenharmony_ci omap_set_lcd_dma_ext_controller(ext_mode); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (!ext_mode) 73262306a36Sopenharmony_ci if ((r = alloc_palette_ram()) < 0) 73362306a36Sopenharmony_ci goto fail4; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if ((r = setup_fbmem(req_vram)) < 0) 73662306a36Sopenharmony_ci goto fail5; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci pr_info("omapfb: LCDC initialized\n"); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return 0; 74162306a36Sopenharmony_cifail5: 74262306a36Sopenharmony_ci if (!ext_mode) 74362306a36Sopenharmony_ci free_palette_ram(); 74462306a36Sopenharmony_cifail4: 74562306a36Sopenharmony_ci omap_free_lcd_dma(); 74662306a36Sopenharmony_cifail3: 74762306a36Sopenharmony_ci free_irq(fbdev->int_irq, lcdc.fbdev); 74862306a36Sopenharmony_cifail2: 74962306a36Sopenharmony_ci clk_disable_unprepare(lcdc.lcd_ck); 75062306a36Sopenharmony_cifail1: 75162306a36Sopenharmony_ci clk_put(lcdc.lcd_ck); 75262306a36Sopenharmony_cifail0: 75362306a36Sopenharmony_ci return r; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic void omap_lcdc_cleanup(void) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci if (!lcdc.ext_mode) 75962306a36Sopenharmony_ci free_palette_ram(); 76062306a36Sopenharmony_ci free_fbmem(); 76162306a36Sopenharmony_ci omap_free_lcd_dma(); 76262306a36Sopenharmony_ci free_irq(lcdc.fbdev->int_irq, lcdc.fbdev); 76362306a36Sopenharmony_ci clk_disable_unprepare(lcdc.lcd_ck); 76462306a36Sopenharmony_ci clk_put(lcdc.lcd_ck); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciconst struct lcd_ctrl omap1_int_ctrl = { 76862306a36Sopenharmony_ci .name = "internal", 76962306a36Sopenharmony_ci .init = omap_lcdc_init, 77062306a36Sopenharmony_ci .cleanup = omap_lcdc_cleanup, 77162306a36Sopenharmony_ci .get_caps = omap_lcdc_get_caps, 77262306a36Sopenharmony_ci .set_update_mode = omap_lcdc_set_update_mode, 77362306a36Sopenharmony_ci .get_update_mode = omap_lcdc_get_update_mode, 77462306a36Sopenharmony_ci .update_window = NULL, 77562306a36Sopenharmony_ci .suspend = omap_lcdc_suspend, 77662306a36Sopenharmony_ci .resume = omap_lcdc_resume, 77762306a36Sopenharmony_ci .setup_plane = omap_lcdc_setup_plane, 77862306a36Sopenharmony_ci .enable_plane = omap_lcdc_enable_plane, 77962306a36Sopenharmony_ci .setcolreg = omap_lcdc_setcolreg, 78062306a36Sopenharmony_ci}; 781