445 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/arch/arm/mach-omap1/lcd_dma.c4 *5 * Extracted from arch/arm/plat-omap/dma.c6 * Copyright (C) 2003 - 2008 Nokia Corporation7 * Author: Juha Yrjölä <juha.yrjola@nokia.com>8 * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com>9 * Graphics DMA and LCD DMA graphics tranformations10 * by Imre Deak <imre.deak@nokia.com>11 * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc.12 * Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com>13 * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc.14 *15 * Copyright (C) 2009 Texas Instruments16 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>17 *18 * Support functions for the OMAP internal DMA channels.19 */20 21#include <linux/module.h>22#include <linux/spinlock.h>23#include <linux/interrupt.h>24#include <linux/io.h>25 26#include <linux/omap-dma.h>27 28#include <linux/soc/ti/omap1-soc.h>29#include <linux/soc/ti/omap1-io.h>30 31#include "lcdc.h"32#include "lcd_dma.h"33 34int omap_lcd_dma_running(void)35{36 /*37 * On OMAP1510, internal LCD controller will start the transfer38 * when it gets enabled, so assume DMA running if LCD enabled.39 */40 if (cpu_is_omap15xx())41 if (omap_readw(OMAP_LCDC_CONTROL) & OMAP_LCDC_CTRL_LCD_EN)42 return 1;43 44 /* Check if LCD DMA is running */45 if (cpu_is_omap16xx())46 if (omap_readw(OMAP1610_DMA_LCD_CCR) & OMAP_DMA_CCR_EN)47 return 1;48 49 return 0;50}51 52static struct lcd_dma_info {53 spinlock_t lock;54 int reserved;55 void (*callback)(u16 status, void *data);56 void *cb_data;57 58 int active;59 unsigned long addr;60 int rotate, data_type, xres, yres;61 int vxres;62 int mirror;63 int xscale, yscale;64 int ext_ctrl;65 int src_port;66 int single_transfer;67} lcd_dma;68 69void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres,70 int data_type)71{72 lcd_dma.addr = addr;73 lcd_dma.data_type = data_type;74 lcd_dma.xres = fb_xres;75 lcd_dma.yres = fb_yres;76}77EXPORT_SYMBOL(omap_set_lcd_dma_b1);78 79void omap_set_lcd_dma_ext_controller(int external)80{81 lcd_dma.ext_ctrl = external;82}83EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller);84 85void omap_set_lcd_dma_single_transfer(int single)86{87 lcd_dma.single_transfer = single;88}89EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer);90 91void omap_set_lcd_dma_b1_rotation(int rotate)92{93 if (cpu_is_omap15xx()) {94 printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n");95 BUG();96 return;97 }98 lcd_dma.rotate = rotate;99}100EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation);101 102void omap_set_lcd_dma_b1_mirror(int mirror)103{104 if (cpu_is_omap15xx()) {105 printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n");106 BUG();107 }108 lcd_dma.mirror = mirror;109}110EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror);111 112void omap_set_lcd_dma_b1_vxres(unsigned long vxres)113{114 if (cpu_is_omap15xx()) {115 pr_err("DMA virtual resolution is not supported in 1510 mode\n");116 BUG();117 }118 lcd_dma.vxres = vxres;119}120EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres);121 122void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale)123{124 if (cpu_is_omap15xx()) {125 printk(KERN_ERR "DMA scale is not supported in 1510 mode\n");126 BUG();127 }128 lcd_dma.xscale = xscale;129 lcd_dma.yscale = yscale;130}131EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale);132 133static void set_b1_regs(void)134{135 unsigned long top, bottom;136 int es;137 u16 w;138 unsigned long en, fn;139 long ei, fi;140 unsigned long vxres;141 unsigned int xscale, yscale;142 143 switch (lcd_dma.data_type) {144 case OMAP_DMA_DATA_TYPE_S8:145 es = 1;146 break;147 case OMAP_DMA_DATA_TYPE_S16:148 es = 2;149 break;150 case OMAP_DMA_DATA_TYPE_S32:151 es = 4;152 break;153 default:154 BUG();155 return;156 }157 158 vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres;159 xscale = lcd_dma.xscale ? lcd_dma.xscale : 1;160 yscale = lcd_dma.yscale ? lcd_dma.yscale : 1;161 BUG_ON(vxres < lcd_dma.xres);162 163#define PIXADDR(x, y) (lcd_dma.addr + \164 ((y) * vxres * yscale + (x) * xscale) * es)165#define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1)166 167 switch (lcd_dma.rotate) {168 case 0:169 if (!lcd_dma.mirror) {170 top = PIXADDR(0, 0);171 bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);172 /* 1510 DMA requires the bottom address to be 2 more173 * than the actual last memory access location. */174 if (cpu_is_omap15xx() &&175 lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32)176 bottom += 2;177 ei = PIXSTEP(0, 0, 1, 0);178 fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1);179 } else {180 top = PIXADDR(lcd_dma.xres - 1, 0);181 bottom = PIXADDR(0, lcd_dma.yres - 1);182 ei = PIXSTEP(1, 0, 0, 0);183 fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1);184 }185 en = lcd_dma.xres;186 fn = lcd_dma.yres;187 break;188 case 90:189 if (!lcd_dma.mirror) {190 top = PIXADDR(0, lcd_dma.yres - 1);191 bottom = PIXADDR(lcd_dma.xres - 1, 0);192 ei = PIXSTEP(0, 1, 0, 0);193 fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1);194 } else {195 top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);196 bottom = PIXADDR(0, 0);197 ei = PIXSTEP(0, 1, 0, 0);198 fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1);199 }200 en = lcd_dma.yres;201 fn = lcd_dma.xres;202 break;203 case 180:204 if (!lcd_dma.mirror) {205 top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);206 bottom = PIXADDR(0, 0);207 ei = PIXSTEP(1, 0, 0, 0);208 fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0);209 } else {210 top = PIXADDR(0, lcd_dma.yres - 1);211 bottom = PIXADDR(lcd_dma.xres - 1, 0);212 ei = PIXSTEP(0, 0, 1, 0);213 fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0);214 }215 en = lcd_dma.xres;216 fn = lcd_dma.yres;217 break;218 case 270:219 if (!lcd_dma.mirror) {220 top = PIXADDR(lcd_dma.xres - 1, 0);221 bottom = PIXADDR(0, lcd_dma.yres - 1);222 ei = PIXSTEP(0, 0, 0, 1);223 fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0);224 } else {225 top = PIXADDR(0, 0);226 bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1);227 ei = PIXSTEP(0, 0, 0, 1);228 fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0);229 }230 en = lcd_dma.yres;231 fn = lcd_dma.xres;232 break;233 default:234 BUG();235 return; /* Suppress warning about uninitialized vars */236 }237 238 if (cpu_is_omap15xx()) {239 omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U);240 omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L);241 omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U);242 omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L);243 244 return;245 }246 247 /* 1610 regs */248 omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U);249 omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L);250 omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U);251 omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L);252 253 omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1);254 omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1);255 256 w = omap_readw(OMAP1610_DMA_LCD_CSDP);257 w &= ~0x03;258 w |= lcd_dma.data_type;259 omap_writew(w, OMAP1610_DMA_LCD_CSDP);260 261 w = omap_readw(OMAP1610_DMA_LCD_CTRL);262 /* Always set the source port as SDRAM for now*/263 w &= ~(0x03 << 6);264 if (lcd_dma.callback != NULL)265 w |= 1 << 1; /* Block interrupt enable */266 else267 w &= ~(1 << 1);268 omap_writew(w, OMAP1610_DMA_LCD_CTRL);269 270 if (!(lcd_dma.rotate || lcd_dma.mirror ||271 lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale))272 return;273 274 w = omap_readw(OMAP1610_DMA_LCD_CCR);275 /* Set the double-indexed addressing mode */276 w |= (0x03 << 12);277 omap_writew(w, OMAP1610_DMA_LCD_CCR);278 279 omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1);280 omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U);281 omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L);282}283 284static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id)285{286 u16 w;287 288 w = omap_readw(OMAP1610_DMA_LCD_CTRL);289 if (unlikely(!(w & (1 << 3)))) {290 printk(KERN_WARNING "Spurious LCD DMA IRQ\n");291 return IRQ_NONE;292 }293 /* Ack the IRQ */294 w |= (1 << 3);295 omap_writew(w, OMAP1610_DMA_LCD_CTRL);296 lcd_dma.active = 0;297 if (lcd_dma.callback != NULL)298 lcd_dma.callback(w, lcd_dma.cb_data);299 300 return IRQ_HANDLED;301}302 303int omap_request_lcd_dma(void (*callback)(u16 status, void *data),304 void *data)305{306 spin_lock_irq(&lcd_dma.lock);307 if (lcd_dma.reserved) {308 spin_unlock_irq(&lcd_dma.lock);309 printk(KERN_ERR "LCD DMA channel already reserved\n");310 BUG();311 return -EBUSY;312 }313 lcd_dma.reserved = 1;314 spin_unlock_irq(&lcd_dma.lock);315 lcd_dma.callback = callback;316 lcd_dma.cb_data = data;317 lcd_dma.active = 0;318 lcd_dma.single_transfer = 0;319 lcd_dma.rotate = 0;320 lcd_dma.vxres = 0;321 lcd_dma.mirror = 0;322 lcd_dma.xscale = 0;323 lcd_dma.yscale = 0;324 lcd_dma.ext_ctrl = 0;325 lcd_dma.src_port = 0;326 327 return 0;328}329EXPORT_SYMBOL(omap_request_lcd_dma);330 331void omap_free_lcd_dma(void)332{333 spin_lock(&lcd_dma.lock);334 if (!lcd_dma.reserved) {335 spin_unlock(&lcd_dma.lock);336 printk(KERN_ERR "LCD DMA is not reserved\n");337 BUG();338 return;339 }340 if (!cpu_is_omap15xx())341 omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1,342 OMAP1610_DMA_LCD_CCR);343 lcd_dma.reserved = 0;344 spin_unlock(&lcd_dma.lock);345}346EXPORT_SYMBOL(omap_free_lcd_dma);347 348void omap_enable_lcd_dma(void)349{350 u16 w;351 352 /*353 * Set the Enable bit only if an external controller is354 * connected. Otherwise the OMAP internal controller will355 * start the transfer when it gets enabled.356 */357 if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl)358 return;359 360 w = omap_readw(OMAP1610_DMA_LCD_CTRL);361 w |= 1 << 8;362 omap_writew(w, OMAP1610_DMA_LCD_CTRL);363 364 lcd_dma.active = 1;365 366 w = omap_readw(OMAP1610_DMA_LCD_CCR);367 w |= 1 << 7;368 omap_writew(w, OMAP1610_DMA_LCD_CCR);369}370EXPORT_SYMBOL(omap_enable_lcd_dma);371 372void omap_setup_lcd_dma(void)373{374 BUG_ON(lcd_dma.active);375 if (!cpu_is_omap15xx()) {376 /* Set some reasonable defaults */377 omap_writew(0x5440, OMAP1610_DMA_LCD_CCR);378 omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP);379 omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL);380 }381 set_b1_regs();382 if (!cpu_is_omap15xx()) {383 u16 w;384 385 w = omap_readw(OMAP1610_DMA_LCD_CCR);386 /*387 * If DMA was already active set the end_prog bit to have388 * the programmed register set loaded into the active389 * register set.390 */391 w |= 1 << 11; /* End_prog */392 if (!lcd_dma.single_transfer)393 w |= (3 << 8); /* Auto_init, repeat */394 omap_writew(w, OMAP1610_DMA_LCD_CCR);395 }396}397EXPORT_SYMBOL(omap_setup_lcd_dma);398 399void omap_stop_lcd_dma(void)400{401 u16 w;402 403 lcd_dma.active = 0;404 if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl)405 return;406 407 w = omap_readw(OMAP1610_DMA_LCD_CCR);408 w &= ~(1 << 7);409 omap_writew(w, OMAP1610_DMA_LCD_CCR);410 411 w = omap_readw(OMAP1610_DMA_LCD_CTRL);412 w &= ~(1 << 8);413 omap_writew(w, OMAP1610_DMA_LCD_CTRL);414}415EXPORT_SYMBOL(omap_stop_lcd_dma);416 417static int __init omap_init_lcd_dma(void)418{419 int r;420 421 if (!cpu_class_is_omap1())422 return -ENODEV;423 424 if (cpu_is_omap16xx()) {425 u16 w;426 427 /* this would prevent OMAP sleep */428 w = omap_readw(OMAP1610_DMA_LCD_CTRL);429 w &= ~(1 << 8);430 omap_writew(w, OMAP1610_DMA_LCD_CTRL);431 }432 433 spin_lock_init(&lcd_dma.lock);434 435 r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0,436 "LCD DMA", NULL);437 if (r != 0)438 pr_err("unable to request IRQ for LCD DMA (error %d)\n", r);439 440 return r;441}442 443arch_initcall(omap_init_lcd_dma);444 445